贪吃蛇游戏开发文档
1.系统介绍
1.1系统目标
贪吃蛇是一个益智性游戏。通过本游戏的设计和实现,可以提升java技术能力,提升开发的能力以及掌握项目的开发流程。
1.2开发环境
系统环镜:windows
开发工具:eclipse mars,jdk1.8
1.3需求分析
在一定范围内,生成一条蛇,和随机生成一个实物。当蛇吃到食物以后蛇身变长,
通过键盘的方向键控制蛇的运行方向,当蛇头碰到障碍物或者蛇身,游戏结束。
控制游戏暂停,继续,游戏开始结束游戏。
1.3.1 绘制图形的需求
绘制一个简介清晰的小蛇和食物。其中小蛇包括蛇头,蛇身,蛇尾。
小蛇:绘制成圆形的图像,其中舌头是填充为黄色,蛇身和蛇尾填充为黄色
食物:绘制成方形的图形,食物要求填充为红色。
1.3.2 显示需求
当游戏开始后,小蛇是一直移动状态。当小蛇吃掉食物以后蛇身变长,同时当前食物消失,食物随机产生。蛇身随着蛇的移动不断刷新。
1.3.3 按键的需求
通过键盘方向键(wasd或者上下左右)进行蛇的移动,当蛇头碰到边界蛇身,游戏结束。
通过按钮实现游戏暂停继续,重新开始及结束游戏
在横线对象内绘制线,在图像方法内调用食物对象和贪吃蛇对象,draw()方法
1.4技术要求
面向对象技术
gui编程技术
事件处理机制
1.4.1
面向对象的特性:
封装:隐藏内部的实现细节,提供可以对外访问的借口
继承:子类拥有父类的属性和行为,实现代码的高效率重用。
多态:同一个动作作用于不同的对象产生不同的行为。
-----------------------------------------
类的组成部分·:
属性
方法
构造器(构造了对象的生命周期)
内部类
代码块面向对象
1.4.2gui编程
java提供三个包用于开发gui
java。awt:提供字体绘制图形
javax.swing:提供gui变成的各种组件如窗体,按钮,文本框
java。awt。event:提供事件处理机制
1.4.3
贪吃蛇的移动是持续性的,只要贪吃蛇没有死亡就可以继续以多,用多线程
主线程
键盘监听线程
状态监听线程
绘制动画线程
1.4.4事件处理机制
事件
1)例如键盘
事件源
2)按钮
监听器
3)listner
事件编程的步骤
1.编写事件处理类(事件监听者)
2.实现监听接口
控制贪吃蛇运行方向的绑定,实现接口
implements KeyListener
public void keyControl(KeyEvent e){
//通过对案件的判断修改蛇头移动方向,从而控制贪吃蛇方向
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
//如果蛇头方向不超下可以实现向上移动
if (head.dir.equals(Config.D)) {
break;
}
head.dir=Config.U;
break;
case KeyEvent.VK_DOWN:
//如果蛇头方向不超上就可以实现向下移动
if(head.dir.equals(Config.U)){
break;
}
head.dir=Config.D;
break;
case KeyEvent.VK_RIGHT:
if(head.dir.equals(Config.L)){
break;
}
head.dir=Config.R;
break;
case KeyEvent.VK_LEFT:
if (head.dir.equals(Config.R)) {
break;
}
head.dir=Config.L;
break;
default:
break;
}
}
按钮的监听绑定:
extends JPanel implements ActionListener
3.重写事件处理方法
@Override
public void keyReleased(KeyEvent e) {
// 調用貪吃蛇控制方向的方法
// System.out.println(e.getKeyCode());
snake.keyControl(e);
}
在实现的方法类中调用控制方向的方法
重写按钮的监听方法
//actionevent: 获取事件作用的对象
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
//监听对象是暂停游戏
if (e.getSource() == pause) {
Config.isPause =false;
}
//监听对象是继续游戏
if (e.getSource() == continu) {
Config.isPause =true;
//设置键盘监听焦点
myPanel.setFocusable(true);
myPanel.requestFocus();
}
//监听对象是重新开始游戏
if (e.getSource()== restart) {
//1.停掉当前线程
myPanel.snakeThread.stopThread();
//2.重新生成蛇和食物
Food food =new Food();
myPanel.food =food;
myPanel.snake=new Snake(food);
//还原config的控制条件还原初始状态
Config.isPause=true;
Config.isLive=true;
//3.启动创建一个新的线程(内部类对象)
SnakeThread snakeThread =myPanel.new SnakeThread();
//4.启动线程
snakeThread.start();
myPanel.snakeThread =snakeThread;
//获取键盘焦点
myPanel.setFocusable(true);
myPanel.requestFocus();
}
//监听对象是退出游戏
if (e.getSource() == exit) {
System.exit(0);
}
}
4.指定事件响应者实现注册监听
4.1@Override
public void keyReleased(KeyEvent e) {
// 調用貪吃蛇控制方向的方法
// System.out.println(e.getKeyCode());
snake.keyControl(e);
}
4.2 //注册按钮监听
pause.addActionListener(this);
continu.addActionListener(this);
restart.addActionListener(this);
exit.addActionListener(this);
2.系统设计
2.1类的设计
蛇类 snake
食物类 food
节点类(指定贪吃蛇的颜色和形状) node
常量类 指定方向以及画布的大小 config
按钮类 button
2.2功能设计
1mypanle类实现贪吃蛇对象显示和食物对象显示以及贪吃蛇移动的实现
2.实现蛇类的上下左右移动
3.当蛇头吃到食物时增长蛇头
4.当蛇头碰到障碍物和自己节点时结束游戏
5绑定按钮实现对贪吃蛇状态的控制
3.系统实现
3.1开发食物对象
1)需要在主界面中创建食物对象,Food food =new Food();
2)在food类中创建绘制食物图形的方法public voiddraw(Graphics g);
3)随机生成食物所在的位置public void repair();
3.2贪吃蛇对象
初始化贪吃蛇的位置
//指定蛇头。蛇身,蛇尾节点。
head =new Node(7, 13,Config.R);
body =new Node(7, 12,Config.R);
tail =new Node(7, 11,Config.R);
绑定蛇头蛇身蛇尾的关系
// 绑定蛇头,蛇身,蛇尾节点之间的关系。
head.next =body;
body.pre =head;
body.next= tail;
tail.pre = body;
通过便利绘制蛇的节点,并且在一个方法上设置上颜色和形状draw方法
3.3实现贪吃蛇的移动
//貪吃蛇移動的方法
public void move(){
// 1.添加蛇頭2.去除蛇尾3.吃食物4.死亡檢測
addHead();//添加蛇頭。
removeTail();//去除蛇尾
eat();
deadCheck();
}
贪吃蛇的移动依靠线程
通过重写run方法判断蛇的存货状态来确定蛇是否移动
class SnakeThread extends Thread{ boolean flag =true;//重新开始 @Override public void run(){ //当贪吃蛇没有死亡的时候则应该继续移动 while(Config.isLive && flag){ //Config.isLive判斷貪吃蛇是否存活 try { //当前线程休眠0.3 Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //config.ispause=true:的情况下:代表继续游戏 //config。ispause==false:代表是暂停游戏 if (Config.isLive && Config.isPause) { //该方法适用于重绘的组件,具有页面刷新的效果 //重绘的步骤:调用repaint方法--》调用awt线程--》update方法清楚当前显示--》调用repainrt方法实现绘制图形 repaint();//重绘 }
if (!Config.isLive) { //弹出借宿游戏结束的对话框 JOptionPane.showMessageDialog(MyPanel.this,"游戏结束");
}
}
} //当贪吃蛇没有死亡的时候则应该继续移动 while(Config.isLive && flag){ //Config.isLive判斷貪吃蛇是否存活 try { //当前线程休眠0.3 Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //config.ispause=true:的情况下:代表继续游戏 //config。ispause==false:代表是暂停游戏 if (Config.isLive && Config.isPause) { //该方法适用于重绘的组件,具有页面刷新的效果 //重绘的步骤:调用repaint方法--》调用awt线程--》update方法清楚当前显示--》调用repainrt方法实现绘制图形 repaint();//重绘 }
if (!Config.isLive) { //弹出借宿游戏结束的对话框 JOptionPane.showMessageDialog(MyPanel.this,"游戏结束");
}
}
}
//当贪吃蛇没有死亡的时候则应该继续移动 while(Config.isLive && flag){ //Config.isLive判斷貪吃蛇是否存活 try { //当前线程休眠0.3 Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //config.ispause=true:的情况下:代表继续游戏 //config。ispause==false:代表是暂停游戏 if (Config.isLive && Config.isPause) { //该方法适用于重绘的组件,具有页面刷新的效果 //重绘的步骤:调用repaint方法--》调用awt线程--》update方法清楚当前显示--》调用repainrt方法实现绘制图形 repaint();//重绘 }
if (!Config.isLive) { //弹出借宿游戏结束的对话框 JOptionPane.showMessageDialog(MyPanel.this,"游戏结束");
}
}
}
//当贪吃蛇没有死亡的时候则应该继续移动 while(Config.isLive && flag){ //Config.isLive判斷貪吃蛇是否存活 try { //当前线程休眠0.3 Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //config.ispause=true:的情况下:代表继续游戏 //config。ispause==false:代表是暂停游戏 if (Config.isLive && Config.isPause) { //该方法适用于重绘的组件,具有页面刷新的效果 //重绘的步骤:调用repaint方法--》调用awt线程--》update方法清楚当前显示--》调用repainrt方法实现绘制图形 repaint();//重绘 }
if (!Config.isLive) { //弹出借宿游戏结束的对话框 JOptionPane.showMessageDialog(MyPanel.this,"游戏结束");
设置停止线程的方法 Public void stopthread(); 用来绑定停止按钮,实现暂停游戏
|
3.4实现贪吃蛇吃食物增长
1.eat()方法通过判断食物蛇头是否重合进行,如果重合就实现添加蛇头和随机生成食物的方法
public void eat(){
//判断两个矩形是否重合(蛇头是否碰到食物)
Rectangle a = getHeadRec();
Rectangle b = food.getFoodRec();
if (a.intersects(b)) {
addHead();
food.repair();
}
}
3.5检验贪吃蛇是否死亡
当蛇头碰到边界或者碰到自己节点,修改蛇的状态停止线程的运行
//检测贪吃蛇是否死亡
public void deadCheck(){
//当蛇头碰到边界
//行的范围:0 - config.rows-1
//列的范围:0 - Config.col-1
if (head.row<0 || head.col<0 || head.row>Config.ROWS-1 ||head.col>Config.COLS-1) {
Config.isLive=false;
}
//当蛇头碰到蛇身
//便利蛇身,判断节点与舌头重合
for (Node n =head.next;n!=null;n=n.next) {
//判断蛇头的行和列 和当前蛇身节点的位置是否相同
if (head.row == n.row && head.col==n.col) {
Config.isLive=false;
break;
}
3.6实现按钮与状态的绑定
首先实现implements ActionListener接口
重写接口方法actionPerformed
进行按钮当前状态的判断
//actionevent: 获取事件作用的对象
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
//监听对象是暂停游戏
if (e.getSource() == pause) {
Config.isPause =false;
}
//监听对象是继续游戏
if (e.getSource() == continu) {
Config.isPause =true;
//设置键盘监听焦点
myPanel.setFocusable(true);
myPanel.requestFocus();
}
//监听对象是重新开始游戏
if (e.getSource()== restart) {
//1.停掉当前线程
myPanel.snakeThread.stopThread();
//2.重新生成蛇和食物
Food food =new Food();
myPanel.food =food;
myPanel.snake=new Snake(food);
//还原config的控制条件还原初始状态
Config.isPause=true;
Config.isLive=true;
//3.启动创建一个新的线程(内部类对象)
SnakeThread snakeThread =myPanel.new SnakeThread();
//4.启动线程
snakeThread.start();
myPanel.snakeThread =snakeThread;
//获取键盘焦点
myPanel.setFocusable(true);
myPanel.requestFocus();
}
//监听对象是退出游戏
if (e.getSource() == exit) {
System.exit(0);
把按钮注册监听添加到你定义的按钮中
//注册按钮监听
pause.addActionListener(this);
continu.addActionListener(this);
restart.addActionListener(this);
exit.addActionListener(this);
3.7bug
3.7.1贪吃蛇蛇的节点与食物重合
//判断随机生成食物坐标不能喝贪吃蛇接节点坐标重合 for (Node n=snake.head;n !=null;n=n.next) { //判断节点所在行与食物所在行是否一致 boolean x=n.row ==food.getRow(); //判断节点所在列于十五所在列是否一致 boolean y=n.col==food.getCol(); if (x && y) { //随机生成食物 food.repair(); break; } } |
3.7.2,食物与贪吃蛇蛇头重合
public void eat(){ //判断两个矩形是否重合(蛇头是否碰到食物) Rectangle a = getHeadRec(); Rectangle b = food.getFoodRec(); if (a.intersects(b)) { addHead(); food.repair(); } } //获取蛇头坐标 public Rectangle getHeadRec(){ //获取蛇头坐标矩形
return new Rectangle(head.col*Config.SPAN,head.row*Config.SPAN,Config.SPAN,Config.SPAN); } 获取食物坐标 //获取食物坐标 public Rectangle getFoodRec(){
return new Rectangle(col*Config.SPAN, row*Config.SPAN,Config.SPAN, Config.SPAN);
} |