Allen Kashiwa Blog

羁旅红尘不曾醉,笑歌年少且长行。

专注游戏开发与设计


唯佳人与游戏不可辜负

Just for fun——Unity5制作贪吃蛇小游戏

前言

说出来你可能不信。有一天晚上睡不着觉,就突然想到做游戏开发这么久,还没亲手实现过经典游戏贪吃蛇呢。然后就在床上想实现的方法,越想越手痒,要不是怕把枕边人吵醒,第二天又要上班,真要立刻爬起来把这小游戏实现了。于是想了一会儿,理清思路之后就逼自己入睡了。

第二天下班之后,回到家就开始动手,已经搞定了。为了节约篇幅,本篇文章只简单说下思路。不想看我废话,需要直接参考代码的同学可以到我的Github

准备工作

我使用了Unity自带的创建简单3D物体的功能来制作蛇头和蛇身的prefab。蛇身直接用Cube,而蛇头需要添加BoxCollider和Rigidbody组件。为了将不同的物体区别开,我还写了一个纯色的shader:

Shader "Custom/PureColor" {
	Properties{
		_Color ("颜色", Color) = (0.8 ,0,0,0)
	}
	SubShader {
		Pass{
			Color[_Color]
		}
	}
	FallBack "Diffuse"
}

我们可以创建一个材质,将材质的shader设为Custom/PureColor,再将该材质赋给特定物体。可以用这个shader创建多个材质与其他物体区分开。 如图是我们需要的所有资源:

retro_snaker_res.png

其中,所有的材质都使用上述的shader并指定不同的颜色。 除此之外,我们需要在Project Settings的Input中增加四个按钮: UP,DOWN,LEFT,RIGHT 我们的关卡也很简单: 1.创建几个Sphere物体当做贪吃蛇的食物,设置位置为整数以便于我们的贪吃蛇在运动时能够吃个正着(因为Unity默认的Cube物体的边长是1,Sphere的半径是0.5)。 2.将SphereCollider的半径设小一点,例如0.25以便于我们的贪吃蛇碰到这些食物时看起来像真的吞噬了它们。 3.将SphereCollider设为触发器。 看起来像这样:

retro_snaker_level

实现

首先定义一个枚举来代表节点运动的四个方向:

public enum MoveDir
{
	UP,
	LEFT,
	RIGHT,
	DOWN
}

我们仅需三个类来完成这个游戏的核心部分。 RetroSnaker负责游戏的主要控制,包括游戏状态更新,用户输入,产生新节点等。

部分代码如下:

	void Awake()
	{
		_snakeHeadPrefab = Resources.Load(_snakeHeadResPath) as GameObject;
		_snakeNodePrefab = Resources.Load(_snakeNodeResPath) as GameObject;
	}

	// Use this for initialization
	void Start()
	{
		RetroSnakerStart();
	}

	// Update is called once per frame
	void Update()
	{
		if (_isInPause)
			return;
		_timer += Time.deltaTime;
		if (_timer >= _timeGap)
		{
			_timer = 0.0f;
			MoveSnake();
		}
		ChangeMoveDir();
	}

RetroSnakerStart函数在游戏开始时生成一条起始蛇。MoveSnake函数主要调用当前所有节点的Move函数来完成运动。ChangeMoveDir函数接受用户输入并记录转向

SnakeNode是组成贪吃蛇上每一个节点上的类。主要控制该节点运动。

部分代码如下:

	public void Move()
	{
		switch (moveDir)
		{
			case MoveDir.UP:
				MoveUp();
				break;
			case MoveDir.DOWN:
				MoveDown();
				break;
			case MoveDir.LEFT:
				MoveLeft();
				break;
			case MoveDir.RIGHT:
				MoveRight();
				break;
		}
	}

Apple是我们的贪吃蛇要吃的东西,将挂载到我们创建的Sphere物体上。

代码如下:

using UnityEngine;
using System.Collections;

public class Apple : MonoBehaviour
{
	public static int numberOfObjects = 0;

	#region mono
	// Use this for initialization
	void Start()
	{
		++numberOfObjects;
	}

	void OnTriggerEnter(Collider collider)
	{
		gameObject.SetActive(false);
		--numberOfObjects;
		RetroSnaker snake = GameObject.FindObjectOfType<RetroSnaker>();
		if (snake != null && snake.isActiveAndEnabled)
			snake.OnEatApple();
	}
	#endregion mono

}

在Apple类中我们使用一个静态成员numberOfObjects记录Apple的数量,这样当该成员变为0时,则我们吃光了Apple,完成关卡。

主要难点我感觉在于贪吃蛇接受输入后转向,其余节点在下次绘制时要跟随上一节点的运动方向。所以我用一个list来记录总共需要发生几次转向。用户每产生一个有效的输入就增加一个转向。每次绘制后逐一对这些转向遍历,并将改变了的索引加1,下次绘制时即可改变后一节点的运动方向。如果索引超出了贪吃蛇的长度,则可以移除该转向。 这里贴出完成一次移动后设置节点运动方向,以及移除一个转向的代码:

List<int> tempIndexs = new List<int>();
for (int i = 0; i < _indexs.Count; i++)
	{
		int index = _indexs[i];
		if (index > 0 && index < _snake.Count)
		{
			_snake[index].moveDir = _snake[index - 1].moveDir;
			_indexs[i]++;
		}
		if (_indexs[i] < _snake.Count)
			tempIndexs.Add(_indexs[i]);
	}
	_indexs = tempIndexs;

最后,还是建议你到我的Github查看完整工程。Have Fun!

用支付宝请我喝咖啡

用微信请我吃辣条

最近的文章

Unity开发中异步加载配置文件,像读取数据库一样读取配置信息

数据驱动数据驱动是软件设计与开发中不可忽视的内容,开发电子游戏更是如此。电子游戏世界是由逻辑与数据构建的。在开发过程中,我们基本上会将逻辑与数据分离开来。游戏开发完成后,逻辑部分相对改动较小,而数据的改动则相对频繁。我们可能需要不断修改来让游戏世界达到平衡。因此,在游戏开发按需加载配置是一项很重要的任务。CSV文件使用逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号)作为配置数据的手段是较为常见的。CSV读取方便又能使用Ex...…

继续阅读
更早的文章

Unity5.x新的AssetBundle机制02——压缩

前言本节来讲讲Unity5新的AssetBundle机制中的压缩。你可以不对你的AssetBundles进行压缩,这样虽然文件很大,但是一旦下载好之后访问是最快的。通常都会进行压缩。并且Unity的AssetBundles支持LZMA和LZ4两种格式的压缩。LZMA(Ziv–Markov chain algorithm)格式。这是Unity打包成AssetBundle时的默认格式,会将序列化数据压缩成一个LZMA流,使用时需要整体解包。优点是打包后体积小,缺点是解包时间长导致加载时间长。L...…

继续阅读