Java實(shí)現(xiàn)經(jīng)典游戲2048的示例代碼
前言
2014年Gabriele Cirulli利用周末的時(shí)間寫2048這個(gè)游戲的程序,僅僅只是好玩而已。他想用一種不同的視覺展現(xiàn)效果和更快速的動(dòng)畫來創(chuàng)造屬于自己的游戲版本。
游戲是用java語言實(shí)現(xiàn),采用了swing技術(shù)進(jìn)行了界面化處理,設(shè)計(jì)思路用了面向?qū)ο笏枷搿?/p>
主要需求
每次控制所有方塊向同一個(gè)方向運(yùn)動(dòng),兩個(gè)相同數(shù)字的方塊撞在一起之后合并成為他們的和,每次操作之后會(huì)在空白的方格處隨機(jī)生成一個(gè)2或者4,最終得到一個(gè)“2048”的方塊就算勝利了。如果16個(gè)格子全部填滿并且相鄰的格子都不相同也就是無法移動(dòng)的話,那么游戲就會(huì)結(jié)束。
主要設(shè)計(jì)
1、游戲面板生成顯示
2、方塊設(shè)計(jì)
3、鍵盤監(jiān)聽,方向鍵控制數(shù)字移動(dòng)
4、數(shù)字移動(dòng)邏輯算法處理
5、數(shù)字累加到2048,游戲勝利
功能截圖
游戲開始

移動(dòng)效果

