今天实现一款经典小游戏的实例,贪吃蛇想必大家都有接触过,当然今天实现的细节没有那么全面,只能算是简易版本的小游戏,使用的是Unity中的UGUI,Bug可能有点多,不过大体算是完成了。
大概的原理
整体小游戏的核心其实还蛮简单的,通俗一点讲就是:键盘控制2D网格图片的上下左右移动。我使用的是UGUI中GridLayoutGroup组件添加多个Image实现整体贪吃蛇移动的地图,其实也就是一个个的网格,网格单元就是图片,平时的网格图片都是透明的,贪吃蛇的身体则是其他图片。
我需要控制的就是贪吃蛇的头部和身体,使用键盘WASD键位进行贪吃蛇头部网格移动,后续身体只需要移动到上一节身体的位置就好了,当然理论可行,实际操作还需要考虑其他的问题。
小游戏的效果图
效果图中的小问题咱们先不讨论,旨在整体的实现效果,基于此可以进行游戏的完善
关键代码演示
设置地图中具体坐标的图片,也就是实时更新屏幕画面
public void SetSpriteImage(List<SnakeCell> snakeBody)
{
//设置其他格子
SetDefaultSpriteImage();
//设置food
if (gameController.food.foodCoord != Vector2.zero)
imgArray[(int)(gameController.food.foodCoord.x), (int)(gameController.food.foodCoord.y)].sprite = Resources.Load<Sprite>("002/Textures/Food");
try
{
foreach (var item in snakeBody)
{
imgArray[(int)item.Coord.x, (int)item.Coord.y].sprite = Resources.Load<Sprite>("002/Textures/" + item.SpriteName);
}
}
catch (System.Exception)
{
gameController.isLife = false;
Debug.Log("游戏结束");
}
//判断贪吃蛇 吃食物
if (gameController.snake.head != Vector2.zero && gameController.snake.head == gameController.food.foodCoord)
{
Vector2 direc = (gameController.snake.tail - gameController.snake.head).normalized;
direc = new Vector2(direc.y, direc.x);
if (Vector2.Dot(Vector2.up, direc) == 0)
{
if (gameController.snake.direction == Snake.Direction.Left)
{
gameController.snake.AddSnakeBody(new Vector2(gameController.snake.tail.x, gameController.snake.tail.y + 1));
}
else if(gameController.snake.direction == Snake.Direction.Right)
{
gameController.snake.AddSnakeBody(new Vector2(gameController.snake.tail.x, gameController.snake.tail.y - 1));
}
}else if(Vector2.Dot(Vector2.up, direc) == 1)
{
gameController.snake.AddSnakeBody(new Vector2(gameController.snake.tail.x + 1, gameController.snake.tail.y));
}
else if(Vector2.Dot(Vector2.up, direc) == -1)
{
gameController.snake.AddSnakeBody(new Vector2(gameController.snake.tail.x - 1, gameController.snake.tail.y));
}
else if (Vector2.Dot(Vector2.up, direc) < 0)
{
if(gameController.snake.tail.y < gameController.snake.head.y)
{
gameController.snake.AddSnakeBody(new Vector2(gameController.snake.tail.x, gameController.snake.tail.y - 1));
}
else
{
gameController.snake.AddSnakeBody(new Vector2(gameController.snake.tail.x, gameController.snake.tail.y + 1));
}
}
else if (Vector2.Dot(Vector2.up, direc) > 0)
{
if (gameController.snake.tail.y < gameController.snake.head.y)
{
gameController.snake.AddSnakeBody(new Vector2(gameController.snake.tail.x, gameController.snake.tail.y - 1));
}
else
{
gameController.snake.AddSnakeBody(new Vector2(gameController.snake.tail.x, gameController.snake.tail.y + 1));
}
}
gameController.food.CreateFood(gameController);
}
}
代码解析
这段代码里面涉及到三种图片的刷新,也就是不同标记的属性图片对网格的赋值。
首先需要进行的是普通网格图片的赋值,这里呢没有给出具体的方法实现,但其实内部就是将整体网格遍历一遍进行透明图片的赋值。
然后刷新的图片就是我们贪吃蛇的食物,这里呢就是红色方块,食物的生成位置其实不是完全随机的,生成的位置必须是处于地图网格中且必须不能在贪吃蛇身体进行食物的生成,每次食物一旦被贪吃蛇吃掉就会触发食物的再次生成
最后我们需要考虑的是贪吃蛇吃掉食物之后身体的延长问题,其实有人会觉得身体延长就直接在贪吃蛇尾部进行网格图片的替换就成了,但是这里就存在一个问题,就是尾部身体延长的方向位置怎么摆放。由效果图可知,在贪吃蛇进行身体移动的时候尾部的延伸方向其实是存在多种情况的。
在代码中的体现就是八个方向的判断来决定尾部身体如何生成,具体的判断依据就是贪吃蛇头部和尾部形成的向量与Y轴坐标的点积的值,再根据当前贪吃蛇移动的方向来生成贪吃蛇的身体。(具体以代码为主)
由于编码秉承面向对象的原则,所以当前代码中可能存在多个对象并未给出说明,具体可以查看源码。
代码地址
具体代码我已经上传Github,看这里
目前代码上可能存在蛮多的问题,游戏性上也是不够,但毕竟只是个小游戏,我没打算做得有多好,简简单单实现功能就成。
总结
以前动手写代码还是缺点脑子,写到哪里算哪里,完全没考虑对象封装、游戏框架这种东西,代码不一定有多么实用的功能,但至少能看的明白又舒服,减耦合才是王道!