青衣 发表于 2021-8-17 10:58:34

java实现2048小游戏

本文实例为大家分享了java实现2048小游戏的具体代码,供大家参考,具体内容如下
一、实现效果

二、实现代码
Check表示格子,GameView实现游戏视图界面及功能,是核心。

Check.java


import java.awt.Color;
import java.awt.Font;

// 方格类
public class Check {
public int value;

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);

public Check() {
value = 0; //value为方格中数字
}
//字体颜色
public Color getForeground() {
switch (value) {
case 0:
return new Color(0xcdc1b4);//0的颜色与背景色一致,相当于没有数字
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 font1;
}
if (value < 100) {
return font2;
}
if (value < 1000) {
return font3;
}
if (value < 10000) {
return font4;
}

return font5;
}

}
GameView.java


import java.awt.*;
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{
private static final int jframeWidth = 405;//窗口宽高
private static final int jframeHeight = 530;
private static int score = 0;

Font topicFont = new Font("微软雅黑", Font.BOLD, 50);//主题字体
Font scoreFont = new Font("微软雅黑", Font.BOLD, 28);//得分字体
Font explainFont = new Font("宋体", Font.PLAIN,20);//提示字体

private JFrame jframeMain;
private JLabel jlblTitle;
private JLabel jlblScoreName;
private JLabel jlblScore;
private JLabel jlblTip;
private GameBoard gameBoard;

public GameView() {
init();
}

public void init() {
//1、创建窗口
jframeMain = new JFrame("2048小游戏");
jframeMain.setSize(jframeWidth, jframeHeight);
jframeMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframeMain.setLocationRelativeTo(null);//窗口显示位置居中
jframeMain.setResizable(false);
jframeMain.setLayout(null);//设置绝对布局,以便后面可以用setBounds设置位置

jlblTitle = new JLabel("2048", JLabel.CENTER);
jlblTitle.setFont(topicFont);
jlblTitle.setForeground(Color.BLACK);
jlblTitle.setBounds(50, 0, 150, 60);
jframeMain.add(jlblTitle);


//2、框架窗口搭建好,则需向里面开始添加内容
//设置字体及其颜色、位置
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);

//3、得分区(得分名+分数)
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);

//4、提示说明区
jlblTip = new JLabel("操作: ↑ ↓ ← →, 按esc键重新开始 ",
JLabel.CENTER);
jlblTip.setFont(explainFont);
jlblTip.setForeground(Color.DARK_GRAY);
jlblTip.setBounds(0, 60, 400, 40);
jframeMain.add(jlblTip);

//5、主游戏面板区
gameBoard = new GameBoard();
gameBoard.setBounds(0, 100, 400, 400);
gameBoard.setBackground(Color.GRAY);
gameBoard.setFocusable(true);//焦点即当前正在操作的组件,也就是移动的数字
gameBoard.setLayout(new FlowLayout());
jframeMain.add(gameBoard);
}

// 游戏面板
class GameBoard extends JPanel implements KeyListener {
private static final int CHECK_GAP = 10;//方格之间的间隙
private static final int CHECK_SIZE = 85;//方格大小
private static final int CHECK_ARC = 20;//方格弧度

private Check[][] checks = new Check;
private boolean isadd = true;

public GameBoard() {
initGame();
addKeyListener(this);
}

private void initGame() {
score = 0;
for (int indexRow = 0; indexRow < 4; indexRow++) {
for (int indexCol = 0; indexCol < 4; indexCol++) {
checks = new Check();
}
}
// 最开始时生成两个数
isadd = true;
createCheck();
isadd = true;
createCheck();
}

@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ESCAPE:
initGame();//重新开始游戏(初始化游戏)
break;
case KeyEvent.VK_LEFT:
moveLeft();
createCheck();//调用一次方法创建一个方格数字
judgeGameOver();//创建后判断是否GameOver,若所有格子均满即跳出GameOver
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();//刷新,会自动调用paint()方法,重新绘制移动后的图
}


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
int randomValue = random.nextInt(4);
check.value = ( randomValue % 3 == 0 || randomValue % 3 == 1) ? 2 : 4;//只有[0,4)中的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.value == 0) {
checkList.add(checks);
}
}
}
return checkList;
}
//是否全部格子占满,全部占满则GameOver
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.value == checks.value
|| checks.value == checks.value) {
return false;
}
}
}

return true;
}

