贪吃蛇系列之七——有吃的啦

      在上一个小项目中,我们遗留了一个小问题,就是我们的蛇可以反向运动,这是不被允许的,因此呢,我们先来修复这么一个小BUG。其实问题很简单,我们只要保证蛇在向上的时候不能向下就是了,自然,我们想到的最普遍的就是通过if语句来进行判断。没错,这样我们确实可以达到目的,但是我们的代码却很臃肿,不好。因此,我们有更优雅的解决方案,还是来看代码:
        /** 蛇前进的方向,向上 */
	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

猜你喜欢

转载自my.oschina.net/u/554627/blog/97383
今日推荐