Java课程设计:带注释的2048小游戏实现

2048小游戏

功能要求:

        每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢、相撞时会相加。不断的叠加最终拼凑出2048这个数字就算成功。

package Other; //声明,表示当前代码所在的包名为 Other

import java.awt.*;//这是 Java 的抽象窗口工具包(Abstract Window Toolkit),提供了创建用户界面和处理图形界面事件的类和接口
import java.awt.event.KeyEvent;//这个类表示键盘事件,例如按键按下和释放。
import java.awt.event.KeyListener;//这个接口用于接收和处理键盘事件
import java.awt.event.MouseEvent;//这个类表示鼠标事件,例如鼠标点击、移动和拖动。
import java.awt.event.MouseListener;//这个类表示鼠标事件,例如鼠标点击、移动和拖动。
import java.awt.event.WindowEvent;//这个类表示窗口事件,例如窗口打开、关闭和最小化。
import java.util.*;//导入了整个 java.util 包中的所有类。java.util 包包含了许多用于数据结构、集合、算法和通用工具的类。
import javax.swing.*;//Java Swing 是一个用于创建图形用户界面(GUI)应用程序的库。它提供了许多类和接口,用于创建窗口、按钮、文本框、标签等用户界面组件。


@SuppressWarnings("serial")
public class Game2048 extends JFrame {
    // 移动方向
    final public static int MOVE_UP = 0xf37;
    final public static int MOVE_DOWN = 0xf36;
    final public static int MOVE_LEFT = 0xf35;
    final public static int MOVE_RIGHT = 0xf34;
    // 游戏状态
    final public static int GAME_OVER = 0xf33;
    final public static int GAME_CONTINUE = 0xf32;
    final public static int GAME_WIN = 0xf31;
    // 按钮事件
    final public static int BUTTON_NEW_GAME = 0xf30;
    final public static int BUTTON_ABOUT = 0xf28;
    final public static int BUTTON_EXIT = 0xf27;
    /*
     这段代码是一个 Java 类,定义了一些常量用于表示游戏 2048 的不同状态和操作。
MOVE_UP、MOVE_DOWN、MOVE_LEFT 和 MOVE_RIGHT 常量表示游戏中方块的移动方向,分别对应上、下、左、右。
GAME_OVER、GAME_CONTINUE 和 GAME_WIN 常量表示游戏的不同状态,分别对应游戏结束、游戏继续和游戏胜利。
BUTTON_NEW_GAME、BUTTON_ABOUT 和 BUTTON_EXIT 常量表示游戏界面上按钮的事件,分别对应新游戏、关于和退出按钮。
这些常量用于在游戏逻辑中表示不同的状态和操作,方便代码的编写和理解。
     */

    
    /**
     * 行
     */
    private int column;
    /**
     * 列
     */
    private int row;
    /**
     * 游戏状态
     */
    private int gameState;
    /**
     * 网格集
     */
    private HashMap<Point, Cube> viewList = new HashMap<>();
    /**
     * 计分板
     */
    private JMenuItem scoreBoard;
    /**
     * 新游戏加流程
     */
    private int gameLv;

    /**
     * main函数
     * 
     * @param args
     */
    
    /*
     这段代码是一个 Java 类的成员变量定义部分。每个变量都有自己的注释,解释了它们的用途和含义:
- `column`:表示游戏界面的列数。
- `row`:表示游戏界面的行数。
- `gameState`:表示游戏的状态,可能是进行中、结束、获胜等状态。
- `viewList`:是一个 `HashMap<Point, Cube>` 类型的变量,用于存储游戏界面的网格信息。`Point` 表示网格的坐标,`Cube` 表示网格上的方块。
- `scoreBoard`:表示计分板,可能是一个菜单项或者其他用于显示分数的组件。
- `gameLv`:表示游戏的难度等级。

`main` 函数是 Java 程序的入口点,它接收一个 `String` 类型的参数 `args`,用于传递命令行参数。在这个代码片段中,没有看到 `main` 函数的定义,因此无法提供更多关于它的信息。
     */
    
    
    
    public static void main(String[] args) {
        Game2048 game = new Game2048(400, 500);//创建一个名为 game 的 Game2048 游戏对象,并设置游戏窗口的宽度为 400 像素,高度为 500 像素。
        game.setTitle("2048");//设置游戏窗口的标题为 "2048"。
        game.setLocationRelativeTo(null);//将游戏窗口的位置设置为居中。
        game.setVisible(true);//显示游戏窗口。
        game.newGame();//开始新游戏。
    }
    //创建了一个名为 Game2048 的游戏对象,并设置了游戏窗口的大小、标题和位置。然后,它显示游戏窗口并开始新游戏。

    /**
     * 构造一个默认大小的界面
     */
    public Game2048() {
        this(400, 500);
    }

