☰
Unity零基础开发2D贪吃蛇游戏完整教程
2026/10/17 6:10:06 网站建设 项目流程

哪个男孩手里没攥过一个“哪天我也做个游戏”的念头?但大多数人卡在了“不知道从哪开始”这一步。我用Unity做的第一个完整小项目,就是贪吃蛇2D。这个项目几乎是为零基础量身定制的:不需要美术功底,不需要复杂的数学,C#虽然是一门编程语言,但贪吃蛇用到的语法就那么几行,做完之后你会把Unity里最核心的几个概念全部过一遍:场景、物体、组件、预制体、输入、碰撞、UI。不管拿它当练手项目,还是想先跑通一次完整的游戏开发流程,都很合适。

这篇博文就从完全零基础出发,手把手带着你做一版能玩、能计分、能重开、能拿去给同学朋友炫耀的贪吃蛇。我尽量把每一步为什么这样做讲清楚,而不是只让你对着教程抄代码。做完这个项目,你会发现后面再做任何小游戏的信心都已经攒起来了。

1. 动手前的准备:装好Unity并搞懂三个关键概念

1.1 Unity Hub装哪个版本?选LTS版本,别追新

去Unity官网下载Unity Hub,然后通过Hub安装Unity编辑器。版本选择上,我建议直接选最新的LTS(长期支持)版本,别选带“beta”或者刚发布的“最新版”。LTS的意思是官方会持续维护修复的稳定版本,很多线上教程和社区资源也基于LTS,遇到问题好查答案。版本太新反而可能碰到教程里没提到的情况,零基础阶段少一点变量就少一点烦恼。

安装界面里记得勾选2D模板(或者叫2D URP模板),它会自动把编辑器的默认工作环境设置成2D模式:相机默认是正交投影,新建的Sprite会直接铺在视野中央。如果你选的是3D模板再做2D也不是不行,但视角、光照都要手动调,没必要给自己加难度。

1.2 场景、GameObject、组件:Unity世界的三个基石

零基础最容易懵的就是Unity里一堆窗口和名词。其实Unity的世界观可以用一个生活场景来解释:整个游戏相当于一部舞台剧,场景(Scene)就是舞台本身,舞台上有多少个演员、多少件道具,都在Hierarchy窗口里列着,每个演员或道具就是一个GameObject(游戏对象)。而GameObject本身是空的,它之所以能显示成方块、能播放声音、能移动,是因为挂在上面的组件(Component)。

比如你创建一个“蛇头”GameObject,给它挂上SpriteRenderer组件,它就能显示成一个方块;再挂上Collider2D组件,它就有了“碰撞体积”;再挂上一个自定义的脚本组件,它就有了行为逻辑。也就是说,GameObject是身体,组件是装备,脚本是大脑。

预制体(Prefab)也顺便讲一下,因为贪吃蛇的蛇身会大量用到它。预制体相当于一个“模具”,你先做好一节蛇身,存成预制体,之后所有新增的蛇身都是用这个模具复制出来的。改模具,所有复制出来的实例一起跟着变,这在开发里省了太多事。

1.3 第一次新建项目和认识编辑器界面

打开Unity Hub,新建项目,模板选2D,项目名字我用的Snake2D,保存路径随意。创建完成进入编辑器后,你会看到几个主要窗口:中间最大的Scene窗口,用来摆放物体;右上角是Game窗口,运行时显示画面;左边有Hierarchy和Project等面板;右边是Inspector,用来查看和修改选中物体的组件属性。

第一次进来可以随便点击Scene里的物体,再观察Inspector里组件参数的变化,熟悉“选中-修改”这个基本交互模式。另外记得在Scene窗口顶部把2D开关打开,这样场景会以平面视角显示,方便摆2D的东西。这些不一次性全弄懂也没关系,用到什么学什么,但上面三个核心概念(场景、GameObject、组件)是后面所有操作的底层逻辑。

2. 核心设计思路:动手写代码之前,先想清楚蛇是怎么动的

2.1 蛇的身子怎么存:用List管理每一节

很多人一上来就急着写移动代码,结果写出来的蛇要么自己没有身体,要么身体追不上头。先想数据结构。贪吃蛇本质上是一条由若干节组成的链,每一节在网格上占据一格位置。蛇吃到一个食物,身体末尾会长出一节;正常情况下每走一格,前面出来一个新位置,后面丢掉尾巴那一节。

在C#里,最合适的就是List(列表)。因为数组长度一旦声明就固定了,而蛇的长度是不断变化的。我用了两个List:一个存所有身体节点的Transform,方便每帧同步位置;另一个存每一节的“历史网格坐标”,用来计算新位置。核心操作就是:每一拍把新蛇头位置插入列表头部,如果没吃到食物就把列表尾部的旧节点删掉,这两步做完,蛇就完成了一次前进。

