1. 项目概述为什么选择“跳跳鸟”作为你的第一个Unity 2D实战项目如果你刚接触Unity或者已经看了一堆教程但还没做出一个能跑起来的完整小游戏那么“跳跳鸟”这个项目可能就是为你量身定做的。它不是什么复杂的3A大作而是一个经典的、机制简单的2D小游戏原型核心玩法就是控制一只小鸟在管道障碍物之间跳跃穿行。听起来简单对吧但恰恰是这种简单让它成为了学习Unity 2D游戏开发核心工作流的绝佳沙盒。我见过太多新手一上来就想做开放世界RPG结果在复杂的系统面前迅速迷失信心受挫。而“跳跳鸟”项目它能让你在几天内清晰地走完一个游戏从零到发布的核心闭环创建项目、导入素材、编写控制逻辑、处理物理碰撞、设计游戏循环开始、进行、结束、实现UI交互分数、重新开始最后打包成可执行文件。这个过程里你会亲手触摸到Unity编辑器的大部分核心面板理解GameObject、Component、Prefab、Sprite、Rigidbody 2D、Collider 2D这些基础但至关重要的概念。当你看到自己写的小鸟在屏幕上蹦跶因为你的操作失误而“撞墙”时那种亲手创造世界的成就感是看一百个教程视频也换不来的。基于当前热门的“实战”学习趋势这个项目准备篇我不会空谈理论而是直接带你进入“战备状态”。我们将聚焦于搭建一个清晰、高效、可扩展的项目基础框架避免后期陷入资源混乱、脚本耦合的泥潭。很多教程会跳过这一步直接开始写代码但一个良好的开端是成功的一半尤其是在游戏开发中。2. 项目准备的核心思路构建可维护的工程结构在真正动手写第一行代码或拖拽第一个精灵之前我们必须先规划好项目的“房间布局”。想象一下如果你把所有的家具图片、声音、脚本都胡乱堆在客厅以后想找把螺丝刀某个特定脚本都得翻箱倒柜。Unity项目同样如此一个清晰的文件目录结构是专业开发的起点它能极大提升你的开发效率和后期维护的便利性。2.1 创建项目与初始设置首先打开Unity Hub点击“新建项目”。这里有几个关键选择点项目模板务必选择“2D”模板。Unity的2D和3D模板在初始设置上有显著区别。2D模板会自动将场景视图设置为2D模式并将主摄像机的投影方式Projection设置为“Orthographic”正交这对于2D游戏至关重要因为它消除了透视变形确保精灵大小不会因距离而改变。项目名称与位置给你的项目起个名字比如“FlappyBirdClone”或“MyFirst2DGame”。位置路径不要包含中文或特殊字符最好放在一个专门的开发目录下如D:\UnityProjects\。这是一个老生常谈但总有人踩坑的细节路径中的中文可能导致一些意想不到的打包或资源加载错误。版本管理与初始包如果你打算使用版本控制强烈推荐即使是个人项目可以在此处初始化Git仓库。至于初始包保持默认即可我们后续需要什么再通过Package Manager添加。注意Unity版本的选择上对于2D小游戏选择一个稳定的LTS长期支持版本即可如2022 LTS或2021 LTS。不必追求最新版稳定压倒一切避免遇到新版本的未知Bug影响你的学习进度。创建完成后Unity会打开编辑器。第一件事我们来整理这个“空房间”。2.2 设计科学的项目文件夹结构在Project窗口通常是左下角的Assets文件夹下直接右键创建以下文件夹。这套结构是我经过多个项目总结出来的对于中小型项目非常够用Assets/ ├── 01_Art美术资源 │ ├── Sprites精灵图片 │ ├── UI界面图片、字体 │ └── Materials材质2D项目通常用得少但预留 ├── 02_Audio音频资源 │ ├── BGM背景音乐 │ └── SFX音效 ├── 03_Prefabs预制体 │ ├── Characters角色如Bird.prefab │ ├── Environment环境如Pipe.prefab │ └── UIUI元素预制体 ├── 04_Scripts脚本 │ ├── Core核心逻辑如GameManager.cs │ ├── Characters角色控制如BirdController.cs │ ├── Environment环境交互如PipeSpawner.cs │ ├── UI界面控制如UIManager.cs │ └── Utilities工具类如Extensions.cs ├── 05_Scenes场景 │ ├── 00_Init初始化场景可选 │ ├── 01_MainMenu主菜单 │ └── 02_Game游戏主场景 ├── 06_Animations动画控制器和动画片段 │ └── Controllers如Bird.controller ├── 07_Settings配置资产 │ ├── InputActions新输入系统配置 │ └── ScriptableObjects可脚本化对象如游戏设置 └── 08_External第三方插件或资源包为什么这么分数字前缀强制文件夹按逻辑顺序排列方便查找。按功能模块划分美术、音频、脚本分离符合资源类型管理。Scripts内再细分这是避免“脚本地狱”的关键。想象一下30个脚本都堆在根目录下是什么感觉。按功能分文件夹能让你的代码架构一目了然。Core文件夹放管理全局状态的单例或管理器Utilities放一些静态工具方法比如常用的数学计算、扩展方法等。预留空间比如External文件夹当你从Asset Store购买或下载免费资源时全部导入到这里避免和你的原创资源混在一起。实操心得现在就花5分钟创建好这个结构。养成这个习惯未来当你项目膨胀到几百个资源文件时你会回来感谢我的。另外在Unity中你可以给文件夹设置喜欢的图标在文件夹上右键 - Properties - Custom Icon让项目管理更直观。2.3 核心资源准备与导入设置对于“跳跳鸟”项目我们至少需要以下美术资源小鸟精灵一张png图片最好包含小鸟不同状态比如正常、向上飞、向下坠的帧或者我们就用一张图通过旋转来模拟。管道精灵上下两个管道通常也是一张png我们可以通过代码或编辑器来重复使用它。背景一张横向可以无缝滚动或足够宽的背景图。地面一张长条的地面贴图用于在底部滚动。UI素材开始按钮、游戏结束面板、数字字体等。你可以在一些免费的像素艺术网站如 itch.io, OpenGameArt找到这些资源或者用简单的图形工具自己画。这里我假设你已经找到了或制作好了这些图片。导入资源后的关键设置以小鸟精灵为例将小鸟的png图片拖入Assets/01_Art/Sprites文件夹。在Project窗口中点击该图片查看Inspector面板。Texture Type确保是Sprite (2D and UI)。这是2D游戏精灵的正确类型。Pixels Per Unit (PPU)这个值决定了图片上一个Unity单位对应多少像素。对于像素风游戏通常设置为32, 64, 100等这取决于你的精灵图大小和想要的游戏内尺寸。比如你的小鸟图片是64x64像素PPU设为64那么它在游戏里就是1个单位宽和1个单位高。我建议先设为100这是一个常用的基准值后续可以根据感觉调整。Filter Mode对于像素艺术务必选择Point (no filter)。这能保证你的像素边缘清晰锐利不会因为缩放而变得模糊。如果是高清平滑风格的2D美术可以选择Bilinear。Compression根据项目大小和平台选择对于学习项目选择Low Quality或High Quality均可影响不大。重要提示对于背景和地面这种需要无缝滚动的图片你需要确保图片的左右边缘在颜色和图案上是能够完美衔接的。在导入设置中可以将Wrap Mode设置为Repeat但这通常用于材质贴图。对于2D精灵的滚动我们更多是通过代码控制位置来实现。完成所有精灵的导入和基本设置后你的Project窗口应该已经初具规模了。3. 搭建基础场景与游戏对象现在我们开始布置第一个游戏场景。在Assets/05_Scenes文件夹内右键创建一个新场景命名为“Main”。双击打开它。3.1 创建基础游戏对象一个典型的“跳跳鸟”场景包含以下层级Hierarchy结构Main Camera默认存在负责显示游戏画面。确保其Projection为OrthographicSize属性决定了视野范围。你可以先设为5后续根据背景大小调整。Directional Light在2D项目中除非你使用URP/HDRP并需要2D光照否则这个光源不是必须的。可以删除它以简化场景。GameManager (空对象)这是一个不渲染任何内容的“管理器”对象我们将它作为所有全局控制脚本的挂载点。创建空GameObject重命名为“GameManager”。Background背景容器。创建一个空GameObject命名为“Background”然后将你的背景精灵拖到Hierarchy中使其成为“Background”的子对象。这样便于整体管理。Ground地面。同样创建一个空对象“Ground”作为容器然后把地面精灵拖进去作为其子对象。地面通常需要碰撞体我们稍后添加。Bird (预制体占位符)我们先创建一个空对象命名为“Bird”作为小鸟的占位符。稍后我们会创建小鸟的预制体并替换它。PipesSpawner管道生成器。一个空对象用于挂载控制管道随机生成和移动的脚本。CanvasUI画布。Unity会自动设置其渲染模式为“Screen Space - Overlay”。我们将在其下创建分数文本、开始按钮、游戏结束面板等UI元素。操作技巧善用“空对象”作为逻辑分组容器这能让你的Hierarchy窗口非常整洁。例如你可以创建一个“Environment”空对象然后把“Background”、“Ground”、“PipesSpawner”都拖进去作为其子级。3.2 配置第一个预制体小鸟预制体Prefab是Unity的核心概念之一它允许你创建一个游戏对象的蓝图并在场景中多次实例化所有实例都链接到原始预制体。对预制体的修改会应用到所有实例这太有用了。我们来创建小鸟预制体在Hierarchy中右键点击我们刚才创建的“Bird”空对象选择“2D Object - Sprite”。这会创建一个带有Sprite Renderer组件的对象。将它重命名为“Bird”。在Inspector中点击Sprite Renderer组件的Sprite属性右侧的小圆点选择你导入的小鸟图片。添加刚体Rigidbody 2D这是让小鸟受物理重力影响的关键组件。点击“Add Component”搜索并添加Rigidbody 2D。Body Type选择Dynamic这样它才会受力和重力影响。Gravity Scale重力缩放。默认是1但对于这种轻快的小鸟游戏我们可能希望重力感更强或更弱。可以先保持1后续在脚本中调整或通过测试来修改。一个常见的“跳跳鸟”手感是设置一个中等向上的力来对抗一个较强的重力比如重力设为3。Freeze Rotation强烈建议将Z轴旋转冻结。勾选Freeze Rotation Z。我们不希望小鸟在碰撞或受力时胡乱旋转通常只希望它有一个固定的倾斜角度来表现飞行姿态这个角度我们将通过脚本控制。添加碰撞体Collider 2D没有碰撞体小鸟就会穿墙而过。添加一个Circle Collider 2D或Box Collider 2D。对于简单的小鸟形状Circle Collider 2D通常更合适性能也稍好。添加后使用组件上的编辑手柄调整碰撞体的大小和位置使其大致贴合小鸟的轮廓。不要追求像素级的完美贴合对于这种游戏稍微小一点的碰撞体即让视觉边缘比碰撞体大一点能让玩家感觉更“公平”减少挫败感。这被称为“碰撞体宽容度”。创建标签Tag为了在代码中方便地识别小鸟我们给它创建一个专属标签。在Inspector顶部的Tag下拉菜单中选择“Add Tag...”然后点击“”号新建一个标签命名为“Player”。然后回到小鸟对象将其Tag设置为“Player”。制作预制体现在从Hierarchy窗口中将“Bird”对象直接拖拽到Assets/03_Prefabs/Characters文件夹中。你会看到该对象在Hierarchy中的名字变成蓝色这表示它已经是一个预制体实例了。最后删除Hierarchy中的这个实例。因为我们将在游戏开始时通过代码动态生成小鸟或者从一个初始位置实例化这样更容易控制重生逻辑。至此你的小鸟预制体就准备好了。它包含了渲染、物理和碰撞的所有必要组件。4. 编写第一个脚本小鸟控制器BirdController脚本是游戏的灵魂。我们在Assets/04_Scripts/Characters文件夹上右键选择Create - C# Script命名为BirdController。双击在Visual Studio或Rider中打开。4.1 基础移动与跳跃控制我们先实现最核心的功能点击屏幕或空格键小鸟向上跳跃。using UnityEngine; public class BirdController : MonoBehaviour { // 公开变量方便在Unity编辑器中调整 [Header(Movement Settings)] public float jumpForce 5f; // 跳跃的力大小 public float tiltAmount 30f; // 根据速度倾斜的角度 // 私有引用 private Rigidbody2D rb; private bool isGameStarted false; private bool isAlive true; // 初始化 void Start() { // 获取自身附带的刚体组件 rb GetComponentRigidbody2D(); if (rb null) { Debug.LogError(BirdController: No Rigidbody2D found on this GameObject!); } // 游戏开始时先让小鸟不受重力影响直到玩家第一次点击 rb.gravityScale 0f; rb.velocity Vector2.zero; } void Update() { // 如果游戏未开始等待第一次输入 if (!isGameStarted) { if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) { StartGame(); } return; // 游戏未开始不执行后续更新逻辑 } // 如果小鸟已死亡不再接受输入 if (!isAlive) return; // 检测跳跃输入 if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) { Jump(); } // 根据垂直速度旋转小鸟模拟俯冲和爬升 ApplyTilt(); } void StartGame() { isGameStarted true; isAlive true; // 开始游戏时施加一个初始的小跳跃并启用重力 rb.gravityScale 2.5f; // 启用一个合适的重力 Jump(); // 第一次点击同时作为游戏开始和第一次跳跃 // 这里可以触发游戏开始的全局事件例如通知GameManager // GameManager.Instance.OnGameStart(); } void Jump() { // 跳跃时将垂直速度重置为一个向上的恒定力 // 使用velocity直接赋值比AddForce更直接手感更一致 rb.velocity new Vector2(rb.velocity.x, jumpForce); // 可以在这里播放跳跃音效 // AudioManager.Instance.PlaySFX(Jump); } void ApplyTilt() { // 根据小鸟的垂直速度rb.velocity.y来旋转Z轴 // velocity.y为正向上飞则向上倾斜一个角度 // velocity.y为负向下掉则向下倾斜 // 使用Mathf.Clamp限制旋转角度范围 float targetZRotation Mathf.Clamp(rb.velocity.y * tiltAmount, -60f, 30f); // 使用Lerp平滑旋转避免瞬间切换角度 transform.rotation Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, targetZRotation), Time.deltaTime * 10f); } // 碰撞检测 private void OnCollisionEnter2D(Collision2D collision) { // 当与任何物体发生碰撞时小鸟死亡 Die(); } void Die() { if (!isAlive) return; // 防止多次调用 isAlive false; // 死亡时可以停止旋转或者让它自然下落 // 也可以播放死亡动画或粒子效果 Debug.Log(Bird Died!); // 通知游戏管理器游戏结束 // GameManager.Instance.OnGameOver(); // 触发死亡事件用于更新UI如显示游戏结束面板 // EventManager.Instance.TriggerEvent(BirdDied); } }代码解析与要点[Header(Movement Settings)]这是一个属性标签它会在Unity编辑器的Inspector面板中为下方的变量创建一个分组标题让参数调整更清晰。获取组件在Start方法中使用GetComponentRigidbody2D()获取引用。这是一种缓存Caching操作比在Update里每次调用GetComponent高效得多。输入处理我们同时检测空格键(KeyCode.Space)和鼠标左键(Input.GetMouseButtonDown(0))让PC和移动端通过触屏模拟都能操作。GetKeyDown和GetMouseButtonDown只在按键按下的那一帧返回true。跳跃物理我们没有使用AddForce而是直接设置rb.velocity的y分量。AddForce是持续施加力模拟更真实的物理但手感可能“绵软”且受质量影响。直接设置velocity能提供更即时、一致的手感这是很多街机风格游戏的选择。你可以都试试感受区别。旋转控制ApplyTilt方法根据垂直速度动态旋转小鸟让游戏反馈更生动。我们使用Quaternion.Lerp进行平滑插值避免角度突变。Mathf.Clamp确保旋转角度不会超出合理范围。游戏状态管理我们引入了isGameStarted和isAlive两个布尔变量来控制游戏流程。这是最简单的状态机雏形。在真正的项目中你可能会用一个专门的GameState枚举和GameManager来管理。碰撞死亡OnCollisionEnter2D是Unity提供的回调函数当该物体的碰撞体与另一个碰撞体开始接触时调用。这里我们简单地将任何碰撞都视为死亡。保存脚本回到Unity。在Assets/03_Prefabs/Characters文件夹中找到你的Bird预制体点击它然后在Inspector面板底部点击“Add Component”搜索并添加我们刚写的BirdController脚本。你会看到Jump Force和Tilt Amount两个参数已经显示出来你可以在这里调整它们而无需修改代码。4.2 创建游戏管理器GameManager雏形游戏管理器通常作为单例Singleton存在负责协调游戏全局状态如分数、游戏开始/结束、场景加载等。我们先创建一个简单的版本。在Assets/04_Scripts/Core文件夹创建脚本GameManager。using UnityEngine; using UnityEngine.SceneManagement; // 用于场景加载 using UnityEngine.UI; // 用于UI文本 public class GameManager : MonoBehaviour { // 单例实例 public static GameManager Instance { get; private set; } [Header(UI References)] public Text scoreText; // 在Unity编辑器中拖拽赋值 public GameObject gameOverPanel; // 游戏结束面板 private int currentScore 0; private bool isGameOver false; void Awake() { // 实现一个简单的单例模式 if (Instance null) { Instance this; // DontDestroyOnLoad(gameObject); // 如果需要跨场景取消注释 } else { Destroy(gameObject); } } void Start() { // 初始化UI UpdateScoreUI(); if (gameOverPanel ! null) gameOverPanel.SetActive(false); } // 增加分数例如小鸟穿过一对管道时调用 public void AddScore(int points) { if (isGameOver) return; currentScore points; UpdateScoreUI(); // 可以在这里播放得分音效 } void UpdateScoreUI() { if (scoreText ! null) scoreText.text currentScore.ToString(); } // 游戏结束 public void GameOver() { if (isGameOver) return; isGameOver true; Debug.Log(Game Over! Final Score: currentScore); // 显示游戏结束面板 if (gameOverPanel ! null) { gameOverPanel.SetActive(true); } // 可以在这里停止管道生成、播放死亡音乐等 } // 重新开始游戏由UI按钮调用 public void RestartGame() { // 最简单的方式重新加载当前场景 SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); } // 返回主菜单由UI按钮调用 public void GoToMainMenu() { SceneManager.LoadScene(MainMenu); // 确保你有名为MainMenu的场景 } }将这个脚本挂载到之前创建的“GameManager”空对象上。然后我们需要创建UI来配合它。5. 构建基础用户界面UIUnity的UI系统基于Canvas画布。我们之前已经在场景中有了一个Canvas。创建分数文本在Hierarchy中右键点击Canvas - UI - Text - TextMeshPro。如果你第一次使用TextMeshProUnity会提示你导入TMP Essentials资源包点击确认导入。这是一个更强大、更美观的文本系统。将创建的文本对象重命名为“ScoreText”。调整其位置到屏幕顶部中央Anchor Presets选择Top Center。在Text Input框中输入“0”。调整字体大小、颜色如白色加粗使其醒目。在Inspector中找到“TextMeshPro - Text (UI)”组件将“Font Asset”设置为更美观的字体导入的TMP包里有。将这个“ScoreText”对象拖拽到GameManager脚本的scoreText公共变量上。创建游戏结束面板在Canvas下创建一个空对象重命名为“GameOverPanel”。为其添加一个Image组件UI - Image作为背景设置颜色为半透明黑色。在GameOverPanel下创建两个子对象一个TextMeshPro Text显示“Game Over!”和最终分数。你可以通过脚本动态设置其文本。一个Button(UI - Button)重命名为“RestartButton”。选中这个Button在其子对象Text上修改显示文字为“再来一局”。初始状态下在Inspector中取消勾选“GameOverPanel”对象左侧的复选框使其隐藏。将整个“GameOverPanel”对象拖拽到GameManager脚本的gameOverPanel公共变量上。绑定按钮事件选中“RestartButton”在Inspector的Button组件下方点击“On Click ()”下方的“”号。将场景中的“GameManager”对象拖拽到出现的Runtime Slot中。在下拉菜单中选择GameManager - RestartGame()。用同样的方法你还可以创建一个“返回菜单”的按钮并绑定到GoToMainMenu()方法。现在一个最基础的游戏循环框架就搭起来了小鸟可以跳撞到东西会“死”游戏管理器能更新分数和显示结束界面并且可以重新开始。6. 常见问题与排查技巧实录在项目准备和这最初的搭建阶段你可能会遇到一些典型问题。这里记录几个我踩过的坑和解决方法问题1小鸟的碰撞体位置/大小不对感觉没撞到就死了或者穿过去了。排查在Unity编辑器顶部菜单栏点击Gizmos下拉菜单确保Colliders是勾选状态。这样你就能在Scene视图中看到所有碰撞体的绿色线框。解决选中小鸟预制体在Inspector中调整其Circle Collider 2D组件的Offset偏移和Radius半径。在Scene视图中直接拖拽绿色的线框手柄更直观。记住“碰撞体宽容度”原则让视觉边缘略大于碰撞体。问题2小鸟跳跃手感奇怪要么太“飘”要么太“沉”。排查手感由三个主要参数决定jumpForce跳跃力、gravityScale重力缩放以及你是使用AddForce还是直接设置velocity。解决进行快速迭代测试。将GameManager和BirdController的关键参数如jumpForce,gravityScale,tiltAmount都设为public在Unity编辑器里运行游戏然后不停止运行直接去Inspector里修改这些数值你会立即看到效果变化。这是Unity编辑器的一个强大功能——运行时可修改参数。找到一组让你感觉舒服的数值。问题3UI文本不显示或者显示在错误的位置。排查首先检查Canvas的Render Mode。对于全屏覆盖的UIScreen Space - Overlay是默认且常用的。然后检查Text组件的Rect Transform。锚点Anchors和轴心点Pivot是UI布局的核心。解决对于分数文本这种需要固定在屏幕某处的元素使用Anchor Presets在Rect Transform组件左上角点击方框图标。比如固定在顶部中央就选择“Top Center”。同时确保Text组件的Color的Alpha通道不是0完全透明。问题4脚本编译错误或者组件引用为null。排查查看Unity编辑器底部的Console窗口。红色错误信息会明确指出哪一行代码出了问题。最常见的错误是NullReferenceException意思是你在代码中尝试使用一个没有赋值的变量为null。解决对于public变量确保你在Inspector中已经将对应的游戏对象拖拽赋值。对于通过GetComponent在Start或Awake中获取的私有变量检查该游戏对象上是否确实挂载了该组件。养成使用Debug.Log或Debug.LogError输出关键信息帮助调试的习惯。例如在Awake里Debug.Log(“GameManager Awake”)。问题5预制体修改后场景中的实例没有更新。排查在Hierarchy中预制体实例的名字如果是蓝色的并且右边有一个箭头图标说明它是链接到预制体的。如果箭头是白色的表示已断开链接。解决修改预制体本身在Project窗口双击打开或者选中场景中的实例在Inspector顶部点击“Overrides”下拉按钮然后选择“Apply All”来将实例的修改应用回预制体。反之如果你想用预制体的状态覆盖当前实例的修改就点击“Revert All”。项目准备阶段到此告一段落。你现在拥有了一个结构清晰的项目目录、一个配置好的小鸟预制体、一个能响应输入的小鸟控制器、一个管理游戏状态的GameManager以及一个基础的UI系统。这就像一个房子的地基和主体框架已经搭建完成。接下来你就可以在这个坚实的基础上开始砌墙盖瓦了——下一步就是创建会移动的管道、实现分数计算、添加音效和粒子让游戏真正活起来。记住遇到问题多查文档Unity Manual和Scripting API是最好老师、多利用Unity社区以及最重要的多动手试错。
Unity 2D游戏开发入门:从零构建“跳跳鸟”项目框架与核心机制
发布时间:2026/7/14 6:30:16
1. 项目概述为什么选择“跳跳鸟”作为你的第一个Unity 2D实战项目如果你刚接触Unity或者已经看了一堆教程但还没做出一个能跑起来的完整小游戏那么“跳跳鸟”这个项目可能就是为你量身定做的。它不是什么复杂的3A大作而是一个经典的、机制简单的2D小游戏原型核心玩法就是控制一只小鸟在管道障碍物之间跳跃穿行。听起来简单对吧但恰恰是这种简单让它成为了学习Unity 2D游戏开发核心工作流的绝佳沙盒。我见过太多新手一上来就想做开放世界RPG结果在复杂的系统面前迅速迷失信心受挫。而“跳跳鸟”项目它能让你在几天内清晰地走完一个游戏从零到发布的核心闭环创建项目、导入素材、编写控制逻辑、处理物理碰撞、设计游戏循环开始、进行、结束、实现UI交互分数、重新开始最后打包成可执行文件。这个过程里你会亲手触摸到Unity编辑器的大部分核心面板理解GameObject、Component、Prefab、Sprite、Rigidbody 2D、Collider 2D这些基础但至关重要的概念。当你看到自己写的小鸟在屏幕上蹦跶因为你的操作失误而“撞墙”时那种亲手创造世界的成就感是看一百个教程视频也换不来的。基于当前热门的“实战”学习趋势这个项目准备篇我不会空谈理论而是直接带你进入“战备状态”。我们将聚焦于搭建一个清晰、高效、可扩展的项目基础框架避免后期陷入资源混乱、脚本耦合的泥潭。很多教程会跳过这一步直接开始写代码但一个良好的开端是成功的一半尤其是在游戏开发中。2. 项目准备的核心思路构建可维护的工程结构在真正动手写第一行代码或拖拽第一个精灵之前我们必须先规划好项目的“房间布局”。想象一下如果你把所有的家具图片、声音、脚本都胡乱堆在客厅以后想找把螺丝刀某个特定脚本都得翻箱倒柜。Unity项目同样如此一个清晰的文件目录结构是专业开发的起点它能极大提升你的开发效率和后期维护的便利性。2.1 创建项目与初始设置首先打开Unity Hub点击“新建项目”。这里有几个关键选择点项目模板务必选择“2D”模板。Unity的2D和3D模板在初始设置上有显著区别。2D模板会自动将场景视图设置为2D模式并将主摄像机的投影方式Projection设置为“Orthographic”正交这对于2D游戏至关重要因为它消除了透视变形确保精灵大小不会因距离而改变。项目名称与位置给你的项目起个名字比如“FlappyBirdClone”或“MyFirst2DGame”。位置路径不要包含中文或特殊字符最好放在一个专门的开发目录下如D:\UnityProjects\。这是一个老生常谈但总有人踩坑的细节路径中的中文可能导致一些意想不到的打包或资源加载错误。版本管理与初始包如果你打算使用版本控制强烈推荐即使是个人项目可以在此处初始化Git仓库。至于初始包保持默认即可我们后续需要什么再通过Package Manager添加。注意Unity版本的选择上对于2D小游戏选择一个稳定的LTS长期支持版本即可如2022 LTS或2021 LTS。不必追求最新版稳定压倒一切避免遇到新版本的未知Bug影响你的学习进度。创建完成后Unity会打开编辑器。第一件事我们来整理这个“空房间”。2.2 设计科学的项目文件夹结构在Project窗口通常是左下角的Assets文件夹下直接右键创建以下文件夹。这套结构是我经过多个项目总结出来的对于中小型项目非常够用Assets/ ├── 01_Art美术资源 │ ├── Sprites精灵图片 │ ├── UI界面图片、字体 │ └── Materials材质2D项目通常用得少但预留 ├── 02_Audio音频资源 │ ├── BGM背景音乐 │ └── SFX音效 ├── 03_Prefabs预制体 │ ├── Characters角色如Bird.prefab │ ├── Environment环境如Pipe.prefab │ └── UIUI元素预制体 ├── 04_Scripts脚本 │ ├── Core核心逻辑如GameManager.cs │ ├── Characters角色控制如BirdController.cs │ ├── Environment环境交互如PipeSpawner.cs │ ├── UI界面控制如UIManager.cs │ └── Utilities工具类如Extensions.cs ├── 05_Scenes场景 │ ├── 00_Init初始化场景可选 │ ├── 01_MainMenu主菜单 │ └── 02_Game游戏主场景 ├── 06_Animations动画控制器和动画片段 │ └── Controllers如Bird.controller ├── 07_Settings配置资产 │ ├── InputActions新输入系统配置 │ └── ScriptableObjects可脚本化对象如游戏设置 └── 08_External第三方插件或资源包为什么这么分数字前缀强制文件夹按逻辑顺序排列方便查找。按功能模块划分美术、音频、脚本分离符合资源类型管理。Scripts内再细分这是避免“脚本地狱”的关键。想象一下30个脚本都堆在根目录下是什么感觉。按功能分文件夹能让你的代码架构一目了然。Core文件夹放管理全局状态的单例或管理器Utilities放一些静态工具方法比如常用的数学计算、扩展方法等。预留空间比如External文件夹当你从Asset Store购买或下载免费资源时全部导入到这里避免和你的原创资源混在一起。实操心得现在就花5分钟创建好这个结构。养成这个习惯未来当你项目膨胀到几百个资源文件时你会回来感谢我的。另外在Unity中你可以给文件夹设置喜欢的图标在文件夹上右键 - Properties - Custom Icon让项目管理更直观。2.3 核心资源准备与导入设置对于“跳跳鸟”项目我们至少需要以下美术资源小鸟精灵一张png图片最好包含小鸟不同状态比如正常、向上飞、向下坠的帧或者我们就用一张图通过旋转来模拟。管道精灵上下两个管道通常也是一张png我们可以通过代码或编辑器来重复使用它。背景一张横向可以无缝滚动或足够宽的背景图。地面一张长条的地面贴图用于在底部滚动。UI素材开始按钮、游戏结束面板、数字字体等。你可以在一些免费的像素艺术网站如 itch.io, OpenGameArt找到这些资源或者用简单的图形工具自己画。这里我假设你已经找到了或制作好了这些图片。导入资源后的关键设置以小鸟精灵为例将小鸟的png图片拖入Assets/01_Art/Sprites文件夹。在Project窗口中点击该图片查看Inspector面板。Texture Type确保是Sprite (2D and UI)。这是2D游戏精灵的正确类型。Pixels Per Unit (PPU)这个值决定了图片上一个Unity单位对应多少像素。对于像素风游戏通常设置为32, 64, 100等这取决于你的精灵图大小和想要的游戏内尺寸。比如你的小鸟图片是64x64像素PPU设为64那么它在游戏里就是1个单位宽和1个单位高。我建议先设为100这是一个常用的基准值后续可以根据感觉调整。Filter Mode对于像素艺术务必选择Point (no filter)。这能保证你的像素边缘清晰锐利不会因为缩放而变得模糊。如果是高清平滑风格的2D美术可以选择Bilinear。Compression根据项目大小和平台选择对于学习项目选择Low Quality或High Quality均可影响不大。重要提示对于背景和地面这种需要无缝滚动的图片你需要确保图片的左右边缘在颜色和图案上是能够完美衔接的。在导入设置中可以将Wrap Mode设置为Repeat但这通常用于材质贴图。对于2D精灵的滚动我们更多是通过代码控制位置来实现。完成所有精灵的导入和基本设置后你的Project窗口应该已经初具规模了。3. 搭建基础场景与游戏对象现在我们开始布置第一个游戏场景。在Assets/05_Scenes文件夹内右键创建一个新场景命名为“Main”。双击打开它。3.1 创建基础游戏对象一个典型的“跳跳鸟”场景包含以下层级Hierarchy结构Main Camera默认存在负责显示游戏画面。确保其Projection为OrthographicSize属性决定了视野范围。你可以先设为5后续根据背景大小调整。Directional Light在2D项目中除非你使用URP/HDRP并需要2D光照否则这个光源不是必须的。可以删除它以简化场景。GameManager (空对象)这是一个不渲染任何内容的“管理器”对象我们将它作为所有全局控制脚本的挂载点。创建空GameObject重命名为“GameManager”。Background背景容器。创建一个空GameObject命名为“Background”然后将你的背景精灵拖到Hierarchy中使其成为“Background”的子对象。这样便于整体管理。Ground地面。同样创建一个空对象“Ground”作为容器然后把地面精灵拖进去作为其子对象。地面通常需要碰撞体我们稍后添加。Bird (预制体占位符)我们先创建一个空对象命名为“Bird”作为小鸟的占位符。稍后我们会创建小鸟的预制体并替换它。PipesSpawner管道生成器。一个空对象用于挂载控制管道随机生成和移动的脚本。CanvasUI画布。Unity会自动设置其渲染模式为“Screen Space - Overlay”。我们将在其下创建分数文本、开始按钮、游戏结束面板等UI元素。操作技巧善用“空对象”作为逻辑分组容器这能让你的Hierarchy窗口非常整洁。例如你可以创建一个“Environment”空对象然后把“Background”、“Ground”、“PipesSpawner”都拖进去作为其子级。3.2 配置第一个预制体小鸟预制体Prefab是Unity的核心概念之一它允许你创建一个游戏对象的蓝图并在场景中多次实例化所有实例都链接到原始预制体。对预制体的修改会应用到所有实例这太有用了。我们来创建小鸟预制体在Hierarchy中右键点击我们刚才创建的“Bird”空对象选择“2D Object - Sprite”。这会创建一个带有Sprite Renderer组件的对象。将它重命名为“Bird”。在Inspector中点击Sprite Renderer组件的Sprite属性右侧的小圆点选择你导入的小鸟图片。添加刚体Rigidbody 2D这是让小鸟受物理重力影响的关键组件。点击“Add Component”搜索并添加Rigidbody 2D。Body Type选择Dynamic这样它才会受力和重力影响。Gravity Scale重力缩放。默认是1但对于这种轻快的小鸟游戏我们可能希望重力感更强或更弱。可以先保持1后续在脚本中调整或通过测试来修改。一个常见的“跳跳鸟”手感是设置一个中等向上的力来对抗一个较强的重力比如重力设为3。Freeze Rotation强烈建议将Z轴旋转冻结。勾选Freeze Rotation Z。我们不希望小鸟在碰撞或受力时胡乱旋转通常只希望它有一个固定的倾斜角度来表现飞行姿态这个角度我们将通过脚本控制。添加碰撞体Collider 2D没有碰撞体小鸟就会穿墙而过。添加一个Circle Collider 2D或Box Collider 2D。对于简单的小鸟形状Circle Collider 2D通常更合适性能也稍好。添加后使用组件上的编辑手柄调整碰撞体的大小和位置使其大致贴合小鸟的轮廓。不要追求像素级的完美贴合对于这种游戏稍微小一点的碰撞体即让视觉边缘比碰撞体大一点能让玩家感觉更“公平”减少挫败感。这被称为“碰撞体宽容度”。创建标签Tag为了在代码中方便地识别小鸟我们给它创建一个专属标签。在Inspector顶部的Tag下拉菜单中选择“Add Tag...”然后点击“”号新建一个标签命名为“Player”。然后回到小鸟对象将其Tag设置为“Player”。制作预制体现在从Hierarchy窗口中将“Bird”对象直接拖拽到Assets/03_Prefabs/Characters文件夹中。你会看到该对象在Hierarchy中的名字变成蓝色这表示它已经是一个预制体实例了。最后删除Hierarchy中的这个实例。因为我们将在游戏开始时通过代码动态生成小鸟或者从一个初始位置实例化这样更容易控制重生逻辑。至此你的小鸟预制体就准备好了。它包含了渲染、物理和碰撞的所有必要组件。4. 编写第一个脚本小鸟控制器BirdController脚本是游戏的灵魂。我们在Assets/04_Scripts/Characters文件夹上右键选择Create - C# Script命名为BirdController。双击在Visual Studio或Rider中打开。4.1 基础移动与跳跃控制我们先实现最核心的功能点击屏幕或空格键小鸟向上跳跃。using UnityEngine; public class BirdController : MonoBehaviour { // 公开变量方便在Unity编辑器中调整 [Header(Movement Settings)] public float jumpForce 5f; // 跳跃的力大小 public float tiltAmount 30f; // 根据速度倾斜的角度 // 私有引用 private Rigidbody2D rb; private bool isGameStarted false; private bool isAlive true; // 初始化 void Start() { // 获取自身附带的刚体组件 rb GetComponentRigidbody2D(); if (rb null) { Debug.LogError(BirdController: No Rigidbody2D found on this GameObject!); } // 游戏开始时先让小鸟不受重力影响直到玩家第一次点击 rb.gravityScale 0f; rb.velocity Vector2.zero; } void Update() { // 如果游戏未开始等待第一次输入 if (!isGameStarted) { if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) { StartGame(); } return; // 游戏未开始不执行后续更新逻辑 } // 如果小鸟已死亡不再接受输入 if (!isAlive) return; // 检测跳跃输入 if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) { Jump(); } // 根据垂直速度旋转小鸟模拟俯冲和爬升 ApplyTilt(); } void StartGame() { isGameStarted true; isAlive true; // 开始游戏时施加一个初始的小跳跃并启用重力 rb.gravityScale 2.5f; // 启用一个合适的重力 Jump(); // 第一次点击同时作为游戏开始和第一次跳跃 // 这里可以触发游戏开始的全局事件例如通知GameManager // GameManager.Instance.OnGameStart(); } void Jump() { // 跳跃时将垂直速度重置为一个向上的恒定力 // 使用velocity直接赋值比AddForce更直接手感更一致 rb.velocity new Vector2(rb.velocity.x, jumpForce); // 可以在这里播放跳跃音效 // AudioManager.Instance.PlaySFX(Jump); } void ApplyTilt() { // 根据小鸟的垂直速度rb.velocity.y来旋转Z轴 // velocity.y为正向上飞则向上倾斜一个角度 // velocity.y为负向下掉则向下倾斜 // 使用Mathf.Clamp限制旋转角度范围 float targetZRotation Mathf.Clamp(rb.velocity.y * tiltAmount, -60f, 30f); // 使用Lerp平滑旋转避免瞬间切换角度 transform.rotation Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, targetZRotation), Time.deltaTime * 10f); } // 碰撞检测 private void OnCollisionEnter2D(Collision2D collision) { // 当与任何物体发生碰撞时小鸟死亡 Die(); } void Die() { if (!isAlive) return; // 防止多次调用 isAlive false; // 死亡时可以停止旋转或者让它自然下落 // 也可以播放死亡动画或粒子效果 Debug.Log(Bird Died!); // 通知游戏管理器游戏结束 // GameManager.Instance.OnGameOver(); // 触发死亡事件用于更新UI如显示游戏结束面板 // EventManager.Instance.TriggerEvent(BirdDied); } }代码解析与要点[Header(Movement Settings)]这是一个属性标签它会在Unity编辑器的Inspector面板中为下方的变量创建一个分组标题让参数调整更清晰。获取组件在Start方法中使用GetComponentRigidbody2D()获取引用。这是一种缓存Caching操作比在Update里每次调用GetComponent高效得多。输入处理我们同时检测空格键(KeyCode.Space)和鼠标左键(Input.GetMouseButtonDown(0))让PC和移动端通过触屏模拟都能操作。GetKeyDown和GetMouseButtonDown只在按键按下的那一帧返回true。跳跃物理我们没有使用AddForce而是直接设置rb.velocity的y分量。AddForce是持续施加力模拟更真实的物理但手感可能“绵软”且受质量影响。直接设置velocity能提供更即时、一致的手感这是很多街机风格游戏的选择。你可以都试试感受区别。旋转控制ApplyTilt方法根据垂直速度动态旋转小鸟让游戏反馈更生动。我们使用Quaternion.Lerp进行平滑插值避免角度突变。Mathf.Clamp确保旋转角度不会超出合理范围。游戏状态管理我们引入了isGameStarted和isAlive两个布尔变量来控制游戏流程。这是最简单的状态机雏形。在真正的项目中你可能会用一个专门的GameState枚举和GameManager来管理。碰撞死亡OnCollisionEnter2D是Unity提供的回调函数当该物体的碰撞体与另一个碰撞体开始接触时调用。这里我们简单地将任何碰撞都视为死亡。保存脚本回到Unity。在Assets/03_Prefabs/Characters文件夹中找到你的Bird预制体点击它然后在Inspector面板底部点击“Add Component”搜索并添加我们刚写的BirdController脚本。你会看到Jump Force和Tilt Amount两个参数已经显示出来你可以在这里调整它们而无需修改代码。4.2 创建游戏管理器GameManager雏形游戏管理器通常作为单例Singleton存在负责协调游戏全局状态如分数、游戏开始/结束、场景加载等。我们先创建一个简单的版本。在Assets/04_Scripts/Core文件夹创建脚本GameManager。using UnityEngine; using UnityEngine.SceneManagement; // 用于场景加载 using UnityEngine.UI; // 用于UI文本 public class GameManager : MonoBehaviour { // 单例实例 public static GameManager Instance { get; private set; } [Header(UI References)] public Text scoreText; // 在Unity编辑器中拖拽赋值 public GameObject gameOverPanel; // 游戏结束面板 private int currentScore 0; private bool isGameOver false; void Awake() { // 实现一个简单的单例模式 if (Instance null) { Instance this; // DontDestroyOnLoad(gameObject); // 如果需要跨场景取消注释 } else { Destroy(gameObject); } } void Start() { // 初始化UI UpdateScoreUI(); if (gameOverPanel ! null) gameOverPanel.SetActive(false); } // 增加分数例如小鸟穿过一对管道时调用 public void AddScore(int points) { if (isGameOver) return; currentScore points; UpdateScoreUI(); // 可以在这里播放得分音效 } void UpdateScoreUI() { if (scoreText ! null) scoreText.text currentScore.ToString(); } // 游戏结束 public void GameOver() { if (isGameOver) return; isGameOver true; Debug.Log(Game Over! Final Score: currentScore); // 显示游戏结束面板 if (gameOverPanel ! null) { gameOverPanel.SetActive(true); } // 可以在这里停止管道生成、播放死亡音乐等 } // 重新开始游戏由UI按钮调用 public void RestartGame() { // 最简单的方式重新加载当前场景 SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); } // 返回主菜单由UI按钮调用 public void GoToMainMenu() { SceneManager.LoadScene(MainMenu); // 确保你有名为MainMenu的场景 } }将这个脚本挂载到之前创建的“GameManager”空对象上。然后我们需要创建UI来配合它。5. 构建基础用户界面UIUnity的UI系统基于Canvas画布。我们之前已经在场景中有了一个Canvas。创建分数文本在Hierarchy中右键点击Canvas - UI - Text - TextMeshPro。如果你第一次使用TextMeshProUnity会提示你导入TMP Essentials资源包点击确认导入。这是一个更强大、更美观的文本系统。将创建的文本对象重命名为“ScoreText”。调整其位置到屏幕顶部中央Anchor Presets选择Top Center。在Text Input框中输入“0”。调整字体大小、颜色如白色加粗使其醒目。在Inspector中找到“TextMeshPro - Text (UI)”组件将“Font Asset”设置为更美观的字体导入的TMP包里有。将这个“ScoreText”对象拖拽到GameManager脚本的scoreText公共变量上。创建游戏结束面板在Canvas下创建一个空对象重命名为“GameOverPanel”。为其添加一个Image组件UI - Image作为背景设置颜色为半透明黑色。在GameOverPanel下创建两个子对象一个TextMeshPro Text显示“Game Over!”和最终分数。你可以通过脚本动态设置其文本。一个Button(UI - Button)重命名为“RestartButton”。选中这个Button在其子对象Text上修改显示文字为“再来一局”。初始状态下在Inspector中取消勾选“GameOverPanel”对象左侧的复选框使其隐藏。将整个“GameOverPanel”对象拖拽到GameManager脚本的gameOverPanel公共变量上。绑定按钮事件选中“RestartButton”在Inspector的Button组件下方点击“On Click ()”下方的“”号。将场景中的“GameManager”对象拖拽到出现的Runtime Slot中。在下拉菜单中选择GameManager - RestartGame()。用同样的方法你还可以创建一个“返回菜单”的按钮并绑定到GoToMainMenu()方法。现在一个最基础的游戏循环框架就搭起来了小鸟可以跳撞到东西会“死”游戏管理器能更新分数和显示结束界面并且可以重新开始。6. 常见问题与排查技巧实录在项目准备和这最初的搭建阶段你可能会遇到一些典型问题。这里记录几个我踩过的坑和解决方法问题1小鸟的碰撞体位置/大小不对感觉没撞到就死了或者穿过去了。排查在Unity编辑器顶部菜单栏点击Gizmos下拉菜单确保Colliders是勾选状态。这样你就能在Scene视图中看到所有碰撞体的绿色线框。解决选中小鸟预制体在Inspector中调整其Circle Collider 2D组件的Offset偏移和Radius半径。在Scene视图中直接拖拽绿色的线框手柄更直观。记住“碰撞体宽容度”原则让视觉边缘略大于碰撞体。问题2小鸟跳跃手感奇怪要么太“飘”要么太“沉”。排查手感由三个主要参数决定jumpForce跳跃力、gravityScale重力缩放以及你是使用AddForce还是直接设置velocity。解决进行快速迭代测试。将GameManager和BirdController的关键参数如jumpForce,gravityScale,tiltAmount都设为public在Unity编辑器里运行游戏然后不停止运行直接去Inspector里修改这些数值你会立即看到效果变化。这是Unity编辑器的一个强大功能——运行时可修改参数。找到一组让你感觉舒服的数值。问题3UI文本不显示或者显示在错误的位置。排查首先检查Canvas的Render Mode。对于全屏覆盖的UIScreen Space - Overlay是默认且常用的。然后检查Text组件的Rect Transform。锚点Anchors和轴心点Pivot是UI布局的核心。解决对于分数文本这种需要固定在屏幕某处的元素使用Anchor Presets在Rect Transform组件左上角点击方框图标。比如固定在顶部中央就选择“Top Center”。同时确保Text组件的Color的Alpha通道不是0完全透明。问题4脚本编译错误或者组件引用为null。排查查看Unity编辑器底部的Console窗口。红色错误信息会明确指出哪一行代码出了问题。最常见的错误是NullReferenceException意思是你在代码中尝试使用一个没有赋值的变量为null。解决对于public变量确保你在Inspector中已经将对应的游戏对象拖拽赋值。对于通过GetComponent在Start或Awake中获取的私有变量检查该游戏对象上是否确实挂载了该组件。养成使用Debug.Log或Debug.LogError输出关键信息帮助调试的习惯。例如在Awake里Debug.Log(“GameManager Awake”)。问题5预制体修改后场景中的实例没有更新。排查在Hierarchy中预制体实例的名字如果是蓝色的并且右边有一个箭头图标说明它是链接到预制体的。如果箭头是白色的表示已断开链接。解决修改预制体本身在Project窗口双击打开或者选中场景中的实例在Inspector顶部点击“Overrides”下拉按钮然后选择“Apply All”来将实例的修改应用回预制体。反之如果你想用预制体的状态覆盖当前实例的修改就点击“Revert All”。项目准备阶段到此告一段落。你现在拥有了一个结构清晰的项目目录、一个配置好的小鸟预制体、一个能响应输入的小鸟控制器、一个管理游戏状态的GameManager以及一个基础的UI系统。这就像一个房子的地基和主体框架已经搭建完成。接下来你就可以在这个坚实的基础上开始砌墙盖瓦了——下一步就是创建会移动的管道、实现分数计算、添加音效和粒子让游戏真正活起来。记住遇到问题多查文档Unity Manual和Scripting API是最好老师、多利用Unity社区以及最重要的多动手试错。