    /**
     * 构造一个指定宽高的界面
     * 
     * @param width
     *            宽
     * @param height
     *            高
     */
    public Game2048(int width, int height) {
        column = width / 100;
        row = height / 100;

        this.setLayout(new GridLayout(row, column));
        // 事件监听
        enableEvents(AWTEvent.WINDOW_EVENT_MASK);
        this.setSize(width, height);

        // 利用button 绘制网格
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                viewList.put(new Point(i, j), new Cube());
                this.add(getView(i, j).getView());
            }
        }
        /*这段代码是一个名为 `Game2048` 的类的构造函数,它接受两个整数参数 `width` 和 `height`,用于设置游戏界面的大小。
首先,它计算了每个网格的大小,并设置了游戏界面的布局为网格布局。然后,它启用了事件监听,以便能够响应窗口事件。接着,它设置了游戏界面的大小。
接下来,它使用循环来绘制游戏的网格。它创建了一个 `Point` 对象来表示每个网格的位置,并将其添加到一个列表中。然后,它创建了一个 `Cube` 对象来表示每个网格,并将其添加到游戏界面中。
最后,它返回了一个包含网格视图的列表,以便在游戏中进行操作。
总体来说,这段代码的作用是创建一个 `Game2048` 游戏对象,并设置游戏界面的大小和布局,然后绘制游戏的网格。*/

        // 设置按键监听,添加一个键盘事件监听器的代码。它的作用是当用户在键盘上按下或释放某个键时,会触发监听器中的相应方法。
        this.addKeyListener(new MyKeyListener(this));//将一个自定义的MyKeyListener键盘事件监听器添加到当前组件(通常是一个窗口)上。当用户在键盘上按下或释放按键时,MyKeyListener的相应方法将会被触发。

        JMenuBar jmb = new JMenuBar();//创建一个菜单栏对象。
        JMenu jm = new JMenu("游戏");//创建一个菜单对象,并将其命名为 "游戏"。
        JMenuItem item1 = new JMenuItem("新游戏");//创建一个菜单项对象,并将其文本设置为 "新游戏"。
        item1.addMouseListener(new MyMouseListener(this,
                Game2048.BUTTON_NEW_GAME));//将一个自定义的MyMouseListener鼠标事件监听器添加到菜单项上。当用户鼠标点击该菜单项时,MyMouseListener的相应方法将会被触发。
        JMenuItem item2 = new JMenuItem("退出");//创建另一个菜单项对象,并将其文本设置为 "退出"。
        item2.addMouseListener(new MyMouseListener(this, Game2048.BUTTON_EXIT));//将另一个自定义的MyMouseListener鼠标事件监听器添加到菜单项上。当用户鼠标点击该菜单项时,MyMouseListener的相应方法将会被触发。
        jm.add(item1);//将 "新游戏" 菜单项添加到 "游戏" 菜单中。
        jm.add(item2);//将 "退出" 菜单项添加到 "游戏" 菜单中。

        JMenu jm2 = new JMenu("关于");//创建另一个菜单对象,并将其命名为 "关于"。
        JMenuItem item3 = new JMenuItem("关于");//创建一个菜单项对象,并将其文本设置为 "关于"。
        item3.addMouseListener(new MyMouseListener(this, Game2048.BUTTON_ABOUT));//将另一个自定义的MyMouseListener鼠标事件监听器添加到菜单项上。当用户鼠标点击该菜单项时,MyMouseListener的相应方法将会被触发。
        jm2.add(item3);//将 "关于" 菜单项添加到 "关于" 菜单中。

        scoreBoard = new JMenuItem();//创建一个菜单项对象,用于显示分数板。

        jmb.add(jm);//将 "游戏" 菜单添加到菜单栏中。
        jmb.add(jm2);//将 "关于" 菜单添加到菜单栏中。
        jmb.add(scoreBoard);//将分数板菜单项添加到菜单栏中。
        this.setJMenuBar(jmb);//将菜单栏设置为当前窗口的菜单栏。
    }

    /**
     * 向上移动
     */
    public void up() {
        for (int x = 1; x < row; x++) {
            for (int i = 0; i < column; i++) {
                move(Game2048.MOVE_UP, x, i, true);
            }
        }
//这个循环会遍历游戏界面的每一行(除了第一行),以及每一列。对于每个位置,它调用了move()方法,并传入Game2048.MOVE_UP作为移动方向,x和i作为当前位置的行和列索引,以及true作为一个标志,表示这是一个“真实”的移动。
        //move()方法根据传入的参数执行方块的移动操作
        
        createCube();
        for (int x = 1; x < row; x++) {
            for (int i = 0; i < column; i++) {
                move(Game2048.MOVE_UP, x, i, false);
            }//这个循环会再次遍历游戏界面的每一行(除了第一行),以及每一列。对于每个位置,它再次调用了move()方法,但这次传入Game2048.MOVE_UP作为移动方向,x和i作为当前位置的行和列索引,以及false作为一个标志,表示这是一个“虚拟”的移动,用于创建新的方块。
             //同样地,move()方法根据传入的参数执行方块的移动操作。
             //其他的移动方法(down()、left()和right())的实现方式与up()方法类似,只是它们的移动方向不同。
             //总体来说,这些方法通过循环和调用move()方法来实现方块在游戏界面的上、下、左、右移动,并通过第二个循环来创建新的方块。
        }
    }

    /**
     * 向下移动
     */
    public void down() {
        for (int x = row - 2; x >= 0; x--) {
            for (int y = 0; y < column; y++) {
                move(Game2048.MOVE_DOWN, x, y, true);
            }
        }

        createCube();
        for (int x = row - 2; x >= 0; x--) {
            for (int y = 0; y < column; y++) {
                move(Game2048.MOVE_DOWN, x, y, false);
            }
        }
    }

    /**
     * 向左移动
     */
    public void left() {
        for (int y = 1; y < column; y++) {
            for (int x = 0; x < row; x++) {
                move(Game2048.MOVE_LEFT, x, y, true);
            }
        }

        createCube();
        for (int y = 1; y < column; y++) {
            for (int x = 0; x < row; x++) {
                move(Game2048.MOVE_LEFT, x, y, false);
            }
        }

    }

    /**
     * 向右移动
     */
    public void right() {
        for (int y = column - 2; y >= 0; y--) {
            for (int x = 0; x < row; x++) {
                move(Game2048.MOVE_RIGHT, x, y, true);
            }
        }

        createCube();
        for (int y = column - 2; y >= 0; y--) {
            for (int x = 0; x < row; x++) {
                move(Game2048.MOVE_RIGHT, x, y, false);
            }
        }
    }

    /**
     * 移动
     * 
     * @param move_way
     *            移动方向
     * @param x
     *            横坐标
     * @param y
     *            纵坐标
     */
    private void move(int move_way, int x, int y, boolean isAdd) {//这段代码是一个名为 move() 的方法,
    	                                                          //它根据传入的移动方向(move_way)和坐标(x 和 y)执行相应的方块移动操作。
    	                                                          //根据不同的移动方向,它会进行一系列的交换操作。
        switch (move_way) {
        case Game2048.MOVE_DOWN: { // 这是向下移动的情况。它通过一个循环,从当前位置(x)开始,向下移动到行的最后一个位置(row - 1),每次移动都将当前位置的方块与下一个位置的方块进行交换。这个循环会持续直到到达行的末尾。
            for (; x < row - 1; x++) {
                swap(getView(x + 1, y), getView(x, y), isAdd);
            }
        }
            break;

        case Game2048.MOVE_LEFT: { // 这是向左移动的情况。它通过一个循环,从当前位置(y)开始,向左移动到列的第一个位置(0),每次移动都将当前位置的方块与左边位置的方块进行交换。这个循环会持续直到到达列的开头。
            for (; y > 0; y--) {
                swap(getView(x, y - 1), getView(x, y), isAdd);
            }
        }
            break;

        case Game2048.MOVE_RIGHT: { //这是向右移动的情况。它通过一个循环,从当前位置(y)开始,向右移动到列的最后一个位置(column - 1),每次移动都将当前位置的方块与右边位置的方块进行交换。这个循环会持续直到到达列的末尾。
            for (; y < column - 1; y++) {
                swap(getView(x, y + 1), getView(x, y), isAdd);
            }
        }
            break;

        case Game2048.MOVE_UP: { //这是向上移动的情况。它通过一个循环,从当前位置(x)开始,向上移动到行的第一个位置(0),每次移动都将当前位置的方块与上面位置的方块进行交换。这个循环会持续直到到达行的开头。
            for (; x > 0; x--) {
                swap(getView(x - 1, y), getView(x, y), isAdd);
            }
        }
            break;
        }
    }//在每个循环中,它都调用了 swap() 方法来执行方块的交换操作。
     //swap() 方法接受三个参数:要交换的两个方块的视图,以及一个布尔值 isAdd,用于指示这次交换是真实的移动还是仅用于创建新方块。

    /**
     * 单向交换实现移动
     * 
     * @param next
     *            移动至目标位置
     * @param now
     *            需要移动的目标
     * @param isAdd
     *            是否是第一次移动
     */
    private void swap(Cube next, Cube now, boolean isAdd) {//这段代码是一个名为 swap() 的方法,用于在两个 Cube 对象之间进行交换操作。根据布尔值 isAdd 的不同,交换的方式也有所不同。
        if (isAdd) {
            if (now.getNum() != 0 && next.getNum() == 0) {//如果当前方块 now 的数字不为 0 且下一个方块 next 的数字为 0
                next.setText(now.getNum());//将 next 的文本设置为 now 的数字
                now.setText(0);//将 now 的文本设置为 0
                next.setIsAdded(now.isAdded());//并将 next 的添加状态设置为 now 的添加状态。
                now.setIsAdded(false);//调用了 now 对象的 setIsAdded(false) 方法。
            } else if (!now.isAdded() && !next.isAdded()//如果当前方块和下一个方块都没有被添加
                    && next.getNum() == now.getNum() && now.getNum() != 0) {//且它们的数字相等且不为 0
                next.setText(now.getNum() * 2);//将 next 的文本设置为 now 的数字乘以 2
                now.setText(0);//将 now 的文本设置为 0
                next.setIsAdded(true);//并将 next 的添加状态设置为 true
                now.setIsAdded(false);//将 now 的添加状态设置为 false
            }
        } else {
            if (next.getNum() == 0) { //如果下一个方块 next 的数字为 0
                next.setText(now.getNum()); //将 next 的文本设置为 now 的数字
                now.setText(0); //将 now 的文本设置为 0。
            }
            now.setIsAdded(false);//无论是否进行添加操作,都将 now 和 next 的添加状态设置为 false。
            next.setIsAdded(false);//无论是否进行添加操作,都将 now 和 next 的添加状态设置为 false。
        }
    }

    /**
     * 获取指定控件
     * 
     * @param x
     * @param y
     * @return Cube
     */
    private Cube getView(int x, int y) { //定义了一个名为 getView 的私有方法,它接受两个整数类型的参数 x 和 y。
        return viewList.get(new Point(x, y));//方法的主体部分,它返回 viewList 集合中对应于 (x, y) 坐标的 Cube 对象。
    }//这段代码是一个方法的定义,它的目的是根据给定的 x 和 y 坐标,从一个叫做 viewList 的集合中获取对应的 Cube 对象。
     //在这里,viewList 是一个集合,它存储了 Cube 对象。new Point(x, y) 创建了一个新的 Point 对象,其中 x 和 y 是传递给方法的坐标。
     //get() 方法是集合的一个方法,用于根据给定的键获取对应的值。在这个例子中,集合的键是 Point 对象,它表示坐标,而值是 Cube 对象。
     //因此,整个方法的作用是:根据给定的 x 和 y 坐标,在 viewList 集合中找到对应的 Cube 对象,并返回该对象。
    /**
     * 生成随机控件 随机位置
     */
    private void createCube() {
        int x;
        int y;
//声明了两个整数变量 x 和 y,用于存储生成的随机位置的坐标。

        do {
            x = (int) (Math.random() * 1000 % row);
            y = (int) (Math.random() * 1000 % column);
        } while (getView(x, y).getNum() != 0);
/*
 然后,通过调用 Math.random() 函数和一些算术运算,生成一个介于 0 和 1000 之间的随机数,
 并将其取模以限制在 row 和 column 的范围内,确保生成的位置在游戏界面内。
 接下来,使用一个 do-while 循环,检查生成的位置是否已经存在一个方块。如果方块数量不为 0,则重新生成位置,直到找到一个可用的位置。
 一旦找到可用位置,将方块的文本设置为 2 或 4,具体取决于随机数是否大于 0.5。
 最后,调用 isOverGame() 方法检查游戏是否结束。
 */
        getView(x, y).setText(Math.random() > 0.5 ? 2 : 4);
        isOverGame();
    }//createCube() 方法是一个用于生成随机控件(在这种情况下是一个方块)的私有方法。
     //它的目的是在游戏界面的随机位置创建一个方块。

    /**
     * 检测游戏状态
     */
    private void isOverGame() { //isOverGame() 方法是一个用于检查游戏是否结束的私有方法。
        int score = 0; //首先初始化一个变量 score 来存储游戏得分
        int state = Game2048.GAME_OVER;//并将状态设置为 GAME_OVER,表示游戏结束。
            //然后,使用两个嵌套的循环遍历游戏界面的每个位置。
        for (int x = 0; x < row; x++) {
            for (int y = 0; y < column; y++) {

                // 计算得分
                score += getView(x, y).getNum();
                //在循环内部,通过调用 getView(x, y).getNum() 方法获取每个位置上方块的数字,并将其加到得分中。
                
                if (getView(x, y).getNum() == 0) {  //如果某个位置上的方块数字为 0
                    state = Game2048.GAME_CONTINUE; //则将状态设置为 GAME_CONTINUE,表示游戏可以继续。
                } else if (getView(x, y).getNum() == 2048 * (gameLv + 1)) { //另外,如果某个位置上的方块数字等于 2048 的 (gameLv + 1) 倍
                    state = Game2048.GAME_WIN; //则将状态设置为 GAME_WIN,表示玩家获胜。
                }
            }
        }

        if (state != Game2048.GAME_CONTINUE && state != Game2048.GAME_WIN) { //循环结束后,检查状态是否不等于 GAME_CONTINUE 和 GAME_WIN
            gameState = Game2048.GAME_OVER;  //如果满足条件,表示游戏结束,将游戏状态设置为 GAME_OVER。
        } else { 
            gameState = state;
        }//否则,将游戏状态设置为之前计算的状态。

        scoreBoard.setText("得分:" + score);
        //最后,通过调用 scoreBoard.setText("得分:" + score) 方法更新得分显示。
    }
        //这段代码的主要目的是检查游戏是否结束,并根据游戏状态更新得分显示。
        //它通过遍历游戏界面的每个位置,计算得分,并根据得分和方块数字来判断游戏的结束状态。
    /**
     * 获取游戏状态
     * 
     * @return int
     */
    public int getGameState() {
        return gameState;
    }//getGameState() 方法是一个公共方法,用于获取游戏的状态。
    //它返回一个整数类型的变量 gameState,表示游戏的当前状态。

    /**
     * 初始化游戏数据
     */
    private void initialise() { //initialise() 方法是一个私有方法,用于初始化游戏数据。
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                getView(i, j).setText(0);
            }//在方法内部,使用两个嵌套的循环遍历游戏界面的每个位置,将每个位置上的方块文本设置为 0。
        }

        createCube();//接下来,调用 createCube() 方法两次,生成两个初始的方块。
        createCube();//接下来,调用 createCube() 方法两次,生成两个初始的方块。
        gameLv = 0;//然后,将 gameLv 设置为 0,表示游戏难度级别。
        this.setTitle("2048小游戏");//最后,通过调用 setTitle("2048") 方法设置游戏窗口的标题为 "2048小游戏"。
        
    }

    /**
     * 开启新游戏
     */
    public void newGame() { //方法是一个用于开启新游戏的公共方法。
        if (gameState == Game2048.GAME_CONTINUE) { //首先,检查游戏状态是否为 GAME_CONTINUE
            int jop = JOptionPane.showConfirmDialog(null, "是否开始新一轮游戏?", "Tips",
                    JOptionPane.YES_NO_OPTION);//如果是,表示游戏正在进行中,需要确认是否开始新一轮游戏。
       //使用 JOptionPane.showConfirmDialog() 方法显示一个确认对话框,
       //提示用户是否开始新一轮游戏,并提供 "是" 和 "否" 两个选项。
            if (jop == JOptionPane.YES_OPTION) {
                initialise();
            }//根据用户的选择,判断 jop 的值。如果用户选择 "是"(即 jop 等于 JOptionPane.YES_OPTION),
             //则调用 initialise() 方法初始化游戏数据
        } else {
            initialise();
        }//如果游戏状态不是 GAME_CONTINUE,也会直接调用 initialise() 方法初始化游戏数据。
    }

    /**
     * 重载窗体事件-控制关闭
     */
    protected void processWindowEvent(WindowEvent e) { //processWindowEvent() 方法是一个用于处理窗口事件的 protected 方法。它被重载以控制窗口的关闭操作。
        if (e.getID() == WindowEvent.WINDOW_CLOSING) { //如果接收到的窗口事件的 ID 为 WindowEvent.WINDOW_CLOSING,表示用户尝试关闭窗口。
            if (getGameState() == Game2048.GAME_CONTINUE) { //首先,检查游戏状态是否为 GAME_CONTINUE。如果是,表示游戏正在进行中。
                int jop = JOptionPane.showConfirmDialog(null, "是否退出游戏?", //使用 JOptionPane.showConfirmDialog() 方法显示一个确认对话框,询问用户是否退出游戏。
                        "Tips", JOptionPane.YES_NO_OPTION); //创建一个带有提示信息和 "是"、"否" 两个选项的提示对话框。用户可以根据自己的需求选择相应的选项。

                if (jop == JOptionPane.YES_OPTION) {
                    super.processWindowEvent(e);
                }
                //根据用户的选择,判断 jop 的值。如果用户选择 "是"(即 jop 等于 JOptionPane.YES_OPTION),则调用父类的 processWindowEvent(e) 方法处理窗口关闭事件。
            } else {
                super.processWindowEvent(e);
            }//如果游戏状态不是 GAME_CONTINUE,也会直接调用父类的 processWindowEvent(e) 方法处理窗口关闭事件。
        }
    }

    /**
     * 进入下一难度游戏
     */
    public void nextLv() { //是一个公共方法,用于执行游戏等级的提升操作。
        gameLv++; //将变量 gameLv 的值增加 1,用于表示游戏等级的增加。
        this.setTitle(2048 * (gameLv + 1) + "");//这行代码用于设置游戏窗口的标题。通过计算 2048 * (gameLv + 1) 的结果,并将结果与空字符串拼接,得到最终的标题字符串。每次游戏等级提升时,窗口标题会显示相应的等级信息。
    }/*
    这段代码的主要目的是在游戏中提升等级,并更新游戏窗口的标题以显示当前的等级信息。每次调用 nextLv() 方法,游戏等级会增加,并且窗口标题会根据等级的变化而更新。
    */

    /**
     * 关于
     */
    public void about() { //是一个公共方法,用于显示关于游戏的信息。
        JOptionPane.showMessageDialog(null, "QQ邮箱:1291672308@qq.com");
    }/*
    这行代码使用 JOptionPane.showMessageDialog() 方法显示一个消息对话框。
    null 表示将对话框显示在没有特定父组件的位置。消息对话框的内容是 "QQ 邮箱:1291672308@qq.com",即显示了一个 QQ 邮箱地址。
    */
}

