贪吃蛇java 暂停_Java实现贪吃蛇小游戏

三月份的时候用Java写了一个贪吃蛇的小游戏,写完的时候颇有成就感,现在在这里做一下总结。

先把需要用到的图片资源放在这里,分别为上、下、左、右方向的蛇头,蛇身,食物,标题,可以通过右击另存为的方式下载:

ee62f80512ad87e7366a9c9fe16e0ab5.png          

2f557becab155a81f3f286be8517823c.png         

b38c6b530075f44bf2315ccad79cc355.png        

d7d700dab7b73cf77259bd457e7de2a7.png        

e2b51d0c4c3e1235bfafa14bd8a08ab6.png         

13da45e7c5edd7d6e9206725da180a6e.png

8313b003b539753363e5753e74b94da7.png

我们想要得到的游戏效果如下:

刚进入游戏的初始界面为图1,按空格开始游戏,通过上、下、左、右键改变蛇的移动方向,蛇可以穿过边界从另一端出来,吃到食物后多一个蛇身并且食物刷新位置,按空格键会暂停游戏,界面如图2,再按空格重新开始游戏,如果蛇头碰到自己的身子,那么游戏结束,按p键重新开始游戏,界面如图3,右上角的积分系统可有可无,这里出于篇幅考虑,省去积分系统:

e63fa850447eae6838bb8fd9e649194d.png  

c333f90caa46963999f94e73dc279b95.png

d61568f3112ae4029fdd6ee2e4b55dcd.png

个人认为,要想用Java实现这个游戏,首先需要具备一些最基础的GUI的知识,比如JFrame、JPanel的使用,这里就不再赘述;除此之外,还需要实现画图、键盘监听、计时器的功能,这里通过几个小demo展示一下这几个功能是怎么实现的。

·画图的实现:主要用到JPanel类的paint(Graphics g)方法和ImageIcon类的paintIcon(Component c, Graphics g, int x, int y),代码如下:

1 import javax.swing.*;2 import java.awt.*;3 class PaintDemo extendsJPanel{4 //得到需要画的图片的资源

5 ImageIcon body=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\body.png");6 //构造方法

7 publicPaintDemo(){8 this.setSize(200,200);9 this.setBackground(Color.white);10 }11 //在new PaintDemo()时会自动调用一次paint方法

12 public voidpaint(Graphics g){13 //ImageIcon类的一个方法,以此就能把对应的图片画在指定的位置上

14 body.paintIcon(this,g,100,100);15 }16 }17 public classSimpleTest {18 public static void main(String[] args) throwsException {19 JFrame f=new JFrame("Demo");20 //前两个参数决定了弹出框的位置,后两个参数决定了框的宽高

21 f.setBounds(100,150,200,200);22 //关闭窗口

23 f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);24 //只有把JPanel放在JFrame里才能看得到JPanel

25 f.add(newPaintDemo());26 f.setVisible(true);27 }28 }

实现效果如图:

1f751f02365855c0c6bda62ec9a788fd.png

这样一来,我们就可以通过画图,一步步得到游戏的界面了。

·键盘监听的实现:游戏里我们是要通过键盘监听来实现暂停、蛇的移动和重新开始的功能。在这个demo里,我们实现的是,键盘监听一个JPanel,当我们按下↓键时,控制台输出“你往下了”,按下↑键时,控制台输出“你往上了”,代码如下:

1 import javax.swing.*;2 import java.awt.*;3 import java.awt.event.*;4 class KeyDemo extends JPanel implementsKeyListener {5 publicKeyDemo(){6 this.setSize(200,200);7 this.setBackground(Color.white);8 //让键盘事件监听这个JPanel

9 this.addKeyListener(this);10 //使焦点保持在这个JPanel上,如果焦点不在这上面,键盘事件就不起作用了。

11 this.setFocusable(true);12 }13 public voidkeyPressed(KeyEvent e) {14 //得到我们按下的键的对应的数字常量

15 int keyCode=e.getKeyCode();16 //如果是↑键

17 if (keyCode==KeyEvent.VK_UP){18 System.out.println("你往上了");19 }else if (keyCode==KeyEvent.VK_DOWN){20 System.out.println("你往下了");21 }22 }23 public voidkeyTyped(KeyEvent e) {}24 public voidkeyReleased(KeyEvent e) {}25 }26 public classSimpleTest {27 public static void main(String[] args) throwsException {28 JFrame f=new JFrame("Demo");29 f.setBounds(100,150,200,200);30 f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);31 f.add(newKeyDemo());32 f.setVisible(true);33 }34 }

