1. 从“Hello World”到“Hello Unity”:为什么你的笔记需要重新定义
如果你刚刚打开Unity Hub,看着那个新建项目的按钮,心里盘算着“今天我要学会Unity”,然后一头扎进某个教程,跟着敲了三天代码,最后发现自己只会让一个方块在屏幕上跳来跳去,对“材质球”、“预制体”、“协程”这些词依然一知半解——那么,你很可能正踩在无数Unity初学者都踩过的坑里。这个坑的名字叫“碎片化学习”。市面上不缺Unity教程,B站、油管、各种付费课程,从安装到发布应有尽有。但问题在于,这些知识像散落的珍珠,你学了一堆,却不知道如何把它们串成一条能戴出去的项链。你记了一堆笔记,可能是OneNote里的几页,也可能是Obsidian里零散的卡片,但当你真正想做一个自己的小游戏时,却发现笔记里找不到能直接用的“组合拳”。
这正是“Unity入门笔记汇总”这个标题背后真正的痛点。它不是一个简单的教程目录搬运,而是对你个人知识体系的第一次“重构”。我见过太多新手,包括几年前的我自己,把学习等同于“收藏”和“记录”,误以为存下了就等于学会了。真正的入门,是从你能够将“场景视图操作”、“C#脚本编写”、“物理组件应用”这几个孤立的知识点,串联成一个解决具体问题的工作流开始的。比如,你想做一个“点击屏幕,小球朝点击位置发射”的功能。这需要你懂得如何在脚本中获取鼠标输入(Input.mousePosition),理解屏幕坐标到世界坐标的转换(Camera.ScreenToWorldPoint),掌握给物体施加力的方法(Rigidbody.AddForce),还要知道如何实例化预制体(Instantiate)。你的笔记,就应该是为了打通这些关节而存在的。
所以,这份“汇总”的终极目标,是帮你构建一个属于你自己的、可检索、可复用、有上下文联系的Unity知识库。它不同于任何现成的课程大纲,因为它必须包含你自己的思考、踩过的坑、和独特的解决方案。下面,我就以一个过来人的身份,和你拆解一下,如何打造一份能让你从“知道”走向“做到”的Unity入门笔记体系。
2. 笔记体系架构:打造你的个人Unity知识引擎
2.1 核心原则:基于问题与项目,而非基于菜单栏
很多人的笔记结构是照搬Unity编辑器菜单:“File”, “Edit”, “GameObject”, “Component”… 这种结构对于查阅官方手册有用,但对于学习和创作是灾难性的。它强化了知识的孤立性。
我推荐的笔记结构是“三维度架构法”:
概念维度(What & Why):记录核心概念的本质与原理。例如:
- GameObject与Component模型:用“空物体(容器)+ 功能卡片(组件)”的类比来理解,这是Unity一切的基础。
- 预制体(Prefab):不仅仅是“可复用的模板”,要理解它的“实例化”与“覆写”机制,以及它在资源管理和场景构建中的核心作用。
- 坐标系统:区分模型本地坐标、世界坐标、屏幕坐标、视口坐标。笔记里可以画一张简单的转换关系图,并备注关键API:
Transform.localPosition,Transform.position,Camera.ScreenToWorldPoint。 - 生命周期(MonoBehaviour):
Awake,OnEnable,Start,Update,FixedUpdate,LateUpdate,OnDisable,OnDestroy的执行顺序和用途。用一个表格来对比是最清晰的。
方法名 调用时机 常见用途 Awake脚本实例被创建时,无论是否激活。 初始化内部数据、获取组件引用。 OnEnable每当对象变为激活(Active)状态时。 注册事件监听、启动协程。 Start在 Update第一次执行前,仅当脚本激活时调用一次。依赖于其他对象初始化的设置。 Update每帧调用一次,频率与设备性能相关。 处理非物理相关的实时逻辑,如输入检测。 FixedUpdate固定时间间隔调用(默认0.02秒),与帧率无关。 执行物理计算(如力、速度)。 LateUpdate在所有 Update执行完毕后调用。摄像机跟随、基于当前帧所有对象状态的逻辑。 OnDisable对象变为非激活状态时。 取消事件监听、停止协程。 OnDestroy对象被销毁前。 清理资源、保存数据。 操作维度(How):记录解决特定问题的具体步骤和代码片段。这部分要像“菜谱”一样清晰。
- 如何实现一个简单的角色移动?
- 方案A(物理驱动):添加
Rigidbody组件,在FixedUpdate中使用Rigidbody.AddForce或修改Rigidbody.velocity。 - 方案B(变换驱动):在
Update中直接修改Transform.position。 - 笔记要点:对比两种方案的差异(物理反馈 vs. 直接控制),附上核心代码块,并注明方案B可能导致的穿墙问题(需结合碰撞检测)。
- 方案A(物理驱动):添加
- 如何实现UI血条跟随3D物体?
- 步骤:使用
Camera.WorldToScreenPoint将世界坐标转为屏幕坐标,将该坐标赋值给UI RectTransform的anchoredPosition。 - 笔记要点:记录下UI Canvas的渲染模式(Screen Space - Overlay/Camera/World),不同模式下的坐标处理方式不同。这是极易混淆的点。
- 步骤:使用
- 如何实现一个简单的角色移动?
项目维度(Context):这是最容易被忽略,却最重要的维度。为每一个你尝试做的迷你项目(如“2D平台跳跃原型”、“3D第一人称射击Demo”)单独建立笔记。在这份笔记里,你不是在记录通用知识,而是在记录决策过程。
- 项目:2D平台跳跃
- 需求:角色跳跃、二段跳、蹬墙跳。
- 技术选型:为什么选择
Rigidbody2D而不是直接控制Transform?(因为需要稳定的物理碰撞和重力模拟)。 - 实现细节:如何检测地面?我用的是
Physics2D.Raycast向下发射射线,并记录了射线长度和LayerMask的设置参数。 - 遇到的问题:角色在斜坡上滑动怎么办?解决方案:调整
Physics Material 2D的摩擦系数,或者用代码在检测到斜坡时限制横向速度。 - 可复用脚本:将“跳跃控制”、“地面检测”封装成独立的C#类,并记录下它们的公共接口(Public Methods)。
- 项目:2D平台跳跃
我的实操心得:千万不要把这三个维度的笔记混在一起。我推荐使用任何支持双向链接的笔记工具(如Obsidian、Logseq)。在“概念维度”的“预制体”笔记中,你可以链接到“操作维度”的“如何实例化预制体”笔记,再链接到“项目维度”的“那个使用了大量敌人预制体的射击游戏”笔记。这样,当你查看“预制体”概念时,你能立刻看到它的所有应用场景和具体代码,知识就活了。
2.2 工具选型与信息组织:让笔记流动起来
工欲善其事,必先利其器。但工具的核心是服务于工作流,而不是制造负担。
- 核心工具:Visual Studio Code + Unity是编码标配。但笔记工具,我更倾向于Obsidian。原因在于其强大的双向链接、图谱视图和本地Markdown存储。你的笔记就是一堆
.md文件,完全由你掌控,无需担心平台倒闭或收费模式变更。它的“每日笔记”功能非常适合记录当天学习Unity时零碎的想法和问题。 - 信息捕获:
- 截图与标注:使用Snipaste或ShareX这类工具,可以快速截取Unity编辑器界面、Inspector配置、Console报错信息,并直接粘贴到笔记中,附上文字说明。一张好的截图胜过千言万语。
- 代码片段管理:Obsidian的代码块高亮很好用。但对于更复杂的、可复用的工具类脚本,我建议在Unity项目内建立一个“
ScriptTemplates”文件夹,将验证过的通用脚本(如单例模式基类、对象池、音频管理器)放在那里。在笔记中只需记录该脚本的用途、接口说明和项目内的相对路径。
- 模板化:为“概念笔记”、“功能实现笔记”、“项目复盘笔记”创建模板。模板可以预设好标题、元数据(如创建日期、相关标签、关联概念)和基本结构。这能极大降低记录时的心理成本,保证笔记质量的一致性。例如,一个“功能实现”笔记模板可以包含:[功能描述]、[涉及的核心API]、[步骤详解]、[示例代码]、[注意事项]、[相关链接]。
3. 核心模块精讲与笔记要点
3.1 编辑器生存指南:别在界面上迷路
Unity编辑器看似复杂,但核心工作区就那几个。你的笔记应该帮你建立肌肉记忆。
- Scene视图与Game视图:这是你构建和测试世界的画布。笔记重点:
- Scene视图导航:鼠标中键平移,Alt+左键旋转,Alt+右键缩放。这是高频操作,必须熟练。
- 2D/3D模式切换:在Scene视图左上角。做2D游戏时务必切换到2D模式,否则视角操作会很别扭。
- Game视图分辨率:测试UI适配性的关键。记录下你目标平台(如1920x1080, 2340x1080)的常见分辨率,并在这里快速切换测试。
- Hierarchy与Project窗口:
- Hierarchy:这是当前场景的对象清单。学会使用“Create Empty”创建空对象作为文件夹来组织场景结构。给对象起有意义的名字,并利用好“图层(Layer)”和“标签(Tag)”进行筛选。
- Project窗口:这是你的资源仓库。建立清晰的文件夹结构是专业性的体现。我的通用结构是:
Assets/Art(美术资源)、Assets/Prefabs(预制体)、Assets/Scripts(脚本)、Assets/Scenes(场景)、Assets/Resources(需动态加载的资源)。重要笔记:理解Resources文件夹的特殊性(可以使用Resources.Load动态加载),以及它的性能影响(会增加包体大小和初始化时间)。
- Inspector(检视器):这是你与任何对象(GameObject或Asset)对话的窗口。笔记核心:
- 组件(Component):理解每个组件就是一个功能模块。尝试阅读官方文档中对常用组件(如
Transform,Rigidbody,Collider,Renderer,AudioSource)的每一个参数的简要说明,并记录下你的理解。 - 序列化字段:在脚本中声明
public变量或使用[SerializeField]属性标记的私有变量,会暴露在Inspector中。这是实现“数据驱动设计”的基础,让你无需修改代码就能调整游戏参数。
- 组件(Component):理解每个组件就是一个功能模块。尝试阅读官方文档中对常用组件(如
3.2 C#脚本编程:与Unity引擎对话的语法
Unity用的是C#,但Unity的C#有它自己的“方言”和最佳实践。
- MonoBehaviour是基石:你写的几乎所有脚本都要继承自
MonoBehaviour。这意味着你获得了访问生命周期事件的能力。笔记里必须有一张清晰的生命周期流程图。 - 常用API精要(你的武器库清单):
GameObject与Component操作:GameObject.Find/GameObject.FindWithTag:慎用!性能开销大,尤其别在Update里用。笔记里要红字警告。GetComponent<T>()/GetComponentInChildren<T>():获取组件。在Awake或Start中缓存引用,避免每帧调用。Instantiate/Destroy:创建和销毁对象。理解Destroy是异步的,对象会在当前帧结束后才真正被销毁。
- 输入处理:
Input.GetKeyDown/Input.GetMouseButtonDown:用于单次触发(如跳跃、开枪)。Input.GetAxis(“Horizontal”):用于平滑的轴向输入(如角色移动)。去Edit -> Project Settings -> Input Manager里看看默认的输入配置,并记录下如何自定义。
- 时间控制:
Time.deltaTime:这是帧与帧之间的时间间隔。任何与帧率相关的运动(如在Update中修改position)都必须乘以Time.deltaTime,以确保在不同帧率的设备上速度一致。把这个原则刻在笔记的显眼位置。Time.fixedDeltaTime:FixedUpdate的固定时间间隔。
- 协程(Coroutine):理解
yield return是关键。它用于处理需要跨帧执行的序列或延迟任务。经典用例:yield return new WaitForSeconds(2f); //等待2秒。笔记要记录如何启动协程(StartCoroutine)和停止(StopCoroutine)。
踩坑实录:早期我曾犯过一个错误,在
Update里用transform.Translate(Vector3.forward * speed);移动物体,结果在一台高配电脑和一台旧手机上速度天差地别。直到我理解了Time.deltaTime,将其改为transform.Translate(Vector3.forward * speed * Time.deltaTime);,问题才解决。这个教训让我明白,引擎提供的每一个工具都有其设计目的,deltaTime就是用来解决帧率无关性问题的。
3.3 物理与碰撞:让世界真实互动
物理系统让游戏世界“活”起来,但也是最容易出“鬼畜”BUG的地方。
刚体(Rigidbody):赋予物体物理属性的核心组件。笔记重点参数:
- Mass(质量):影响惯性。两个物体碰撞时,质量小的会被弹开更远。
- Drag(阻力):空气阻力,用于模拟物体运动逐渐停止的效果。
- Use Gravity(使用重力):勾选后受重力影响。
- Is Kinematic(是否为运动学):如果勾选,物体将不受物理引擎的力驱动,但可以通过直接设置
position/rotation来移动,并能够影响其他非运动学刚体。常用于平台、移动的电梯等。
碰撞体(Collider):定义物体的物理形状。笔记要对比:
- Box Collider:方形,性能最好。
- Sphere Collider:球形。
- Capsule Collider:胶囊形,非常适合用于角色控制器。
- Mesh Collider:使用模型网格,最精确但性能消耗最大。非必要不使用。
碰撞检测与响应:这是核心中的核心,必须用表格理清:
方法名 所属类 触发条件 用途 OnCollisionEnterMonoBehaviour两个都有Collider和Rigidbody的物体开始接触时。 播放碰撞音效、造成伤害、得分。 OnCollisionStayMonoBehaviour接触持续每一帧。 持续伤害(如站在火焰上)。 OnCollisionExitMonoBehaviour接触分离时。 停止持续伤害效果。 OnTriggerEnterMonoBehaviour一个物体进入另一个物体的**触发器(Is Trigger勾选)**范围时。 拾取物品、进入区域触发事件。 OnTriggerStayMonoBehaviour停留在触发器内每一帧。 在光环内持续回复生命值。 OnTriggerExitMonoBehaviour离开触发器时。 离开安全区。 关键区别:
Collision是实心的碰撞,物体会被阻挡;Trigger是虚的,物体可以穿过,仅用于检测。
3.4 UI系统:与玩家交互的界面
Unity的UI系统(uGUI)基于Canvas(画布)和Rect Transform。
- Canvas:所有UI元素的根容器。它的“Render Mode”决定了UI如何渲染:
- Screen Space - Overlay:覆盖在屏幕最上层,无视摄像机。适用于全屏UI、HUD。
- Screen Space - Camera:指定一个摄像机,UI被渲染在该摄像机的特定距离上。适用于世界空间中的UI(如血条)。
- World Space:UI作为一个3D物体存在于场景中。适用于游戏内的显示屏、告示牌。
- Rect Transform:UI的Transform,但多了锚点(Anchors)和轴心点(Pivot)的概念。
- 锚点:决定了UI元素相对于父Canvas或父UI的位置关系。例如,锚点在左上角,那么无论屏幕分辨率如何变化,它都会保持在左上角。这是实现UI自适应布局的关键。笔记里需要保存几个常用锚点预设的截图(如拉伸全屏、居中、贴边)。
- Pivot:元素的旋转和缩放的基准点。
- 事件系统:
EventTrigger组件或实现IPointerClickHandler等接口,来处理UI的点击、拖拽等交互。记录下如何为一个按钮添加点击音效:通常是在按钮的onClick事件监听里,调用AudioSource.Play()。
4. 实战:构建你的第一个可交互Demo并记录
现在,让我们把上面的笔记要点串联起来,完成一个经典的小Demo:“点击屏幕任意位置,在点击处生成一个彩色小球,小球受重力下落并与地面碰撞。” 请在你的笔记中新建一个名为“项目:点击生成物理小球”的页面,并按照以下结构记录:
1. 项目目标与拆解
- 目标:实现鼠标点击交互与物理对象生成。
- 拆解任务:
- 检测鼠标点击事件。
- 将鼠标的屏幕坐标转换为游戏世界坐标。
- 在目标位置动态创建一个球体。
- 为球体添加物理属性和随机颜色。
2. 实现步骤与代码
- 创建地面:在场景中创建一个Plane或Cube,缩放作为地面,添加
Collider。 - 创建生成器脚本:创建一个空物体,命名为“BallSpawner”,并挂载以下脚本:
using UnityEngine; public class BallSpawner : MonoBehaviour { public GameObject ballPrefab; // 将在Inspector中拖入预制体 public Camera mainCamera; // 主摄像机引用 void Update() { // 1. 检测鼠标左键点击 if (Input.GetMouseButtonDown(0)) { // 2. 获取鼠标在屏幕上的位置 Vector3 mouseScreenPos = Input.mousePosition; // 重要:鼠标的Z轴需要设置为摄像机到生成平面的距离 // 假设我们在世界平面Z=0上生成,摄像机在Z=-10,那么距离就是10 mouseScreenPos.z = 10f; // 这个值需要根据你的摄像机位置调整 // 3. 将屏幕坐标转换为世界坐标 Vector3 spawnWorldPos = mainCamera.ScreenToWorldPoint(mouseScreenPos); // 4. 实例化小球预制体 GameObject newBall = Instantiate(ballPrefab, spawnWorldPos, Quaternion.identity); // 5. (可选) 为小球添加随机颜色 Renderer ballRenderer = newBall.GetComponent<Renderer>(); if (ballRenderer != null) { ballRenderer.material.color = new Color( Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f) ); } } } }- 创建小球预制体:
- 在场景中创建一个Sphere,为其添加
Rigidbody组件(使其受重力)。 - 添加一个
Collider(Sphere Collider已默认存在)。 - 将这个Sphere从Hierarchy拖到Project窗口的
Assets/Prefabs文件夹中,这样就创建了一个预制体。 - 删除场景中的原始Sphere。
- 将Project中的这个Ball预制体,拖到
BallSpawner脚本的ballPrefab公开字段上。
- 在场景中创建一个Sphere,为其添加
3. 笔记中的“为什么”与“坑点”
- 为什么
mouseScreenPos.z很重要?ScreenToWorldPoint需要知道屏幕上的点对应世界空间中的哪条射线。Z值代表从摄像机出发,沿视线方向的距离。设置一个合适的正值,才能在世界中找到一个确定的点,而不是摄像机的位置(Z=0)。 - 如何确定Z值?一个简单的方法是:在
Start方法里,打印出你想要生成物体的平面的世界坐标,然后用Camera.WorldToScreenPoint反算一下Z方向的深度。或者,更通用的做法是,从摄像机发射一条射线(Raycast)到鼠标位置,用碰撞点作为生成位置,这样更精确,尤其适用于3D场景中非平面的位置。 - 预制体的好处:在这个Demo中,我们配置好一个带物理属性的小球后,将其保存为预制体。之后无论生成多少个,它们都拥有完全相同的初始组件和属性,保证了行为一致性,也方便统一修改。
5. 高频问题排查与进阶学习路径
5.1 你一定会遇到的五个问题及解决思路
脚本挂了,但Inspector里没显示/变量值不保存?
- 检查:脚本文件名必须与类名完全一致(包括大小写)。如果修改了类名,必须同步修改文件名。
- 检查:脚本编译错误。查看Console窗口(Window -> General -> Console),所有错误必须解决后,脚本才能正常加载。
- 检查:是否在非
Awake/Start/OnEnable的生命周期方法外,试图在编辑模式下访问或修改还未初始化的对象?
物体穿墙了(碰撞失效)?
- 检查:双方都有
Collider吗?至少一方有Rigidbody吗?(对于Collision检测是必须的)。 - 检查:
Collider的Is Trigger是否被误勾选了?勾选后只会触发OnTrigger事件,不会产生物理阻挡。 - 检查:物体移动速度是否过快?如果一帧内移动的距离超过了
Collider的尺寸,就可能“穿透”。对于高速移动的物体(如子弹),可以考虑使用Rigidbody的连续碰撞检测(Collision Detection属性设置为Continuous或Continuous Dynamic),或者使用射线检测来预判碰撞。
- 检查:双方都有
NullReferenceException(空引用异常)- 这是Unity新手的第一大杀手。意思是你在尝试访问一个为
null(空)的对象。 - 排查:在Console里点击错误信息,它会定位到出错的代码行。
- 常见原因:
- 在Inspector中公开引用的字段(如
public GameObject target;)没有拖入任何对象。 - 使用
GameObject.Find或GetComponent没有找到目标,返回值是null,但你直接使用了它。 - 对象已经被
Destroy了,但你的代码还在试图访问它。
- 在Inspector中公开引用的字段(如
- 防御性编程:在使用一个可能为
null的引用前,先判断if (target != null) { ... }。
- 这是Unity新手的第一大杀手。意思是你在尝试访问一个为
UI元素看不到或者位置奇怪?
- 检查:Canvas的
Render Mode是否适合你的需求? - 检查:UI元素的
Rect Transform锚点设置是否正确?尝试将锚点设置为“拉伸(Stretch)”,然后调整边距,看看是否能铺满父级。 - 检查:UI元素的层级顺序,后绘制的会盖在先绘制的上面。可以通过调整Hierarchy中的上下顺序,或修改
Canvas Renderer的Sort Order。
- 检查:Canvas的
游戏发布后运行速度很慢?
- 初步排查:在编辑器里打开
Window -> Analysis -> Profiler。这是性能分析的神器。运行游戏,观察CPU和GPU的占用情况,找到最耗时的函数或渲染调用(Draw Call)。 - 常见性能瓶颈:
- Draw Call过高:每个使用不同材质的物体都会产生一个Draw Call。可以通过合并使用相同材质的静态物体(Static Batching)、使用图集(Sprite Atlas)来减少。
- 脚本效率低:在
Update中进行了复杂的计算、频繁的Find或GetComponent调用、没有对象池而频繁Instantiate/Destroy。 - 物理计算复杂:场景中动态刚体过多、网格碰撞体复杂。
- 初步排查:在编辑器里打开
5.2 如何让你的笔记和技能持续进化
当你完成了基础Demo,笔记里也积累了一定内容后,如何避免停滞不前?
- 制定迷你项目挑战:不要一直跟着教程做。给自己定一个每周小目标,例如“用一周时间,做一个Flappy Bird的克隆体”。在这个过程中,你会主动去搜索“如何让小鸟下坠”、“如何生成无限循环的管道”、“如何计分”,这些主动寻找答案的过程,会让知识记得更牢,并且你会把解决过程详细记录在“项目笔记”里。
- 阅读官方手册与API文档:教程是“鱼”,官方文档是“渔”。当你对一个组件或API有疑问时,第一反应应该是去Unity官方文档(
https://docs.unity3d.com/)查看。尝试阅读Rigidbody或Vector3的文档,虽然一开始可能枯燥,但你会获得最准确、最全面的信息。把重要的参数说明和示例代码片段摘录到你的概念笔记中。 - 拆解Asset Store上的免费项目:Unity Asset Store有海量的免费示例项目和资源。下载一个简单的2D或3D游戏Kit,在编辑器中打开,反向工程它的场景结构、脚本组织、资源管理方式。这是学习最佳实践的捷径。把你的分析心得记下来:“这个项目用单例模式管理游戏状态”、“它用ScriptableObject来配置敌人属性,很灵活”。
- 建立你的“代码工具箱”:随着学习深入,你会积累一些通用的工具脚本,比如:
Singleton<T>:一个泛型单例模式基类,用于管理器(如GameManager, AudioManager)。ObjectPool:一个简单的对象池,用于管理频繁创建销毁的物体(如子弹、特效)。ExtensionMethods:一些实用的C#扩展方法,比如transform.SetPositionX(float x)。 把这些脚本整理好,放在一个独立的Unity工程里,或者记录在笔记的“工具箱”章节,并附上使用说明。未来开始新项目时,直接拿来就用,效率倍增。
学习Unity,或者说学习任何一项复杂的技能,都是一个“学习-实践-记录-反思-再学习”的循环。这份“入门笔记汇总”,就是你在这个循环中为自己搭建的脚手架。它开始可能简陋,但随着你项目的复杂、问题的深入、解决方案的积累,它会逐渐成长为一个充满你个人智慧和经验的宝藏库。记住,最好的笔记不是最漂亮的,而是你最常回头去看、去用的那一个。现在,打开Unity,创建一个新场景,然后打开你的笔记软件,开始你的第一次有目的的记录吧。从“今天,我让一个Cube跳了起来,并弄明白了为什么必须乘以Time.deltaTime”开始。