class Cube { //定义一个类,名为 Cube
    private int num; //这是一个私有成员变量,用于存储整数类型的数据。
    private JButton btn; //这是一个私有成员变量,用于表示按钮对象。
    private boolean isAdded; //这是一个私有成员变量,用于表示布尔类型的状态。

    /**
     * 构造一个方块
     */
    public Cube() { //这是 Cube 类的构造方法,用于创建一个 Cube 对象。公共的构造函数 Cube()。
        btn = new JButton(); //在构造方法内部,创建了一个新的 JButton 对象,并将其赋值给成员变量 btn。
        btn.setFont(new Font("微软雅黑", Font.BOLD, 24)); //设置按钮的字体为 "微软雅黑",字体样式为加粗,字体大小为 24 像素。
        btn.setEnabled(false); //将按钮设置为不可用状态,即按钮变为灰色且无法点击。
        num = 0; //将成员变量 num 的值初始化为 0。
        isAdded = false; //将成员变量 isAdded 的值初始化为 false。
    }
    /*
     *这个构造方法的主要目的是创建一个 Cube 对象,并初始化其成员变量。
     *其中,创建了一个按钮对象,并设置了字体样式和大小,以及将按钮设置为不可用状态。
     *此外,还将整数变量 num 初始化为 0,以及布尔变量 isAdded 初始化为 false。
     */

