java贪吃蛇开发总结
相信每一个java学者在成长的时候都编写过一些小游戏,我花了两天的时间编写了贪吃蛇,这其中当然也参考了别人的代码。对于学者来说,通过这个案例也成长了不少。
对于面向对象来说,贪吃蛇所包含的类应该有Snake类、Food类、Ground类、GamePanel类(继承JPanel),这些都是属于Entity(实体)类,所以把他们放到一个包下面,Controller类继承KeyAdapter(键盘事件监听类库)实现SnakeListener 接口,可以看出Controller是一个控制蛇和监听键盘事件的类。SnakeGameActivity类包含main函数,是游戏的入口。Global类用于定义面板格局。列入面板的长宽,格子的大小。
所有的包以及类

Controller负责控制游戏的开始、蛇的移动以及监听键盘,监听键盘调用了keyPressed键盘事件,判断键盘的输入。

实体类蛇、食物、障碍物通过对象把他们抽象出来,声明它们所具有的属性和方法,比如说:蛇具有移动和改变方向的方法。

SnakeLiener(单词错误,是SnakeListener)负责监听蛇每次的移动,具体实现方法见Controller。
Global定义了窗口的宽高以及每块方格的大小。

整个项目很简单,但对于刚接触编程的人来说在这其中却能学到很多东西。
package com.Leekin.Entity; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.util.LinkedList; import com.Leekin.Listener.SnakeLiener; import com.Leekin.Util.Global; /** * Snake Class * @author Leekin * */ public class Snake { //the Listener for Snake Move private SnakeLiener snakeListener; //LinkedList private boolean life = true; private LinkedList<Point> body = new LinkedList<Point>(); 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 int oldDirection,newDirection; private Point tail; //储存尾巴 public Snake(){ init(); } private void init() { 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 * 去掉尾巴加头 * @param food */ public void eatFood(Food food){ body.add(tail); } /** * * 去尾加头 */ //move Method public void move(){ tail = body.removeLast(); int x = body.getFirst().x; int y= body.getFirst().y; /** * 获得新的头部 * 要判断方向才能确定头部 * 在初始化构造的时候默认的方向是向右 * */ if(this.oldDirection+newDirection != 0) this.oldDirection = this.newDirection; switch(oldDirection){ case UP: y--; if(y<0)y=Global.HEIGHT - 1; break; case DOWN: y++; if(y >= Global.HEIGHT) y = 0; break; case LEFT: x--; if(x<0)x=Global.WIDTH-1; break; case RIGHT: x++; if(x>=Global.WIDTH) x=0; break; } body.addFirst(new Point(x,y)); } //changedirection method public void changeDirection(int direction){ this.newDirection = direction; //this.direction= direction; } //DrawMe Method public void DrawMe(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 boolean isEatSelf(){ for(int i = 0; i < body.size();i++){ if(body.get(i).equals(getHead())){ return false; } } return false; } public void addSnakeListener(SnakeLiener snakeLiener){ if(snakeLiener != null) this.snakeListener = snakeLiener; } public void start(){ new SnakeDriver().start(); } //获得蛇头的方法 public Point getHead(){ return body.getFirst(); } public void setlife(boolean life){ this.life = life; } private class SnakeDriver extends Thread{ @Override public void run() { 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秒)
package com.Leekin.Entity; import java.awt.Graphics; import javax.swing.JPanel; /** * Ganme Panel class * @author Leekin * */ public class GamePanel extends JPanel { //Initialize object private Snake snake; private Food food; private Ground ground; //show method public void disPlay(Snake snake,Food food,Ground ground){ this.snake = snake; this.food = food; this.ground = ground; repaint(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if(snake!=null&&food!=null&&ground!=null){ snake.DrawMe(g); food.DrawMe(g); ground.DrawMe(g); } } }
GamePanel类是JPanel的子类,复写父类的paintComponen方法,是游戏板面的对象。
package com.Leekin.Entity; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.util.Random; import com.Leekin.Util.Global; /** * Ground class * @author Leekin * */ public class Ground { private int[][] rocks = new int[Global.WIDTH][Global.HEIGHT]; public Ground(){ for(int y = 0;y< Global.HEIGHT;y++){ for(int x = 0;x<Global.WIDTH;x++){ if(y==0||y==Global.HEIGHT-1) rocks[y][x] = 1; if(x==0||x==Global.HEIGHT-1){ rocks[y][x] = 1; } } } } //DrawMe public void DrawMe(Graphics g){ g.setColor(Color.orange); for(int y = 0;y<Global.HEIGHT;y++){ for(int x = 0;x<Global.WIDTH;x++){ if(rocks[y][x] == 1){ g.fill3DRect(x*Global.CELL_SIZE, y*Global.CELL_SIZE, Global.CELL_SIZE, Global.CELL_SIZE, true); } } } } /** * 设置食物点的位置 * @return */ public Point getPoint(){ int x,y; do{ x = new Random().nextInt(Global.WIDTH); y = new Random().nextInt(Global.HEIGHT); }while(rocks[x][y]==1); return new Point(x,y); } //is eat by snake public boolean isEatBySnake(Snake snake){ Point head = snake.getHead(); for(int x = 0;x<Global.WIDTH;x++){ for(int y = 0;y<Global.HEIGHT;y++){ if(rocks[x][y] == 1 && head.x == x && head.y == y) return true; } } return false; } }
Ground类,数组rocks用于储存障碍物,便用镶套for循环遍历,在用if判断,符合条件的生成边框障碍物。当蛇头的点与障碍物重合时就判定为蛇撞到障碍物,调用gameover的方法。
package com.Leekin.Entity; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import com.Leekin.Util.Global; /** * Food class * @author Leekin * */ public class Food extends Point { //DrawMe method public void DrawMe(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 * 只要判断蛇头的点是否和食物的点重合 * 得到蛇头 * @param snake * @return */ public boolean isEatBySnak(Snake snake){ Point head = snake.getHead(); if(this.equals(head)) return true; return false; } public void addFood(Point p){ this.x = p.x; this.y= p.y; } }
Food类是point的子类,因为生成的food是一个点。判断是否被蛇吃到的方法是蛇头的点是否和食物的点重合。当吃到蛇就会增加一个方格,同时addFood方法生成新的食物。
整个布局是以左上角的点为(0,0)的坐标,但坐标点与坐标的位置相反,它排列了数组在上面。

浙公网安备 33010602011771号