在上一个小项目中,我们遗留了一个小问题,就是我们的蛇可以反向运动,这是不被允许的,因此呢,我们先来修复这么一个小BUG。其实问题很简单,我们只要保证蛇在向上的时候不能向下就是了,自然,我们想到的最普遍的就是通过if语句来进行判断。没错,这样我们确实可以达到目的,但是我们的代码却很臃肿,不好。因此,我们有更优雅的解决方案,还是来看代码:
我把代码都放在迅雷快传上了,大家奔走相告吧,这里给出链接,一篇博文对应一个项目文件,大家直接导入工程就可以运行:
http://kuai.xunlei.com/d/nNdkCSP3vUXTUAQAa50
/** 蛇前进的方向,向上 */
public static final int DIR_UP = -1;
/** 蛇前进的方向,向下 */
public static final int DIR_DOWN = 1;
/** 蛇前进的方向,向左 */
public static final int DIR_LEFT = -2;
/** 蛇前进的方向,想右 */
public static final int DIR_RIGHT = 2;
/**
* 改变蛇的运动方向
* @param direction 蛇的新的运动方向
*/
public void changeDirection(int direction){
if(this.direction + direction != 0){
this.direction = direction;
}
}
我想各位聪明的看官已经很清楚了,我们做了什么,这里运用了一点技巧,我们把相反方向上的两个方向的值设置为一正一负,那么我们的问题就解决了,也很优雅。嗯嗯,好了,再次运行你的程序,就算你把键盘戳烂了都不会出现那种情况了,呵呵。
解决了一点小问题,我们继续我们的贪吃蛇。我们的蛇到目前为止已经基本上OK了,接下来我们将要开始对蛇的食物进行编码,我们有吃的啦!!当然,我们新建一个叫Food的类,放在com.gulang.snake.entity包下面。下面就是我们编写的Food类,请看代码:
package com.gulang.snake.entity;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import com.gulang.snake.view.GameView;
/**
* 食物对象
* @author [email protected]
*
*/
public class Food {
/** 食物的大小,我们这里是和蛇的一个身体节点一样大 */
public static final int FOOD_SIZE = Snake.BODY_SIZE;
/** 食物的位置,横坐标 */
private int x;
/** 食物的位置,纵坐标 */
private int y;
/**
* 构造一个食物对象,并且设置食物出现的位置在游戏的窗口上的任何一个有效的地方
* 1.不能出现在窗口外;
* 2.不能出现在有蛇的位置;
* 3.不能出现在有石块的地方
*
* @param 游戏中的蛇的对象
*/
public Food(Snake snake){
//我们先将我们的游戏窗口网格化,并计算出每一行有多少个单元格,每一列有多少个单元格
int cellsInRow = GameView.WINDOW_WIDTH / Snake.BODY_SIZE;
int cellsInCol = GameView.WINDOW_HEIGHT / Snake.BODY_SIZE;
//我们用随机数类来产生我们的食物的横坐标和纵坐标,并且保证食物的坐标不产生在蛇的身体上
Random r = new Random();
do{
createXY(r, cellsInRow, cellsInCol);
} while (isInSnakeBody(snake));
}
/**
* 生成新的食物的坐标
* @param r 随机数对象
* @param cellsInRow 一行中的单元格个数
* @param cellsInCol 一列中的单元格个数
*/
private void createXY(Random r, int cellsInRow, int cellsInCol){
x = r.nextInt(cellsInRow) * Snake.BODY_SIZE;
y = r.nextInt(cellsInCol) * Snake.BODY_SIZE;
}
/**
* 判断当前食物的位置是否在蛇的身体上
* @param snake 蛇
* @return 如果当前的食物在蛇的身体上则返回true,否则返回false
*/
public boolean isInSnakeBody(Snake snake){
for(Snake.Body body : snake.getSnakeBody()){
if(this.x == body.getX() && this.y == body.getY()){
return true;
}
}
return false;
}
/**
* 将食物绘制出来
* @param g
*/
public void drawMe(Graphics g){
//获得画笔原来的颜色
Color c = g.getColor();
//将画笔的颜色设置为绿色
g.setColor(Color.GREEN);
//绘制一个3D的绿色方块作为食物
g.fill3DRect(x, y, FOOD_SIZE, FOOD_SIZE, true);
//将画笔的颜色还原回去
g.setColor(c);
}
}
我们现在已经有了Snake类的经验,因此我想大家对于该类的理解应该不存在什么有困难的地方。当然,有几点关于游戏的细节还是需要注意的,比如我们Food的构造方法,我们食物的产生地必须满足几个条件,这些我都写在注释上了,大家应该也看到了。接着呢,为了看到效果,我们只需要在GameView中添加关于Food类的代码就可以了,我想能够看到这里的你也应该没有什么问题了,当然,惯例孤狼还是贴一下代码,请看:
/**
* 绘制界面的方法
*/
@Override
public void paint(Graphics g) {
//将窗口清空
g.clearRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
//绘制蛇的对象
snake.drawMe(g);
//绘制食物
food.drawMe(g);
}
到这里呢,还有一点小问题,是我们之前在写Snake类时留下来的,大家回想一下我们在写Snake类时设置的蛇的边长是30,但是我们的游戏的窗口的边长是800,这在我们将窗口网格化的时候就会出现问题,导致我们蛇的位置和食物的位置对不齐,因此呢我们首先将蛇的身体的大小改为20,然后再将蛇的初始化位置修正。我们原来是让蛇在窗口的正中央,但是网格化以后我们需要的其实是在最中间的那一个,因此我们修改蛇的构造方法,看代码:
/**
* 初始化一条蛇
*/
public Snake(){
//初始化蛇最开始前进的方向为向右
direction = DIR_RIGHT;
//我们将蛇的第一节身体初始化在游戏窗口的中央
int cellsInRow = GameView.WINDOW_WIDTH / BODY_SIZE;
int cellsInCol = GameView.WINDOW_HEIGHT / BODY_SIZE;
int startX = cellsInRow / 2 * BODY_SIZE;
int startY = cellsInCol / 2 * BODY_SIZE;
//初始化蛇的身体,即向body链表中添加数据,我们初始化蛇的初始节点为3个
for(int i = 0; i < 3; i++){
//逐个的计算出蛇的每一个节点的位置
Body body = new Body(startX - i * BODY_SIZE, startY);
snakeBody.add(body);
}
}
那么到这里呢,我们的这个小项目就做完了,运行一下看看吧,是不是感觉已经有一点贪吃蛇游戏的雏形了,还是小有成就感的吧,呵呵。我们现在可以控制我们的蛇去吃食物了,但是遗憾的是蛇现在经过食物的时候还不能吃掉食物,这个我们将在下一次的项目中来完成这个功能。那今天就到这里吧,晚安啦,各位,希望天亮以后我还有机会完成后面的博文,呵呵,芥末日快乐啦。
我把代码都放在迅雷快传上了,大家奔走相告吧,这里给出链接,一篇博文对应一个项目文件,大家直接导入工程就可以运行:
http://kuai.xunlei.com/d/nNdkCSP3vUXTUAQAa50