    /**
     * 设置文本内容
     * 
     * @param n
     *            数值
     */
    public void setText(int n) { //这是一个公共方法,用于设置文本内容。
        num = n; //将传入的整数参数 n 赋值给成员变量 num。
        btn.setText(n != 0 ? n + "" : "");//如果 n 不等于 0,则将 n 转换为字符串并添加一个空字符串作为后缀,否则设置为空字符串。这行代码的目的是根据 n 的值来设置按钮的文本内容。
    }
    /*
     *这段代码的主要目的是根据传入的整数参数 n 来更新按钮的文本内容。
     *如果 n 不等于 0,则将其转换为字符串并显示在按钮上;如果 n 等于 0,则将按钮的文本内容设置为空字符串。
 */
    /**
     * 获取控件
     * 
     * @return JButton
     */
    public JButton getView() { //这是一个公共方法,用于获取控件。
        return btn; //返回成员变量 btn,其类型为 JButton。
    }/*
    这个方法的主要目的是返回 JButton 类型的控件对象,以便外部代码可以获取和操作该按钮。
    通过调用 getView() 方法,外部代码可以获取到 Cube 类中维护的按钮控件。
    */

    /**
     * 获取数值
     * 
     * @return int
     */
    public int getNum() { //这是一个公共方法,用于获取数值。
        return num;       //返回成员变量 num,其类型为整数类型 int。
    }/*
    这个方法的主要目的是返回整数类型的数值,该数值由成员变量 num 存储。
    通过调用 getNum() 方法,外部代码可以获取到 Cube 类中维护的当前数值。
    */

