Java 实现2048游戏之详细教程

一、整体项目结构

使用Maven来管理项目结构

二、基本功能实现

(一)创建游戏窗口(静态)

package com.baidu.czy;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.net.URI;
import java.net.URL;

import javax.swing.*;

//本类继承自JFrame,创建游戏窗口,只需要new本类对象
public class GameStart extends JFrame  {

    File f;
    URI uri;
    URL url;
    private ActionEvent e;

    //  添加背景音乐
    public GameStart() {
        try {
            f = new File("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\bg1.wav");
            uri = f.toURI();
            url = uri.toURL();  //解析地址
            AudioClip aau;
            aau = Applet.newAudioClip(url);
            aau.loop();  //循环播放
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static final long serialVersionUID = 1L;

    //用于存放数据的二维数组,构成4*4网格的游戏界面数值,数组中的值就是其对应位置方格的值,0代表无值
    private int Numbers[][] = new int[4][4];

    public void init() {
        this.setTitle("2048游戏");
        this.setLocation(450, 100);
        this.setSize(800, 600);
        //自定义,不使用面板布局格式
        this.setLayout(null);

        //添加标签
        JLabel jLabel = new JLabel("欢迎来到2048游戏!");
        jLabel.setFont(new Font("华文行楷", Font.CENTER_BASELINE, 40));
        jLabel.setForeground(new Color(0X0000FF));
        jLabel.setBounds(20, 500, 400, 50);
        this.add(jLabel);

        //添加图片1( Public ImageIcon(String filename)//参数可以是绝对路径也可以是相对路径 )
        JLabel jLabel1 = new JLabel(new ImageIcon("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\c.png"));
        jLabel1.setBounds(400, 5, 400, 600);
        this.add(jLabel1);

        //添加图片2
        JLabel jLabel13 = new JLabel(new ImageIcon("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\d.png"));
        jLabel13.setBounds(1, 80, 400, 600);
        this.add(jLabel13);

        // 开始游戏按钮
        ImageIcon imgicon = new ImageIcon("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\start.png");
        JButton start = new JButton(imgicon);
        start.setFocusable(false);//设置此按钮不可获取焦点
        start.setBorderPainted(false);//设置此按钮没有边框
        start.setFocusPainted(false);//设置不绘制边框,设置 paintFocus属性,对于要绘制的焦点状态,该属性必须为 true。paintFocus 属性的默认值为 true。一些外观没有绘制焦点状态;它们将忽略此属性
        start.setContentAreaFilled(false);//设置不绘制边框,设置 contentAreaFilled 属性。如果该属性为 true,则按钮将绘制内容区域。如果希望有一个透明的按钮,比如只是一个图标的按钮,那么应该将此属性设置为 false。不要调用 setOpaque(false)。contentAreaFilled 属性的默认值为 true。
        start.setBounds(5, 10, 120, 30);// 设置按钮的x,y坐标位置和宽度与高度
        this.add(start);

        //后退一步按钮
        ImageIcon backicon = new ImageIcon("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\backicon.png");
        JButton back = new JButton(backicon);
        back.setFocusable(false);
        back.setBorderPainted(false);
        back.setFocusPainted(false);
        back.setContentAreaFilled(false);
        back.setBounds(270, 10, 120, 30);// 设置按钮的x,y坐标位置和宽度与高度
        this.add(back);

        // 关于按钮
        ImageIcon imgicon2 = new ImageIcon("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\about.png");
        JButton about = new JButton(imgicon2);
        about.setFocusable(false);
        about.setBorderPainted(false);
        about.setFocusPainted(false);
        about.setContentAreaFilled(false);
        about.setBounds(160, 10, 70, 30);
        this.add(about);

        // 分数显示
        JLabel scoreLabel = new JLabel("分数:0");
        scoreLabel.setBounds(5, 45, 120, 30);
        scoreLabel.setFont(new Font("幼圆", Font.CENTER_BASELINE, 18));
        scoreLabel.setForeground(new Color(0x000000));
        this.add(scoreLabel);

        //静音按钮
        JCheckBox isSoundBox = new JCheckBox("静音");
        isSoundBox.setBounds(320, 45, 120, 30);
        isSoundBox.setFont(new Font("黑体", Font.CENTER_BASELINE, 18));
        isSoundBox.setFocusable(false);
        isSoundBox.setBorderPainted(false);
        isSoundBox.setFocusPainted(false);
        isSoundBox.setContentAreaFilled(false);
        this.add(isSoundBox);

        this.setDefaultCloseOperation(EXIT_ON_CLOSE);//使用 System exit 方法退出应用程序。仅在应用程序中使用。
        this.setResizable(false); //设置此窗体是否可由用户调整大小。
        this.setVisible(true);// 显示界面

        // 创建事件处理类,将事件源绑定监听器
        ComponentListener cl = new ComponentListener(this, Numbers, scoreLabel, start, about, back, isSoundBox);
        start.addActionListener(cl);
        about.addActionListener(cl);
        back.addActionListener(cl);
        isSoundBox.addActionListener(cl);
        this.addKeyListener(cl);
    }

    // 重写窗体
    @Override
    //paint是Java中AWT画图方法
    public void paint(Graphics g) {
        //调用父类的构造方法
        super.paint(g);
        //设置画笔颜色
        g.setColor(new Color(0x66FF66));
        //填充整个4*4圆角矩形区域,使用当前颜色填充指定的圆角矩形
        g.fillRoundRect(15, 110, 370, 370, 15, 15);// 大矩形框

        g.setColor(new Color(0xFFFAFA));
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                //填充每一个4*4小方格区域
                g.fillRoundRect(25 + i * 90, 120 + j * 90, 80, 80, 15, 15);// 小矩形框
            }
        }

        // 调整数字的位置并上色
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                //如果小方格上数字不为0,则说明有值,进行绘制背景色与数字
                if (Numbers[j][i] != 0) {
                    int FontSize = 30;
                    int MoveX = 0, MoveY = 0;
                    switch (Numbers[j][i]) {
                        case 2:
                            g.setColor(new Color(0xFF0000));
                            FontSize = 30;
                            MoveX = 0;
                            MoveY = 0;
                            break;
                        case 4:
                            g.setColor(new Color(0xede0c8));
                            FontSize = 30;
                            MoveX = 0;
                            MoveY = 0;
                            break;
                        case 8:
                            g.setColor(new Color(0xf2b179));
                            FontSize = 30;
                            MoveX = 0;
                            MoveY = 0;
                            break;
                        case 16:
                            g.setColor(new Color(0xf59563));
                            FontSize = 29;
                            MoveX = -5;
                            MoveY = 0;
                            break;
                        case 32:
                            g.setColor(new Color(0xf67c5f));
                            FontSize = 29;
                            MoveX = -5;
                            MoveY = 0;
                            break;
                        case 64:
                            g.setColor(new Color(0xf65e3b));
                            FontSize = 29;
                            MoveX = -5;
                            MoveY = 0;
                            break;
                        case 128:
                            g.setColor(new Color(0xedcf72));
                            FontSize = 28;
                            MoveX = -10;
                            MoveY = 0;
                            break;
                        case 256:
                            g.setColor(new Color(0xedcc61));
                            FontSize = 28;
                            MoveX = -10;
                            MoveY = 0;
                            break;
                        case 512:
                            g.setColor(new Color(0xedc850));
                            FontSize = 28;
                            MoveX = -10;
                            MoveY = 0;
                            break;
                        case 1024:
                            g.setColor(new Color(0xedc53f));
                            FontSize = 27;
                            MoveX = -15;
                            MoveY = 0;
                            break;
                        case 2048:
                            g.setColor(new Color(0xedc22e));
                            FontSize = 27;
                            MoveX = -15;
                            MoveY = 0;
                            break;
                        default:
                            g.setColor(new Color(0x000000));
                            break;
                    }
                    //数字不为0的小方格覆盖原色,根据不同的值上不同的色
                    g.fillRoundRect(25 + i * 90, 120 + j * 90, 80, 80, 15, 15);// 小矩形框覆盖上色
                    g.setColor(new Color(0x000000));
                    g.setFont(new Font("Kristen ITC", Font.PLAIN, FontSize));
                    //绘制字符串,参数分别为:要绘制的字符串,字符串绘制的x坐标,y坐标
                    g.drawString(Numbers[j][i] + "", 25 + i * 90 + 30 + MoveX,
                            120 + j * 90 + 50 + MoveY);
                }
            }
        }
    }

}

