• 贪吃蛇的java代码分析(二)


    1. 代码剖析

    贪吃蛇是一款十分经典的小游戏,对初入coding的朋友来说,拿贪吃蛇这样一个案例来练手十分合适,并不高的难度和成功后的成就感都是学习所必须的。下面我将依照我当时的思路,来逐步分析实现的整个过程。

    让我们逐一分析。首先,整个游戏最基本的元素是地图。在java中用于绘图的类是swing和awt,在这里主要用到swing类。swing中用于窗口显示的类有JFrame及其子类。JFrame可以直接添加组件,但其本质是将组件添加到JFrame中的一个默认面板里,为了代码清晰,我会使用JPanel面板来绘制全部的动画,之后再将面板添加到JFrame窗体之中即可。

    我们可能会疑惑于贪吃蛇的蛇身,它是由什么组成的?如何实现移动?我们可以把贪吃蛇的蛇身理解成一个集合,它有固定的起始元素,代表游戏一开始时的蛇身。当贪吃蛇吃到点时,集合就添加一个元素,蛇的长度就加一。那么,集合中的元素是什么呢?要理解这个问题,首先得关注蛇身移动所处的环境。在JFrame窗体中,是由X、Y轴坐标对位置进行区分。贪吃蛇的蛇身可以看做是一个一个联系紧密的点,在坐标轴上显示出来。每当朝某个方向移动时,蛇的坐标就按照某个规律变化。例如,我们操控贪吃蛇向上移动时,蛇的全体坐标的Y轴就减一;如果蛇的第一个坐标与蛇身的某个坐标重合,就代表贪吃蛇碰到自己;如果蛇的第一个坐标碰到了边界,蛇就撞墙。这就是贪吃蛇的本质。 
    我们来建立建立蛇身上每一个点的对象,蛇身就是由一个一个这样的对象所组成的:

    public class snakeNode {
        private int x;
        private int y;
        private String color;
        public snakeNode() {};
        public snakeNode(int x, int y, String color) {
            this.x = x;
            this.y = y;
            this.color = color;
            }
        public int getX() {
            return x;
            }
        public void setX(int x) {
            this.x = x;
            }
        public int getY() {
            return y;
            }
        public void setY(int Y) {
            this.y = y;
            }
        public String getColor() {
            return color;
            }
        public void setColor(String color) {
            this.color = color;
            }
                这串代码表示蛇身上的每一个点,通过建立snakeNode的对象,指定不同的X轴和Y轴的值,就能组成一个蛇身。
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    接下来我们要给每一个蛇身上的点设置范围,因为贪吃蛇有移动范围的限制,超过某个距离或者长度,就会越界导致游戏的终止。经过考虑,我们将范围设置在:

    public class mainMap extends JPanel {
        private int width = 20;
        private int length = 30;
        private int unit = 20;
        }
        上面的代码定义了一个面板类,我们之后的操作都要在上面进行。类中定义了变量width和length。我们将蛇身的移动范围限制在X轴上0~20,Y轴上0~30,至于变量unit,稍后再进行分析。
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    接着,我们需要一个集合,用来存储蛇身上的各个点。我们需要定义一个变量,用来表示随机出现的点(贪吃蛇的目标),并且定义一个变量Length用来表示蛇的长度。代码如下:

    public class mainMap extends JPanel {
        private final int width = 20;
        private final int length = 30;
        private final int unit = 20;
        private ArrayList<snakeNode> snake = new ArrayList<>();
        private snakeNode newNode = new snakeNode(0,0,Color.WHITE);
        private int Length;
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    定义类的成员变量之后,我们开始定义构造方法,这样在构造mainMap的对象后程序就会开始运行。我们需要在构造方法中给集合添加一些元素,代表初始蛇身,也需要使用一个方法,用来创造随机点。代码如下:

    public mainMap() {
        snake.add(new snakeNode(width/2,length/2,Color.RED);
        snake.add(new snakeNode(width/2,length/2+1,Color.BLUE);
        snake.add(new snakeNode(width/2,length/2+2,Color.GREY);
        Length = snake.size();
        createNode();
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    createNode是创造随机点的方法,让我们思考一下:创造随机点有哪些要求?首先,随机点的范围肯定不能超出限制,否则游戏将无法继续;其次,随机点不能出现在蛇身上,也就是随机点的坐标不能和蛇身体上的任意坐标相同,否则就会出现BUG。按照此要求,我们创作出代码如下:

    public void createNode() {
        int newX = 0;
        int newY = 0;
        boolean flag = true;
        while(flag){
        X = new Random().nextInt(width);
        Y = new Random().nextInt(length);
        for(int x = 0; x < Length; x++) {
            if(snake.get(x).getX() == newX && snake.get(x).getY() == newY) {
            flag = true;
            break;
            }
            flag = false; 
        }
    }
        Color color = new Color(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255));
        newNode.setX(newX);
        newNode.setY(newY);
        newNode.setColor(color);
        }
        这个方法随机产生0~width,0~length的随机数,通过循环判断是否与蛇身的点重合来产生随机点,同时产生随机的颜色,这里使用了Color的构造方法,不清楚的话可以通过API来查询。
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    接下来是我们进行游戏中至关重要的一点,就是对蛇的移动进行控制。我们使用“wsad”或者键盘上的“上下左右”来控制蛇身的移动变化。这其中的原理想必很多人都能马上想到:监听器。这里我们要设置监听器的对象不再是一个按钮,一个标签,而是整个面板。我们要对整个面板增加一个键盘监听器,用来监听自己在键盘上的动作。这里我们统一一下,用”↑↓←→”来控制方向。当我们使用键盘捕捉到相应的动作后,该如何继续呢?该如何编写事件的处理?

    我们来翻阅一下API。查看API中的KeyListener,我们可以查到KeyEvent,他有静态的常量用来表示键盘上相应的点触。VK_UP代表上箭头,VK_DOWN代表下箭头,VK_LEFT代表左箭头,VK_RIGHT代表右箭头。我们马上可以联想到:通过getKeyCode方法获取到键盘事件,和四个常量进行比较,如果符合,就可以按照对应的方向调用方法,来移动蛇身。我们可以定义一个Move()方法,并且定义一个变量direction代表方向,通过对direction不同的赋值传递给Move(),来对蛇身产生不同的移动效果。接下来贴代码:

    public mainMap() {
        snake.add(new snakeNode(width/2,length/2,Color.RED);
        snake.add(new snakeNode(width/2,length/2+1,Color.BLUE);
        snake.add(new snakeNode(width/2,length/2+2,Color.GREY);
        Length = snake.size();
        createNode();
        this.addKeyListener(new KeyAdaper() {
            public void KeyPressed(KeyEvent e) {
                int direction = 0;
                switch(e.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        direction = 1;
                        break;
                    case KeyEvent.VK_DOWN:
                        direction = -1;
                        break;
                    case KeyEvent.VK_LEFT:
                        direction = 2;
                        break;
                    case KeyEvent.VK_RIGHT:
                        direction = -2;
                        break;
                    default:
                        break;
                    }
                Move(direction);
                }
            });
        }
        //通过按下不同的方向键,我们得到了不同的direction变量,接下来我们定义一个Move()方法,传递direction变量来控制坐标的移动,从而得到蛇身变化的效果。 
    
        public void Move(int direction) {
            int firstX = snake.get(0).getX();
            int firstY = snake.get(0).getY();
            switch(direction) {
                case 1:
                    firstY--;
                    break;
                case -1:
                    firstY++;
                    break;
                case 2:
                    firstX--;
                    break;
                case -2:
                    firstX++;
                    break;
                default:
                    break;
                }
            for(int x = 0; x < Length; x++) {
                if(snake.get(x).getX()==firstX&&snake.get(x).getY()==firstY) {
                Dead("不好意思,您碰到自己啦~~~~!!!!");
                }
            }//这个方法遍历蛇身集合中的每一个元素,拿出X轴和Y轴的值进行比较,来保证蛇头的第一个点没有触碰到蛇身的其他点。如果碰到了,就调用Dead()方法结束游戏,Dead()方法随后定义
    
            if(firstX < 0 || firstX > width - 1 || firstY < 0 || firstY > length -1) {
            Dead("不好意思,您撞墙啦");
            }//很简单,判断蛇头的坐标有没有超出界限
    
            for(int x = Length - 1; x >0; x--) {
                snake.get(x).setX(snake.get(x-1).getX());
                snake.get(x).setY(snake.get(x-1).getY());
            }
            snake.get(0).setX(firstX);
            snake.get(0).setY(firstY);
            }//这段代码从后往前遍历集合,把最后一个集合的X轴和Y轴赋值为前一个元素的X轴和Y轴,把移动后的firstX和firstY坐标赋值给第一个元素,那么蛇身的整体位置就进行了变化,也就可以达到蛇身移动的效果了。
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    通过以上代码,我们已经初步搭建了贪吃蛇的基本逻辑框架。我们造出了蛇身,设置了按键后的蛇身移动的规律,也设置了蛇移动的范围。我们先给出总览的代码,这样有助于查漏补缺:

    public class mainMap extends JPanel {
        private final int width = 20;
        private final int length = 30;
        private final int unit = 20;
        private ArrayList<snakeNode> snake = new ArrayList<>();
        private snakeNode newNode = new snakeNode(0,0,Color.WHITE);
        private int Length;
    
        public mainMap() {//这是构造方法
        snake.add(new snakeNode(width/2,length/2,Color.RED);
        snake.add(new snakeNode(width/2,length/2+1,Color.BLUE);
        snake.add(new snakeNode(width/2,length/2+2,Color.GREY);
        Length = snake.size();
    
        createNode();//这是创造随机点的方法
    
        this.addKeyListener(new KeyAdaper() {//这是设置键盘事件的方法
            public void KeyPressed(KeyEvent e) {
                int direction = 0;
                switch(e.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        direction = 1;
                        break;
                    case KeyEvent.VK_DOWN:
                        direction = -1;
                        break;
                    case KeyEvent.VK_LEFT:
                        direction = 2;
                        break;
                    case KeyEvent.VK_RIGHT:
                        direction = -2;
                        break;
                    default:
                        break;
                    }
                Move(direction);
                }
            });
        }
        }
        public void Move(int direction) {//这是移动蛇身的方法
            int firstX = snake.get(0).getX();
            int firstY = snake.get(0).getY();
            switch(direction) {
                case 1:
                    firstY--;
                    break;
                case -1:
                    firstY++;
                    break;
                case 2:
                    firstX--;
                    break;
                case -2:
                    firstX++;
                    break;
                default:
                    break;
                }
            for(int x = 0; x < Length; x++) {
                                if(snake.get(x).getX()==firstX&&snake.get(x).getY()==firstY) {
                Dead("不好意思,您碰到自己啦~~~~!!!!");
                }
            }
    
            if(firstX < 0 || firstX > width - 1 || firstY < 0 || firstY > length -1) {
            Dead("不好意思,您撞墙啦");
            }
            for(int x = Length - 1; x >0; x--) {
                snake.get(x).setX(snake.get(x-1).getX());
                snake.get(x).setY(snake.get(x-1).getY());
            }
            snake.get(0).setX(firstX);
            snake.get(0).setY(firstY);
            }
    
        public void createNode() {//这是创造随机点的方法
        int newX = 0;
        int newY = 0;
        boolean flag = true;
        while(flag){
        X = new Random().nextInt(width);
        Y = new Random().nextInt(length);
        for(int x = 0; x < Length; x++) {
            if(snake.get(x).getX() == newX && snake.get(x).getY() == newY) {
            flag = true;
            break;
            }
            flag = false; 
        }
    }
        Color color = new Color(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255));
        newNode.setX(newX);
        newNode.setY(newY);
        newNode.setColor(color);
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    以上就是我们共同完成的步骤,如果全部实现并且加以理解,那么其实整个贪吃蛇的整体思路基本已经是掌握了。但这并没有结束,我们还有许多的细节问题需要完成。我将在下一节中继续来完成剩下的部分,包括蛇的定时移动,吃掉点的方法,已经画出蛇的样子。

  • 相关阅读:
    mvc中压缩html
    简单瀑布流
    MVC4 WebAPI
    css实现隔行换色
    网站变黑白
    reset.css
    选择文本改变浏览器默认的背景色和前景色
    DataBinder.Eval用法范例
    精妙SQL語句
    asp.net面试题收集
  • 原文地址:https://www.cnblogs.com/hanfeihanfei/p/6245996.html
Copyright © 2020-2023  润新知