    /**
     * 是否是相加而成 限当前移动次有效,移动结束后改回默认值-false
     * 
     * @return
     */
    public boolean isAdded() { //这是一个公共方法,用于获取是否是相加而成的状态。
        return isAdded; //返回成员变量 isAdded,其类型为布尔类型 boolean。
    }/*
    这个方法的主要目的是返回一个布尔类型的状态值,表示是否是通过相加操作而成。通过调用 isAdded() 方法,外部代码可以获取到 Cube 类中维护的当前相加状态。
    根据注释所述,该方法仅在当前移动次有效,并且在移动结束后会将该状态改回默认值 false。
    */

    /**
     * 修改生成方式
     * 
     * @param b
     *            true-相加而成
     */
    public void setIsAdded(boolean b) { //这是一个公共方法,用于设置生成方式的状态。
        isAdded = b; //:将传入的布尔参数 b 赋值给成员变量 isAdded。
    }
    /*
     * 这个方法的主要目的是修改成员变量 isAdded 的值,以控制生成方式的状态。
     * 通过调用 setIsAdded(boolean b) 方法,外部代码可以将生成方式的状态设置为相加而成(当 b 的值为 true 时)或其他方式。
    */
}

class MyKeyListener implements KeyListener { //这段代码定义了一个名为 MyKeyListener 的类,
	                                         //该类实现了 KeyListener 接口。这个类用于处理键盘事件,并定义了一些常量来表示键盘上的特定按键。
    /*
     * 键盘代码 w/87 s/83 a/65 d/68 up/38 down/40 left/37 right/39 f1/112 f2/113
     * f3/114
     */
    final public static int KEY_W = 0xf57; //定义了常量 KEY_W,其值为十六进制数 0xf57。在不同的键盘布局中,W 键可能对应不同的整数值。
    final public static int KEY_S = 0xf53; //定义了常量 KEY_S,其值为十六进制数 0xf53。在不同的键盘布局中,S 键可能对应不同的整数值。
    final public static int KEY_A = 0xf41; //定义了常量 KEY_A,其值为十六进制数 0xf41。在不同的键盘布局中,A 键可能对应不同的整数值。
    final public static int KEY_D = 0xf44; //定义了常量 KEY_D,其值为十六进制数 0xf44。在不同的键盘布局中,D 键可能对应不同的整数值。
    final public static int KEY_UP = 0xf26;    //定义了常量 KEY_UP,其值为十六进制数 0xf26。在不同的键盘布局中,向上箭头键可能对应不同的整数值。
    final public static int KEY_DOWN = 0xf28;  //定义了常量 KEY_DOWN,其值为十六进制数 0xf28。在不同的键盘布局中,向下箭头键可能对应不同的整数值。
    final public static int KEY_LEFT = 0xf25;  //定义了常量 KEY_LEFT,其值为十六进制数 0xf25。在不同的键盘布局中,向左箭头键可能对应不同的整数值。
    final public static int KEY_RIGHT = 0xf27; //定义了常量 KEY_RIGHT,其值为十六进制数 0xf27。在不同的键盘布局中,向右箭头键可能对应不同的整数值。

