角色奔跑时摄像机晃动问题

角色在奔跑的时候,摄像机不停地轻微晃动,马马虎虎的人似乎注意不到,但是稍一仔细的人就发现了。而且发现以后一般都很留意这个,容易给人造成轻微眩晕的感觉。

我查看了一下摄像机上依附的脚本,角色在奔跑的时候摄像机是根据角色的transform来决定摄像机的位置方向变化的。于是到PlayerController里面查看角色transform在奔跑状态下是如何变化的。没有找到直接在脚本中控制奔跑状态下transform的代码。看了一下角色的Animator组件的设置,Apply Root Motion是被选中的。这就意味着角色的transform是直接由动画来控制的。而动画中角色奔跑的时候存在轻微左右摇摆,这就难怪摄像机会跟着左右摇摆。将Apply Root Motion去除勾选,摄像机即停止晃动了。

我这边这个例子是不需要Animator来控制角色的transform,所以这样做是没问题的。

<think>我们通常希望摄像机跟随角色移动,并且有平滑的跟随效果。常见的做法是使用一个独立的摄像机控制器脚本,让摄像机跟随目标(角色)并保持一定的距离和角度。 以下是一个简单的第三人称摄像机跟随脚本,它会使摄像机保持在角色后方一定距离,并可以围绕角色旋转(通过鼠标输入控制视角旋转)。 我们将实现: 1. 摄像机始终跟随角色 2. 摄像机角色保持一定距离(可调整) 3. 通过鼠标移动控制摄像机围绕角色旋转(水平和垂直) 4. 避免摄像机穿墙(使用射线检测防止摄像机被墙壁阻挡) 注意:这个脚本需要挂载在摄像机上,并且摄像机的初始位置应该设置在角色的后方。 代码如下: ```csharp using UnityEngine; public class ThirdPersonCamera : MonoBehaviour { [Header("Camera Settings")] public Transform target; // 跟随的目标(角色) public float distance = 5.0f; // 摄像机与目标的距离 public float height = 2.0f; // 摄像机相对于目标的高度 public float rotationDamping = 3.0f; // 旋转阻尼(平滑度) public float heightDamping = 2.0f; // 高度阻尼(平滑度) [Header("Input Settings")] public string horizontalInput = "Mouse X"; public string verticalInput = "Mouse Y"; public float sensitivity = 5.0f; // 鼠标灵敏度 private float currentRotationAngle; // 当前旋转角度(水平) private float currentHeight; // 当前高度 private float wantedRotationAngle; // 目标旋转角度 private float wantedHeight; // 目标高度 private Vector3 offset; // 初始偏移量(可不用) void Start() { // 如果目标未指定,尝试寻找玩家角色 if (target == null) { target = GameObject.FindGameObjectWithTag("Player").transform; if (target == null) { Debug.LogError("Camera target is not assigned and no player found!"); enabled = false; } } // 初始化当前角度和高度 currentRotationAngle = transform.eulerAngles.y; currentHeight = transform.position.y; } void LateUpdate() { if (!target) return; // 获取鼠标输入来旋转摄像机 wantedRotationAngle += Input.GetAxis(horizontalInput) * sensitivity; wantedHeight = target.position.y + height; // 平滑过渡当前值 currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime); currentHeight = Mathf.Lerp(currentHeight, wantedHeight, heightDamping * Time.deltaTime); // 将角度转换为旋转 Quaternion currentRotation = Quaternion.Euler(0, currentRotationAngle, 0); // 计算摄像机位置(在目标后方一定距离和高度) Vector3 targetPosition = target.position; Vector3 cameraPosition = targetPosition; cameraPosition -= currentRotation * Vector3.forward * distance; // 向后移动 cameraPosition = new Vector3(cameraPosition.x, currentHeight, cameraPosition.z); // 设置摄像机位置 transform.position = cameraPosition; // 摄像机始终看向目标 transform.LookAt(targetPosition); } } ``` ### 代码解释: 1. **参数说明**: - `target`:摄像机跟随的目标(角色)。 - `distance`:摄像机与目标保持的距离。 - `height`:摄像机相对于目标的高度(即摄像机在目标上方多高)。 - `rotationDamping` 和 `heightDamping`:用于平滑过渡的阻尼系数(值越大,跟随越紧)。 - 鼠标输入控制:通过`horizontalInput`和`verticalInput`设置输入轴(默认为鼠标移动),`sensitivity`控制灵敏度。 2. **LateUpdate**: - 在LateUpdate中更新摄像机位置,确保在角色移动之后更新。 - 获取鼠标输入,更新目标旋转角度(水平旋转)和高度(垂直方向)。 - 使用`Mathf.LerpAngle`和`Mathf.Lerp`平滑过渡角度和高度,避免摄像机抖动。 3. **位置计算**: - 根据当前旋转角度(水平)和距离,计算摄像机在目标后方的位置。 - 调整高度到目标高度(target.position.y + height)。 - 使用`transform.LookAt`让摄像机始终看向目标位置。 ### 防止摄像机穿墙: 上面的代码还没有处理摄像机被墙壁阻挡的情况。我们可以添加射线检测,当摄像机与目标之间有障碍物,将摄像机拉近到障碍物后方。 在`LateUpdate`中设置摄像机位置之前,添加以下代码: ```csharp // 防止穿墙:从目标位置向摄像机方向发射射线 Vector3 dir = (transform.position - target.position).normalized; float actualDistance = distance; RaycastHit hit; if (Physics.Raycast(target.position, dir, out hit, distance)) { // 如果射线碰到物体,将摄像机拉近到碰撞点 actualDistance = hit.distance * 0.9f; // 留一点空间,避免嵌入 } // 重新计算摄像机位置(使用actualDistance代替distance) cameraPosition = target.position - currentRotation * Vector3.forward * actualDistance; cameraPosition = new Vector3(cameraPosition.x, currentHeight, cameraPosition.z); ``` ### 使用步骤: 1. 将脚本挂载到主摄像机上。 2. 将角色对象拖拽到脚本的`target`字段。 3. 调整参数(distance, height, damping等)以达到理想效果。 ### 注意事项: - 如果需要垂直旋转(抬头低头),可以扩展代码,将垂直旋转角度限制在-30度到80度之间(避免翻转)。 - 如果场景中有多个墙壁,射线检测可能需要使用LayerMask来忽略某些层。 §§相关问题§§: 1. 如何实现第一人称视角的摄像机控制? 2. 怎样添加摄像机碰撞检测防止穿墙? 3. 如何实现摄像机角色奔跑的震动效果? 4. 怎样实现不同视角(第一人称/第三人称)的切换?</think># Unity摄像机设置指南 下面我将详细介绍如何在Unity中设置角色跟随摄像机,包括实现平滑跟随、旋转控制以及碰撞检测等功能。 ## 完整摄像机控制器代码 ```csharp using UnityEngine; public class ThirdPersonCamera : MonoBehaviour { [Header("Target Settings")] public Transform target; // 跟随的目标(角色) public Vector3 targetOffset = new Vector3(0, 1.5f, 0); // 目标位置偏移 [Header("Camera Settings")] public float distance = 5.0f; // 摄像机与目标的距离 public float minDistance = 1.0f; // 最小距离 public float maxDistance = 10.0f; // 最大距离 public float height = 2.0f; // 摄像机高度 public float minHeight = 1.0f; // 最小高度 public float maxHeight = 5.0f; // 最大高度 [Header("Rotation Settings")] public float rotationSpeed = 3.0f; // 旋转速度 public float xRotationLimit = 80f; // X轴旋转限制 [Header("Smoothing Settings")] public float positionSmoothTime = 0.1f; // 位置平滑间 public float rotationSmoothTime = 0.1f; // 旋转平滑间 [Header("Collision Settings")] public LayerMask collisionLayers; // 碰撞检测层 public float collisionOffset = 0.2f; // 碰撞偏移量 private float currentX; // 当前X轴旋转角度 private float currentY; // 当前Y轴旋转角度 private Vector3 positionVelocity; // 位置平滑速度 private Quaternion rotationVelocity; // 旋转平滑速度 void Start() { // 如果没有指定目标,尝试寻找玩家角色 if (target == null) { GameObject player = GameObject.FindGameObjectWithTag("Player"); if (player != null) target = player.transform; } // 初始化旋转角度 Vector3 angles = transform.eulerAngles; currentX = angles.y; currentY = angles.x; } void LateUpdate() { if (!target) return; // 获取鼠标输入 currentX += Input.GetAxis("Mouse X") * rotationSpeed; currentY -= Input.GetAxis("Mouse Y") * rotationSpeed; // 限制垂直旋转角度 currentY = Mathf.Clamp(currentY, -xRotationLimit, xRotationLimit); // 计算旋转 Quaternion rotation = Quaternion.Euler(currentY, currentX, 0); // 计算目标位置 Vector3 targetPosition = target.position + targetOffset; // 计算期望位置 Vector3 desiredPosition = targetPosition - rotation * Vector3.forward * distance; desiredPosition.y = targetPosition.y + height; // 碰撞检测 RaycastHit hit; if (Physics.Linecast(targetPosition, desiredPosition, out hit, collisionLayers)) { // 如果检测到碰撞,调整摄像机位置 float adjustedDistance = Vector3.Distance(targetPosition, hit.point) - collisionOffset; desiredPosition = targetPosition - rotation * Vector3.forward * adjustedDistance; } // 平滑移动摄像机位置 transform.position = Vector3.SmoothDamp(transform.position, desiredPosition, ref positionVelocity, positionSmoothTime); // 平滑旋转摄像机 Quaternion desiredRotation = Quaternion.LookRotation(targetPosition - transform.position); transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, rotationSmoothTime * Time.deltaTime * 50); } // 在编辑器中显示碰撞检测 void OnDrawGizmosSelected() { if (target) { Gizmos.color = Color.green; Gizmos.DrawLine(target.position + targetOffset, transform.position); } } } ``` ## 摄像机设置步骤 ### 1. 基本设置 1. 创建新摄像机对象或使用主摄像机 2. 添加`ThirdPersonCamera`脚本 3. 将角色对象拖拽到`Target`字段 4. 调整基本参数: - `Distance`:摄像机角色的距离(推荐3-5) - `Height`:摄像机高度(推荐1.5-2.5) - `Target Offset`:Y轴偏移(角色眼睛高度) ### 2. 旋转设置 ```csharp [Header("Rotation Settings")] public float rotationSpeed = 3.0f; // 旋转速度 public float xRotationLimit = 80f; // X轴旋转限制 ``` - 调整`Rotation Speed`控制鼠标灵敏度 - 设置`X Rotation Limit`限制垂直旋转角度(防止摄像机翻转) ### 3. 平滑设置 ```csharp [Header("Smoothing Settings")] public float positionSmoothTime = 0.1f; // 位置平滑间 public float rotationSmoothTime = 0.1f; // 旋转平滑间 ``` - 增加平滑间使摄像机移动更柔和 - 减少平滑间使摄像机响应更迅速 ### 4. 碰撞检测设置 ```csharp [Header("Collision Settings")] public LayerMask collisionLayers; // 碰撞检测层 public float collisionOffset = 0.2f; // 碰撞偏移量 ``` 1. 在Inspector中选择`Collision Layers`(通常选择Environment) 2. 设置`Collision Offset`防止摄像机嵌入墙壁 3. 使用`OnDrawGizmosSelected`可视化检测线 ## 摄像机工作流程 1. **输入处理**: - 通过鼠标输入计算摄像机旋转角度 - 限制垂直旋转角度防止翻转 2. **位置计算**: - 根据旋转角度和距离计算期望位置 - 应用高度偏移 3. **碰撞检测**: - 使用`Physics.Linecast`检测摄像机角色之间的障碍物 - 遇到障碍物调整摄像机位置 4. **平滑过渡**: - 使用`Vector3.SmoothDamp`平滑移动位置 - 使用`Quaternion.Slerp`平滑旋转方向 ## 高级功能扩展 ### 1. 添加鼠标滚轮缩放 ```csharp void LateUpdate() { // ...现有代码... // 滚轮缩放 float scroll = Input.GetAxis("Mouse ScrollWheel"); distance = Mathf.Clamp(distance - scroll * 5, minDistance, maxDistance); height = Mathf.Clamp(height - scroll * 2, minHeight, maxHeight); } ``` ### 2. 添加摄像机震动效果 ```csharp public IEnumerator Shake(float duration, float magnitude) { Vector3 originalPosition = transform.localPosition; float elapsed = 0.0f; while (elapsed < duration) { float x = Random.Range(-1f, 1f) * magnitude; float y = Random.Range(-1f, 1f) * magnitude; transform.localPosition = new Vector3(originalPosition.x + x, originalPosition.y + y, originalPosition.z); elapsed += Time.deltaTime; yield return null; } transform.localPosition = originalPosition; } ``` ### 3. 添加焦点切换功能 ```csharp public void SwitchTarget(Transform newTarget) { target = newTarget; // 重置摄像机位置 currentX = newTarget.eulerAngles.y; currentY = newTarget.eulerAngles.x; } ``` ## 最佳实践建议 1. **层级管理**: - 将摄像机放在单独的层级 - 使用`Ignore Raycast`层避免摄像机碰撞检测干扰 2. **性能优化**: - 减少LateUpdate中的复杂计算 - 使用对象池管理摄像机特效 3. **移动设备适配**: - 将鼠标输入替换为触摸输入 - 添加虚拟摇杆控制摄像机旋转 4. **多平台支持**: - 添加输入抽象层处理不同设备 - 使用Unity的Input System包
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值