2.2 移动方式:用时间间隔控制步伐,而不是每帧都动

如果让蛇每帧都移动一次,画面刷新率是60帧每秒,蛇会快得看不清。真正贪吃蛇的移动方式是按“拍”前进:每隔一小段时间,往前挪一格。这个间隔决定了游戏难度,经典贪吃蛇大概是一拍0.15秒左右。

具体实现不需要用复杂的物理系统。在Update函数里用一个计时器:每帧累加Time.deltaTime(这一帧过去了多少秒),当累加值超过移动间隔时,执行一次“走一步”的动作,然后把计时器归零。这种移动方式和帧率无关,不管游戏跑在30帧还是120帧,蛇的节奏都是一致的。

2.3 场景怎么搭:直接摆Sprite而不是用Tilemap

搭建游戏区域有两种思路。一种是用Tilemap瓦片地图画背景格子和墙,优点是地图清晰、扩展方便,但零基础要先学Tilemap工具,学习曲线陡。另一种是直接用Sprite(精灵图片)摆出边界和蛇身,这更接近“用积木搭房子”的思路,代码也直观。我做第一版时用的就是后一种,跑了没几天就上手了。

关键设计定下来:每个方块边长0.5单位,蛇头的Sprite长宽都设为0.5,移动步长就是0.5。食物也按0.5的整数倍坐标生成。整个游戏区域比如由四面墙围成一个矩形,玩家需要在这个区域里控制蛇移动。

3. 一步步实现:从空场景到能玩的贪吃蛇

3.1 搭建场景:摄像头、背景、墙和蛇头

新建项目里自带一个Main Camera。把Scene背景设置成深色,比如深蓝黑色,这样灰色地板和白色蛇身对比明显。这个背景其实是相机视图的固有色,不参与游戏逻辑,纯粹为了好看。

然后创建四个墙:在Hierarchy里右键创建一个2D Object -> Sprite,选择一张纯色的方形Sprite。把四个墙分别移动到游戏区域的上、下、左、右边界,颜色用深灰色。为了检测碰撞,给每面墙挂BoxCollider2D,这里不需要勾IsTrigger,因为撞墙就是要触发游戏结束。

接着做蛇头。新建一个Sprite对象,挂SpriteRenderer,颜色白色,尺寸0.5×0.5,再加BoxCollider2D并勾选IsTrigger,这样蛇头碰到食物时能触发检测,同时自身不会把食物“推走”。别忘了添加Rigidbody2D,类型选Kinematic(运动学刚体)。这是很多人漏掉的:2D触发器要正常工作,两个碰撞体里至少有一个物体带Rigidbody2D。蛇头作为主动移动的一方,加Kinematic刚体最合适。

3.2 创建预制体:蛇身和食物都从预制体生成

蛇身的预制体和蛇头长得一样,只是颜色稍浅一点,碰撞体和刚体逻辑相同。把做好的蛇身拖到Project窗口里,生成一个预制体资源。食物也做成一个预制体,红色方块。这里有个小技巧:给食物加上BoxCollider2D并勾选IsTrigger,标签(Tag)设为“Food”,蛇头撞上去时通过标签判断这是食物而不是墙。

预制体的好处在这一刻就体现出来了:蛇吃到食物要加长,我们Instantiate一个预制体就行;食物被吃掉要换位置,也只是移动那一个食物实例,不用新建销毁。整个游戏里我们一直复用这两个预制体。

3.3 蛇的移动与方向控制:核心脚本Snake.cs

现在写真正的移动脚本。思路已经很清楚:方向用一个Vector2表示,每拍走一步。在Update里读取上下左右键,并做180度掉头保护:如果当前方向是向右,就不能直接按左键,否则蛇会一头扎进自己身体。这个保护极其重要,不做的话第一次玩就会原地自噬。

