Java實(shí)現(xiàn)2048小游戲(可直接運(yùn)行)
運(yùn)行效果:

1.項(xiàng)目結(jié)構(gòu)

2.代碼
BaseData接口
package com.hsy.game;
import java.awt.*;
public interface BaseData {
Font topicFont = new Font("微軟雅黑", Font.BOLD, 50);
Font scoreFont = new Font("微軟雅黑", Font.BOLD, 28);
Font normalFont = new Font("宋體", Font.PLAIN, 20);
Font font1 = new Font("宋體", Font.BOLD, 46);
Font font2 = new Font("宋體", Font.BOLD, 40);
Font font3 = new Font("宋體", Font.BOLD, 34);
Font font4 = new Font("宋體", Font.BOLD, 28);
Font font5 = new Font("宋體", Font.BOLD, 22);
int normalFontData = 20;
int topicFontData = 30;
void init();
void showView();
}
GameView類
package com.hsy.game;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GameView implements BaseData{
private static final int jFrameWidth = 400;
private static final int jFrameHeight = 530;
private static int score = 0;
private JFrame jFrameMain;
private JLabel jLblTitle;
private JLabel jLblScoreName;
private JLabel jLblScore;
private GameBoard gameBoard;
private JLabel jlblTip;
public GameView() {
init();
}
@Override
public void init() {
jFrameMain = new JFrame("2048小游戲");
jFrameMain.setSize(jFrameWidth, jFrameHeight);
jFrameMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrameMain.setLocationRelativeTo(null);
jFrameMain.setResizable(false);
jFrameMain.setLayout(null);
jLblTitle = new JLabel("2048", JLabel.CENTER);
jLblTitle.setFont(topicFont);
jLblTitle.setForeground(Color.BLACK);
jLblTitle.setBounds(50, 0, 150, 60);
jFrameMain.add(jLblTitle);
// 分?jǐn)?shù)區(qū)
jLblScoreName = new JLabel("得 分", JLabel.CENTER);
jLblScoreName.setFont(scoreFont);
jLblScoreName.setForeground(Color.WHITE);
jLblScoreName.setOpaque(true);
jLblScoreName.setBackground(Color.GRAY);
jLblScoreName.setBounds(250, 0, 120, 30);
jFrameMain.add(jLblScoreName);
jLblScore = new JLabel("0", JLabel.CENTER);
jLblScore.setFont(scoreFont);
jLblScore.setForeground(Color.WHITE);
jLblScore.setOpaque(true);
jLblScore.setBackground(Color.GRAY);
jLblScore.setBounds(250, 30, 120, 30);
jFrameMain.add(jLblScore);
// 說明:
jlblTip = new JLabel("操作: ↑ ↓ ← →, 按esc鍵重新開始 ",
JLabel.CENTER);
jlblTip.setFont(normalFont);
jlblTip.setForeground(Color.DARK_GRAY);
jlblTip.setBounds(0, 60, 400, 40);
jFrameMain.add(jlblTip);
gameBoard = new GameBoard();
gameBoard.setBounds(0, 100, 400, 400);
gameBoard.setBackground(Color.GRAY);
gameBoard.setFocusable(true);
gameBoard.setLayout(new FlowLayout());
jFrameMain.add(gameBoard);
}
// 游戲面板需要對(duì)鍵值實(shí)現(xiàn)偵聽,
// 這里采用內(nèi)部類來繼承 JPanel 類,
// 并實(shí)現(xiàn)接口 KeyListener 中的 keyPressed 方法,
// 方格是通過
@SuppressWarnings("serial")
class GameBoard extends JPanel implements KeyListener {
private static final int CHECK_GAP = 10;
private static final int CHECK_ARC = 20;
private static final int CHECK_SIZE = 86;
private Check[][] checks = new Check[4][4];
private boolean isAdd = true;
public GameBoard() {
initGame();
addKeyListener(this);
}
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ESCAPE:
initGame();
break;
case KeyEvent.VK_LEFT:
moveLeft();
createCheck();
judgeGameOver();
break;
case KeyEvent.VK_RIGHT:
moveRight();
createCheck();
judgeGameOver();
break;
case KeyEvent.VK_UP:
moveUp();
createCheck();
judgeGameOver();
break;
case KeyEvent.VK_DOWN:
moveDown();
createCheck();
judgeGameOver();
break;
default:
break;
}
repaint();
}
private void initGame() {
score = 0;
for (int indexRow = 0; indexRow < 4; indexRow++) {
for (int indexCol = 0; indexCol < 4; indexCol++) {
checks[indexRow][indexCol] = new Check();
}
}
// 生成兩個(gè)數(shù)
isAdd = true;
createCheck();
isAdd = true;
createCheck();
}
private void createCheck() {
List<Check> list = getEmptyChecks();
if (!list.isEmpty() && isAdd) {
Random random = new Random();
int index = random.nextInt(list.size());
Check check = list.get(index);
// 2, 4出現(xiàn)概率3:1
check.value = (random.nextInt(4) % 3 == 0) ? 2 : 4;
isAdd = false;
}
}
// 獲取空白方格
private List<Check> getEmptyChecks() {
List<Check> checkList = new ArrayList<>();
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (checks[i][j].value == 0) {
checkList.add(checks[i][j]);
}
}
}
return checkList;
}
private boolean judgeGameOver() {
jLblScore.setText(score + "");
if (!getEmptyChecks().isEmpty()) {
return false;
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
//判斷是否存在可合并的方格
if (checks[i][j].value == checks[i][j + 1].value
|| checks[i][j].value == checks[i + 1][j].value) {
return false;
}
}
}
return true;
}
private boolean moveLeft() {
for (int i = 0; i < 4; i++) {
for (int j = 1, index = 0; j < 4; j++) {
if (checks[i][j].value > 0) {
if (checks[i][j].value == checks[i][index].value) {
score += checks[i][index++].value <<= 1;
checks[i][j].value = 0;
isAdd = true;
} else if (checks[i][index].value == 0) {
checks[i][index].value = checks[i][j].value;
checks[i][j].value = 0;
isAdd = true;
} else if (checks[i][++index].value == 0) {
checks[i][index].value = checks[i][j].value;
checks[i][j].value = 0;
isAdd = true;
}
}
}
}
return isAdd;
}
private boolean moveRight() {
for (int i = 0; i < 4; i++) {
for (int j = 2, index = 3; j >= 0; j--) {
if (checks[i][j].value > 0) {
if (checks[i][j].value == checks[i][index].value) {
score += checks[i][index--].value <<= 1;
checks[i][j].value = 0;
isAdd = true;
} else if (checks[i][index].value == 0) {
checks[i][index].value = checks[i][j].value;
checks[i][j].value = 0;
isAdd = true;
} else if (checks[i][--index].value == 0) {
checks[i][index].value = checks[i][j].value;
checks[i][j].value = 0;
isAdd = true;
}
}
}
}
return isAdd;
}
private boolean moveUp() {
for (int i = 0; i < 4; i++) {
for (int j = 1, index = 0; j < 4; j++) {
if (checks[j][i].value > 0) {
if (checks[j][i].value == checks[index][i].value) {
score += checks[index++][i].value <<= 1;
checks[j][i].value = 0;
isAdd = true;
} else if (checks[index][i].value == 0) {
checks[index][i].value = checks[j][i].value;
checks[j][i].value = 0;
isAdd = true;
} else if (checks[++index][i].value == 0){
checks[index][i].value = checks[j][i].value;
checks[j][i].value = 0;
isAdd = true;
}
}
}
}
return isAdd;
}
private boolean moveDown() {
for (int i = 0; i < 4; i++) {
for (int j = 2, index = 3; j >= 0; j--) {
if (checks[j][i].value > 0) {
if (checks[j][i].value == checks[index][i].value) {
score += checks[index--][i].value <<= 1;
checks[j][i].value = 0;
isAdd = true;
} else if (checks[index][i].value == 0) {
checks[index][i].value = checks[j][i].value;
checks[j][i].value = 0;
isAdd = true;
} else if (checks[--index][i].value == 0) {
checks[index][i].value = checks[j][i].value;
checks[j][i].value = 0;
isAdd = true;
}
}
}
}
return isAdd;
}
@Override
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
drawCheck(g, i, j);
}
}
// GameOver
if (judgeGameOver()) {
g.setColor(new Color(64, 64, 64, 150));
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.WHITE);
g.setFont(topicFont);
FontMetrics fms = getFontMetrics(topicFont);
String value = "Game Over!";
g.drawString(value,
(getWidth()-fms.stringWidth(value)) / 2,
getHeight() / 2);
}
}
// 繪制方格
// Graphics2D 類擴(kuò)展了 Graphics 類,
// 提供了對(duì)幾何形狀、坐標(biāo)轉(zhuǎn)換、顏色管理和文本布局更為復(fù)雜的控制
private void drawCheck(Graphics g, int i, int j) {
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
gg.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
Check check = checks[i][j];
gg.setColor(check.getBackground());
// 繪制圓角
// x - 要填充矩形的 x 坐標(biāo)。
// y - 要填充矩形的 y 坐標(biāo)。
// width - 要填充矩形的寬度。
// height - 要填充矩形的高度。
// arcwidth - 4 個(gè)角弧度的水平直徑。
// archeight - 4 個(gè)角弧度的垂直直徑。
gg.fillRoundRect(CHECK_GAP + (CHECK_GAP + CHECK_SIZE) * j,
CHECK_GAP + (CHECK_GAP + CHECK_SIZE) * i,
CHECK_SIZE, CHECK_SIZE, CHECK_ARC, CHECK_ARC);
gg.setColor(check.getForeground());
gg.setFont(check.getCheckFont());
// 對(duì)文字的長寬高測量。
FontMetrics fms = getFontMetrics(check.getCheckFont());
String value = String.valueOf(check.value);
//使用此圖形上下文的當(dāng)前顏色繪制由指定迭代器給定的文本。
//getAscent()是FontMetrics中的一個(gè)方法,
//它返回某字體的基線(baseline)到該字體中大多數(shù)字符的升部(ascender)之間的距離
//getDescent 為降部
gg.drawString(value,
CHECK_GAP + (CHECK_GAP + CHECK_SIZE) * j +
(CHECK_SIZE - fms.stringWidth(value)) / 2,
CHECK_GAP + (CHECK_GAP + CHECK_SIZE) * i +
(CHECK_SIZE - fms.getAscent() - fms.getDescent()) / 2
+ fms.getAscent());
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
@Override
public void showView() {
jFrameMain.setVisible(true);
}
}
Check類
package com.hsy.game;
import java.awt.Color;
import java.awt.Font;
public class Check {
public int value;
public Check() {
clear();
}
public void clear() {
value = 0;
}
public Color getForeground() {
switch (value) {
case 0:
return new Color(0xcdc1b4);
case 2:
case 4:
return Color.BLACK;
default:
return Color.WHITE;
}
}
public Color getBackground() {
switch (value) {
case 0:
return new Color(0xcdc1b4);
case 2:
return new Color(0xeee4da);
case 4:
return new Color(0xede0c8);
case 8:
return new Color(0xf2b179);
case 16:
return new Color(0xf59563);
case 32:
return new Color(0xf67c5f);
case 64:
return new Color(0xf65e3b);
case 128:
return new Color(0xedcf72);
case 256:
return new Color(0xedcc61);
case 512:
return new Color(0xedc850);
case 1024:
return new Color(0xedc53f);
case 2048:
return new Color(0xedc22e);
case 4096:
return new Color(0x65da92);
case 8192:
return new Color(0x5abc65);
case 16384:
return new Color(0x248c51);
default:
return new Color(0x248c51);
}
}
public Font getCheckFont() {
if (value < 10) {
return BaseData.font1;
}
if (value < 100) {
return BaseData.font2;
}
if (value < 1000) {
return BaseData.font3;
}
if (value < 10000) {
return BaseData.font4;
}
return BaseData.font5;
}
}
Test類
package com.hsy.game;
public class Test {
public static void main(String[] args) {
new GameView().showView();
}
}
運(yùn)行Test即可
總結(jié)
到此這篇關(guān)于Java實(shí)現(xiàn)2048小游戲的文章就介紹到這了,更多相關(guān)Java 2048小游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?Shell?springboot通用Shell啟動(dòng)腳本方式
這篇文章主要介紹了Java?Shell?springboot通用Shell啟動(dòng)腳本方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
IDEA Maven Mybatis generator 自動(dòng)生成代碼(實(shí)例講解)
下面小編就為大家分享一篇IDEA Maven Mybatis generator 自動(dòng)生成代碼的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12
指定springboot的jar運(yùn)行內(nèi)存方式
這篇文章主要介紹了指定springboot的jar運(yùn)行內(nèi)存方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
java正則表達(dá)式的應(yīng)用 java讀取文件并獲取電話號(hào)碼
這篇文章主要介紹了java正則表達(dá)式的應(yīng)用,應(yīng)用的內(nèi)容是java讀取文件并獲取電話號(hào)碼,感興趣的小伙伴們可以參考一下2015-11-11
啟用設(shè)置org.slf4j.Logger打印并輸出日志方式
這篇文章主要介紹了啟用設(shè)置org.slf4j.Logger打印并輸出日志方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
SpringBoot整合Spring Data Elasticsearch的過程詳解
這篇文章主要介紹了SpringBoot整合Spring Data Elasticsearch的過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
編譯期動(dòng)態(tài)替換三方包中的Class文件過程詳解
這篇文章主要為大家介紹了編譯期動(dòng)態(tài)替換三方包中的Class文件過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
RestTemplate如何使用JSON發(fā)送Post請(qǐng)求
這篇文章主要介紹了RestTemplate如何使用JSON發(fā)送Post請(qǐng)求問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09