(二)实现监听(具体功能的实现)

package com.baidu.czy;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.Random;
import javax.swing.*;

public class ComponentListener extends KeyAdapter implements ActionListener {

    private GameStart UI;// 界面对象
    private int Numbers[][];// 存放数据的数组
    private Random rand = new Random();
    private int BackUp[][] = new int[4][4];//用于备份数组,供回退时使用
    private int BackUp2[][] = new int[4][4];//用于备份数组,供起死回生时使用
    public JLabel lb;   //分数标签
    int score = 0;
    int tempscore, tempscore2;//记录回退的分数值
    public JButton bt, about, back;
    public JCheckBox isSoundBox;
    //是否胜利,true:胜利,false:失败
    private boolean isWin = false;
    //是否复活,true:使用复活,false:不使用复活
    private boolean relive = false;
    //是否可以回退,true:不可回退,false:可以回退  (是否已经进行过一次回退了)
    private boolean hasBack = false;
    //是否播放音乐,true:播放音效,false:不播放音效
    private boolean isSound = true;
    //事件
    private ActionEvent e;

    public ComponentListener(GameStart UI, int[][] Numbers, JLabel lb, JButton bt, JButton about, JButton back, JCheckBox isSoundBox) {
        this.UI = UI;
        this.Numbers = Numbers;
        this.lb = lb;
        this.bt = bt;
        this.about = about;
        this.back = back;
        this.isSoundBox = isSoundBox;
    }

