☰
Unity2021第三人称场景制作教程:从角色控制到光照美化
2026/9/26 12:11:18 网站建设 项目流程

简介:一份基于Unity 2021打造的期末作业——第三人称漫游精美场景模型,面向游戏开发初学者、在校学生,可用于完成大作业或系统学习Unity项目开发全流程。资源内含山谷、房屋、桌椅等精细3D场景模型,实现鼠标控制小狐狸移动、血条、菜单与按钮等交互,覆盖角色控制器、场景搭建、UI布局等多模块。压缩包文件总数两千个,包含一千五百九十六个脚本、一百五十四个模型、一百一十九个预制体、八百一十二个贴图、四十七个场景文件及动画、音频等资产,整体约一百一十三兆,目录结构清晰,便于按模块检索与复用。已有两千九百九十人学习或下载。通过该工程可掌握第三人称视角实现、移动控制、界面与材质渲染等技能,为独立游戏开发积累实战经验。

1. 用 Unity2021 做第三人称精美场景:这个期末作业到底在考什么

第三人称漫游是 Unity 期末作品里最稳的“大综合题”:它强制你同时处理角色控制器、镜头跟随、场景布局、光照和碰撞,任何一个环节断了,交上去的演示视频都会露怯。很多同学做这个题目的翻车点不在代码,而在“角色走了两步就穿模”“场景模型摆得像仓库”“灯光一开画面惨白”。这篇笔记就按 Unity2021 版本的固定路径来讲:先在空场景里把第三人称控制器跑通,再往里面做地形和视觉,最后把曝光、阴影、碰撞这些细节收干净。适合准备期末作业的同学照着复现,更适合已经装了 Unity2021 但不知道从哪下手的从业者顺一遍思路。

2. 先让“能走的角色”活起来:第三人称控制器的搭建顺序

2.1 为什么我推荐直接用 CharacterController,而不是 Rigidbody

第三人称角色移动,Unity 里无非两条路:Rigidbody 物理驱动,或者 CharacterController 参数驱动。期末作品我一般建议直接用 CharacterController。Rigidbody 要处理摩擦力、质心、碰撞材质,调起来很玄学,角色一受外力就原地转圈。CharacterController 本质是“胶囊体 + 简化物理”,它自己处理坡度、台阶、碰撞,你只需要给速度向量。

下面是最小可用的第三人称移动脚本,基于相机朝向计算移动方向,角色自动转向:

using UnityEngine; public class ThirdPersonMovement : MonoBehaviour { public float moveSpeed = 4f; public float rotateSpeed = 8f; public float jumpHeight = 1.2f; public Transform cameraTransform; private CharacterController controller; private float verticalVelocity; private const float GRAVITY = -9.81f; void Start() { controller = GetComponent<CharacterController>(); Cursor.lockState = CursorLockMode.Locked; } void Update() { float h = Input.GetAxis("Horizontal"); float v = Input.GetAxis("Vertical"); // 关键一行:把镜头朝向投影到地面,再把输入映射成世界方向 Vector3 forward = cameraTransform.forward; Vector3 right = cameraTransform.right; forward.y = 0f; right.y = 0f; forward.Normalize(); right.Normalize(); Vector3 moveDir = (forward * v + right * h).normalized; // 角色朝向缓动,避免原地甩头 if (moveDir.magnitude > 0.1f) { Quaternion targetRot = Quaternion.LookRotation(moveDir); transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, rotateSpeed * Time.deltaTime); } // 重力与跳跃 if (controller.isGrounded && verticalVelocity < 0f) { verticalVelocity = -2f; // 轻微负值防止贴地抖动 } if (Input.GetButtonDown("Jump") && controller.isGrounded) { verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * GRAVITY); } verticalVelocity += GRAVITY * Time.deltaTime; Vector3 finalMove = moveDir * moveSpeed; finalMove.y = verticalVelocity; controller.Move(finalMove * Time.deltaTime); } }

这个脚本的逻辑顺序要讲清楚。先取相机 forward 和 right 并去掉 y 分量,是为了让“按 W 往前走”永远朝着镜头看的方向,而不是世界坐标的死方向。这是第三人称漫游和第一人称最大的区别:玩家在旋转视角时,操控角色的移动方向会跟着变。如果漏了forward.y = 0f,角色在上下坡时会往天空方向飘,这是我见过最多的第三人称翻车现场。

参数上,moveSpeed在校园场景里建议 3 到 5,太快镜头跟不上,太慢画面显得拖沓。rotateSpeed是 Slerp 的插值速度,8 到 12 比较跟手;低于 3 角色会像按了减速带。跳跃高度看场景有没有平台设计,没有就保持在 1.0 到 1.2,太高会破坏地面碰撞的信任感。

2.2 Cinemachine 免费相机:为什么漫游体验一半靠镜头

角色能走了,镜头如果还挂在 Update 里手动写,会出现一个典型的“拖尾感”:角色停住镜头还在晃,或者角色转弯镜头甩过头。Unity2021 版本里,Cinemachine 已经是成熟的官方式包,光标跟随用 FreeLook 相机,几乎不需要写代码。

操作顺序我习惯这样走。先给角色加一个空物体做为“头部锚点”,位置在角色头顶上方 0.5 米;然后从菜单Cinemachine -> Create FreeLook Camera创建相机,把Follow拖到角色根节点,把Look At拖到这个锚点。接下来要改三个关键参数:

  • Orbitals里的三个轨道半径,建议是 4 / 6 / 8,分别控制近距离、中距离、远距离的环绕半径,半径越小越容易穿过墙壁。
  • Orbital Y的角速度默认 3,现场候会感觉到迟钝,改成 4 到 5,追角色更直接。
  • Lookahead Time设成 0.3 到 0.5,镜头会预测角色的运动方向,显得平滑。

FreeLook 相机不写成代码有三个实打实的好处:它自动处理角色转身时的镜头插值;它内置了“避免遮挡”逻辑,角色靠近 May 起来时会自动回拉;它支持不同高度轨道切换,方便展示场景的高低差。期末演示里,你用鼠标右键拖动镜头环绕角色,一瞬间就能让评审意识到场景模型的立体感。这个“环绕感”是手动 Camera 脚本很难做出来的。

2.3 输入系统:Unity2021 用旧的 Input Manager 足够应付作业

Unity2021 同时支持老版 Input Manager 和新版 Input System 包。新版功能更全,支持手柄震动、组合键、Action 资产,但它有一个显著代价:你需要在 Inspector 里配置 Input Action 资产,然后再写一个 PlayerInput 组件来激活它。对期末作品来说,这个学习曲线不值当。

我的建议是:项目创建时选择Input System Package (Active)会造成麻烦,就直接在Edit -> Project Settings -> Player -> Active Input Handling里选择Both或Input Manager (Old)。上面脚本里出现的Input.GetAxis和Input.GetButtonDown都是旧输入接口,默认情况下它们在 Unity2021 中是可用的,不用做任何额外配置。

如果在Project Creation Wizard里选了“URP + 新输入系统”的模板,新建工程后去Window -> Package Manager里确认安装的是哪个输入包。如果已经装了Input System且冲突,就把Active Input Handling切到Both,新旧接口可以共存。这是很多同学工程崩的起点:角色脚本没错,但 API 被新输入系统屏蔽了,Move 方法完全不响应。

3. 从素材堆到场景模型:精美场景的铺设与光照全流程

3.1 先画漫游动线再放模型:场景设计的唯一原则

拿到一堆精美场景模型后,最容易犯的错是“哪里好看往哪放”。结果角色一出生就卡在树桩里,或者要穿过一条毫无引导的暗巷。我的习惯是:先在纸上画一条 L 形或 U 形动线,明确玩家从出生点走到终点时,视线会被哪些物体引导。

动线决定了场景骨架。以“森林小径 + 废墟遗迹”这个经典题材为例,我会规划三段式:出生点是一个开阔的草地,引导玩家看向远处的高塔;中段是一条两侧有高墙或树木的窄路,玩家必须转弯才能看到下一个目标;终点是一个有高低差的废墟台阶,让镜头能从上往下俯拍全景。

这条动线直接决定了场景模型的摆放密度。靠近动线的模型要控制数量,留出视线通透区;远离动线的背景模型可以密集摆放,用来填充视野边缘。判断标准很简单:在 Game 视图里用 Cinemachine 拉一圈,如果视线任何角度都能看到 3 个以上的视觉锚点,场景就不显得空。

3.2 用 Unity2021 地形工具打底,再用手工模型做细节

Unity 自带地形工具,在GameObject -> 3D Object -> Terrain创建。地形有两个主要用途:第一是提供大范围的高度起伏,第二是刷纹理让地面材质过渡自然。地形建模时我会把分辨率控制在Terrain Settings -> Resolution下,Map Size 用 200x200,Detail Resolution 不用拉满,保持默认 1024,不然地形烘焙会慢到吃午饭。

刷地形纹理要遵循“明暗分区”。先刷一层大面积的草地作为基底,然后选一张泥土或碎石纹理,用Brush Size在路径边缘和树根附近刷第二层。不推荐做第五层、第六层纹理叠加,那是给大团队做的精细地表,期末场景时间不够,而且多层纹理在低端显卡上可能会出现明显的spread闪烁。

地形的坑在树和草。Unity 地形树用Paint Trees刷,草用Paint Details刷,这两项对性能的消耗远大于手工摆放。我的建议:漫游路线两侧的树用手工Prefab摆放,并且给每一棵树套一个LOD Group;远处的树群才用 Terrain 的批量刷写。

手工模型负责“记忆点”。买来的场景模型包里通常有巨石、断柱、栅栏,这些物体不要随机旋转摆放,要按“三角形构图”放:三个主要物体成 L 形布局,中间夹一条可走的路,模型之间互相遮挡一部分。核心 ComfyObject 于营造层次感,这些手动放置的模型,才是期末评分时让画面有质感的来源。

3.3 URP 管线选择:为什么画面发灰先查渲染管线

Unity2021 项目创建模板里有一项3D Sample Scene (URP),这个顺手选的选项决定了后续所有场景模型看起来什么样。URP(通用渲染管线)对光照和后期效果的表现比内置渲染管线的默认效果更好,色调偏写实。但如果你是从网上下载的老模型包,材质多半是Standard Shader写的,在 URP 下会变成粉红色或紫色。

检查方法很直观:选中角色或场景物体,如果材质显示为洋红色,说明模型材质不兼容 URP。解决办法有两个。第一个是不要切换管线,新建项目时选3D Sample Scene (Built-in)模板,继续用内置渲染管线,老材质完全兼容,只是画面质感要靠自己调。第二个是坚持用 URP,把所有模型的 Shader 手动换成Universal Render Pipeline/Lit,并重新配置Albedo、Metallic、Smoothness贴图。

我的实际建议:如果是买的精品场景模型,优先用内置管线。这些模型的贴图、法线、粗糙度都是为 Built-in 调好的,换到 URP 后皮肤发灰、树叶透光,需要大量手工校正。如果是从头开始新建场景素材,再考虑 URP。因为 URP 的全局雾、环境光、后处理管线更顺手,问题是重新配置材质的成本。期末时间紧,不要临阵换管线。

3.4 光照三步走:方向光、补光、烘焙

第一步是方向光。GameObject -> Light -> Directional Light创建太阳,Rotation 大概设置在 X 轴 45 度、Y 轴 -30 度左右,这个角度会在地面拉出长阴影,物体立体感最强。阴影选项里Shadow Type选 Soft Shadows,Strength保持 0.9 左右,不要拉满,否则阴影会死黑。

第二步是补漫反射。一个方向光会让背光面全黑,更像夜晚而不是白天。在场景反方向加一个Directional Light当作天光,强度设成 0.3 到 0.5,颜色偏淡蓝或淡紫,Shadow Type设No Shadows。这层补光的存在感很弱,但能保住暗部细节。这个补光在烘焙时要开着,否则完全体烘焙后暗部全是黑的。

第三步是选择烘焙还是实时光照。Unity2021 的Window -> Rendering -> Lighting窗口里,如果选Mixed Lighting,静态物体可以烘焙出高精度的阴影,动态角色保留实时阴影。我建议先尝试这个模式。具体步骤:所有地形、树、建筑、石头的Static勾选;把灯光 Mode 设为Mixed;关闭窗口里的Auto Generate,手动点Generate Lightning。烘焙完成后,光照贴图会替代实时阴影,运行时有明显的性能提升。

光照贴图的常用参数:Lightmapper选Progressive GPU(如果你的显卡是近五年产出的),Direct Samples和Indirect Samples默认即可,关键是把Backface Tolerance调低到 0。如果场景出现大面积漏光,大部分原因是模型不是封闭体:给墙体底部加一个平面MeshCollider,漏光现象会显著减少。这一部分特别看耐心,多试几次烘焙你是不会亏的。

4. 把散步变成漫游:动画、交互与碰撞的整合

4.1 角色动画状态机:一个 BlendTree 搞定走路和跑步

第三人称漫游的角色动画不需要炫技,因为焦点在“漫游”场景。一个功能完整的 Locomotion 状态机只需要一个BlendTree:结合一个 speed 参数,在 0 和 1 之间混合 Idle、Walk、Run 三个动作剪辑。

创建步骤:导入角色模型,在Models文件夹选中角色,进Animation页签设置好 Rig 的 Animation Type 为 Humanoid。然后在 Characters 目录下创建一个Animator Controller,双击打开后右键创建Blend Tree,双击进入这个树。树的参数表这样配:

  • Idle 动作:Motion是Idle动画,Threshold设为 0
  • Walk 动作:Motion是Walk动画,Threshold设为 0.6
  • Run 动作:Motion是Run动画,Threshold设为 1

最后在运动脚本里把实际速度映射成参数。代码改动很小:

Animator animator; float speedParameter; void Update() { float currentSpeed = new Vector3(controller.velocity.x, 0f, controller.velocity.z).magnitude; float targetSpeed = currentSpeed / 4f; // 4 对应 moveSpeed speedParameter = Mathf.Lerp(speedParameter, targetSpeed, Time.deltaTime * 6f); animator.SetFloat("speed", speedParameter); }

这段逻辑的节奏是:controller.velocity已经包含了重力,所以一定要单独提取水平分量的速度;/ 4f是为了把 4 米每秒对应成参数 1,让 blend 阈值能对上。Lerp是给动画一个过渡缓冲,否则参数跳变时角色会从走瞬间切到跑。

动画不出这个动作,先检查 Animator Controller 引用是否挂在了角色模型的根骨骼上。很多人把 Animator 组件挂在角色的空物体上,动画能播但角色位置与动画偏移。正确做法是 Animator 挂在带 SkinnedMeshRenderer 的模型节点,不要和 CharacterController 挂在同一个根节点。

4.2 场景碰撞:MeshCollider 的正确打开方式

场景里一堆精美场景模型,如果角色走过去直接穿模,再好看的画面也白费。碰撞体方案优先级是这样排的:地形自带碰撞;石头、断墙、平地用BoxCollider或CapsuleCollider;形状复杂且需要精确碰撞的古树、雕像,用MeshCollider,但一定要等 XML 注释。

原因很简单:MeshCollider 的Convex(凸体)选项,只有勾选 Convex 才能参与物理碰撞,但凸体要求网格必须是凸的。你对一棵树勾 Convex,树的树冠部分会被暴力压缩成一个凸包,角色会被空气墙挡住。不勾 Convex,MeshCollider 只能做静态碰撞,动态物体穿过去。正确做法是:树和复杂装饰物,用若干个球形碰撞体围柱形包裹,树冠不需要碰撞;角色碰到树干不会穿透就够了。

碰撞体的一个细节坑:MeshCollider 属于静态碰撞,它不能挂在带刚体的动态物体上。如果你的场景模型只要有群铺在地板上手动摆放,就全部设为Static,不要让它们的 Transform 在运行时报 Cody 移动。否则MeshCollider会一直报non-convex MeshCollider with non-kinematic Rigidbody的错误日志。

4.3 触发器小事件:让漫游不只是走路

期末作品要体现“交互维度”,在漫游路线上加两三个触发事件很有必要。最常见的是触发对话、开门、播一段过场。我用过一个很轻量的方案:在特定位置放一个TriggerZone,玩家进入后播放一个动画和一段音频,让场景“活”起来。

using UnityEngine; public class StoryTrigger : MonoBehaviour { public GameObject door; public AudioClip openSound; private bool triggered = false; void OnTriggerEnter(Collider other) { if (!triggered && other.CompareTag("Player")) { triggered = true; door.GetComponent<Animator>().SetTrigger("Open"); AudioSource.PlayClipAtPoint(openSound, transform.position); } } }

这个脚本的要点:triggered布尔锁保证触发只发生一次;CompareTag判断是否是玩家,避免敌人或杂物触发;AudioSource.PlayClipAtPoint是临时播放,不需要在场景里预制 AudioSource。触发器区域用BoxCollider设成Is Trigger,尺寸在漫游路径宽度的一半,不要太大会让玩家还没靠近就提前触发。

5. 避坑手册:Unity2021 第三人称场景的六个常见问题

5.1 角色从坡顶滑下来或者卡在台阶边

现象:上坡时角色速度很慢,或者完全推不动,停在坡沿;下坡时角色突然加速滑落。

原因:CharacterController 的Slope Limit默认 45 度。坡度超过这个值,控制器会认为那是墙,不让你走。下坡加速则是Step Offset太小,角色的小腿没有跨过台阶的能力时物理引擎就直接降速。

解决:把Slope Limit加到 60 度,Step Offset调到 0.3 到 0.5 米。这能解决绝大多数地形台阶问题。如果是美术精心做的台阶模型,可以故意把 Step Offset 调大,让角色“自动踩上去”。

5.2 镜头穿墙或镜头怼到角色脸上

现象:在狭窄通道里,FreeLook 相机穿过墙壁,画面一半是墙体;角色贴墙后镜头被压到角色正面 10 厘米处。

原因:Cinemachine FreeLook 的Camera Collider参数没调。它虽然自带防遮挡,但默认的Radius太短,墙壁太薄的话判定容易失败。

解决:选中 FreeLook 相机,找到Body一节里的Camera Collider,把Radius从 0.1 加到 0.3 以上;把Collide Against只勾选场景的Ignore Raycast层。注意把墙和地形的 Layer 设置成Default,否则 collider 连地形都检测不到。

5.3 烘焙后场景全黑,阴影死黑一片

现象:点完 Generate Lighting,场景变成一团黑,只有角色周围亮一点。

原因:光照贴图烘焙时,场景的 Ambient Occlusion 和间接光强度设得太低。默认的AO强度是 0 到 1,但你真的调到 1,阴影里就完全没细节了。另一个原因是方向光Intensity设得太低,低于 0.5 时烘焙光照几乎全黑。

解决:为烘焙准备三个阶段,方向光强度至少 1.0;Lighting 窗口里Environment Lighting -> Ambient Intensity设为 1;Lightmapping Settings -> Ambient Occlusion勾选后,Indirect Contributions保持默认,Direct Contributions调成 0.6。这三项配合后烘焙出来的阴影是深灰色,而不是死黑。

5.4 模型加载后是紫红色

现象:从网上下载的场景模型拖进场景,整个模型变成亮紫色。

原因:材质 Shader 不兼容。项目用了 URP,模型 Shader 是 Built-in Standard,CRP 在 2021 下不支持直接映射到 URP/Lit。

解决:要么按前文说的回退到内置渲染管线;要么在 URP 下挨个换 Shader。一个省事的中间方案:用Window -> Rendering -> Render Pipeline Converter,选Built-in to URP然后勾选Material Upgrade,批量把材质升级。注意升级后再看贴图里的Normal Map是否丢失,URP 升级经常把法线贴图弄丢。

5.5 动画和移动方向不一致

现象:角色模型向前走,动画却在原地踩四五个步;或角色转向有个明显的“闪腰”动作。

原因:角色模型在导入时的朝向前方(forward)没校正。Unity 的内置Humanoid期望模型面向 Z 轴正方向,如果你的模型是面向 X 轴建模,它的 Animator 会把 90 度偏转算进去,旋转时就会先转 90 度让你看到侧面。

解决:选中角色模型,进Models页签里的Rig,在Avatar Definition用Humanoid,然后改Model Importer的Rotation,把X、Y、Z的三轴偏移调好,让模型在预览里正对 Z 正方向。这个需要每个模型单独检查,建议所有角色模型统一在导入器里校正到正前方向,不要在设计游戏中手动补。

5.6 角色掉出地图

现象:角色走着走着直接穿过地形,站在地形下方,镜头拍出一个剖面视角。

原因:地形 Collider 没开启,或者地形高度图生成时有问题。地形默认带TerrainCollider,但如果你是从外部导入的.obj模型,它的碰撞体可能没有生成。

解决:针对手工导入的模型,进入编辑器检查 Inspector 里是否有MeshCollider组件。如果没有,手动添加并勾选Convex(地形模型一般是一个整体大网格,勾选 Convex 会得到正确的凹陷避让效果)。不推荐用地形替代外部模型,地形的 Collider 在复杂交叉面上经常出现漏缝。

6. 期末展示前花两小时做的三件事:性能、视角与演示动线

第一件,给每个大模型挂 LOD Group。Unity2021 的LOD Group支持在场景里右键添加,但手动给每个石头、树挂一个太累。更高效的办法是:选中所有同类模型,在Model Importer的LOD Group页签里生成 3 级 LOD(100%、60%、20%)。运行时角色远离模型,引擎自动切换低模,帧率立刻上来。期末演示最怕画面卡顿,卡顿一两次,评审对“精美”的判断会直接降档。

第二件,做一次遮挡剔除和遮挡区域烘焙。在Windows -> Rendering -> Occlusion Culling里,把场景中静态物体的Static勾选后,手动给主要建筑和地形生成Occlusion Areas。然后在 Game 视图里跑全路线,可以看到渲染SetPass calls掉一半。这一步不需要写代码,只需要勾选和点按钮,但收益非常明显。

第三件,录制一段有动线的演示路径。我的习惯是:在游戏编辑器中用Time.timeScale = 0暂停运行,手动调整 Cinemachine FreeLook 的角度,找到两个能展示场景全景的角度;然后用快捷键Ctrl+Shift+F10打开 Game View 面板,播放一段角色自动沿导航路径行走的NavMeshAgent路径。设置方法:给地面加 NavMeshSurface,角色加 NavMeshAgent,指定一个终点坐标。这样在答辩现场,角色会自动走完全程,你只需要在旁边拖动视角,展示场景细节。这段自动漫游的路径,能在导播演示时极大提升“场景模型”的观赏性。

做这个项目的完整体验总结起来就一句话:人物和场景的配合、碰撞和相机的配合,才是第三人称漫游真正的门槛。我每做一个期末场景,都会在提交前拿第三人称视角完整跑一遍动线,看着看着就会发现哪面墙反了、哪个树挡住视线了。这个习惯帮我避免过至少三次在评审现场翻车。希望这篇笔记能帮你在 Unity2021 里少走这几段弯路,顺利交出你自己的精美场景作品。

本文还有配套的精品资源,点击获取

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

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

立即咨询