运行效果:

1.项目结构

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);
// 分数区
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);
}
// 游戏面板需要对键值实现侦听,
// 这里采用内部类来继承 JPanel 类,
// 并实现接口 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();
}
}
// 生成两个数
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出现概率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 Q[YSXZ[\JYJNCBBCBBOB]BXB]\HHBH\H\]NBXYKYNB[\]K][\]K]BBXX\XXX[[YNBXXX
HX\
NCBBXXX\
H[YHHCBBXXY
H]
[YJHB]\]
NB
B]\PY][B]\UNCBCBBXXX
H]
[YJHB]\]
NB]\]YYMJN
B]\]YLNB]\]MNMB]\]NMMB]\]YN
B]\]YLNLB]\]YNMB]\]YJN
LLB]\]YL
NLB]\]YN
B]\]YJN
MB]\]
YNLNNLB]\]
XXJNMB]\]JNY][B]\]JNCBCBBXXX
HY
[YH L
H]\\]KNCBY
[YH L
H]\\]KCBY
[YH L
H]\\]KBCBY
[YH L
H]\\]K
CBB]\\]K
NCBBCBBOB]B\B]\HHBH\H\]NBXYKYNBXX\\XX]XXZ[[H\H]YUY]KY]NCBCBBOB]B/c\clB.σBb,9i:/l]yk
9l##,y.,:/9&al]H
9l##z+'9c..cy%c"9al#9&.#i&i&+/c. |