相信每一个java学者在成长的时候都编写过一些小游戏,我花了两天的时间编写了贪吃蛇,这其中当然也参考了别人的代码。对于学者来说,通过这个案例也成长了不少。
对于面向对象来说,贪吃蛇所包含的类应该有Snake类、Food类、Ground类、GamePanel类(继承JPanel),这些都是属于Entity(实体)类,所以把他们放到一个包下面,Controller类继承KeyAdapter(键盘事件监听类库)实现SnakeListener 接口,可以看出Controller是一个控制蛇和监听键盘事件的类。SnakeGameActivity类包含main函数,是游戏的入口。Global类用于定义面板格局。列入面板的长宽,格子的大小。
所有的包以及类
Controller负责控制游戏的开始、蛇的移动以及监听键盘,监听键盘调用了keyPressed键盘事件,判断键盘的输入。
实体类蛇、食物、障碍物通过对象把他们抽象出来,声明它们所具有的属性和方法,比如说:蛇具有移动和改变方向的方法。
SnakeLiener(单词错误,是SnakeListener)负责监听蛇每次的移动,具体实现方法见Controller。
Global定义了窗口的宽高以及每块方格的大小。
整个项目很简单,但对于刚接触编程的人来说在这其中却能学到很多东西。
packagecom.Leekin.Entity;importjava.awt.Color;importjava.awt.Graphics;importjava.awt.Point;importjava.util.LinkedList;importcom.Leekin.Listener.SnakeLiener;importcom.Leekin.Util.Global;/*** Snake Class
*@authorLeekin
**/
public classSnake {//the Listener for Snake Move
privateSnakeLiener snakeListener;//LinkedList
private boolean life = true;private LinkedList body = new LinkedList();public static final int UP = 1;public static final int DOWN = -1;public static final int LEFT = 2;public static final int RIGHT = -2;private intoldDirection,newDirection;private Point tail; //储存尾巴
publicSnake(){
init();
}private voidinit() {int x = Global.WIDTH/2;int y = Global.HEIGHT/2;for(int i = 0;i < 3;i++){
body.add(new Point(x-1,y));
}this.oldDirection = newDirection=RIGHT;
}/*** eat mothd
* 去掉尾巴加头
*@paramfood*/
public voideatFood(Food food){
body.add(tail);
}/***
* 去尾加头*/
//move Method
public voidmove(){
tail=body.removeLast();int x =body.getFirst().x;int y=body.getFirst().y;/*** 获得新的头部
* 要判断方向才能确定头部
* 在初始化构造的时候默认的方向是向右
**/
if(this.oldDirection+newDirection != 0)this.oldDirection = this.newDirection;switch(oldDirection){caseUP:
y--;if(y<0)y=Global.HEIGHT - 1;break;caseDOWN:
y++;if(y >= Global.HEIGHT) y = 0;break;caseLEFT:
x--;if(x<0)x=Global.WIDTH-1;break;caseRIGHT:
x++;if(x>=Global.WIDTH) x=0;break;
}
body.addFirst(newPoint(x,y));
}//changedirection method
public void changeDirection(intdirection){this.newDirection =direction;//this.direction= direction;
}//DrawMe Method
public voidDrawMe(Graphics g){
g.setColor(Color.blue);for(Point p:body){
g.fill3DRect(p.x*Global.CELL_SIZE,p.y*Global.CELL_SIZE, Global.CELL_SIZE,Global.CELL_SIZE,true);
}
}//is eat self method
public booleanisEatSelf(){for(int i = 0; i < body.size();i++){if(body.get(i).equals(getHead())){return false;
}
}return false;
}public voidaddSnakeListener(SnakeLiener snakeLiener){if(snakeLiener != null)this.snakeListener =snakeLiener;
}public voidstart(){newSnakeDriver().start();
}//获得蛇头的方法
publicPoint getHead(){returnbody.getFirst();
}public void setlife(booleanlife){this.life =life;
}private class SnakeDriver extendsThread{
@Overridepublic voidrun() {while(life){
move();
snakeListener.snakeMoved(Snake.this);try{
Thread.sleep(300);
}catch(InterruptedException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
Snake实现逻辑,其中移动速度用线程来控制,Thread.sleep(300);(休眠0.3秒)
packagecom.Leekin.Entity;importjava.awt.Graphics;importjavax.swing.JPanel;/*** Ganme Panel class
*@authorLeekin
**/
public class GamePanel extendsJPanel {//Initialize object
privateSnake snake;privateFood food;privateGround ground;//show method
public voiddisPlay(Snake snake,Food food,Ground ground){this.snake =snake;this.food =food;this.ground =ground;
repaint();
}
@Overrideprotected voidpaintComponent(Graphics g) {super.paintComponent(g);if(snake!=null&&food!=null&&ground!=null){
snake.DrawMe(g);
food.DrawMe(g);
ground.DrawMe(g);
}
}
}
GamePanel类是JPanel的子类,复写父类的paintComponen方法,是游戏板面的对象。
packagecom.Leekin.Entity;importjava.awt.Color;importjava.awt.Graphics;importjava.awt.Point;importjava.util.Random;importcom.Leekin.Util.Global;/*** Ground class
*@authorLeekin
**/
public classGround {private int[][] rocks = new int[Global.WIDTH][Global.HEIGHT];publicGround(){for(int y = 0;y< Global.HEIGHT;y++){for(int x = 0;x
rocks[y][x]= 1;if(x==0||x==Global.HEIGHT-1){
rocks[y][x]= 1;
}
}
}
}//DrawMe
public voidDrawMe(Graphics g){
g.setColor(Color.orange);for(int y = 0;y
g.fill3DRect(x*Global.CELL_SIZE, y*Global.CELL_SIZE,
Global.CELL_SIZE, Global.CELL_SIZE,true);
}
}
}
}/*** 设置食物点的位置
*@return
*/
publicPoint getPoint(){intx,y;do{
x= newRandom().nextInt(Global.WIDTH);
y= newRandom().nextInt(Global.HEIGHT);
}while(rocks[x][y]==1);return newPoint(x,y);
}//is eat by snake
public booleanisEatBySnake(Snake snake){
Point head=snake.getHead();for(int x = 0;x
}
}return false;
}
}
Ground类,数组rocks用于储存障碍物,便用镶套for循环遍历,在用if判断,符合条件的生成边框障碍物。当蛇头的点与障碍物重合时就判定为蛇撞到障碍物,调用gameover的方法。
packagecom.Leekin.Entity;importjava.awt.Color;importjava.awt.Graphics;importjava.awt.Point;importcom.Leekin.Util.Global;/*** Food class
*@authorLeekin
**/
public class Food extendsPoint {//DrawMe method
public voidDrawMe(Graphics g){
g.setColor(Color.green);
g.fill3DRect(x*Global.CELL_SIZE, y*Global.CELL_SIZE,
Global.CELL_SIZE,Global.CELL_SIZE,true);
}/*** is eat by snak
* 只要判断蛇头的点是否和食物的点重合
* 得到蛇头
*@paramsnake
*@return
*/
public booleanisEatBySnak(Snake snake){
Point head=snake.getHead();if(this.equals(head))return true;return false;
}public voidaddFood(Point p){this.x =p.x;this.y=p.y;
}
}
Food类是point的子类,因为生成的food是一个点。判断是否被蛇吃到的方法是蛇头的点是否和食物的点重合。当吃到蛇就会增加一个方格,同时addFood方法生成新的食物。
整个布局是以左上角的点为(0,0)的坐标,但坐标点与坐标的位置相反,它排列了数组在上面。