本文实例为大家分享了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[4][4]; 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[indexRow][indexCol] = 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[i][j].value == 0) { checkList.add(checks[i][j]); } } } 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[i][j].value == checks[i][j + 1].value || checks[i][j].value == checks[i + 1][j].value) { return false; } } } return true; } private void moveLeft() { //找到一个非空格子后checks[i][j].value > 0,可分为三种情况处理 for (int i = 0; i < 4; i++) { for (int j = 1, index = 0; j < 4; j++) { if (checks[i][j].value > 0) { //第一种情况:checks[i][j](非第1列)与checks[i][index]的数相等,则合并乘以2,且得分增加 if (checks[i][j].value == checks[i][index].value) { score += checks[i][index].value *= 2; checks[i][j].value = 0; isadd = true; } else if (checks[i][index].value == 0) { //第二种:若checks[i][index]为空格子,checks[i][j]就直接移到最左边checks[i][index] 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]不为空格子,并且数字也不相等,若其旁边为空格子,则移到其旁边 checks[i][index].value = checks[i][j].value; checks[i][j].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[i][j].value > 0) { if (checks[i][j].value == checks[i][index].value) { score += checks[i][index].value *= 2; 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; } } } } } private void 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 *= 2; 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; } } } } } private void 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 *= 2; 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; } } } } } @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[i][j]; //不同数字设置背景色 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的区别
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自学编程网。
- 本文固定链接: https://zxbcw.cn/post/205390/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)