01 / 06 · 8 分钟
镜头与操作
跟随镜头
到目前为止的项目
Assets/
Scenes/
Arena.unity
Knight
Scripts/
CharacterSwitcher.csSwapCamera.cs→FollowCamera.cs
你已经有的这一课新增这一课替换
打开 Arena.unity。tab 01 里的 SwapCamera 已经会跟着当前角色走,但它是硬甩过去的,还老盯着脚,并且随随便便就穿过墙。这一页用三个步骤把它重写一遍,这里是第一步。
死死贴在角色身上的相机很廉价;带一点延迟追上去的相机才有游戏感。
动态图解
角色先动,相机随后在几帧之内追上来。
跟随脚本
FollowCamera.cs — remove SwapCamera from Main Camera first
using UnityEngine;
public class FollowCamera : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private Vector3 offset = new Vector3(0f, 6f, -8f);
[SerializeField] private float smoothTime = 0.15f;
[SerializeField] private Vector3 lookAtOffset = new Vector3(0f, 1.5f, 0f);
private Vector3 velocity; // SmoothDamp keeps its own state here
void LateUpdate()
{
if (target == null) return;
Vector3 wanted = target.position + offset;
transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
transform.LookAt(target.position + lookAtOffset);
}
}用 Lerp 还是 SmoothDamp?
✓ Vector3.Lerp(a, b, t * dt)
读起来简单,但实际的平滑程度会随帧率略微变化。
✓ Vector3.SmoothDamp(...)
像弹簧一样,跟帧率无关,而且 smoothTime 用的是真实秒数。
继续跟随当前角色
把 target 硬编码成 Knight,按 2 和 3 的时候就直接废了。应该去问切换器 —— 就像 SwapCamera 原来那样。
C# 脚本
[SerializeField] private CharacterSwitcher switcher;
void LateUpdate()
{
Transform target = switcher.ActiveCharacter; // not a fixed reference
Vector3 wanted = target.position + offset;
transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
transform.LookAt(target.position + lookAtOffset);
}为什么一定要用 LateUpdate
Unity 执行各个 Update 的顺序是不确定的,你的相机可能在角色移动之前就先跑了。
LateUpdate 在所有 Update 结束后才运行,所以相机看到的永远是那一帧的最终位置。
做错的后果是:角色移动时会明显抖动一两像素。
提前看 —— 让玩家看到前方的路
把相机朝行进方向稍微偏移一点,玩家就能更早注意到前面会出现什么。只花三行代码。
C# 脚本
[SerializeField] private float lookAhead = 2.5f;
private CharacterController targetCC;
void LateUpdate()
{
Vector3 ahead = targetCC.velocity.normalized * lookAhead;
ahead.y = 0f;
Vector3 wanted = target.position + offset + ahead;
transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
transform.LookAt(target.position + lookAtOffset);
}阻止相机穿墙
add before assigning the position
[SerializeField] private LayerMask obstacles;
Vector3 pivot = target.position + lookAtOffset;
Vector3 dir = (wanted - pivot).normalized;
float dist = Vector3.Distance(pivot, wanted);
// is there a wall between the character and where the camera wants to be?
if (Physics.SphereCast(pivot, 0.3f, dir, out RaycastHit hit, dist, obstacles))
wanted = hit.point - dir * 0.25f; // pull the camera in front of it常见错误
smoothTime 设成 0 就跟原来的 SwapCamera 一样硬甩过去;超过 0.5 秒相机又拖得太远,角色会跑出屏幕。先放在 0.1 到 0.25 秒之间,再慢慢调。
把 velocity 声明在 LateUpdate 里面,SmoothDamp 每一帧都会被重置,运动就变得弹来弹去、完全不对。它必须是一个字段,因为 SmoothDamp 就靠它在帧与帧之间保存状态。
在 Hierarchy 里把 Main Camera 拖成角色的子物体,它就会继承父物体的所有旋转,跟这个脚本互相打架。让相机留在场景根部,跟随这件事交给脚本。