    private Game2048 game;

    /**
     * 构造一个键盘监听器
     * 
     * @param game
     *            主界面
     */
    public MyKeyListener(Game2048 game) { //是一个公共构造函数的声明,
        this.game = game; //它接受一个 Game2048 类型的参数,并将其赋值给内部的 game 变量。
    }

    @Override  //这是一个注解,表示该方法覆盖了父类的同名方法。
    
    public void keyPressed(KeyEvent e) { //这是一个键盘事件监听器的方法,当用户按下键盘上的某个键时会触发该方法。
        int keyCode = e.getKeyCode() + 0xf00; //获取用户按下的键的虚拟键码,并将其转换为特定的游戏键值。0xf00 是一个偏移量,用于将键码映射到游戏中定义的键值范围。

        switch (game.getGameState()) { //根据游戏的当前状态来处理不同的按键操作。
        case Game2048.GAME_CONTINUE: { //处理游戏继续状态下的按键操作。
            switch (keyCode) {//根据用户按下的键来执行相应的游戏操作。
            case MyKeyListener.KEY_W:
            case MyKeyListener.KEY_UP: {
                game.up();
            }//如果用户按下了 W 键,执行 game.up() 方法,游戏中的方块向上移动。
                break;

            case MyKeyListener.KEY_S:
            case MyKeyListener.KEY_DOWN: {
                game.down();
            }//如果用户按下了 S 键,执行 game.down() 方法,游戏中的方块向下移动。
                break;

            case MyKeyListener.KEY_A:
            case MyKeyListener.KEY_LEFT: {
                game.left();
            }//如果用户按下了 A 键,执行 game.left() 方法,游戏中的方块向左移动。
                break;

            case MyKeyListener.KEY_D:
            case MyKeyListener.KEY_RIGHT: {
                game.right();
            }// 如果用户按下了 D 键,执行 game.right() 方法,游戏中的方块向右移动。
                break;
            }
        }
            break;

        case Game2048.GAME_OVER: { //这是一个 switch 语句的分支,当 Game2048.GAME_OVER 这个常量的值与 switch 表达式的值匹配时,就会执行这个分支处理游戏结束状态下的按键操作。
            int jop = JOptionPane //一个变量,用于存储用户在确认对话框中选择的按钮。它的类型是 int,值是 JOptionPane.YES_OPTION(表示用户选择了是)或 JOptionPane.NO_OPTION(表示用户选择了否)。
                    .showConfirmDialog(null, "很遗憾,你没能达成本次目标,是否开启新游戏?", "游戏结束",//这行代码调用了 JOptionPane.showConfirmDialog 方法来显示一个确认对话框。第一个参数 null 表示没有父窗口。
                    		                                                                   //第二个参数是要显示的消息。第三个参数是对话框的标题。第四个参数是对话框的选项类型,这里使用了 JOptionPane.YES_NO_OPTION,表示有两个选项:是和否。
                            JOptionPane.YES_NO_OPTION);
//综上所述,这段代码会在游戏结束时显示一个确认对话框,询问用户是否要开启新游戏。用户可以选择是或否,选择的结果会存储在变量 jop 中。
            if (jop == JOptionPane.YES_OPTION) {
                game.newGame();
            }
        }//如果用户在游戏结束对话框中选择了 "是"(即 JOptionPane.YES_OPTION),执行相应的操作,可能是重新开始游戏。
            break;

        case Game2048.GAME_WIN: { //理游戏胜利状态下的按键操作。
            int jop = JOptionPane.showConfirmDialog(null,
                    "你已完成本次目标:" + game.getTitle() + ",是否进入更高难度游戏?", "恭喜晋级",
                    JOptionPane.YES_NO_OPTION);  //
          //如果用户在游戏胜利对话框中选择了 "是"(即 JOptionPane.YES_OPTION),执行相应的操作进入下一关卡。
            if (jop == JOptionPane.YES_OPTION) { //这行代码检查用户在确认对话框中选择的按钮。如果用户选择了 "是"(即 JOptionPane.YES_OPTION),则执行下面的操作。
                game.nextLv(); //这行代码调用了 game 对象的 nextLv 方法。
            }
        }
            break; //这行代码结束了 switch 语句的执行,使程序流程跳出当前的 switch 块。
        }
    }