   @Override
   public void actionPerformed(ActionEvent e) {
          if (e.getSource() == bt ) {
              //游戏开始
              isWin = false;
              //各个小格赋初值0
              for (int i = 0; i < 4; i++)
                  for (int j = 0; j < 4; j++)
                      Numbers[i][j] = 0;
                      //游戏开始,分数为0
                      score = 0;
                      lb.setText("分数:" + score);
                      //生成4个0-3之间的随机数
                      int r1 = rand.nextInt(4);
                      int r2 = rand.nextInt(4);
                      int c1 = rand.nextInt(4);
                      int c2 = rand.nextInt(4);
                      //由r1,c1;r2,c2组成两个初始值,所以初始值的坐标不能重复
                      while (r1 == r2 && c1 == c2) {
                          r2 = rand.nextInt(4);
                          c2 = rand.nextInt(4);
                      }
                      // 生成初始数字(2或者4)
                      int value1 = rand.nextInt(2) * 2 + 2;
                      int value2 = rand.nextInt(2) * 2 + 2;

                      // 把数字存进对应的位置
                      Numbers[r1][c1] = value1;
                      Numbers[r2][c2] = value2;
                      //数字更改,重新绘制图形,为此组件创建图形上下文
                      UI.paint(UI.getGraphics());

        } else if (e.getSource() == about) {
            //点击了关于标签
            JOptionPane.showMessageDialog(UI, "游戏规则:\n"
                    + "1、开始时棋盘内随机出现两个数字,出现的数字仅可能为2或4\n"
                    + "2、玩家可以选择上下左右四个方向,若棋盘内的数字出现位移或合并,视为有效移动\n"
                    + "3、玩家选择的方向上若有相同的数字则合并,每次有效移动可以同时合并,但不可以连续合并\n"
                    + "4、合并所得的所有新生成数字相加即为该步的有效得分\n"
                    + "5、玩家选择的方向行或列前方有空格则出现位移\n"
                    + "6、每有效移动一步,棋盘的空位(无数字处)随机出现一个数字(依然可能为2或4)\n"
                    + "7、棋盘被数字填满,无法进行有效移动,判负,游戏结束\n"
                    + "8、棋盘上出现2048,判胜,游戏结束。\n"
            );
        } else if (e.getSource() == back && hasBack == false) {
            System.out.println("回退");
            //点击了回退一步标签,而且只能回退一次,只有再执行一次上下左右操作才可以再次回退
            hasBack = true;
            //判断本次回退是回退上一步,还是复活,回退上上步
            if (relive == false) {
                //替换上一步的分数
                score = tempscore;
                lb.setText("分数:" + score);
                for (int i = 0; i < BackUp.length; i++) {
                    Numbers[i] = Arrays.copyOf(BackUp[i], BackUp[i].length);
                }
            } else {
                //选择了起死回生
                score = tempscore2;
                lb.setText("分数:" + score);
                for (int i = 0; i < BackUp2.length; i++) {
                    Numbers[i] = Arrays.copyOf(BackUp2[i], BackUp2[i].length);
                }
                //再给一次复活的机会
                relive = false;
            }
            //重新绘制
            UI.paint(UI.getGraphics());
        } else if (e.getSource().equals(isSoundBox)) {
            //是否选中静音复选框
            if (isSoundBox.isSelected()) {
                isSound = false;
            } else {
                isSound = true;
            }
        }
    }

