2048的代码不是我写的,是从牛客网的项目平台找到的项目
项目代码链接(代码放在了文章最后)
https://git.nowcoder.com/11000160/2048-java/blob/master/Game2048.java
录制的效果图:
游戏逻辑分析
2048大家都玩过,我就不介绍了,没玩过了可以去玩儿一下,直接说游戏的逻辑
1.通过上、下、左、右移动,使相邻的相同元素进行合并,进而数字相加得到 2048的结果的游戏。
2.每次生成得到数字块是 2或4 ,生成的位置是在空白位置随机出现。
3.游戏最开始随机出现两个数字块
4.每次传入一个移动方向(上下左右),所有数字块都向该方向移动直到边界,相同两个数字块会合并成两数之和变成一个数字块。
5.若移动后没有一个数字块发生位移则移动无效,不会产生新数字块。
6.当场景没有空位且无法移动则游戏失败。
7.游戏分数为场景内最大的数字,当数字为2048时游戏结束。
代码的分析
感觉代码已经很简洁了 200来行就能写出这个游戏。
简易的思维导图
文字版
Game2048
main主方法
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE可以关闭窗口);
f.setTitle("2048");设置窗口标题
f.setResizable(true);面板可以调节
f.add(new Game2048(), BorderLayout.CENTER);把游戏放入面板
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);可见
});
class Tile数字块类
数字块类
private boolean merged;是否合并
private int value;值
构造方法
-
Tile(int val) {
-
value = val;
-
get 和set方法
-
getValue()
-
setMerged(boolean m)
canMergeWith(Tile other)判断两个块是否可合并
-
return !merged && other != null && !other.merged && value == other.getValue();
-
值相等,另一块不是空,本次移动没有和另一块合并,都可以合并
int mergeWith(Tile other) {合并方法
-
if (canMergeWith(other)) {
-
value *= 2;合并值翻倍
-
merged = true;
-
return value;
-
-
return -1;
movesAvailable判断是否可以移动
clearMerged清楚面板
遍历所有格子 设置false
变量
State静态成员
-
start, won, running, over游戏状态
colorTable颜色库
-
new Color(0x701710), new Color(0xFFE4C3), new Color(0xfff4d3),
-
new Color(0xffdac3), new Color(0xe7b08e), new Color(0xe7bf8e),
-
new Color(0xffc4c3), new Color(0xE7948e), new Color(0xbe7e56),
-
new Color(0xbe5e56), new Color(0x9c3931), new Color(0x701710)};
final static int target = 2048;目标分数
static int highest;游戏高度
static int score;游戏分数
private Color gridColor = new Color(0xBBADA0);方格颜色
private Color emptyColor = new Color(0xCDC1B4);空颜色
private Color startColor = new Color(0xFFEBCD);开始颜色
private Random rand = new Random();随机数
private Tile tiles;数字块数组 存放全部数字块
private int side = 4;格子大小4*4
private State gamestate = State.start;游戏状态
private boolean checkingAvailableMoves;可否移动状态
Game2048构造方法
setPreferredSize(new Dimension(900, 700));
setBackground(new Color(0xFAF8EF));
setFont(new Font("SansSerif", Font.BOLD, 48));
setFocusable(true);
监听鼠标
监听键盘
paintComponent绘制组件
初始化画图
setRenderingHint抗锯齿
startGame开始游戏
if (gamestate != State.running) {
score = 0;
highest = 0;
gamestate = State.running;
tiles = new Tileside;
addRandomTile();
addRandomTile();
}
drawGrid绘制面板
g.setColor(gridColor);设置颜色
g.fillRoundRect(200, 100, 499, 499, 15, 15);填充背景
判断游戏状态
drawTile绘制数字块
fillRoundRect在二维面板 r行c列画value
addRandomTile随机生成数字块
随机找空位置添加 4或2
move移动数字块
根据方向进行移动
移动后序处理
-
if (moved) {
-
if (highest < target) {
-
clearMerged();
-
addRandomTile();
-
if (!movesAvailable()) {
-
gamestate = State.over;
-
}
-
} else if (highest == target)
-
gamestate = State.won;
-
}
一系列移动方法
boolean moveUp() {
-
return move(0, -1, 0);
boolean moveDown() {
-
return move(side * side - 1, 1, 0);
boolean moveLeft() {
-
return move(0, 0, -1);
boolean moveRight() {
-
return move(side * side - 1, 0, 1);
代码
package com.hanxu51.game2048;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class Game2048 extends JPanel {
enum State {
start, won, running, over
}
final Color[] colorTable = {
new Color(0x701710), new Color(0xFFE4C3), new Color(0xfff4d3),
new Color(0xffdac3), new Color(0xe7b08e), new Color(0xe7bf8e),
new Color(0xffc4c3), new Color(0xE7948e), new Color(0xbe7e56),
new Color(0xbe5e56), new Color(0x9c3931), new Color(0x701710)};
final static int target = 2048;
static int highest;
static int score;
private Color gridColor = new Color(0xBBADA0);
private Color emptyColor = new Color(0xCDC1B4);
private Color startColor = new Color(0xFFEBCD);
private Random rand = new Random();
private Tile[][] tiles;
private int side = 4;
private State gamestate = State.start;
private boolean checkingAvailableMoves;
public Game2048() {
setPreferredSize(new Dimension(900, 700));
setBackground(new Color(0xFAF8EF));
setFont(new Font("SansSerif", Font.BOLD, 48));
setFocusable(true);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
startGame();
repaint();
}
});
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
moveUp();
break;
case KeyEvent.VK_DOWN:
moveDown();
break;
case KeyEvent.VK_LEFT:
moveLeft();
break;
case KeyEvent.VK_RIGHT:
moveRight();
break;
}
repaint();
}
});
}
@Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawGrid(g);
}
void startGame() {
if (gamestate != State.running) {
score = 0;
highest = 0;
gamestate = State.running;
tiles = new Tile[side][side];
addRandomTile();
addRandomTile();
}
}
void drawGrid(Graphics2D g) {
g.setColor(gridColor);
g.fillRoundRect(200, 100, 499, 499, 15, 15);
if (gamestate == State.running) {
for (int r = 0; r < side; r++) {
for (int c = 0; c < side; c++) {
if (tiles[r][c] == null) {
g.setColor(emptyColor);
g.fillRoundRect(215 + c * 121, 115 + r * 121, 106, 106, 7, 7);
} else {
drawTile(g, r, c);
}
}
}
} else {
g.setColor(startColor);
g.fillRoundRect(215, 115, 469, 469, 7, 7);
g.setColor(gridColor.darker());
g.setFont(new Font("SansSerif", Font.BOLD, 128));
g.drawString("2048", 310, 270);
g.setFont(new Font("SansSerif", Font.BOLD, 20));
if (gamestate == State.won) {
g.drawString("you made it!", 390, 350);
} else if (gamestate == State.over)
g.drawString("game over", 400, 350);
g.setColor(gridColor);
g.drawString("click to start a new game", 330, 470);
g.drawString("(use arrow keys to move tiles)", 310, 530);
}
}
void drawTile(Graphics2D g, int r, int c) {
int value = tiles[r][c].getValue();
g.setColor(colorTable[(int) (Math.log(value) / Math.log(2)) + 1]);
g.fillRoundRect(215 + c * 121, 115 + r * 121, 106, 106, 7, 7);
String s = String.valueOf(value);
g.setColor(value < 128 ? colorTable[0] : colorTable[1]);
FontMetrics fm = g.getFontMetrics();
int asc = fm.getAscent();
int dec = fm.getDescent();
int x = 215 + c * 121 + (106 - fm.stringWidth(s)) / 2;
int y = 115 + r * 121 + (asc + (106 - (asc + dec)) / 2);
g.drawString(s, x, y);
}
private void addRandomTile() {
int pos = rand.nextInt(side * side);
int row, col;
do {
pos = (pos + 1) % (side * side);
row = pos / side;
col = pos % side;
} while (tiles[row][col] != null);
int val = rand.nextInt(10) == 0 ? 4 : 2;
tiles[row][col] = new Tile(val);
}
private boolean move(int countDownFrom, int yIncr, int xIncr) {
boolean moved = false;
for (int i = 0; i < side * side; i++) {
int j = Math.abs(countDownFrom - i);
int r = j / side;
int c = j % side;
if (tiles[r][c] == null)
continue;
int nextR = r + yIncr;
int nextC = c + xIncr;
while (nextR >= 0 && nextR < side && nextC >= 0 && nextC < side) {
Tile next = tiles[nextR][nextC];
Tile curr = tiles[r][c];
if (next == null) {
if (checkingAvailableMoves)
return true;
tiles[nextR][nextC] = curr;
tiles[r][c] = null;
r = nextR;
c = nextC;
nextR += yIncr;
nextC += xIncr;
moved = true;
} else if (next.canMergeWith(curr)) {
if (checkingAvailableMoves)
return true;
int value = next.mergeWith(curr);
if (value > highest)
highest = value;
score += value;
tiles[r][c] = null;
moved = true;
break;
} else
break;
}
}
if (moved) {
if (highest < target) {
clearMerged();
addRandomTile();
if (!movesAvailable()) {
gamestate = State.over;
}
} else if (highest == target)
gamestate = State.won;
}
return moved;
}
boolean moveUp() {
return move(0, -1, 0);
}
boolean moveDown() {
return move(side * side - 1, 1, 0);
}
boolean moveLeft() {
return move(0, 0, -1);
}
boolean moveRight() {
return move(side * side - 1, 0, 1);
}
void clearMerged() {
for (Tile[] row : tiles)
for (Tile tile : row)
if (tile != null)
tile.setMerged(false);
}
boolean movesAvailable() {
checkingAvailableMoves = true;
boolean hasMoves = moveUp() || moveDown() || moveLeft() || moveRight();
checkingAvailableMoves = false;
return hasMoves;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("2048");
f.setResizable(true);
f.add(new Game2048(), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class Tile {
private boolean merged;
private int value;
Tile(int val) {
value = val;
}
int getValue() {
return value;
}
void setMerged(boolean m) {
merged = m;
}
boolean canMergeWith(Tile other) {
return !merged && other != null && !other.merged && value == other.getValue();
}
int mergeWith(Tile other) {
if (canMergeWith(other)) {
value *= 2;
merged = true;
return value;
}
return -1;
}
}