在上一个项目中,我们已经有了石块的对象,但是问题就是我们只绘制了一块石头,这可不是我们想要的最终结果,所以,在这个版本中,我们将引入一个新的类,GameMap,同样的,新建这个类,放在我们的com.gulang.snake.entity包中。下面呢,就是我们这个游戏地图的代码,大家请看:
好了,这个类也比较简单,主要就是负责地图的创建和绘制。唯一需要大家注意的就是我们在构造地图的时候,设置石头的坐标的时候要注意row和col的位置噢,这个地方的坐标转换要细心一点。当然,做错了也不要紧,运行之后发现不是我们要的效果可以回来改的嘛。我想大家看这个数组的构造也知道我们在游戏窗口的四周布上了石块,运行后得到的结果如下图:
至于为什么我们在顶上放了两排石块呢,是因为我们的游戏窗口上面的一部分被窗口的标题栏遮住了,为了能够让大家看到效果,我就设置了两排。
那么有了我们的游戏地图后,我们就不需要直接去绘制石块了,因此,我们去掉GameView中对Stone的引用,改为对GameMap的引用。同时,我们需要修改Snake类中的isEatStone()方法,修改后的方法如下:
同样的,这个项目和以前项目的源文件: http://kuai.xunlei.com/d/Ae2cA1x2zAzfUAQA7a7
package com.gulang.snake.entity;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
/**
* 游戏的地图
* @author [email protected]
*
*/
public class GameMap {
/** 地图数据 */
private int[][] mapData = new int[][]{
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
/** 装游戏中石头的集合 */
private List<Stone> stones = new ArrayList<Stone>();
/**
* 游戏地图的构造方法
*/
public GameMap(){
for(int row = 0; row < mapData.length; row++){
for(int col = 0; col < mapData[row].length; col++){
if(mapData[row][col] == 1){
stones.add(new Stone(col * Stone.SIZE, row * Stone.SIZE));
}
}
}
}
/**
* 绘制游戏的地图
* @param g
*/
public void drawMe(Graphics g){
for(Stone stone : stones){
stone.drawMe(g);
}
}
public List<Stone> getStones() {
return stones;
}
}
这里呢,我还是简单的来说明一下我们的游戏地图是怎么一回事。在我们的游戏开发中呢,游戏的地图是很重要的一部分,通常在我们的游戏开发中,我们的游戏开发引擎会提供给我们一个地图编辑器,我想原因大家也很清楚了,看了我们上面的代码。在我们的游戏中,不同的素材会有不同的特性,就比如说我们这个游戏中石头和食物就是两种很相似的但是却拥有不同特性的对象。这些对象的特性对于我们来说是有意义的,但是对于计算机而言,他们是一样的。所以要想让计算机知道这是不同的两个对象,我们就要先制定一些约定,比如说我们这个地图对象中,我们就约定0表示什么都没有,1表示的是石头。因此地图编辑器就会把我们游戏中所有合法的素材都提供给我们,我们用这些素材去组拼出一个游戏的地图。最后生成的就是一个多维的数组,就是我们GameMap类中的mapData数组。当然,我们这里的环境是很简单的,因此我们自己就可以直接用二维数组的方式写出来了,在真是的游戏中地形是可以设计的很复杂的,那时候再直接用1,2,3,4来构造就比较困难了,因此我们的游戏引擎一定会有一个可视化的地图编辑器提供给我们使用,有兴趣的同学可以自己去写一个我们贪吃蛇的地图编辑器,我们在这里就不再深入的讨论这个了。
好了,这个类也比较简单,主要就是负责地图的创建和绘制。唯一需要大家注意的就是我们在构造地图的时候,设置石头的坐标的时候要注意row和col的位置噢,这个地方的坐标转换要细心一点。当然,做错了也不要紧,运行之后发现不是我们要的效果可以回来改的嘛。我想大家看这个数组的构造也知道我们在游戏窗口的四周布上了石块,运行后得到的结果如下图:
至于为什么我们在顶上放了两排石块呢,是因为我们的游戏窗口上面的一部分被窗口的标题栏遮住了,为了能够让大家看到效果,我就设置了两排。
那么有了我们的游戏地图后,我们就不需要直接去绘制石块了,因此,我们去掉GameView中对Stone的引用,改为对GameMap的引用。同时,我们需要修改Snake类中的isEatStone()方法,修改后的方法如下:
/**
* 判断蛇是否吃到了石头
* @return 如果蛇吃到了石头则返回true,否则返回false
*/
public boolean isEatStone(){
Body head = snakeBody.getFirst();
List<Stone> stones = gameView.getGameMap().getStones();
for(Stone stone : stones){
if(head.x == stone.getX() && head.y == stone.getY()){
return true;
}
}
return false;
}
这段代码我也就不解释了,我想大家也都看明白了。那么到这里,我们这个版本的贪吃蛇就算完成了,运行看看,嗯,跟我们记忆中的贪吃蛇游戏已经比较相似了。但是我们运行一段时间后就会发现,我们食物有时候竟然会跑到石头上面去,这显然是不合理的,因此,我们在Food类中添加下面的方法,来确保食物在生成的时候不会跑到石头上面:
/**
* 判断食物是否出现在石块上
* @return 如果食物出现在石块上则返回true,否则返回false
*/
public boolean isOnStones(){
List<Stone> stones = gameView.getGameMap().getStones();
for(Stone stone : stones){
if(x == stone.getX() && y == stone.getY()){
return true;
}
}
return false;
}
这段代码呢我想也不用我多做解释了,然后我们在Food类中的构造方法中加上对这个方法的调用就可以了。
do{
createXY(r, cellsInRow, cellsInCol);
} while (isInSnakeBody(snake) || isOnStones());
嗯嗯,至此,我们这个贪吃蛇的项目就基本上完成了,运行一下,基本上已经没有大问题了。最后呢,还存在一些其他的问题我们还没有处理,比如说我们的蛇现在是可以穿过自己的身体的,这显然也是不可以的,这个问题呢,我们放在下一个项目中来解决。同时,想一个项目中我们也对这个小项目来做一下总结,毕竟除了做,我们更多的是需要反思与总结,这样才进步的更快。那么,下个项目再见吧。
同样的,这个项目和以前项目的源文件: http://kuai.xunlei.com/d/Ae2cA1x2zAzfUAQA7a7