    // 键盘监听,监听游戏焦点的←,↑,→,↓;方向键键值:左:37上:38右:39下:40
    public void keyPressed(KeyEvent event) {

        int Counter = 0;// 记录数字有效移动位数,判断是否移动了
        int NumCounter = 0;// 记录当前有数字的小方格数量,判断是否已满
        int NumNearCounter = 0;// 记录相邻格子数字相同的对数

        hasBack = false;
        //每次进行真正的移位合并操作之前,记录前一步

        //记录上上步
        if (BackUp != null || BackUp.length != 0) {
            tempscore2 = tempscore;// 先把分数备份好
            // 下面的for循环调用java.util.Arrays.copyOf()方法复制数组,实现备份
            for (int i = 0; i < BackUp.length; i++) {
                BackUp2[i] = Arrays.copyOf(BackUp[i], BackUp[i].length);
            }
        }
        //记录上步
        tempscore = score;// 先把分数备份好
        // 下面的for循环调用java.util.Arrays.copyOf()方法复制数组,实现备份
        for (int i = 0; i < Numbers.length; i++) {
            BackUp[i] = Arrays.copyOf(Numbers[i], Numbers[i].length);
        }

        if (isWin == false) {
            switch (event.getKeyCode()) {

                case 37:
                    // 向左移动
                    /*
                    (1)在移动的过程中,判断与其相邻的格子,如果相邻的格子为空,则移动,并将当前的格子清0
                    (2)移动后,若格子相邻并且数值相等,则求和并且清0
                     */
                    if (isSound == true) {
                        new PlaySound("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\move.wav").start();// 播放移位音乐
                    }
                    //经过这个循环,把每行有值的格子,都被搬到最左边了,同一行右侧有值的格子,覆盖左侧值为0的格子
                    for (int h = 0; h < 4; h++)
                        for (int l = 0; l < 4; l++)
                            if (Numbers[h][l] != 0) {
                                int temp = Numbers[h][l];
                                //per相当于是相邻位置
                                int pre = l - 1;
                                while (pre >= 0 && Numbers[h][pre] == 0) {
                                    Numbers[h][pre] = temp;
                                    //移动后清0
                                    Numbers[h][pre + 1] = 0;
                                    pre--;
                                    Counter++;
                                }
                            }
                    //表盘当前左侧相邻相等的值会相加,造成左边值为【和】,相邻右边值为【0】
                    for (int h = 0; h < 4; h++)
                        for (int l = 0; l < 4; l++)
                            if (l + 1 < 4
                                    && (Numbers[h][l] == Numbers[h][l + 1])     //相邻两列数值相加,并要求两列不同时为0
                                    && (Numbers[h][l] != 0 || Numbers[h][l + 1] != 0)) {
                                if (isSound == true)
                                    new PlaySound("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\merge.wav").start();
                                //数值合并
                                Numbers[h][l] = Numbers[h][l] + Numbers[h][l + 1];
                                Numbers[h][l + 1] = 0;
                                Counter++;
                                score += Numbers[h][l];
                                if (Numbers[h][l] == 2048) {
                                    isWin = true;
                                }
                            }
                    //经过这个循环,把每行有值的格子,都被搬到最左边了,同一行右侧有值的格子,覆盖左侧值为0的格子
                    for (int h = 0; h < 4; h++)
                        for (int l = 0; l < 4; l++)
                            if (Numbers[h][l] != 0) {
                                int temp = Numbers[h][l];
                                int pre = l - 1;
                                while (pre >= 0 && Numbers[h][pre] == 0) {
                                    Numbers[h][pre] = temp;
                                    Numbers[h][pre + 1] = 0;
                                    pre--;
                                    Counter++;
                                }
                            }
                    break;

                case 39:// 向右移动
                    if (isSound == true)
                        new PlaySound("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\move.wav").start();
                    for (int h = 3; h >= 0; h--)
                        for (int l = 3; l >= 0; l--)
                            if (Numbers[h][l] != 0) {
                                int temp = Numbers[h][l];
                                int pre = l + 1;
                                while (pre <= 3 && Numbers[h][pre] == 0) {
                                    Numbers[h][pre] = temp;
                                    Numbers[h][pre - 1] = 0;
                                    pre++;
                                    Counter++;
                                }
                            }

                    for (int h = 3; h >= 0; h--)
                        for (int l = 3; l >= 0; l--)
                            if (l + 1 < 4
                                    && (Numbers[h][l] == Numbers[h][l + 1])
                                    && (Numbers[h][l] != 0 || Numbers[h][l + 1] != 0)) {
                                if (isSound == true)
                                    new PlaySound("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\merge.wav").start();
                                Numbers[h][l + 1] = Numbers[h][l]
                                        + Numbers[h][l + 1];
                                Numbers[h][l] = 0;
                                Counter++;
                                score += Numbers[h][l + 1];
                                if (Numbers[h][l + 1] == 2048) {
                                    isWin = true;
                                }
                            }
                    for (int h = 3; h >= 0; h--)
                        for (int l = 3; l >= 0; l--)
                            if (Numbers[h][l] != 0) {
                                int temp = Numbers[h][l];
                                int pre = l + 1;
                                while (pre <= 3 && Numbers[h][pre] == 0) {
                                    Numbers[h][pre] = temp;
                                    Numbers[h][pre - 1] = 0;
                                    pre++;
                                    Counter++;
                                }
                            }
                    break;

                case 38:
                    // 向上移动
                    if (isSound == true)
                        new PlaySound("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\move.wav").start();
                    for (int l = 0; l < 4; l++)
                        for (int h = 0; h < 4; h++)
                            if (Numbers[h][l] != 0) {
                                int temp = Numbers[h][l];
                                int pre = h - 1;
                                while (pre >= 0 && Numbers[pre][l] == 0) {
                                    Numbers[pre][l] = temp;
                                    Numbers[pre + 1][l] = 0;
                                    pre--;
                                    Counter++;
                                }
                            }
                    for (int l = 0; l < 4; l++)
                        for (int h = 0; h < 4; h++)
                            if (h + 1 < 4
                                    && (Numbers[h][l] == Numbers[h + 1][l])
                                    && (Numbers[h][l] != 0 || Numbers[h + 1][l] != 0)) {
                                if (isSound == true)
                                    new PlaySound("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\merge.wav").start();
                                Numbers[h][l] = Numbers[h][l] + Numbers[h + 1][l];
                                Numbers[h + 1][l] = 0;
                                Counter++;
                                score += Numbers[h][l];
                                if (Numbers[h][l] == 2048) {
                                    isWin = true;
                                }
                            }

                    for (int l = 0; l < 4; l++)
                        for (int h = 0; h < 4; h++)
                            if (Numbers[h][l] != 0) {
                                int temp = Numbers[h][l];
                                int pre = h - 1;
                                while (pre >= 0 && Numbers[pre][l] == 0) {
                                    Numbers[pre][l] = temp;
                                    Numbers[pre + 1][l] = 0;
                                    pre--;
                                    Counter++;
                                }
                            }
                    break;

                case 40:
                    // 向下移动
                    if (isSound == true)
                        new PlaySound("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\move.wav").start();
                    for (int l = 3; l >= 0; l--)
                        for (int h = 3; h >= 0; h--)
                            if (Numbers[h][l] != 0) {
                                int temp = Numbers[h][l];
                                int pre = h + 1;
                                while (pre <= 3 && Numbers[pre][l] == 0) {
                                    Numbers[pre][l] = temp;
                                    Numbers[pre - 1][l] = 0;
                                    pre++;
                                    Counter++;
                                }
                            }
                    for (int l = 3; l >= 0; l--)
                        for (int h = 3; h >= 0; h--)
                            if (h + 1 < 4
                                    && (Numbers[h][l] == Numbers[h + 1][l])
                                    && (Numbers[h][l] != 0 || Numbers[h + 1][l] != 0)) {
                                if (isSound == true)
                                    new PlaySound("E:\\JavaProject\\HandleGame2048(2)\\src\\main\\resources\\res\\merge.wav").start();
                                Numbers[h + 1][l] = Numbers[h][l]
                                        + Numbers[h + 1][l];
                                Numbers[h][l] = 0;
                                Counter++;
                                score += Numbers[h + 1][l];
                                if (Numbers[h + 1][l] == 2048) {
                                    isWin = true;
                                }
                            }

                    for (int l = 3; l >= 0; l--)
                        for (int h = 3; h >= 0; h--)
                            if (Numbers[h][l] != 0) {
                                int temp = Numbers[h][l];
                                int pre = h + 1;
                                while (pre <= 3 && Numbers[pre][l] == 0) {
                                    Numbers[pre][l] = temp;
                                    Numbers[pre - 1][l] = 0;
                                    pre++;
                                    Counter++;
                                }
                            }
                    break;

            }
            //移位,合并,移位完成后,判断是否有可重复值
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    //判断相邻左右位置有没有重复值
                    if (Numbers[i][j] == Numbers[i][j + 1]
                            && Numbers[i][j] != 0) {
                        NumNearCounter++;
                    }
                    //判断相邻上下位置有没有重复值
                    if (Numbers[i][j] == Numbers[i + 1][j]
                            && Numbers[i][j] != 0) {
                        NumNearCounter++;
                    }
                    if (Numbers[3][j] == Numbers[3][j + 1]//第四行只需要判断是否与右边有重复
                            && Numbers[3][j] != 0) {
                        NumNearCounter++;
                    }
                    if (Numbers[i][3] == Numbers[i + 1][3]//第四列只需要判断与下边是否有重复
                            && Numbers[i][3] != 0) {
                        NumNearCounter++;
                    }
                }
            }
            //判断不为0的空余格子数
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    if (Numbers[i][j] != 0) {
                        NumCounter++;
                    }
                }
            }
            System.out.println(Counter);

            //有效移位数>0,则补充一个新的2或者4
            if (Counter > 0) {
                lb.setText("分数:" + score);
                //随机产生0~3的数字,选中位置
                int r1 = rand.nextInt(4);
                int c1 = rand.nextInt(4);
                while (Numbers[r1][c1] != 0) {
                    r1 = rand.nextInt(4);
                    c1 = rand.nextInt(4);
                }
                //产生2或4
                int value1 = rand.nextInt(2) * 2 + 2;
                Numbers[r1][c1] = value1;
            }

            if (isWin == true) {
                UI.paint(UI.getGraphics());
                JOptionPane.showMessageDialog(UI, "恭喜你赢了!\n您的最终得分为:" + score);
            }

            if (NumCounter == 16 && NumNearCounter == 0) {
                //移动后满格并且没有可合并的小格子,游戏结束relive:复活一次
                relive = true;
                JOptionPane.showMessageDialog(UI, "没地方可以合并咯!!"
                        + "\n很遗憾,您输了~>_<~" + "\n悄悄告诉你,游戏有起死回生功能哦,不信你“退一步”试试?"
                        + "\n说不定能扭转乾坤捏 (^_~)");
            }
            UI.paint(UI.getGraphics());
        }

    }

}

