ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unity 2021第三人称漫游作业:场景搭建、角色控制与性能优化实战

Unity 2021第三人称漫游作业:场景搭建、角色控制与性能优化实战 简介这份资源是面向Unity初学者与高校学生的期末大作业完整工程基于Unity2021版本制作主题为第三人称漫游精美场景适合需要提交课程设计或想系统练习3D游戏开发的学习者。项目围绕角色控制器、3D场景搭建、光照材质、UI系统与输入管理展开包含山谷、房屋、桌椅等模型元素并实现鼠标控制小狐狸移动、血条显示以及菜单按钮交互覆盖从场景布置到交互逻辑的完整流程。压缩包共约2000个文件以cs脚本、png贴图、fbx模型、prefab预制体、unity场景与mat材质为主另有anim动画、shader着色器、uss/uxml界面文件及mp3音效等整体约113MB目录结构清晰便于按模块查阅与二次修改。目前已有2994人学习下载读者可借此掌握第三人称漫游的实现思路、场景美化技巧与UGUI交互方法是兼顾参考与练手的实用工程。1. 从一份 Unity 2021 期末作业说起第三人称漫游到底要交付什么每年期末季问得最多的一类问题就是「unity期末作业-第三人称漫游精美场景模型unity2021版本」这种题目到底怎么落地。它听起来像课程作业实际考的是三件事角色能不能被稳定操控、摄像机能不能舒服地跟随、场景能不能撑住「精美」两个字。很多人一上来就去找 unity 人物模型资源结果模型拖进场景发现没有 Avatar、动画不生效、摄像机穿墙、帧率掉到 20最后交上去的只是一个能走但很别扭的 Demo。这篇笔记面向两类人一类是刚学完 UGUI 和基础脚本、要交作业的学生另一类是想用 Unity 2021 快速搭一个可演示的第三人称漫游原型、后面再往数字孪生或仿真实验方向扩的从业者。我会按「场景怎么搭 → 角色怎么控 → 摄像机怎么跟 → 性能怎么保 → 怎么验收」的顺序讲参数给到能直接抄坑也标出来。整套方案在 Unity 2021.3 LTS 上验证过不依赖任何付费插件用内置的 Character Controller 和 Cinemachine 就能跑通。2. 场景搭建与资源导入把「精美」拆成可执行的清单2.1 先定场景规模再决定资源规格「精美场景模型」最容易翻车的地方是一开始就往里塞高模。第三人称漫游的视角离地面很近玩家会盯着墙面、地面、植被看所以真正影响观感的不是模型面数而是贴图分辨率、法线细节和光照。我的做法是先定漫游范围一个 200m × 200m 的封闭区域用 Terrain 做地形建筑和道具控制在 30 个以内。资源规格上Unity 2021 对移动端和 PC 端的要求差别很大。如果是 PC 演示单模型 5 万面以内、贴图 2K 足够如果要打包到手机单模型压到 1 万面、贴图 1K并且必须做 LOD。下面这张表是我常用的资源验收标准资源类型PC 演示移动端检查项建筑模型≤5 万面≤1 万面是否有 LOD Group植被用 Terrain 笔刷用 GPU Instancer 或合并Draw Call 是否合并贴图2KASTC 压缩1KASTC 6x6Mipmap 是否开启材质Standard / URP LitURP Lit是否共用材质球导入模型时有个高频问题材质变成紫红色。这几乎都是渲染管线不匹配导致的——模型用的是 Built-in 材质而项目建的是 URP。解决办法是 Edit → Rendering → Materials → Convert Selected Built-in Materials to URP全选转换一遍。如果转换后还是紫红检查 Shader 是否被项目裁剪掉了在 Graphics Settings 里把 Always Included Shaders 补上。2.2 用 ProBuilder 快速搭白盒再替换精模直接摆精模效率极低我一般先用 ProBuilder 搭白盒把动线、房间比例、楼梯高度定下来。ProBuilder 在 Package Manager 里搜「ProBuilder」安装即可免费。搭白盒时注意第三人称角色的肩宽约 0.5m门洞至少留 1.2m楼梯每级高度不超过 0.3m否则 Character Controller 的 Step Offset 会卡住。白盒确认后用 Prefab 替换。替换时保持 Transform 的 Position 和 Rotation 不变只换 Mesh。这一步建议用脚本批量做避免手动拖拽出错// 批量把选中物体的 Mesh 替换为指定 Prefab 的 Mesh // 挂在编辑器脚本目录下使用 using UnityEngine; using UnityEditor; public class ReplaceMeshTool : EditorWindow { public GameObject targetPrefab; [MenuItem(Tools/批量替换 Mesh)] static void Open() GetWindowReplaceMeshTool(替换 Mesh); void OnGUI() { targetPrefab (GameObject)EditorGUILayout.ObjectField(目标 Prefab, targetPrefab, typeof(GameObject), false); if (GUILayout.Button(替换选中物体) targetPrefab ! null) { foreach (var go in Selection.gameObjects) { var src targetPrefab.GetComponentInChildrenMeshFilter(); var dst go.GetComponentMeshFilter(); if (src ! null dst ! null) { Undo.RecordObject(dst, Replace Mesh); dst.sharedMesh src.sharedMesh; // 用 sharedMesh 避免材质实例化 } } } } }这段脚本的关键点是sharedMesh用mesh会为每个物体生成独立网格实例内存直接翻倍。参数上targetPrefab选你要替换成的精模选中场景里所有白盒物体后点按钮即可。替换完记得检查 Collider白盒的 BoxCollider 通常比精模的 MeshCollider 性能好能保留就保留。2.3 光照与后处理让场景「看起来贵」的两个开关场景精美度一半靠光照。Unity 2021 默认是实时 GI但期末作业这种静态场景用 Baked GI 更划算。Window → Rendering → Lighting把 Lighting Mode 设为 Baked Indirect然后点 Generate Lighting。烘焙前把不需要参与烘焙的物体设为 Contribute GI 关闭否则烘焙时间会很长。后处理用 URP 自带的 Volume 就行。加一个 Global VolumeOverride 里加 Bloom、Color Adjustments、Vignette。Bloom 的 Threshold 设 1.0、Intensity 设 0.3 左右太高会糊。Color Adjustments 的 Post Exposure 拉 0.2Saturation 拉 10画面立刻通透。注意后处理对性能有影响移动端要把 HDR 关掉Bloom 的 Downscale 设为 Quarter。3. 第三人称角色控制Character Controller 的参数怎么调才不飘3.1 为什么不用 Rigidbody 而用 Character Controller第三人称漫游的核心需求是「指哪走哪、不滑不飘」。Rigidbody 受物理引擎影响在斜坡和台阶上容易打滑还要处理摩擦力、阻力一堆参数。Character Controller 是运动学控制器移动完全由代码控制配合Move()和SimpleMove()能做出很跟手的手感。代价是要自己处理重力但代码量很小。角色模型导入后先检查 Rigby 的 Avatar 是否配置正确。在 Inspector 里选 Rig → Animation Type → Humanoid然后 Configure 一下确保骨骼映射没有红叉。如果模型是 Generic 的动画也能用但 Mixamo 的动画建议转 Humanoid方便复用。3.2 移动脚本把速度、重力、转向拆成独立参数下面这份脚本是我在多个漫游项目里复用的版本基于 Character Controller支持相机相对移动和鼠标转向using UnityEngine; [RequireComponent(typeof(CharacterController))] public class ThirdPersonController : MonoBehaviour { [Header(移动参数)] public float walkSpeed 3.5f; // 行走速度漫游场景 3~4 比较自然 public float runSpeed 7f; // 奔跑速度按住 Left Shift 触发 public float rotationSmoothTime 0.12f; // 转向平滑时间越小越跟手 [Header(物理参数)] public float gravity -15f; // 重力比真实值大一点手感更实 public float jumpHeight 1.2f; // 跳跃高度按空格触发 private CharacterController controller; private Transform cameraTransform; private float verticalVelocity; private float turnSmoothVelocity; void Start() { controller GetComponentCharacterController(); cameraTransform Camera.main.transform; } void Update() { // 1. 读取输入 float h Input.GetAxisRaw(Horizontal); float v Input.GetAxisRaw(Vertical); Vector3 inputDir new Vector3(h, 0, v).normalized; // 2. 相机相对方向 Vector3 moveDir Vector3.zero; if (inputDir.magnitude 0.1f) { float targetAngle Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg cameraTransform.eulerAngles.y; float angle Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, rotationSmoothTime); transform.rotation Quaternion.Euler(0, angle, 0); moveDir Quaternion.Euler(0, targetAngle, 0) * Vector3.forward; } // 3. 速度与重力 float speed Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed; if (controller.isGrounded) { verticalVelocity -2f; // 贴地避免悬空 if (Input.GetButtonDown(Jump)) verticalVelocity Mathf.Sqrt(jumpHeight * -2f * gravity); } verticalVelocity gravity * Time.deltaTime; // 4. 合并移动 Vector3 velocity moveDir * speed Vector3.up * verticalVelocity; controller.Move(velocity * Time.deltaTime); } }逻辑上分四步读输入、算相机相对方向、处理重力、合并移动。参数里walkSpeed和runSpeed的比值建议 1:2差距太小没有奔跑感太大容易穿模。rotationSmoothTime设 0.1~0.15低于 0.1 转向会抖高于 0.2 会感觉角色在漂。gravity用 -15 而不是 -9.8是因为游戏里的重力通常要夸张一点跳跃落地才干脆。3.3 动画状态机Idle、Walk、Run 的过渡条件动画用 Animator 做混合树最省事。新建一个 Blend Tree类型选 1D参数用Speed把 Idle、Walk、Run 三个动画按 0、3.5、7 的阈值摆进去。然后在脚本里把controller.velocity.magnitude传给 Animator// 在 ThirdPersonController 的 Update 末尾追加 float currentSpeed new Vector3(controller.velocity.x, 0, controller.velocity.z).magnitude; animator.SetFloat(Speed, currentSpeed, 0.1f, Time.deltaTime);SetFloat的第三个参数是阻尼时间0.1 秒能让动画过渡更顺。注意controller.velocity是包含重力的所以要把 y 分量去掉再取模否则跳跃时动画会乱切。如果动画出现滑步检查 Walk 动画的播放速度和walkSpeed是否匹配——一般动画自带的位移速度要和脚本速度对齐不对齐就调 Animator 的 Speed 倍率。4. 摄像机跟随Cinemachine 三分钟搞定但参数要改4.1 用 Cinemachine 搭基础跟随Unity 2021 里 Cinemachine 是官方包Package Manager 搜「Cinemachine」安装。场景里右键 → Cinemachine → Create FreeLook Camera它会自动生成一个 FreeLook 相机绑定 Look At 和 Follow 到角色。FreeLook 自带三个轨道Top、Middle、Bottom默认参数就能用但有几个地方必须改。第一把 Follow 的 Binding Mode 设为 World Space否则角色旋转时相机会跟着转晕。第二Lens 的 Field of View 设 60太大边缘会畸变太小视野憋屈。第三Orbital Transposer 的 X Axis 里Speed 设 300 左右Input Axis Name 保持「Mouse X」。Y Axis 的 Speed 设 2Range 设 -30 到 70限制俯仰角防止相机翻到地下。4.2 相机穿墙的三种处理方案第三人称漫游最影响体验的就是相机穿墙。Cinemachine 自带一个 Cinemachine Collider 扩展加上去之后相机会自动避开障碍物。在 FreeLook 的 Extensions 里 Add Extension → Cinemachine Collider参数这样设参数建议值说明Collide AgainstDefault检测层别包含角色本身Minimum Distance From Target0.5离角色最近距离Camera Radius0.3相机碰撞半径Damping Into Collision0.2进入碰撞的缓冲Damping From Collision0.5离开碰撞的缓冲如果场景里有大量薄墙Cinemachine Collider 可能会抖动这时候改用射线检测手动推相机。思路是从角色头部向相机方向发一条 SphereCast打到墙就把相机拉到碰撞点前面。这种方案代码多一点但更可控适合有复杂室内场景的作业。4.3 鼠标锁定与灵敏度漫游场景一般要锁定鼠标按 Esc 解锁。在脚本里加void Update() { if (Input.GetKeyDown(KeyCode.Escape)) { Cursor.lockState CursorLockMode.None; Cursor.visible true; } if (Input.GetMouseButtonDown(0)) { Cursor.lockState CursorLockMode.Locked; Cursor.visible false; } }灵敏度不要写死在 Cinemachine 里而是通过CinemachineFreeLook.m_XAxis.m_MaxSpeed动态调。给玩家一个设置面板滑条范围 100~500默认 300。这样不同人用不同鼠标都能找到舒服的手感。5. 避坑与排查期末作业最容易翻车的 5 个点5.1 角色移动时抖动或卡顿现象角色在平地上走得很顺一上斜坡或台阶就开始抖甚至卡住不动。 原因Character Controller 的 Slope Limit 和 Step Offset 默认值不适合当前场景。Slope Limit 默认 45 度Step Offset 默认 0.3如果台阶高度超过 0.3 就会卡。 解决在 Character Controller 组件里把 Step Offset 调到 0.35~0.4Slope Limit 根据场景调室内楼梯多就设 50。另外检查角色胶囊体的 Height 和 RadiusHeight 至少 1.8Radius 0.3太小会掉进地面缝隙。5.2 相机跟随延迟严重现象角色已经转弯了相机过一秒才转过来操作像在开船。 原因Cinemachine 的 Damping 参数太大或者 Follow 的 Binding Mode 设成了 Lock To Target With World Up 但角色旋转太快。 解决把 FreeLook 的 X Axis 和 Y Axis 的 Damping 都降到 0.2 以下Follow Damping 设 0.1。如果还是延迟检查脚本里rotationSmoothTime是不是设太大了0.12 是上限。5.3 场景烘焙后光照全黑现象点 Generate Lighting 之后场景要么全黑要么只有实时阴影没有间接光。 原因Lighting Settings 里 Auto Generate 没关或者场景物体没有标记为 Static或者 Lightmap 分辨率设太低。 解决先关掉 Auto Generate手动烘焙。把静态物体勾上 StaticLightmap Resolution 设 20~40 texels per unit太高烘焙慢太低有噪点。如果还是黑检查 Directional Light 的 Mode 是不是 Baked以及 Environment Lighting 的 Source 是不是 Skybox。5.4 打包到手机后画面拉伸现象在编辑器里比例正常打包到手机后 UI 和画面被拉伸变形。 原因Canvas 的 Canvas Scaler 没设对或者相机的 Aspect Ratio 没适配。 解决Canvas Scaler 的 UI Scale Mode 设为 Scale With Screen SizeReference Resolution 设 1920×1080Match 设 0.5。相机方面如果用的是固定 FOV横屏和竖屏切换时会变形建议在脚本里根据Screen.width / Screen.height动态调 FOV竖屏时把 FOV 调大。5.5 帧率突然掉到 20 以下现象场景搭完跑起来只有 20 多帧打开 Profiler 发现 Draw Call 好几百。 原因材质球没有共用每个物体一个材质实例或者植被没有合并每个草一个 Draw Call。 解决检查材质球相同贴图的物体用同一个材质。植被用 Terrain 的 Detail 或者 GPU Instancer 合并。另外把 Camera 的 Far Clip Plane 从 1000 降到 200远景用 Skybox 代替。如果用了实时阴影把 Shadow Distance 降到 50阴影分辨率设 1024。6. 验收与进阶怎么证明这份作业「能打」6.1 用 Profiler 和 Frame Debugger 做一次体检交作业前跑一遍 Profiler重点看三项CPU 的Camera.Render耗时、Batch Count、SetPass Calls。PC 端 Batch Count 控制在 200 以内移动端 100 以内。如果超标用 Frame Debugger 逐帧看是哪个物体在打断合批。常见原因是不同材质球交替渲染把相同材质的物体在 Hierarchy 里排在一起能缓解。6.2 加一个「漫游路径录制」功能演示时不用手操答辩或演示时手动走容易翻车我一般会加一个路径录制按 R 开始记录角色位置再按 R 停止按 P 回放。核心是用ListVector3存位置回放时用Vector3.Lerp插值using System.Collections.Generic; using UnityEngine; public class PathRecorder : MonoBehaviour { public float recordInterval 0.1f; // 采样间隔 private ListVector3 path new ListVector3(); private bool isRecording, isPlaying; private float timer; private int playIndex; void Update() { if (Input.GetKeyDown(KeyCode.R)) { isRecording !isRecording; if (isRecording) path.Clear(); } if (Input.GetKeyDown(KeyCode.P)) { isPlaying true; playIndex 0; } if (isRecording) { timer Time.deltaTime; if (timer recordInterval) { path.Add(transform.position); timer 0; } } if (isPlaying playIndex path.Count - 1) { transform.position Vector3.Lerp(path[playIndex], path[playIndex 1], Time.deltaTime * 10f); if (Vector3.Distance(transform.position, path[playIndex 1]) 0.1f) playIndex; } } }recordInterval设 0.1 秒10Hz 采样足够平滑数据量也小。回放时把 Character Controller 关掉否则会和transform.position冲突。这个功能在演示时非常实用路径固定镜头稳定评委一看就知道你考虑过交付场景。6.3 从作业到作品三个可以继续挖的方向如果这份作业想拿高分或者当作品集可以在三个方向加码。一是加一个简单的 UI 数字滚轮效果显示当前坐标或帧率用 UGUI 的 Scroll Rect 加 Mask 就能做网上有现成思路。二是把场景做成数字孪生风格的俯视图加一个正交相机切换按 Tab 切换视角这在仿真实验类题目里很加分。三是做包体优化把贴图压缩格式从 RGBA32 改成 ASTCBuild 时勾上 Strip Engine Code包体能小一半。我自己的习惯是每次交作业前把项目从零打包一次在一台没装 Unity 的机器上跑一遍。这一步能暴露 90% 的「在我电脑上好好的」问题。希望帮到你。本文还有配套的精品资源点击获取
返回列表