多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Unity2021第三人称漫游大作业:从跑通到避坑的完整指南

Unity2021第三人称漫游大作业:从跑通到避坑的完整指南 简介这份资源是面向Unity初学者与在校学生的期末大作业级项目基于Unity2021版本实现第三人称漫游玩法帮助读者掌握角色控制、场景搭建与UI交互等核心开发流程。压缩包共约2000个文件以cs脚本、png贴图、fbx模型、prefab预制体、unity场景与mat材质为主另含anim动画、shader着色器及uss/uxml界面文件整体约113MB结构完整便于直接导入学习。项目内含山谷、房屋、桌椅等精细场景模型通过鼠标控制小狐狸移动并配有血条、菜单与按钮等UGUI元素覆盖CharacterController移动、光照与PBR材质、Animator动画及Input Manager输入管理等知识点。目前已有2994人学习下载适合作为课程设计参考或Unity入门实战案例帮助读者快速理解第三人称漫游项目的完整组织方式与实现思路。1. 从一份 Unity2021 期末作业说起第三人称漫游到底能跑出什么效果如果你正在搜「unity3d unity游戏 大作业」大概率是两种情况要么课程期末要交一个能跑、能看、能演示的完整场景要么想找一个结构清晰、能拆开学的第三人称漫游工程。这份资源就是围绕这个需求做的Unity2021 版本第三人称视角角色在包含山谷、房屋、桌椅等元素的场景里自由行走鼠标控制朝向带血条、菜单和按钮。它不是那种只有空场景加一个胶囊体的教学 Demo而是把角色控制器、场景模型、光照材质、UI 交互这几块都串起来了。适合两类人新手拿来对着改参数、换模型熟手拿来拆结构、看别人怎么组织一个能交作业的完整工程。下面按「先跑起来、再拆模块、最后避坑」的顺序讲。2. 把工程跑起来Unity2021 打开后先做哪几件事2.1 版本对齐与首次导入的等待成本拿到一个 Unity 工程第一件事不是急着点 Play而是确认版本。这份资源标注的是 Unity2021常见做法是用 2021.3 LTS 打开因为 LTS 对第三方模型和旧版 API 的兼容更稳。如果你用 2022 或 2023 打开Unity 会提示升级 API这时候先别点确认尤其是用了旧版 Input Manager 的项目升级后输入配置可能被重置。导入过程有个容易被忽略的点首次打开时 Unity 会重新导入所有资源、生成 Library 文件夹模型多的时候这一步可能卡几分钟到十几分钟进度条不动不代表死机看硬盘灯还在闪就等着。导入完成后先看 Console 有没有红色报错黄色警告可以先放一边。提示导入前把工程放在纯英文路径下中文路径在部分 Unity 版本里会导致模型引用丢失这是血泪经验。2.2 场景层级与关键对象的定位打开工程后在 Project 窗口的 Scenes 文件夹里找主场景双击加载。加载后先看 Hierarchy 面板一个典型的第三人称漫游工程层级大概长这样层级对象作用常见组件Main Camera跟随角色的视角Camera、跟随脚本Player / 角色根节点承载角色逻辑CharacterController、脚本角色模型子节点只负责显示Animator、SkinnedMeshRendererEnvironment山谷、房屋、桌椅MeshRenderer、ColliderCanvas血条、菜单、按钮Canvas、Slider、ButtonDirectional Light主光源Light这里有个关键区分角色根节点和模型子节点要分开。根节点挂 CharacterController 和移动脚本模型子节点只挂 Animator 和渲染组件。很多人把 CharacterController 直接挂在带 Animator 的模型上结果动画位移和控制器位移打架角色走起来一抖一抖的。2.3 输入配置与鼠标控制的绑定关系第三人称漫游的鼠标控制核心是鼠标横向移动改变角色朝向纵向移动改变摄像机俯仰。这份工程用的是 Unity 旧版 Input Manager在 Edit Project Settings Input Manager 里能看到 Horizontal、Vertical、Mouse X、Mouse Y 这几条轴。先确认 Mouse X 和 Mouse Y 的 Type 是 Mouse MovementSensitivity 默认 0.1 左右。如果鼠标转视角没反应八成是这两条轴被改过或者脚本里读的轴名对不上。角色移动一般读 Horizontal 和 Vertical对应 WASD 和方向键。// 角色移动与转向的典型写法挂在角色根节点上 public class ThirdPersonMove : MonoBehaviour { public float moveSpeed 5f; // 移动速度作业场景 4-6 比较跟手 public float rotateSpeed 200f; // 转向速度太低会感觉鼠标拖不动 private CharacterController controller; void Start() { controller GetComponentCharacterController(); } void Update() { // 读取输入轴Horizontal/Vertical 对应 WASD float h Input.GetAxis(Horizontal); float v Input.GetAxis(Vertical); // 按角色自身朝向计算移动方向 Vector3 move transform.forward * v transform.right * h; controller.Move(move * moveSpeed * Time.deltaTime); // 鼠标横向控制角色旋转 float mouseX Input.GetAxis(Mouse X) * rotateSpeed * Time.deltaTime; transform.Rotate(0, mouseX, 0); } }这段代码里 moveSpeed 和 rotateSpeed 是两个最常调的参数。moveSpeed 给 5 左右场景尺度大就往上加rotateSpeed 给 200 左右低于 100 会觉得鼠标转视角很肉。Time.deltaTime 不能省否则帧率一变移动速度就跟着变。2.4 摄像机跟随的两种常见实现摄像机跟随有两种做法一种是摄像机作为角色的子节点简单但转向时会跟着角色一起转视角容易晕另一种是摄像机独立用脚本每帧算位置。这份工程用的是后者常见写法是摄像机保持在角色后上方固定偏移。// 摄像机跟随挂在 Main Camera 上 public class CameraFollow : MonoBehaviour { public Transform target; // 拖入角色根节点 public float distance 5f; // 摄像机与角色的距离 public float height 2f; // 摄像机高度 public float smooth 10f; // 跟随平滑度 void LateUpdate() { // 目标位置角色后方 distance上方 height Vector3 targetPos target.position - target.forward * distance Vector3.up * height; // 平滑插值避免摄像机硬切 transform.position Vector3.Lerp(transform.position, targetPos, smooth * Time.deltaTime); // 始终看向角色 transform.LookAt(target.position Vector3.up * 1.5f); } }用 LateUpdate 而不是 Update是因为要等角色移动完再算摄像机位置否则摄像机会比角色慢半拍。distance 和 height 决定视角远近高低作业演示一般 distance 给 4 到 6height 给 1.5 到 2.5。smooth 给 10 左右太低摄像机拖尾明显太高又失去平滑感。3. 场景模型与光照材质精美感从哪来3.1 模型导入设置与缩放对齐场景里的山谷、房屋、桌椅常见来源是 Blender 或 Maya 导出 FBX 再导入 Unity。导入后第一件事是检查 Scale Factor。不同建模软件单位不一样Blender 默认 1 单位是 1 米导出 FBX 时如果没调导入 Unity 可能大 100 倍或小 100 倍。在模型的 Inspector 里Model 标签页下有 Scale Factor常见值是 1 或 0.01。如果角色和房屋比例明显不对先调这里别急着在场景里手动缩放手动缩放会带来碰撞体和物理计算的连锁问题。另一个设置是 Generate Colliders。环境模型需要碰撞角色才走不穿墙。但桌椅这类小物件如果每个都生成碰撞体场景里碰撞体数量会很多影响性能。常见做法是只给房屋墙体、地面、大块岩石生成碰撞体小装饰物用简单 Box Collider 代替。3.2 光照系统Directional Light 与烘焙的取舍场景要「精美」光照占一半。这份工程用的是 Directional Light 做主光源模拟太阳。在 Lighting 窗口里Environment 部分可以设置天空盒和环境光。关键选择是实时阴影还是烘焙阴影。实时阴影效果好但吃性能烘焙阴影性能好但静态。作业场景如果不需要动态时间变化常见做法是把环境模型设为 Static然后烘焙光照贴图。烘焙参数里 Lightmap Resolution 给 20 到 40 之间太低阴影糊太高烘焙慢且贴图占内存。注意烘焙前确认模型已经设为 Static否则烘焙不会包含它场景里会出现一块亮一块暗的玄学现象。3.3 PBR 材质与纹理的常见配置Unity 的 Standard Shader 是 PBR 材质核心参数是 Albedo、Metallic、Smoothness。Albedo 是基础颜色贴图Metallic 控制金属感Smoothness 控制光滑度。材质类型MetallicSmoothness典型对象木材00.2-0.4桌椅、房屋木结构岩石00.1-0.3山谷、地面金属0.8-1.00.6-0.9装饰金属件布料00.1-0.2窗帘、旗帜一个常见翻车点是贴图导入后颜色发灰或发暗。检查纹理的 sRGB 选项颜色贴图要勾 sRGB法线贴图和粗糙度贴图不能勾。另外纹理的 Wrap Mode 如果是 Repeat平铺时边缘会重复地面这类大面积对象用 Repeat单个物件用 Clamp。3.4 后期处理与画面观感如果工程用了 Post Processing Stack场景观感会明显提升。常见配置是加 Anti-aliasing、Bloom、Color Grading。Bloom 让亮部有光晕Color Grading 调色调。但后期处理在作业演示机上可能拖帧。常见做法是给一个开关演示时开平时关。Post Processing Volume 的 Weight 控制强度给 0.5 到 0.8 之间比较自然给满会过曝。4. UI 与交互血条、菜单、按钮怎么串起来4.1 Canvas 渲染模式与分辨率适配UI 部分的核心是 Canvas。Canvas 的 Render Mode 有三种Screen Space Overlay、Screen Space Camera、World Space。血条和菜单一般用 Screen Space Overlay始终显示在最上层。分辨率适配靠 Canvas Scaler。UI Scale Mode 选 Scale With Screen SizeReference Resolution 设 1920x1080Match 给 0.5这样在不同分辨率下 UI 不会跑偏。如果 Match 给 0 或 1横竖屏切换时 UI 会严重变形。4.2 血条实现Slider 与 Image 的配合血条常见实现是 Slider 组件。Slider 的 Fill Area 里放一个 Image 作为填充Value 从 0 到 1 控制血量百分比。// 血条控制挂在 Canvas 或血条对象上 using UnityEngine.UI; public class HealthBar : MonoBehaviour { public Slider slider; // 拖入 Slider 组件 public Gradient gradient; // 血量颜色渐变 public Image fill; // 拖入 Fill 的 Image public void SetHealth(float current, float max) { // 归一化血量Slider 的 Value 范围是 0-1 float ratio current / max; slider.value ratio; // 根据血量比例改变填充颜色 fill.color gradient.Evaluate(ratio); } }SetHealth 接收当前血量和最大血量内部做归一化。Gradient 让血条从绿到红渐变Evaluate 按比例取色。如果血条不动先检查 Slider 的 Min Value 和 Max Value 是不是 0 和 1很多人改成 0 和 100 后忘了脚本里没乘 100。4.3 菜单与按钮的事件绑定菜单和按钮用 Button 组件OnClick 事件可以拖脚本方法也可以代码绑定。代码绑定更灵活适合动态生成的按钮。// 按钮事件绑定示例 using UnityEngine.UI; public class MenuController : MonoBehaviour { public Button startButton; public GameObject menuPanel; void Start() { // 代码绑定点击事件 startButton.onClick.AddListener(OnStartClicked); } void OnStartClicked() { // 隐藏菜单面板开始游戏 menuPanel.SetActive(false); Time.timeScale 1f; // 恢复时间流速 } }Time.timeScale 是菜单系统的关键。打开菜单时设为 0 暂停游戏关闭时设回 1。如果忘了恢复游戏会一直卡在暂停状态这是新手最常见的翻车点之一。4.4 角色动画与 Animator 状态切换角色移动时要有走路动画这靠 Animator。Animator Controller 里建一个 Blend Tree用 Speed 参数在 Idle、Walk、Run 之间切换。// 动画参数驱动挂在角色模型子节点上 public class PlayerAnimation : MonoBehaviour { private Animator animator; private CharacterController controller; void Start() { animator GetComponentAnimator(); controller GetComponentInParentCharacterController(); } void Update() { // 用控制器速度驱动动画混合 Vector3 horizontalVel controller.velocity; horizontalVel.y 0; animator.SetFloat(Speed, horizontalVel.magnitude); } }Speed 参数在 Blend Tree 里映射到不同动画。如果动画不切换检查 Animator 的 Controller 有没有赋值以及参数名是否和代码里一致大小写敏感。5. 避坑与排查这份工程最容易卡住的五个地方5.1 角色穿墙或掉出场景现象角色走到墙边直接穿过去或者走到地面边缘一直往下掉。原因环境模型没有碰撞体或者 CharacterController 的碰撞层没配对。CharacterController 默认会和所有带 Collider 的对象碰撞但如果模型没生成 Collider就没有阻挡。解决给墙体、地面加 Mesh Collider 或 Box Collider确认 Collider 不是 Trigger。CharacterController 的 Slope Limit 和 Step Offset 也要检查Slope Limit 太低上不了坡Step Offset 太低上不了台阶。5.2 鼠标转视角时角色抖动现象鼠标转视角时角色模型抖动或者摄像机跟着抖。原因角色旋转和摄像机跟随在同一帧里互相影响或者模型子节点有额外旋转。解决角色旋转放在 Update摄像机跟随放在 LateUpdate。模型子节点的 Rotation 保持 0所有旋转由根节点控制。如果用了 Animator 的 Root Motion关掉 Apply Root Motion否则动画位移会和脚本位移叠加。5.3 光照烘焙后场景出现黑斑现象烘焙完成后墙面或地面出现不规则黑斑。原因模型 UV 重叠或光照贴图分辨率不够。UV 重叠导致多个面共用同一块贴图区域烘焙时互相干扰。解决在建模软件里检查 UV 展开确保没有重叠。Unity 里提高 Lightmap Resolution或者给问题模型单独设置更高的 Scale In Lightmap。如果还是不行把该模型设为非 Static用实时阴影。5.4 UI 按钮点不动现象菜单按钮鼠标点上去没反应。原因Canvas 前面有透明 Image 挡住了射线或者按钮的 Interactable 被关了或者 EventSystem 缺失。解决检查 Hierarchy 里有没有多余的透明面板盖在按钮上。确认场景里有 EventSystem 对象没有的话 UI 事件完全不工作。按钮的 Interactable 要勾上。5.5 打包后运行报错或黑屏现象编辑器里跑得好好的打包成 exe 后黑屏或报错。原因场景没有加入 Build Settings或者资源路径用了 Editor 专用 API或者分辨率设置不对。解决File Build Settings 里把主场景拖进 Scenes In Build。检查脚本里有没有用 UnityEditor 命名空间打包后这些 API 不可用。Player Settings 里确认分辨率默认值全屏模式给个合理分辨率。6. 进阶技巧把这份作业改成能拿得出手的作品跑通只是第一步要让这份作业在演示时有点亮点有几个投入产出比高的改法。第一是加一个简单的交互反馈。角色走到可交互物体附近时物体高亮或显示提示。实现方式是给物体加 Collider 设为 Trigger角色进入时改材质 Emission 或显示 UI 提示。这个改动代码量不大但演示效果明显。第二是优化场景性能。打开 Stats 窗口看 Draw Calls 和 Batches如果 Draw Calls 超过 200考虑把静态环境模型合并。Unity 有 Static Batching把环境对象设为 Static 就能自动合并。另外纹理压缩格式选对Android 用 ETC2PC 用 DXT能省不少内存。第三是加一个简单的存档或设置面板。设置面板里放音量滑块和画质下拉用 PlayerPrefs 存。PlayerPrefs 虽然简单但对作业来说够用读取写入就几行代码。// 音量设置存取示例 public class SettingsManager : MonoBehaviour { public void SaveVolume(float volume) { // 存音量到本地 PlayerPrefs.SetFloat(Volume, volume); PlayerPrefs.Save(); } public float LoadVolume() { // 读取默认 0.8 return PlayerPrefs.GetFloat(Volume, 0.8f); } }PlayerPrefs 的坑在于它存的是注册表或 plist卸载游戏不会自动清。作业演示无所谓但如果要反复测试记得在代码里加个清除入口。第四是给角色加个简单的跳跃。CharacterController 自带 isGrounded 判断配合垂直速度就能实现。// 跳跃逻辑接在移动脚本里 public float jumpHeight 1.5f; public float gravity -9.81f; private float verticalVelocity; void Update() { if (controller.isGrounded verticalVelocity 0) { verticalVelocity -2f; // 贴地避免悬空 } if (Input.GetButtonDown(Jump) controller.isGrounded) { // 根据跳跃高度反推初速度 verticalVelocity Mathf.Sqrt(jumpHeight * -2f * gravity); } verticalVelocity gravity * Time.deltaTime; // 把垂直速度并入移动向量 Vector3 move new Vector3(horizontalMove.x, verticalVelocity, horizontalMove.z); controller.Move(move * Time.deltaTime); }jumpHeight 给 1.5 左右gravity 用 -9.81 模拟真实重力。Mathf.Sqrt 那段是根据物理公式反推初速度直接给固定值也行但不同跳跃高度要手调。isGrounded 判断要配合 verticalVelocity 小于 0否则角色落地瞬间会反复触发跳跃。最后说个习惯每次改完参数先在小场景里跑一遍再应用到主场景。我见过太多人直接在主场景调调崩了没有后悔药只能重新导入。从那以后我每次动光照和材质参数前都先复制一份场景做备份确认效果再覆盖。希望帮到你。本文还有配套的精品资源点击获取
返回列表