实验内容
一、实现贪吃蛇游戏的基本功能,屏幕上随机出现一个豆子,上下左右控制蛇的移动,吃到豆子以后蛇的身体加长一节。
二、蛇碰到边界,蛇死亡,游戏结束。
三、对蛇吃到豆子进行分值计算,并显示。
实验平台
通过高级程序设计语言(JAVA)实现贪吃蛇。
相关知识
①JAVA图形用户界面设计GUI
使用AWT组件库设计图形界面,如窗口Frame、面板Panel等。
引入
import java.awt.Frame;
import java.awt.Panel;
②链表LinkedList
一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
引入 LinkedList 类
import java.util.LinkedList;
普通创建方法
LinkedList<Node> Body = new LinkedList<>();
③键盘事件,如KeyEvent,KeyListener等
引入
//按键事件
import java.awt.event.KeyEvent;
//键盘监听
import java.awt.event.KeyListener;
按键常量
当按下一个键时会调用KeyPressed处理器,当松开一个键时会调用KeyReleased处理器,当输入一个统一编码时会调用KeyTyped处理器。
每个按键事件有一个相关的按键字符和按键代码,分别由KeyEvent中的getKeyChar()和getKeyCode()方法返回。
④线程thread
创建线程
Thread thread = new Thread();
实现Runnable接口
public class GameRunThread implements Runnable {
public void run() {
System.out.println("...");
}
}
调用run()方法
如果直接调用Thread类的run方法即thread.run(),那么相当于普通对象调用自身方法一样,是同步的,并不是一个新的线程。
调用start()方法
调用了start方法thread.start(),即通知线程规划器,当前线程已经初始化好了,准备就绪。线程规划器则会去以一个新的线程去执行run方法。
调用了start方法知识通知了线程规划器,并不代表先通知,就一定会被先执行的。
⑤双缓冲技术
双缓冲技术的工作原理
先在内存中分配一个和我们动画窗口一样大的空间(在内存中的空间我们是看不到的),然后利用getGraphics()方法去获得双缓冲画笔,接着利用双缓冲画笔给空间我们想画的东西,最后将它全部一次性的显示到我门的屏幕上.这样在我门的动画窗口上面是显示出来就非常的流畅了.避免了上面的闪烁效果。
执行过程
repaint() 到update()再到paint(),而我们的双缓冲代码就写在update()里。
(1)定义一个Graphics对象gBuffer和一个Image对象iBuffer。按屏幕大小建立一个缓冲对象给iBuffer。然后取得iBuffer的Graphics赋给gBuffer。此处可以把gBuffer理解为逻辑上的缓冲屏幕,而把iBuffer理解为缓冲屏幕上的图象。
(2)在gBuffer(逻辑上的屏幕)上用paint(Graphics g)函数绘制图象。
(3)将后台图象iBuffer全部一次性的绘制到我们的动画窗口,然后把我们内存中分配的空间窗口关闭调用dispose()方法.
代码
import java.awt.Image;
import java.awt.Graphics;
private Image iBuffer;
private Graphics gBuffer;
public void update(Graphics g) {
if(iBuffer == null) {
//创建用于双缓冲的屏幕外可绘制图像
iBuffer = createImage(DrawView.WIDTH * DrawView.SIZE + 1, DrawView.HEIGHT * DrawView.SIZE + 1);
gBuffer = iBuffer.getGraphics();
}
gBuffer.setColor(getBackground());
gBuffer.fillRect(0,0,DrawView.WIDTH * DrawView.SIZE + 1, DrawView.HEIGHT * DrawView.SIZE + 1);
//调用paint(),将缓冲图象的画传入
paint(gBuffer);
//再将此缓冲图像一次性绘到代表屏幕的Graphics对象,即该方法传入的“g”上
g.drawImage(iBuffer,0,0,this);
}
具体步骤
①Node类:蛇是由一个一个节点组成,所以首先将节点抽象为一个类,获取或者访问的方法,set()和get()方法。
②Snake类:此类是一条蛇抽象出来的一个类。一种包含了存储节点的 LinkedList 类型的集合,作为蛇的Body。
(1)getDirection()方法:获取蛇的方向;
(2)setDirection()方法:设置蛇的方向;
(3)getSnakeHead()方法:获取蛇的头部节点;
(4)getSnakeBody()方法:获取蛇的Body;
(5)snakeMove()方法:蛇的移动是在头部加一个节点,尾部减去一个节点,0,1,2,3分别代表上右下左四个方向;
(6)eatEgg()方法:如果蛇的头节点与egg的节点相等,随机生成一个egg,Body增加一个节点,此时score加1;
(7)snakeRunInterface()方法:蛇越界的方法,如果蛇的头节点不在定义的范围内,则静态逻辑变量gameState为false。
③SnakeGameView类:该类继承了Frame类,是一个游戏界面的类,用于布局游戏的界面。创建一个线程thread。
(1)setEgg()方法:设置egg节点;
(2)getScore()方法:获得分数;
(3)showView()方法:图形界面,包括主界面,分数,操作提示等;
(4)在主方法中启动线程。
④DrawView类:此类继承了Panel类,从而拥有了paint()方法的使用权,其中存在一个问题,就是游戏在不断的重画过程中,会出现游戏的闪烁问题,就引出了双缓冲的技术。
(1)paint(Graphics g)方法:用于游戏面板的重画,像网格、蛇、蛋的重画;
(2)update(Graphics g)方法:更新画面,利用双缓冲的技术解决界面闪烁的问题;
(3)drawGridding(Graphics g)方法:画网格,用for循环分别画横线和竖线;
(4)drawSnake(Graphics g)方法:画蛇;
(5)drawEgg(Graphics g)方法:画蛋。
⑤GameControl类:此类装配了KeyListener接口,用于监听键盘,从而实现按键对游戏的操作。
keyReleased(KeyEvent ke)方法:分四种情况,调用Snake中的setDirection()方法,给定实参0或1或2或3,分别代表上右下左四个方向。
⑥GameRunThread类:此类装配了Runnable接口,是一个线程类,用于不断重画界面。
run()方法:如果SnakeGameView类里面的静态逻辑变量gameState为true,重新绘制界面。