    @Override //这是一个注解,表示该方法覆盖了父类的同名方法。
    public void keyReleased(KeyEvent e) {
    } //方法在键盘按键释放时被调用。它接收一个 KeyEvent 对象作为参数,该对象包含了关于按键事件的信息,如按键的字符、键码和按下状态等。

    @Override //这是一个注解,表示该方法覆盖了父类的同名方法。
    public void keyTyped(KeyEvent e) {
    } //方法在键盘输入字符时被调用。它也接收一个 KeyEvent 对象作为参数,该对象包含了关于按键输入事件的信息,如输入的字符和键码等。
}
/*
 这两段代码是 Java 中定义的两个方法,它们是键盘事件监听器接口 KeyListener 的实现。这个接口用于接收和处理键盘事件,包括按键按下、按键释放和按键输入。
 */

class MyMouseListener implements MouseListener { //这一行声明了一个名为 MyMouseListener 的类,并且该类实现了 MouseListener 接口。MouseListener 接口包含了处理鼠标事件的方法,例如鼠标按下、鼠标释放、鼠标进入和鼠标退出等。
    private Game2048 game; //这一行声明了一个私有成员变量 game,它的类型是 Game2048。Game2048 是一个自定义的类或接口,这里它是一个代表游戏的类。
    private int btnEvn; //这一行声明了另一个私有成员变量 btnEvn,它的类型是 int。btnEvn 是一个用于表示按钮事件的整数。