实现效果很简单,就是在控制台有对应的输出。

这样一来,我们就可以通过键盘来控制游戏的暂停、重新开始以及蛇的移动了。

·计时器的实现:单说计时器可能让人很迷糊,计时器跟贪吃蛇有什么关系,难道这个游戏还有时间限制吗?此计时器非彼计时器,这里的计时器实现了一个重要的功能,就是让我们的蛇“动”了起来。其实我们平时看的喜羊羊等动画,它们本身就是一张张的图片,我们之所以觉得他们是“动”的,是因为这些图片切换的实在太快了,我们的肉眼根本无法察觉到喜羊羊的一个动作其实就是几十张图片的快速切换而已。这也引入了一个叫做“帧”的概念,一帧可以理解为一个画面。如果大家有玩过王者荣耀,应该会注意到游戏里有一个叫做“帧率”的东西,帧率就是每秒播放的帧数,当帧率很低,比如一秒只放两张图片时,就会出现大家口中的“卡成PPT”。而我们的贪吃蛇要想动起来,本质也就是通过高频率地刷新画面,产生“动”的感觉。

在这个demo里,我们实现的是在界面上不停的在随机位置画图,主要用的是Timer类和ActionListener接口,具体代码如下:

1 import javax.swing.*;2 import java.awt.*;3 import java.awt.event.*;4 importjava.util.Random;5 class TimerDemo extends JPanel implementsActionListener {6 ImageIcon food=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\food.png");7 Random r=newRandom();8 //初始位置

9 int x=r.nextInt(500);10 int y=r.nextInt(500);11 //创建一个Timer计时器,在调用timer.start()方法后,每100毫秒就会调用一次对应的actionPerformed()方法

12 Timer timer=new Timer(100,this);13 publicTimerDemo(){14 this.setSize(500,500);15 //在new这个JPanel的时候直接启动计时器

16 timer.start();17 }18 public voidpaint(Graphics g){19 food.paintIcon(this,g,x,y);20 }21 public voidactionPerformed(ActionEvent e) {22 x=r.nextInt(500);23 y=r.nextInt(500);24 //重新调用一次paint()方法

25 repaint();26 }27 }28 public classSimpleTest {29 public static void main(String[] args) throwsException {30 JFrame f=new JFrame("Demo");31 f.setBounds(100,150,500,500);32 f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);33 f.add(newTimerDemo());34 f.setVisible(true);35 }36 }

实现效果如图:

1850a04065bc7e4cd943ba7b2bfcaeed.png

这样一来,我们就可以想办法让蛇动起来了。至于蛇为什么吃到食物可以多一节身体,又为什么穿过边界可以从另一端出来,就需要好好思考了。

有了以上的预备知识,我们就可以着手写贪吃蛇的代码了!

大致可以分为以下几个步骤:

1.设计游戏图纸

2.创建窗口JFrame

3.在窗口上添加画布JPanel

4.在画布上添加标题

5.在画布上添加黑色游戏区

6.在游戏区放置静态的蛇

7.加上开始提示

8.让蛇动起来

9.实现游戏暂停

10.实现转向功能

11.添加食物

12.吃掉食物

13.添加死亡条件

14.实现“重新开始”功能

一些要点

①我们需要把贪吃蛇移动的区域想成一个网格,蛇头其实就是在一个个格子之间移动。

②蛇的方向可以通过设置一个direction变量来控制。

③蛇的部位的坐标可以用两个数组来表示,X数组储存部位的横坐标,Y数组储存部位的纵坐标,于是X[0]、Y[0]为蛇头坐标,X[1]、Y[1]为第一节身体的坐标,以此类推。

④蛇的移动其实就是头部不断移动,然后每节身体取代前一节部位,所以具体实现时只需要考虑头的移动,后面身体的移动就是取代前一部位的位置而已。

⑤蛇吃到食物就是蛇头的坐标与食物的坐标重合,然后蛇的长度加一。

⑥蛇死亡就是蛇头与任意一节身体的坐标重合。

⑦通过标志位来判断游戏的各种状态。