using System.Collections.Generic; using UnityEngine; public class Snake : MonoBehaviour { public GameObject segmentPrefab; // 蛇身预制体 public float moveInterval = 0.15f; // 每拍移动间隔 public float stepLength = 0.5f; // 每格边长,也是移动步长 private List<Transform> segments = new List<Transform>(); private Vector2 direction = Vector2.right; private Vector2 nextDirection = Vector2.right; private float playTimer; private int growthNeeded; // 待增长节数 void Start() { segments.Add(transform); // 蛇头就是挂脚本的物体本身 } void Update() { // 读取输入,且禁止向当前方向的相反方向掉头 if (Input.GetKeyDown(KeyCode.UpArrow) && direction != Vector2.down) nextDirection = Vector2.up; else if (Input.GetKeyDown(KeyCode.DownArrow) && direction != Vector2.up) nextDirection = Vector2.down; else if (Input.GetKeyDown(KeyCode.LeftArrow) && direction != Vector2.right) nextDirection = Vector2.left; else if (Input.GetKeyDown(KeyCode.RightArrow) && direction != Vector2.left) nextDirection = Vector2.right; playTimer += Time.deltaTime; if (playTimer >= moveInterval) { playTimer = 0f; MoveOneStep(); } } void MoveOneStep() { direction = nextDirection; Vector3 newHeadPos = segments[0].position + (Vector3)(direction * stepLength); GameObject newNode = Instantiate(segmentPrefab, newHeadPos, Quaternion.identity); segments.Insert(0, newNode.transform); if (growthNeeded > 0) { growthNeeded--; } else { Transform tail = segments[segments.Count - 1]; segments.RemoveAt(segments.Count - 1); Destroy(tail.gameObject); } } public void Grow() { growthNeeded++; } }

这里的核心逻辑就是MoveOneStep方法:先在头部前方插入一节新身体,如果没吃到食物就把尾巴删掉。这样蛇的光是“整体前进了一格”,而不是散得到处都是。用List而不是数组,就是为了方便这种插入和删除,这也是List相对数组的核心优势。

3.4 食物的生成与吃到食物的判定

食物逻辑单独写一个Food脚本。在Start或“被吃后重生”时调用RandomPosition方法:

void RandomPosition() { float x = Random.Range(-8.5f, 8.5f); float y = Random.Range(-4.5f, 4.5f); x = Mathf.Round(x * 2f) / 2f; // 0.5整数倍 y = Mathf.Round(y * 2f) / 2f; transform.position = new Vector2(x, y); }

这里有个特别容易踩的坑:如果直接让食物坐标随机在(-8.5f, 8.5f),生成的坐标可能带小数点后很多位,蛇的步长是0.5,可能永远对不齐。所以务必用Mathf.Round把坐标取成0.5的整数倍。做完全局对齐后,蛇头只要到了食物所在的格子,就能吃到了。

蛇头吃到食物的检测,可以在Snake脚本里加OnTriggerEnter2D:

void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Food")) { Grow(); score++; other.GetComponent<Food>().RandomPosition(); // 更新UI分数 } else if (other.CompareTag("Wall")) { GameOver(); } }

3.5 撞自己身体的判定:别漏掉这个

撞墙会死,很多新手做了。但撞自己身体也会死,这个经常被忽略。判定方法有很多,对零基础最直观的就是每走一步之后,遍历segments列表里除了蛇头以外的所有节点,比较它们的位置和蛇头位置是否重合。因为所有位置都是0.5整数倍,精度可控,直接用==比较即可。

bool HitSelf() { Vector2 headPos = segments[0].position; for (int i = 1; i < segments.Count; i++) { if ((Vector2)segments[i].position == headPos) return true; } return false; }

这段代码放在MoveOneStep之后调用,一旦发现自撞,就进入游戏结束流程。

3.6 分数UI和重新开始

在场景里创建一个Canvas,右键UI -> Text即可。把得分Text拖到Snake脚本的public字段上,在吃到食物后更新text.text = "得分:" + score。

游戏结束时,显示一个“游戏结束,按R重新开始”的文字,然后设置Time.timeScale = 0f暂停游戏。重新开始时调用SceneManager.LoadScene重载当前场景,并把Time.timeScale恢复为1。零基础只要记住:Time.timeScale是全局时间缩放,设成0游戏就暂停了。

using UnityEngine.SceneManagement; void GameOver() { Time.timeScale = 0f; gameOverText.gameObject.SetActive(true); } void Update() { // 在GameOver状态时按R重开 if (gameOverText.gameObject.activeSelf && Input.GetKeyDown(KeyCode.R)) { Time.timeScale = 1f; SceneManager.LoadScene(SceneManager.GetActiveScene().name); } }

到这里,一个能玩的贪吃蛇已经跑起来了。如果你能独立把上面这些代码不报错地跑通,你就已经掌握了Unity开发的第一轮完整闭环:搭场景、做预制体、写脚本、碰撞交互、UI反馈。但真到这一步,后面还有几个坑等着你。

4. 跑通之后回头看:那些必须遇到的坑和解决办法

4.1 快速连按方向键,为什么蛇会“吞输入”甚至反向

我第一版做的贪吃蛇有个烦人的问题:快速连续按了上、左两个键,蛇可能先往上,但还没等移动就又吃到左边输入,结果该往上走的一拍变成了往左走,直接造成90度急转,有时还会导致撞墙。原因在于输入读取和移动计算被放在同一个Update里,输入被无缓冲地覆盖了。

解决办法是引入“暂存下一拍的方向”机制,也就是我在代码里写的nextDirection。每次按键只更新nextDirection,真正移动时再把direction赋值为nextDirection。这样即使在两拍之间按了一堆键,也只会有最后一次按键生效。这是游戏输入处理里的经典技巧:输入缓冲。以后做任何需要控制方向的项目都会用到。

4.2 快速移动的触发器丢失:为什么你的蛇偶尔穿过食物

用OnTriggerEnter2D判断碰撞时,如果蛇移动间隔很短、速度很快,可能会出现一种诡异现象:蛇头明明从食物位置扫过去了,却什么都没发生。这其实是物理检测采样的经典问题:两帧之间物体移动距离超过了碰撞体大小,触发器的采样点直接跳过了对方。

解决办法有两个。一是把移动间隔限制在不太小的值,比如0.1秒以上,保证单拍步长不至于让碰撞体完全跳过目标。二是改用主动检测:在MoveOneStep里用Physics2D.OverlapPointAll检查新蛇头位置有没有贴着食物。我最终版本用的是后者,因为更稳,不受帧率影响。零基础可以先按第一种跑起来,等真遇到问题再升级成主动检测。

4.3 食物生成到墙上或蛇身上,怎么办

食物随机生成时,可能生成到墙里,也可能生成到蛇的当前位置。墙的问题好解决:随机范围直接取在游戏区域内部,四面墙内缩一个步长就保证了不碰墙。蛇身的问题则要去查:生成食物后遍历一遍所有蛇身节点的位置,如果重合就重新随机。

零基础容易图省事,食物生成一遍就完事。但玩个几分钟,食物刷在蛇身上的概率其实不低,尤其蛇长到十几节之后。千万别偷懒,这个校验必须加。

4.4 蛇长到几百节之后,画面卡了怎么办

贪吃蛇理论上可以一直吃,蛇身会越来越长。我实测长到三百节左右,Instantiate和Destroy的频繁调用开始让GC(垃圾回收)压力变大,游戏出现微小卡顿。零基础阶段可以暂时不管,但如果你想做更完善的版本,有两个思路。

一是减少Instantiate和Destroy:吃食物时不再新生成节点,而是把队列尾部的一个旧节点“挪”到头部。具体做法是所有蛇身节点共用一群预先实例化的对象,用队列维护“当前活跃”的节点。这就是最简单的对象池思想。二是把位置同步改成只移动头部节点并间隔同步身体节点,大幅减少每帧的操作量。这些优化对零基础不是必须,但知道原理很重要,它们会出现在你未来的每一个Unity项目里。

5. 从贪吃蛇出发:再往前走的升级方向和路线

5.1 玩法扩展:加分道具、障碍物、双人对战

基础版跑通之后,游戏性还很薄。加一个加速道具,蛇吃到后在一定时间内的移动间隔减半,立刻刺激起来。加障碍物也是好方向,在游戏区域中间摆几个石头,蛇要绕着走。想再进一步,可以做双人对战:一条蓝蛇一条橙蛇,谁先撞到对方身体谁输。这些扩展用到的技术点基本都在贪吃蛇里出现过,不太需要新知识。

5.2 视觉和音效升级:一个能拿得出手的版本

纯白方块版适合跑通逻辑,但不适合展示。做几个简单的Sprite:蛇头画上两个黑点当眼睛,蛇身颜色从深到浅渐变,食物加一个2D动画让它小幅度旋转。音效可以在吃到食物时播放一个短促的点击声。这些资源可以先用代码生成或简单图片编辑工具随便画一个,重要的是体验“给游戏加反馈”的感觉:音效和动画会让十分的内容看起来像八十分。

5.3 做完贪吃蛇之后,下一步练什么

贪吃蛇完成之后不要急着碰大型项目。我建议按顺序做三件事:第一,把官方入门教程里的火箭发射小项目跟着做一遍,巩固场景和脚本基础;第二,做一款打砖块,你会在里面学到鼠标交互和子弹运动;第三,做一款井字棋或者记忆翻牌,掌握简单的游戏状态管理。每一步都能复用你已经在贪吃蛇里学会的技能,难度梯度刚刚好。

最后分享一点个人感受。做项目最忌讳的是“教程看了一遍就觉得自己会了”:打开Unity自己动手把贪吃蛇从零写一遍,遇到报错就看报错信息,主动寻找解决方案,这个过程本身才是真正的学习。贪吃蛇虽然是个几十年前就有的老游戏,但它涵盖了游戏开发最核心的思想:数据结构选型、时间驱动逻辑、碰撞交互、界面反馈、状态管理。把它吃透,你就拥有了独立完成任意一款休闲小游戏的底气,也把“那个男孩想用Unity做一款游戏”的念头,变成了一个真正落地的东西。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询