    public MyMouseListener(Game2048 game, int btnEvn) { //这一行定义了类的构造函数,它接受两个参数:一个 Game2048 类型的对象和一个整数。这些参数将被用于初始化类的成员变量。
        this.game = game; //这一行将传入的 game 参数赋值给类的成员变量 game,以便在类的其他地方使用。
        this.btnEvn = btnEvn; //这一行将传入的 btnEvn 参数赋值给类的成员变量 btnEvn,以便在类的其他地方使用。
    }
/*
 这段代码是一个 Java 类的定义,该类名为 MyMouseListener,并且实现了 MouseListener 接口。
 这个类的目的是处理鼠标事件,并与游戏的逻辑相关。通过实现 MouseListener 接口的方法,可以在鼠标事件发生时执行相应的操作。
 */

    @Override //这是一个注解,表示该方法覆盖了父类的同名方法。
    public void mouseClicked(MouseEvent e) {
    	//当鼠标按钮被单击时调用的方法。
    }

    @Override //这是一个注解,表示该方法覆盖了父类的同名方法。
    public void mouseEntered(MouseEvent e) {
    	//当鼠标进入组件的边界时调用的方法。
    }

    @Override //这是一个注解,表示该方法覆盖了父类的同名方法。
    public void mouseExited(MouseEvent e) {
    	//当鼠标退出组件的边界时调用的方法。
    }

    @Override //这是一个注解,表示该方法覆盖了父类的同名方法。
    public void mousePressed(MouseEvent e) {
    	//当鼠标按钮被按下时调用的方法。
    }
/*
 *定义了一个名为 MyMouseListener 的类,该类实现了 MouseListener 接口。
 *在这个类中,有四个方法,每个方法都使用了 @Override 注解,表示这些方法覆盖了 MouseListener 接口中的同名方法。
 */
    
    @Override //这是一个注解,表示该方法覆盖了父类的同名方法。
              //使用 @Override 注解的目的是确保方法的签名与父类中的方法签名匹配,这样可以避免在编译时出现错误。同时,它还可以提高代码的可读性,让开发者更清楚地知道该方法是覆盖了父类的方法
    
    public void mouseReleased(MouseEvent e) { //这是方法的签名,声明了该方法是一个公共的(public),空返回值(void)的方法,接收一个 MouseEvent 类型的参数 e。这个方法在鼠标按钮释放时被调用。
        switch (btnEvn) { //这是一个 switch 语句,根据 btnEvn 的值来执行不同的操作。btnEvn 是一个整数类型的变量,它表示按钮事件。
        case Game2048.BUTTON_NEW_GAME: { //这是 switch 语句的一个分支,如果 btnEvn 的值等于 Game2048.BUTTON_NEW_GAME,则执行该分支的代码。
            game.newGame(); //在这个分支中,调用了 game.newGame() 方法。game 是一个对象,它具有 newGame() 方法,用于重新开始游戏。
        }
            break; //表示结束当前分支的执行,防止在匹配成功后再执行其他分支。

        case Game2048.BUTTON_ABOUT: { //这是另一个分支,如果 btnEvn 的值等于 Game2048.BUTTON_ABOUT,则执行该分支的代码。
            game.about(); //在这个分支中,调用了 game.about() 方法。game 对象具有 about() 方法,用于显示关于游戏的信息。

        }
            break; //同样,表示结束当前分支的执行。


        case Game2048.BUTTON_EXIT: { //这是第三个分支,如果 btnEvn 的值等于 Game2048.BUTTON_EXIT,则执行该分支的代码。
            game.processWindowEvent(new WindowEvent(game,
                    WindowEvent.WINDOW_CLOSING)); //在这个分支中,调用了 game.processWindowEvent() 方法,并传递了一个 WindowEvent 对象作为参数。game 对象具有 processWindowEvent() 方法,用于处理窗口事件。
            									//在这里,传递了一个 WindowEvent.WINDOW_CLOSING 类型的窗口事件,用于关闭窗口。
        }
            break; //结束当前分支的执行。
        }
    }

}

 

物联沃分享整理
物联沃-IOTWORD物联网 » Java课程设计:带注释的2048小游戏实现

发表评论