⑧蛇移动的速度是由计时器的第一个参数决定的。如果参数为1000,那么是每秒调用一次,蛇移动的很慢;如果参数为10,那么是每10毫秒调用一次,即每秒调用100次,这样一来蛇移动的速度将非常快。

我的设计图纸:

2b71eb9de79834c9ba75ada6fa4b74f2.png

我的代码实现,具体信息在注释中:

1 package贪吃蛇;2 import java.awt.*;3 import java.awt.event.*;4 importjava.util.Random;5 import javax.swing.*;6 class GamePanel extends JPanel implementsKeyListener,ActionListener{7 //需要用到的图片

8 ImageIcon title=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\title.jpg");9 ImageIcon right=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\right.png");10 ImageIcon left=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\left.png");11 ImageIcon up=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\up.png");12 ImageIcon down=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\down.png");13 ImageIcon body=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\body.png");14 ImageIcon food=new ImageIcon("C:\\Users\\apple\\Desktop\\pic\\food.png");15

16 //计时器,调用start()后,每100ms调用一次actionPerformed()方法,以此达到蛇移动的效果

17 Timer timer = new Timer(100,this);18

19 //蛇的一些初始信息20 //count用来让paint()判断是否要在初始位置画蛇

21 intcount;22 //蛇的长度

23 intlength;24 //蛇的部位的坐标数组,数组的长度是蛇的最大长度,不能太小也不用太大,随意就可以了。

25 int[] snakeX=new int[100];26 int[] snakeY=new int[100];27 //移动的方向

28 String direction;29 //用方法来初始化蛇的信息,当我们第一次进入游戏或者游戏失败选择重新开始时会调用这个方法

30 public voidsnakeSetup() {31 count=1;32 //初始长度为3

33 length=3;34 //蛇头初始坐标

35 snakeX[0]=75;36 snakeY[0]=75;37 //第一节身体的初始坐标

38 snakeX[1]=50;39 snakeY[1]=75;40 //第二节身体的初始坐标

41 snakeX[2]=25;42 snakeY[2]=75;43 //蛇头默认的方向

44 direction="right";45 }46

47 //游戏状态

48 boolean isStart=false;49 boolean isFail=false;50

51 //食物状态,刚进游戏的时候是没有食物的,就相当于被吃掉了的状态

52 boolean isEaten=true;53 //食物坐标

54 intfoodX;55 intfoodY;56 Random random=newRandom();57 //每次paint都会调用一次这个方法,如果食物是被吃掉的状态,则给出新的食物的坐标

58 public voidfoodPoint() {59 if(isEaten) {60 isEaten=!(isEaten);61 foodX=25+25*random.nextInt(34);62 foodY=75+25*random.nextInt(24);63 }64 }65

66 //画布

67 publicGamePanel() {68 this.setLayout(null);69 this.setBackground(Color.WHITE);70 //初始化蛇的信息

71 this.snakeSetup();72 //键盘监听

73 this.addKeyListener(this);74 //设置焦点

75 this.setFocusable(true);76 //直接启动timer,蛇之所以不直接移动是因为actionPerformed()中有标志位控制是否做事情。

77 timer.start();78 }79

80 //把需要的组件画上去