private void moveLeft() {
//找到一个非空格子后checks.value > 0,可分为三种情况处理
for (int i = 0; i < 4; i++) {
for (int j = 1, index = 0; j < 4; j++) {
if (checks.value > 0) {
//第一种情况:checks(非第1列)与checks的数相等,则合并乘以2,且得分增加
if (checks.value == checks.value) {
score += checks.value *= 2;
checks.value = 0;
isadd = true;
} else if (checks.value == 0) {
//第二种:若checks为空格子,checks就直接移到最左边checks
checks.value = checks.value;
checks.value = 0;
isadd = true;
} else if (checks[++index].value == 0) {
//第三种:若checks不为空格子,并且数字也不相等,若其旁边为空格子,则移到其旁边
checks.value = checks.value;
checks.value = 0;
isadd = true;
}
}
}
}
}

private void moveRight() {
for (int i = 0; i < 4; i++) {
for (int j = 2, index = 3; j >= 0; j--) {
if (checks.value > 0) {
if (checks.value == checks.value) {
score += checks.value *= 2;
checks.value = 0;
isadd = true;
} else if (checks.value == 0) {
checks.value = checks.value;
checks.value = 0;
isadd = true;
} else if (checks[--index].value == 0) {
checks.value = checks.value;
checks.value = 0;
isadd = true;
}
}
}
}
}

private void moveUp() {
for (int i = 0; i < 4; i++) {
for (int j = 1, index = 0; j < 4; j++) {
if (checks.value > 0) {
if (checks.value == checks.value) {
score += checks.value *= 2;
checks.value = 0;
isadd = true;
} else if (checks.value == 0) {
checks.value = checks.value;
checks.value = 0;
isadd = true;
} else if (checks[++index].value == 0){
checks.value = checks.value;
checks.value = 0;
isadd = true;
}
}
}
}
}

private void moveDown() {
for (int i = 0; i < 4; i++) {
for (int j = 2, index = 3; j >= 0; j--) {
if (checks.value > 0) {
if (checks.value == checks.value) {
score += checks.value *= 2;
checks.value = 0;
isadd = true;
} else if (checks.value == 0) {
checks.value = checks.value;
checks.value = 0;
isadd = true;
} else if (checks[--index].value == 0) {
checks.value = checks.value;
checks.value = 0;
isadd = true;
}
}
}
}
}

@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, 100));//RGBA最后一个A可以视为透明度
g.fillRect(0, 0, getWidth(), getHeight());//填充矩形(游戏面板),将暗黑色填充上去
g.setColor(Color.WHITE);
g.setFont(topicFont);
FontMetrics fms = getFontMetrics(topicFont);//FontMetrics字体测量,该类是Paint的内部类,通过getFontMetrics()方法可获取字体相关属性
String value = "Game Over!";
g.drawString(value, (getWidth()-fms.stringWidth(value)) / 2, getHeight() / 2);//字体居中显示
}
}

// 绘制方格
// Graphics2D 类是Graphics 子类,拥有强大的二维图形处理能力
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;
//不同数字设置背景色
gg.setColor(check.getBackground());
// 绘制圆角
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());

// 文字测量,并对文字进行绘制
FontMetrics fms = getFontMetrics(check.getCheckFont());
String value = String.valueOf(check.value);
//使用此图形上下文的当前颜色绘制由指定迭代器给定的文本。
//getAscent()是FontMetrics中的一个方法,
//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) {
}

}

public void showView() {
jframeMain.setVisible(true);
}

}
Main.java


public class Main {
public static void main(String[] args) {
new GameView().showView();
}
}
三、重难点讲解
3.1 数字移动问题
数字移动是一难点,分三种情况,以moveLeft()为例
(1)按左键,若最左边是相同的,则合并

(2)若左边是空格,则直接移动到最左即可

(3)若最左边不为空格,且不相等,则看它右边是否是空格,是则移动到其旁边

3.2 绘图问题—抗锯齿
java提供的Graphics 2D,它是Graphics 子类


Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
gg.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_NORMALIZE);
上面这两个语句实现的功能是消除文字锯齿,字体更清晰顺滑,可以看下图没有setRenderingHint和有setRenderingHint的区别

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持CodeAE代码之家。
原文链接:https://blog.csdn.net/weixin_39615182/article/details/113502243

文档来源:服务器之家http://www.zzvips.com/article/181154.html
页: [1]
查看完整版本: java实现2048小游戏