(三)实现线程播放音乐

package com.baidu.czy;

import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

//播放声音的线程
public class PlaySound extends Thread {
    private String filename;

    public PlaySound(String wavfile) {

        filename = "" + wavfile;
    }
    public void run() {
        File soundFile = new File(filename);
        AudioInputStream audioInputStream = null;
        try {
            //获得音频输入流
            audioInputStream = AudioSystem.getAudioInputStream(soundFile);
        } catch (Exception e1) {
            e1.printStackTrace();
            return;
        }
        //指定声音流中特定数据安排
        AudioFormat format = audioInputStream.getFormat();
        SourceDataLine auline = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
        try {
            //从混频器获得源数据行
            auline = (SourceDataLine) AudioSystem.getLine(info);
            //打开具有指定格式的行,这样可使行获得所有所需的系统资源并变得可操作。
            auline.open(format);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        //允许数据行执行数据 I/O
        auline.start();
        int nBytesRead = 0;
        // 这是缓冲
        byte[] abData = new byte[512];
        try {
            while (nBytesRead != -1) {
                //从音频流读取指定的最大数量的数据字节,并将其放入给定的字节数组中
                nBytesRead = audioInputStream.read(abData, 0, abData.length);
                if (nBytesRead >= 0)
                    //通过此源数据行将音频数据写入混频器
                    auline.write(abData, 0, nBytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;
        } finally {
            auline.drain();
            auline.close();
        }
    }
}

(四)测试类

package com.baidu.czy.test;

import com.baidu.czy.GameStart;

public class StartGameTest {
    public static void main(String[] args) {
         GameStart view = new GameStart();
         view.init();
         new GameStart();

    }
}

三、运行结果

原文地址:https://www.cnblogs.com/my-program-life/p/10987657.html

时间: 2024-11-09 00:55:47

Java 实现2048游戏之详细教程的相关文章

10分钟教你用Python做个打飞机小游戏超详细教程

更多精彩尽在微信公众号[程序猿声] 我知道你们一定想先看效果如何 00 目录 整体框架 开始之前-精灵类Sprite 子弹类class Bullet 玩家飞机类class Player 敌机类class Enemy 游戏主体循环以及帧率设置 让子弹飞 刷出敌机 打怪 把飞机敌机子弹都画出来 处理键盘事件 分数显示 和 GameOver 最终代码 01 前言 这次还是用python的pygame库来做的游戏.关于这个库的内容,读者可以上网了解一下.本文只讲解用到的知识.代码参考自网上,自己也做了一

Java环境变量配置超详细教程

概述 Java的环境配置并不是特别难,但是对刚上手的新手来说确实是一个大问题 首先下载jdk安装包网址进去选择自己需要的版本下载就行了,这里演示的是jdk-8u131-windows-x64_8.0.1310.11,版本稍老,道理一样 安装 下载后直接双击运行,这时会让你选择安装路径默认是C盘,这里改成自己想要存放的地方,我放在了我D盘的ROUTE文件夹下的java目录下的jdk1.8.0_131然后点击确定,下一步,开始跑进度条,但是不一会就会弹窗出来jre的安装路径我们点击更改,然后选择一个

【数据结构】10分钟教你用栈求解迷宫老鼠问题超详细教程附C++源代码

问题描述 给定一张迷宫地图和一个迷宫入口,然后进入迷宫探索找到一个出口.如下图所示: 该图是一个矩形区域,有一个入口和出口.迷宫内部包含不能穿越的墙壁或者障碍物.这些障碍物沿着行和列放置,与迷宫的边界平行.迷宫的入口在左上角,出口在右下角. 问题分析 首先要有一张迷宫地图,地图由两部分组成: (1)一是迷宫中各处的位置坐标, (2)二是迷宫各位置处的状态信息,即该处是墙还是路 所以,该迷宫地图可由一个二维数组来表示.数组的横纵坐标表示迷宫各处的位置坐标,数组元素表示各位置处的状态信息. 2.在这

2016年netty/mina/java nio视频教程java游戏服务器设计教程

2016年netty/mina/Javanio视频教程Java游戏服务器设计教程 需要的加qq:1225462853,备注:程序员学习视频 其他视频都可以索要(Netty   NET    C++ 等等) 互联网架构师教程:http://blog.csdn.net/pplcheer/article/details/71887910 netty录制时间为2015.11-2016.2月份  netty教程为加密视频!      netty12个课程已全部录制完成,相信通过这12节课的分析能让大家对n

程序设计C语言二级考试教程 Java基础视频教程 安卓软件开发教程 Unity3D游戏制作入门教程

热门推荐电脑办公计算机基础知识教程 Excel2010基础教程 Word2010基础教程 PPT2010基础教程 五笔打字视频教程 Excel函数应用教程 Excel VBA基础教程 WPS2013表格教程 更多>平面设计PhotoshopCS5教程 CorelDRAW X5视频教程 Photoshop商业修图教程 Illustrator CS6视频教程 更多>室内设计3Dsmax2012教程 效果图实例提高教程 室内设计实战教程 欧式效果图制作实例教程 AutoCAD2014室内设计 Aut

网页版《2048游戏》教程 - 构建页面

1.     游戏标题 <2048>游戏的标题包含游戏名称.开始新游戏的按钮和游戏分数等三项内容. 创建游戏页面index.html: <!DOCTYPE html> <html > <head > < meta charset= "UTF-8"> < title>2048</title > < link rel= "stylesheet" type ="text/c

网页版《2048游戏》教程 - 游戏初始化

3.1.     初始化棋盘格 我们在main.js文件中,创建newgame()方法用于开始新的游戏.而开始新游戏需要完成两件事情,一是初始化棋盘格,一是在随机两个格子生成两个数字. $(function () { newgame(); }); function newgame() { // 初始化棋盘格 init(); // 在随机两个格子生成数字 generateOneNumber(); generateOneNumber(); } 我们通过编写init()方法来完成棋盘格的初始化工作.棋

网页版《2048游戏》教程 - 完成游戏逻辑

1.     捕获键盘事件 <2048>游戏的操作主要是依靠键盘的上.下.左.右来完成,首先我们需要在game.js文件中捕获键盘响应的事件. $(document).keydown(function (event) { switch (event.keyCode) { case 37://left break; case 38://up break; case 39://right break; case 40://down break; default : break; } }); 键盘事

网页版《2048游戏》教程 - 游戏优化

1.     GameOver部分 下面我们来分析游戏是如何结束的.一种情况是棋盘格中没有空的格子了,一种情况是棋盘格中没有可以移动的格子了. 首先,完成isgameover()方法的逻辑. function isgameover() { if (nospace(board) && nomove(board)) { gameover(); } } 其次,完成棋盘格中没有空的格子. function nospace(board) { for (var i = 0; i < 4; i++