81 public voidpaint(Graphics g) {82 //每次调用paint使count+1,因为游戏初始自动会调用一次,所以游戏初始的时候到这里count就是2了,以此来判断是否蛇是否是初始化的。

83 count++;84 //画标题

85 title.paintIcon(this, g, 25, 11);86 //画游戏面板,一个实心长方形

87 g.fillRect(25, 75, 850, 600);88 //如果没有食物,给出新的食物的坐标,有的话还是先前的坐标

89 this.foodPoint();90 //把食物画上去

91 food.paintIcon(this, g, foodX, foodY);92 //判断游戏状态93 //如果是失败状态,显示“失败”

94 if(isFail) {95 g.setColor(Color.RED);96 g.setFont(new Font("微软雅黑",Font.BOLD,40));97 g.drawString("失败!按p键重新开始!", 300, 200);98 //如果不是失败也不在游戏中,就是刚进游戏or暂停状态,显示提示信息

99 }else if(!(isStart)) {100 g.setColor(Color.WHITE);101 g.setFont(new Font("微软雅黑",Font.BOLD,40));102 g.drawString("按空格开始游戏", 300, 200);103 }104 //蛇头的位置和方向,direction是根据键盘的输入改变的,up就画向上的蛇头,down就画向下的蛇头,至于移动的方向在actionPerformed()中决定

105 if(direction.equals("right")) {106 right.paintIcon(this, g, snakeX[0], snakeY[0]);107 }else if(direction.equals("left")) {108 left.paintIcon(this, g, snakeX[0], snakeY[0]);109 }else if(direction.equals("up")) {110 up.paintIcon(this, g, snakeX[0], snakeY[0]);111 }else if(direction.equals("down")) {112 down.paintIcon(this, g, snakeX[0], snakeY[0]);113 }114 //蛇身的位置115 //如果是初始游戏,即count是2,画在初始位置

116 if(count==2) {117 body.paintIcon(this, g, snakeX[1], snakeY[1]);118 body.paintIcon(this, g, snakeX[2], snakeY[2]);119 //否则,根据实时的坐标画,坐标在actionPerformed()中不断地改变

120 }else{121 for(int i=1;i

126 if(snakeX[0]==foodX && snakeY[0]==foodY) {127 isEaten=true;128 this.length++;129 }130 //失败判定,改变游戏状态为失败,并提示失败信息

131 for(int i=1;i

143 int keyCode=e.getKeyCode();144 //如果是空格并且游戏不是失败状态,说明是要开始or暂停游戏

145 if(keyCode==KeyEvent.VK_SPACE && !(isFail)) {146 isStart=!(isStart);147 repaint();148 //如果是方向键,改变direction信息

149 }else if(keyCode==KeyEvent.VK_UP) {150 direction="up";151 }else if(keyCode==KeyEvent.VK_DOWN) {152 direction="down";153 }else if(keyCode==KeyEvent.VK_LEFT) {154 direction="left";155 }else if(keyCode==KeyEvent.VK_RIGHT) {156 direction="right";157 //如果是p并且游戏是失败的,改变游戏状态,初始化蛇的信息

158 }else if(keyCode==KeyEvent.VK_P &&isFail) {159 isFail=false;160 this.snakeSetup();161 repaint();162 }163 }164 @Override165 public voidactionPerformed(ActionEvent e) {166 //如果是在游戏中,会不断地改变坐标,让后一节身体取代前一节部位的位置。167 //如果不在游戏中或者失败了,这就是个空方法,蛇的坐标不改变,蛇也就不动了。

168 if(isStart && !(isFail)){169 for(int i=this.length-1;i>0;i--) {170 snakeX[i]=snakeX[i-1];171 snakeY[i]=snakeY[i-1];172 }173 //如果向右,那么头每次向右移动一格,纵坐标不变,一旦越界,从另一头出来。

174 if(direction.equals("right")) {175 snakeX[0] +=25;176 if(snakeX[0]>850) {177 snakeX[0]=75;178 }179 }else if(direction.equals("left")) {180 snakeX[0] -=25;181 if(snakeX[0]<25) {182 snakeX[0]=850;183 }184 }else if(direction.equals("up")) {185 snakeY[0] -=25;186 if(snakeY[0]<75) {187 snakeY[0]=650;188 }189 }else if(direction.equals("down")) {190 snakeY[0] +=25;191 if(snakeY[0]>650) {192 snakeY[0]=75;193 }194 }195 repaint();196 }197 }198 @Override199 public voidkeyTyped(KeyEvent e) {}200 @Override201 public voidkeyReleased(KeyEvent e) {}202 }203 public class贪吃蛇 {204 public static voidmain(String[] args) {205 JFrame f=new JFrame("贪吃蛇");206 f.setBounds(100,150,900,720);207 f.setResizable(false);208 f.add(newGamePanel());209 f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);210 f.setVisible(true);211 }212 }

个人认为这个游戏的核心在于要通过标志位来控制游戏的几种状态,同时还要通过这几个标志位来决定这个蛇是否移动;除此之外,蛇的移动一定得注意到其实只需要控制头的位置,后面的身体只要不断的取代前一节部位的位置就可以了。

当然这只是贪吃蛇的最基本实现,其实还可以加进去很多功能,除了我这里省略了的积分系统,还可以引入等级系统,lv1的时候蛇移动的速度慢,lv5的时候移动快,这通过改变timer的第一个参数的大小就能实现了。更高级的话,甚至可以联系到数据库,搞一个存档功能,联系到网络编程,搞一个联机功能……如果有朝一日我能深谙各种高端的操作了,或许我会回到这最初的起点,写一个超豪华版的贪吃蛇。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值