java贪吃蛇程序思路_贪吃蛇的java代码分析(二)

代码剖析

贪吃蛇是一款十分经典的小游戏,对初入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 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 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

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Java双人贪吃蛇游戏的代码示例: ```java import java.awt.*; import java.awt.event.*; import javax.swing.*; public class SnakeGame extends JFrame { private final int B_WIDTH = 300; private final int B_HEIGHT = 300; private final int DOT_SIZE = 10; private final int ALL_DOTS = 900; private final int RAND_POS = 29; private int DELAY = 140; private final int x[] = new int[ALL_DOTS]; private final int y[] = new int[ALL_DOTS]; private int dots; private int apple_x; private int apple_y; private boolean leftDirection = false; private boolean rightDirection = true; private boolean upDirection = false; private boolean downDirection = false; private boolean inGame = true; private Timer timer; private Image ball; private Image apple; private Image head; private int player1Score = 0; private int player2Score = 0; private boolean player1Turn = true; public SnakeGame() { initGame(); } private void initGame() { addKeyListener(new TAdapter()); setBackground(Color.black); setFocusable(true); setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT)); loadImages(); initGameBoard(); } private void loadImages() { ImageIcon iid = new ImageIcon("dot.png"); ball = iid.getImage(); ImageIcon iia = new ImageIcon("apple.png"); apple = iia.getImage(); ImageIcon iih = new ImageIcon("head.png"); head = iih.getImage(); } private void initGameBoard() { dots = 3; for (int z = 0; z < dots; z++) { x[z] = 50 - z * 10; y[z] = 50; } locateApple(); timer = new Timer(DELAY, new GameCycle()); timer.start(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } private void doDrawing(Graphics g) { if (inGame) { g.drawImage(apple, apple_x, apple_y, this); for (int z = 0; z < dots; z++) { if (z == 0) { g.drawImage(head, x[z], y[z], this); } else { g.drawImage(ball, x[z], y[z], this); } } Toolkit.getDefaultToolkit().sync(); } else { gameOver(g); } } private void gameOver(Graphics g) { String msg = "Game Over!"; String msg2 = "Player 1 Score: " + player1Score + " Player 2 Score: " + player2Score; Font small = new Font("Helvetica", Font.BOLD, 14); FontMetrics metr = getFontMetrics(small); g.setColor(Color.white); g.setFont(small); g.drawString(msg, (B_WIDTH - metr.stringWidth(msg)) / 2, B_HEIGHT / 2); g.drawString(msg2, (B_WIDTH - metr.stringWidth(msg2)) / 2, (B_HEIGHT / 2) + 20); } private void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); if (player1Turn) { player1Score++; } else { player2Score++; } } } private void move() { for (int z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; } if (leftDirection) { x[0] -= DOT_SIZE; } if (rightDirection) { x[0] += DOT_SIZE; } if (upDirection) { y[0] -= DOT_SIZE; } if (downDirection) { y[0] += DOT_SIZE; } } private void checkCollision() { for (int z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } } if (y[0] >= B_HEIGHT) { inGame = false; } if (y[0] < 0) { inGame = false; } if (x[0] >= B_WIDTH) { inGame = false; } if (x[0] < 0) { inGame = false; } if(!inGame) { timer.stop(); } } private void locateApple() { int r = (int) (Math.random() * RAND_POS); apple_x = ((r * DOT_SIZE)); r = (int) (Math.random() * RAND_POS); apple_y = ((r * DOT_SIZE)); } private class TAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT && !rightDirection) { leftDirection = true; upDirection = false; downDirection = false; } if (key == KeyEvent.VK_RIGHT && !leftDirection) { rightDirection = true; upDirection = false; downDirection = false; } if (key == KeyEvent.VK_UP && !downDirection) { upDirection = true; rightDirection = false; leftDirection = false; } if (key == KeyEvent.VK_DOWN && !upDirection) { downDirection = true; rightDirection = false; leftDirection = false; } } } private class GameCycle implements ActionListener { @Override public void actionPerformed(ActionEvent e) { if (inGame) { checkApple(); checkCollision(); move(); if (player1Turn) { player1Turn = false; } else { player1Turn = true; } } repaint(); } } public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame ex = new SnakeGame(); ex.setVisible(true); }); } } ``` 这个示例代码使用了Java Swing库来创建游戏窗口和图形界面,同时实现了基本的游戏逻辑和双人模式。你可以根据自己的需求对代码进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值