《Java程序设计》期末考查
—课程设计报告
题目: 贪吃蛇游戏
专 业:信息管理与信息系统
班 级: 信息管理班
学 号: 200981020136
姓 名: 周南
成 绩:
完成时间: 20##年12月
目 录
第一章.需求分析
1:研究背景
2:研究方案
第二章.系统设计
1 : 编写贪吃蛇游戏中的各个类的主体框架性代码
2 : 编写Controler类与实现蛇移动的事件监听
3 : 蛇的数据结构设计与移动显示
4: 编写与测试表示食物的类并实现吃食物
总结
参考文献
贪吃蛇游戏
第一章. 需求分析
1研究背景
大概在文艺复兴的时候就有人发明的一种游戏,是现在贪吃蛇的前身。后来慢慢的发展就变成了今天的贪吃蛇了、现在贪吃蛇主要应用于手机游戏。
本文运用软件工程思想、JAVA面向对象编程、类的封装、抽象类的使用(接口),游戏编码规范遵循MVC模式,循序渐进实现贪吃蛇的相关功能。
2,研究方案
㈠ ●涉及到几个对象(类)
㈡ ●每个类有什么方法
1 . 首先得有个游戏显示面板(GamePanel)类
该类有一个方法void display(); //用来显示游戏面板
2 . 一个蛇(Snake)类
该类有以下几个方法:
void move(); //移动(走一步)
void changeDirection(); //改变方向
void eatFood(); //吃食物(身体变长)
voolean is EatBody();//蛇是否吃到了自己的身体
void drawMe();//显示
3 . 一个食物类(Food)类
该类有以下方法:
boolean is SnakeEatFood(Snake); //判断蛇是否吃到了食物
void drawMe();//显示
此时类之间的关系如下图所示:
图1 各个类之间的关系
第二章.系统设计
一 : 编写贪吃蛇游戏中的各个类的主体框架性代码
public class Food {
public boolean isSnakeEatFood(Snake snake)
{
System.out.println("Snake's eat food");
return false;
}
public void drawme()
{
System.out.println("Food's drawme");
}
}
package liang.snake.entities;
public class Ground {
public boolean isSnakeEatRock()
{
return false;
}
public void drawme()
{
System.out.println("Ground's drawme");
}
}
package liang.snake.entities;
public class Snake {
public void move()
{
System.out.println("Snake's move");
}
public void changeDirection(int direction)
{
System.out.println("Snake's changeDirection");
}
public void eatFood()
{
System.out.println("Snake's eat food");
}
public boolean isEatBody()
{
System.out.println("Snake's eat body");
return false;
}
public void drawMe(){
System.out.println("Snake's drawMe");
}
package liang.snake.view;
import java.awt.Graphics;
import javax.swing.JPanel;
import liang.snake.entities.Food;
import liang.snake.entities.Ground;
import liang.snake.entities.Snake;
public class GamePanel extends JPanel{
private Snake snake;
private Ground ground;
private Food food;
public void paintComponent(Graphics g) {
snake.drawMe();
ground.drawme();
food.drawme();
}
public void display(Snake snake,Ground ground,Food food)
{
this.snake=snake;
this.ground=ground;
this.food=food;
System.out.println("GamePanel's display");
this.repaint();
}
二 : 编写Controler类与实现蛇移动的事件监听
由于蛇不能直接操作显示面板(GamePanel)所以这里定义一个监听器SnakeListener:里面定义一个方法:
void snakeMove(Snake); 每当蛇移动一步就触发监听器,调用snakeMoved()由Controller去实现监听器的接口,然后由控制器去触发显示面板的事件源,让显示面板完成相应操作。
逻辑图如下:
图2 事件监听建立联系
在Snake 中增加蛇的方向常量:
public static final int UP=1;
public static final int DOWN=-1;
public static final int LEFT=2;
public static final int RIGHT=-2;
并添加一个接口:
package liang.snake.Listener;
import liang.snake.entities.Snake;
public interface SnakeListener {
public void SnakeMove(Snake snake);
}
Controler 实现SnakeListener接口并处理案件消息:代码如下:
package liang.snake.Controller;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import liang.snake.Listener.SnakeListener;
import liang.snake.entities.Food;
import liang.snake.entities.Ground;
import liang.snake.entities.Snake;
import liang.snake.view.GamePanel;
public class Controller extends KeyAdapter implements SnakeListener{
Snake snake=new Snake();
Ground ground=new Ground();
Food food=new Food();
GamePanel gamepanel=new GamePanel();
public Controller(Snake snake, Ground ground, Food food, GamePanel gamepanel) {
this.snake = snake;
this.ground = ground;
this.food = food;
this.gamepanel = gamepanel;
}
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode())
{
case KeyEvent.VK_UP :
snake.changeDirection(Snake.UP);
break;
case KeyEvent.VK_DOWN :
snake.changeDirection(Snake.DOWN);
break;
case KeyEvent.VK_LEFT :
snake.changeDirection(Snake.LEFT);
break;
case KeyEvent.VK_RIGHT :
snake.changeDirection(Snake.RIGHT);
break;
}
}
public void newgame()
{
snake.start();
}
public void SnakeMove(Snake snake) {
gamepanel.display(snake,ground,food);
}
}
开发到此时,Snake中应该增加监听器,并另启动一个线程来不断调用蛇的move();在Snake中提供一个启动线程的方法,在Snake中增加如下代码:
private Set<SnakeListener> listener=new
HashSet<SnakeListener>();
public void addSnakeListener(SnakeListener l)
{
if(l!=null)
this.listener.add(l);
}
}
public void start()
{
new Thread(new SnakeDriver()).run();
}
并在Run方法中增加
for(SnakeListener l: listener)
{
l.SnakeMove(Snake.this); }
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
三 : 蛇的数据结构设计与移动显示
分析:
要区分蛇头和蛇尾巴,这就要求这个数据结构是有序的,
还要经常访问第一个和最后一个节点,所以LinkedList最合适,因为它有getFirst(),getLast()方法和removeLast()方法
蛇下一步移动到哪是由方向控制的,前面已经定义过,蛇透坐标如下图:
图3 蛇头方向计算新蛇头坐标
在Snake的 move()中增加
body.removeLast();
int x=body.getFirst().x;
int y=body.getFirst().y;
switch(direction)
{
case UP:
y--;
break;
case DOWN:
y++;
break;
case LEFT:
x--;
break;
case RIGHT:
x++;
break;
}
Point newhead=new Point(x,y);
//加头
body.addFirst(newhead);
并定义 private int direction;
为了能更方便的维护游戏,这里定义Global类来定义常量如下:
package liang.snake.Global;
public class Global {
public static final int CELL_SIZE=20;
public static int HIGHT=30;
public static int WIDTH=30;
}
并修改游戏中窗口大小为:
jf.setSize(Global.CELL_SIZE*Global.WIDTH,Global.CELL_SIZE*Global.HIGHT);
再加上如下代码,对蛇初始化:
public Snake()
{
init();
}
public void init()
{
int x=Global.WIDTH/2;
int y=Global.HIGHT/2;
int nodenum=3;
for(int i=0;i<nodenum;i++)
{
body.addFirst(new Point(x++,y));
}
this.direction=RIGHT;
}
在蛇的drawMe(Graphics g)中些上如下让蛇显示的代码:
这里的画图采用填充3D矩形来实现:
for(Point p:body)
{
g.fill3DRect(p.x*Global.CELL_SIZE, p.y*Global.CELL_SIZE, Global.CELL_SIZE, Global.CELL_SIZE, true);
}
画图的规则如下图:
图4 计算格子坐标和像素坐标
此时运行Game类就可以看到蛇显示在面板中了,如图:
图5 显示面板显示的蛇
但是蛇越走越长,是因为没有擦除以前的图像,在GamePanel的paintComponent(Graphics g)中添加如下代码:
g.setColor(new Color(0xcfcfcf));
g.fill3DRect(0, 0,Global.CELL_SIZE*Global.WIDTH , Global.CELL_SIZE*Global.HIGHT, true);
此时蛇就可以正常移动了如图:
图6 移动中的蛇
通过按键改变移动方向,此时的蛇身体节点显示为白色,我们再在蛇的绘制方法中位器指定颜色:代码如下:
图7 指定颜色后的蛇
四: 编写与测试表示食物的类并实现吃食物
首先在Snake类中添加 public Point getHead()
{
return body.getFirst();
}
用来获取头结点,然后再Food类中写上如下代码:
package liang.snake.entities;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import liang.snake.Global.Global;
public class Food extends Point{
public void newfood(Point p)
{
this.setLocation(p);
}
public boolean isSnakeEatFood(Snake snake)
{
System.out.println("Snake's eat food");
return this.equals(snake.getHead());
}
public void drawme(Graphics g)
{
System.out.println("Food's drawme");
g.fill3DRect(this.x*Global.CELL_SIZE,this.y*Global.CELL_SIZE, Global.CELL_SIZE, Global.CELL_SIZE, true);
}
}
此时就可以画出食物并判断蛇是否吃到了食物,接下来就可以在Controller中处理吃食物的代码,吃了食物我们只要把先前移动去除的尾巴再给加上就实现了蛇吃食物身体变长的效果了,如下:
定义Point oldTail;
在蛇move()中保存去除的尾巴:oldTail= body.removeLast();
在Controller中的SnakeMove(Snake snake)中添加
if(food.isSnakeEatFood(snake))
{
snake.eatFood();
food.newfood(ground.getPoint());
}
结论
贪吃蛇虽然是一个小游戏,但是它也是我们最常见的游戏之一,大到电脑电视,小到手机游戏机,到处有它的存在,他给人们带来了娱乐。并且通过这次的动手,我发现它的组成与实现其实很麻烦,让我学会了很多东西。
首先感谢学校及老师给了我们这次上机实习的机会.虽然它与实际生活联系不是太密切,但毕竟让我们感到了实战的感觉.在实践编程过程中积累了宝贵的经验.这次数据结构课程设计使我们对此门课充满了乐趣,也学到了很多知识, Java在社会中的用处也随处可见,它的作用也是不会被忽视的, 我们也应该重视.这将会是一门日益重要的学科.,在编程过程中,曾遇到了很多问题,也失败了很多次,不过最终通过不懈的努力,翻阅资料,征求老师和同学的意见还是圆满完成了。这次的设计进一步提高了我对数据结构的认识,也发现了许多问题,更学到了许多知识。这是我对这次课程设计的一点体会.。在这次课程设计的过程中,在我的指导老师曾阳艳的精心指导下,我的编程,理解,动手及思维能力有了提高,同时也对数据结构的感觉更好了.所以在此对他的指导表示感谢!
参考文献
1,《Java实用教程》 郑阿奇
2,《Java实用教程》 耿祥义
3,互联网