代碼實(shí)現(xiàn)
界面布局類
public class Game2048View implements ActionListener{
Block[] blocks; //方塊
JPanel myJPanel; //主面板
JPanel jp1,jp2; //子面板
// int moveFlag; // 用于累計(jì)移動(dòng)的次數(shù)
boolean numFlag; // 用于判斷是否還能加入新的數(shù)字
JLabel scroeValue; //顯示分?jǐn)?shù)
public Game2048View(JFrame myJFrame){
blocks=new Block[16];
// moveFlag=0;
numFlag=true;
this.myJPanel=(JPanel)myJFrame.getContentPane();///獲取內(nèi)容面板
setJp1();
myJPanel.add(jp1,BorderLayout.NORTH);
setJp2();
myJPanel.add(jp2, BorderLayout.CENTER);
myJFrame.addKeyListener(new Game2048Logic(this,myJFrame,blocks,numFlag,scroeValue));
}
public void addc(JPanel jp1,Component component, GridBagConstraints gbc,int gridwidth,int gridheight, int weightx,int weighty,int gridx,int gridy) {
//此方法用來添加控件到容器中
gbc.gridwidth=gridwidth; //該方法是設(shè)置組件水平所占用的格子數(shù),如果為0,就說明該組件是該行的最后一個(gè)
gbc.gridheight=gridheight; //該方法是設(shè)置組件垂直所占用的格子數(shù)
gbc.weightx=weightx; //該方法設(shè)置組件水平的拉伸幅度,如果為0就說明不拉伸,不為0就隨著窗口增大進(jìn)行拉伸,0到1之間
gbc.weighty=weighty; //該方法設(shè)置組件垂直的拉伸幅度,如果為0就說明不拉伸,不為0就隨著窗口增大進(jìn)行拉伸,0到1之間
gbc.gridx=gridx;
gbc.gridy=gridy;
gbc.fill=GridBagConstraints.BOTH;
jp1.add(component,gbc);
}
public void setJp1() {
GridBagLayout gbLayout=new GridBagLayout();
jp1=new JPanel(gbLayout);
JPanel Jtitle=new JPanel();
JLabel title=new JLabel("2048");
title.setFont(new Font("font", Font.PLAIN, 45));//類型、風(fēng)格、大小
title.setHorizontalAlignment(JLabel.LEFT);//jLabel的文本左右對(duì)齊屬性設(shè)置方法,對(duì)齊方式
Jtitle.add(title);
jp1.add(Jtitle);
JPanel Jscroe=new JPanel(new GridLayout(2, 1));//new GridLayout(2, 1)為網(wǎng)格布局樣式。其中的參數(shù)“2”“1”分別為網(wǎng)格的“行數(shù)”和“列數(shù)”。
JLabel scroe=new JLabel("Scroe");
scroe.setFont(new Font("font", Font.PLAIN, 16));
scroe.setHorizontalAlignment(JLabel.CENTER);
scroeValue=new JLabel("0");
scroeValue.setFont(new Font("font", Font.PLAIN, 16));
scroeValue.setHorizontalAlignment(JLabel.CENTER);
Jscroe.add(scroe);
Jscroe.add(scroeValue);
jp1.add(Jscroe);
JPanel Jnoite=new JPanel();
JLabel noite=new JLabel("方向鍵移動(dòng)數(shù)字累加至2048");
noite.setFont(new Font("font", Font.PLAIN, 14));
noite.setHorizontalAlignment(JLabel.LEFT);
Jnoite.add(noite);
jp1.add(Jnoite);
JPanel JnewGame=new JPanel();
JButton newGame=new JButton("New Game");
newGame.setHorizontalAlignment(JButton.CENTER);
newGame.addActionListener(this);
JnewGame.add(newGame);
jp1.add(JnewGame);
GridBagConstraints gbc=new GridBagConstraints();
addc(jp1, Jtitle, gbc, 3, 2, 60, 60, 0, 0);
addc(jp1, Jscroe, gbc, 0, 2, 40, 60, 3, 0);
addc(jp1, Jnoite, gbc, 3, 1, 60, 40, 0, 2);
addc(jp1, JnewGame, gbc, 0, 1, 40, 40, 3, 2);
}
public void setJp2() {
addBlock();
initBlock();
initBlock();
}
/**
* 添加方塊
*/
public void addBlock(){
jp2=new JPanel();
/*
* setLayout是對(duì)當(dāng)前組件設(shè)置為流式布局.組件在窗體中從左到右依次排列 如果排到行的末尾 換行排列
* GridLayout(int rows, int cols, int hgap, int vgap)
創(chuàng)建具有指定行數(shù)和列數(shù)的網(wǎng)格布局。
rows - 該 rows 具有表示任意行數(shù)的值
cols - 該 cols 具有表示任意列數(shù)的值
hgap - 水平間距
vgap - 垂直間距
*/
jp2.setLayout(new GridLayout(4, 4, 5, 5));
for (int i = 0; i < blocks.length; i++) {
blocks[i]=new Block();
blocks[i].setHorizontalAlignment(JLabel.CENTER);// 不透明的標(biāo)簽
blocks[i].setOpaque(true);// 設(shè)置控件不透明
jp2.add(blocks[i]);
}
}
/**
* 初始化方塊
*/
public void initBlock(){
while (numFlag) {
int index=(int) (Math.random()*16);
if (blocks[index].getText().trim().equals("")) {
blocks[index].setValue("2");
break;
} else {
continue;
}
}
}
/**
* 獲得第一個(gè)子面板的高度
*/
public int getMyJPanelHeidth() {
return jp1.getSize().height;
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO 自動(dòng)生成的方法存根
newGame();
}
/**
* 重新開始游戲
*/
public void newGame() {
for (int i = 0; i < blocks.length; i++) {
blocks[i].setValue("");
}
numFlag=true;
scroeValue.setText("0");
initBlock();
initBlock();
}
}業(yè)務(wù)邏輯類
public class Game2048Logic implements KeyListener{
Block[] blocks;
boolean numFlag; // 用于判斷是否還能加入新的數(shù)字
JLabel scroeValue; //顯示分?jǐn)?shù)
int blocksarr[]=new int[4]; //保存一行/列方塊中的數(shù)值
JFrame myJFrame;
int scroe=0;
Game2048View game2048View;
//初始化鍵盤事件
public Game2048Logic(Game2048View game2048View, JFrame myJFrame, Block[] blocks,boolean numFlag,JLabel scroeValue) {
// TODO 自動(dòng)生成的構(gòu)造函數(shù)存根
this.blocks=blocks;
this.numFlag=numFlag;
this.scroeValue=scroeValue;
this.myJFrame=myJFrame;
this.game2048View=game2048View;
}
//初始化按鈕事件
public Game2048Logic() {
// TODO 自動(dòng)生成的構(gòu)造函數(shù)存根
}
public boolean getnumFlag() {
return numFlag;
}
@Override
public void keyPressed(KeyEvent e) {
// TODO 自動(dòng)生成的方法存根
int[] blocksarr=getBlock();
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
colBlock("up");
hasEmptyBlock();
if (Arrays.equals(blocksarr, getBlock())) {
} else {
refershBlock();
}
isGameFail("up");
break;
case KeyEvent.VK_DOWN:
colBlock("down");
hasEmptyBlock();
if (Arrays.equals(blocksarr, getBlock())) {
} else {
refershBlock();
}
isGameFail("down");
break;
case KeyEvent.VK_LEFT:
rowBlock("left");
hasEmptyBlock();
if (Arrays.equals(blocksarr, getBlock())) {
} else {
refershBlock();
}
isGameFail("left");
break;
case KeyEvent.VK_RIGHT:
rowBlock("right");
hasEmptyBlock();
if (Arrays.equals(blocksarr, getBlock())) {
} else {
refershBlock();
}
isGameFail("right");
break;
default:
break;
}
scroeValue.setText(""+scroe);
win();
}
/**
* 垂直方向方塊移動(dòng)處理函數(shù)
*/
public void colBlock(String direction){
int tmp1=0;
int tmp2=0;
int index=0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (blocks[tmp1].getText().trim().equals("")) {
tmp1+=4;
if (tmp1>=16) {
break;
} else {
continue;
}
} else {
blocksarr[index]=Integer.parseInt(blocks[tmp1].getText().trim());
index+=1;
tmp1+=4;
if (tmp1>=16 || index>=4) {
break;
} else {
continue;
}
}
}
switch (direction) {
case "up":
blocksarr=handleBlocksarr(blocksarr);
break;
case "down":
blocksarr=reverseArr(handleBlocksarr(reverseArr(blocksarr)));
break;
default:
break;
}
for (int n = 0; n < blocksarr.length; n++) {
if (blocksarr[n]==0) {
blocks[tmp2].setText("");
blocks[tmp2].setBackground(Color.gray);
} else {
blocks[tmp2].setValue(blocksarr[n]+"");
}
tmp2+=4;
}
index=0;
tmp1=i+1;
tmp2=i+1;
//清空數(shù)組blockarr
for (int n = 0; n < blocksarr.length; n++) {
blocksarr[n]=0;
}
}
}
/**
* 水平方向方塊移動(dòng)處理函數(shù)
*/
public void rowBlock(String direction) {
int tmp1=0;
int tmp2=0;
int index=0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (blocks[tmp1].getText().trim().equals("")) {
tmp1+=1;
if (tmp1>=16) {
break;
} else {
continue;
}
} else {
blocksarr[index]=Integer.parseInt(blocks[tmp1].getText().trim());
index+=1;
tmp1+=1;
if (tmp1>=16 || index>=4) {
break;
} else {
continue;
}
}
}
switch (direction) {
case "left":
blocksarr=handleBlocksarr(blocksarr);
break;
case "right":
blocksarr=reverseArr(handleBlocksarr(reverseArr(blocksarr)));
break;
default:
break;
}
for (int n = 0; n < blocksarr.length; n++) {
if (blocksarr[n]==0) {
blocks[tmp2].setText("");
blocks[tmp2].setBackground(Color.gray);
} else {
blocks[tmp2].setValue(blocksarr[n]+"");
}
tmp2+=1;
}
index=0;
//清空數(shù)組blockarr
for (int n = 0; n < blocksarr.length; n++) {
blocksarr[n]=0;
}
}
}
/**
* 處理并返回一個(gè)數(shù)組
*/
public int[] handleBlocksarr(int[] blocksarr) {
int index=0;
int[] result=new int[4];
for (int i = 0; i < blocksarr.length; i++) {
//排序
if (blocksarr[i]!=0) {
result[index]=blocksarr[i];
index++;
}
}
if (index==0 || index==1) {
for (int i = index; i < result.length; i++) {
result[i]=0;
}
} else {
for (int i = 0; i < blocksarr.length; i++) {
blocksarr[i]=0;
}
switch (index) {
case 2:
if (result[0]==result[1]) {
blocksarr[0]=result[0]+result[1];
scroe+=result[0]*2;
} else {
blocksarr=result;
}
break;
case 3:
if (result[0]==result[1]) {
blocksarr[0]=result[0]+result[1];
scroe+=result[0]*2;
blocksarr[1]=result[2];
} else {
if (result[1]==result[2]) {
blocksarr[0]=result[0];
blocksarr[1]=result[1]+result[2];
scroe+=result[1]*2;
} else {
blocksarr=result;
}
}
break;
case 4:
if (result[0]==result[1]) {
blocksarr[0]=result[0]+result[1];
scroe+=result[0]*2;
if (result[2]==result[3]) {
blocksarr[1]=result[2]+result[3];
scroe+=result[2]*2;
} else {
blocksarr[1]=result[2];
blocksarr[2]=result[3];
}
} else {
if (result[1]==result[2]) {
blocksarr[0]=result[0];
blocksarr[1]=result[1]+result[2];
scroe+=result[1]*2;
blocksarr[2]=result[3];
} else {
blocksarr[0]=result[0];
blocksarr[1]=result[1];
if (result[2]==result[3]) {
blocksarr[2]=result[2]+result[3];
scroe+=result[2]*2;
} else {
blocksarr=result;
}
}
}
break;
default:
break;
}
result=blocksarr;
}
return result;
}
/**
* 反轉(zhuǎn)數(shù)組eg:45000 --> 00054
*/
public int[] reverseArr(int[] arr) {
int[] tmp=new int[arr.length];
int index=arr.length-1;
for (int i = 0; i < arr.length; i++) {
tmp[index]=arr[i];
index--;
}
return tmp;
}
/**
* 刷新方塊,添加新出現(xiàn)的2或4
*/
public void refershBlock(){
if (numFlag==false) {
}
while (numFlag) {
int index=(int) (Math.random()*16);
if (blocks[index].getText().trim().equals("")) {
if (Math.random()<0.8) {
blocks[index].setValue("2");
} else {
blocks[index].setValue("4");
}
break;
} else {
continue;
}
}
}
/**
* 判斷是否有空的方塊
*/
public void hasEmptyBlock() {
for (int i = 0; i < blocks.length; i++) {
if (blocks[i].getText().trim().equals("")) {
this.numFlag=true;
break;
} else {
this.numFlag=false;
}
}
}
/**
* 判斷游戲是否失敗
*/
public void isGameFail(String direction) {
boolean result=true; //true代表失敗 false代表沒有失敗
int tmp=0;
if (numFlag == false) { // 表示沒有空的方塊
switch (direction) {
case "up":
case "down":
for (int i = 0; i < 4; i++) {
tmp=i*4;
for (int j = 0; j < 3; j++) {
if (blocks[tmp].getText().trim().equals(blocks[tmp+1].getText().trim())) {
result = false; //游戲未失敗
break;
} else {
tmp++;
}
}
if (result==false) {
break;
}
}
break;
case "left":
case "right":
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
if (blocks[tmp].getText().trim().equals(blocks[tmp+4].getText().trim())) {
result = false; //游戲未失敗
break;
} else {
tmp+=4;
if (tmp>=16) {
break;
} else {
continue;
}
}
}
tmp=i+1;
if (result==false) {
break;
}
}
break;
default:
break;
}
} else {
result=false;
}
if (result==true) {
JOptionPane.showMessageDialog( null , "Game Over",null , JOptionPane.ERROR_MESSAGE) ;
game2048View.newGame();
} else {
}
}
/**
* 判斷游戲是否成功,即成功累加至2048
*/
public void win() {
for (int i = 0; i < blocks.length; i++) {
if (blocks[i].getText().trim().equals("2048")) {
JOptionPane.showMessageDialog( null , "YOU ARE WIN",null , JOptionPane.ERROR_MESSAGE) ;
game2048View.newGame();
break;
}
}
}
/**
* 獲得全部方塊內(nèi)容,用于對(duì)比
*/
public int[] getBlock() {
int[] blocksarr=new int[16];
for (int i = 0; i < blocks.length; i++) {
if (blocks[i].getText().trim().equals("")) {
blocksarr[i]=0;
} else {
blocksarr[i]=Integer.parseInt(blocks[i].getText().trim());
}
}
return blocksarr;
}
@Override
public void keyReleased(KeyEvent e) {
// TODO 自動(dòng)生成的方法存根
}
@Override
public void keyTyped(KeyEvent e) {
// TODO 自動(dòng)生成的方法存根
}
}總結(jié)
通過此次的《2048游戲》游戲?qū)崿F(xiàn),讓我對(duì)swing的相關(guān)知識(shí)有了進(jìn)一步的了解,對(duì)java這門語言也有了比以前更深刻的認(rèn)識(shí)。
java的一些基本語法,比如數(shù)據(jù)類型、運(yùn)算符、程序流程控制和數(shù)組等,理解更加透徹。java最核心的核心就是面向?qū)ο笏枷?,?duì)于這一個(gè)概念,終于悟到了一些。
以上就是Java實(shí)現(xiàn)經(jīng)典游戲2048的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Java 2048游戲的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot2.7+Minio8 實(shí)現(xiàn)大文件分片上傳
本文主要介紹了Springboot2.7+Minio8 實(shí)現(xiàn)大文件分片上傳,通過文件切片上傳,我們能夠提高文件上傳的速度,優(yōu)化用戶體驗(yàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
Java 實(shí)戰(zhàn)練手項(xiàng)目之醫(yī)院預(yù)約掛號(hào)系統(tǒng)的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SpringBoot+Maven+Vue+mysql實(shí)現(xiàn)一個(gè)醫(yī)院預(yù)約掛號(hào)系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(58)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-08-08
使用JPA雙向多對(duì)多關(guān)聯(lián)關(guān)系@ManyToMany
這篇文章主要介紹了使用JPA雙向多對(duì)多關(guān)聯(lián)關(guān)系@ManyToMany,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06

