You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity相机围绕士兵群体旋转及平滑停止实现问题

相机围绕士兵群体旋转&平滑停止问题解答

嘿,我来帮你拆解这几个问题,结合你写的代码一步步分析:

1. 群体旋转代码的合理性

你的群体旋转思路是完全正确的——通过计算所有士兵的平均位置作为旋转中心,实现相机围绕整个群体旋转,这完全符合需求逻辑。不过有几个可以优化的点:

  • 士兵列表的维护:你在Start()里一次性获取所有士兵,但如果游戏中士兵会动态生成/销毁(比如阵亡、增援),这个列表就会失效。建议改成每帧获取最新的士兵(或者用一个士兵管理器来维护列表,更高效),比如把士兵获取逻辑移到Update()里,用Object.FindObjectsOfTag("Soldier")实时获取。
  • 性能优化:
    • 缓存士兵的Transform而不是GameObject,避免每帧重复访问transform属性;
    • 直接用List.Count代替Linq的Count(),减少不必要的Linq调用。

优化后的核心代码示例:

private void RotateAround() {
    // 实时获取士兵Transform并缓存
    var soldierTransforms = GameObject.FindGameObjectsWithTag("Soldier")
                                    .Select(s => s.transform)
                                    .ToList();
    if(soldierTransforms.Count == 0) return;
    
    Vector3 center = GetAverageLocation(soldierTransforms);
    transform.RotateAround(center, Vector3.up, 100 * Time.deltaTime * speed);
}

private Vector3 GetAverageLocation(List<Transform> transforms) {
    Vector3 total = Vector3.zero;
    foreach(var t in transforms) total += t.position;
    return total / transforms.Count;
}

2. 相机抖动的解决方法

轻微抖动通常是帧同步问题或旋转中心不稳定导致的,试试这几个方案:

  • 改用LateUpdate执行相机逻辑:LateUpdate()会在所有Update()执行完毕后运行,确保士兵的位置已经更新完毕,再计算相机位置,避免因帧顺序导致的错位抖动。
  • 平滑旋转中心:不要直接用每帧的原始平均位置,而是用Vector3.Lerp对中心点做平滑处理,让旋转中心的变化更平缓:
    private Vector3 smoothCenter;
    public float centerSmoothSpeed = 5f;
    
    private void LateUpdate() {
        var soldierTransforms = GameObject.FindGameObjectsWithTag("Soldier")
                                        .Select(s => s.transform)
                                        .ToList();
        if(soldierTransforms.Count == 0) return;
        
        Vector3 targetCenter = GetAverageLocation(soldierTransforms);
        // 平滑更新中心点,避免突变
        smoothCenter = Vector3.Lerp(smoothCenter, targetCenter, centerSmoothSpeed * Time.deltaTime);
        transform.RotateAround(smoothCenter, Vector3.up, 100 * Time.deltaTime * speed);
    }
    
  • 避免频繁创建列表:你之前的错误脚本里每帧都新建SoldiersPositions列表,会产生GC开销,间接导致抖动,建议复用列表或者用临时变量缓存。

3. 平滑停止功能的正确实现方式

你的错误脚本里直接用speed = speed - CamAngle的方式太粗暴,会导致速度突变甚至负数,正确的思路是根据相机与目标朝向的角度差,平滑降低旋转速度,直到对准后停止并跟随。

完整的实现步骤:

  1. 计算群体的平均朝向(注意归一化,避免因士兵朝向分散导致的方向偏差);
  2. 计算相机当前方向(相对于群体中心)与目标朝向的角度差;
  3. 根据角度差动态调整旋转速度,直到角度差小于阈值时停止旋转;
  4. 停止旋转后,保持相机朝向群体,并跟随群体中心移动。

完整代码示例:

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class CameraMove : MonoBehaviour {
    public float rotateSpeed = 0.1f; // 初始旋转速度
    public bool followMode = false; // 开关:开启后平滑停止并跟随
    public float stopSmoothSpeed = 2f; // 停止时的平滑系数
    public float followSmoothSpeed = 3f; // 跟随移动的平滑系数
    public float angleThreshold = 5f; // 角度小于这个值时停止旋转

    private Vector3 smoothCenter;
    private float currentRotateSpeed;

    void Start() {
        currentRotateSpeed = rotateSpeed;
    }

    void LateUpdate() {
        var soldierTransforms = GameObject.FindGameObjectsWithTag("Soldier")
                                        .Select(s => s.transform)
                                        .ToList();
        if(soldierTransforms.Count == 0) return;

        // 计算群体中心与平均朝向
        Vector3 targetCenter = GetAverageLocation(soldierTransforms);
        Vector3 targetForward = GetAverageForward(soldierTransforms).normalized;

        // 平滑更新中心
        smoothCenter = Vector3.Lerp(smoothCenter, targetCenter, followSmoothSpeed * Time.deltaTime);

        if(followMode) {
            // 计算相机当前方向与目标朝向的角度差
            Vector3 cameraDir = (transform.position - smoothCenter).normalized;
            float angleDiff = Vector3.Angle(cameraDir, targetForward);

            // 平滑降低旋转速度,直到角度达标
            if(angleDiff > angleThreshold) {
                currentRotateSpeed = Mathf.Lerp(currentRotateSpeed, 0, stopSmoothSpeed * Time.deltaTime);
                transform.RotateAround(smoothCenter, Vector3.up, 100 * Time.deltaTime * currentRotateSpeed);
            } else {
                currentRotateSpeed = 0;
                // 保持朝向群体中心,并跟随移动(维持原有距离)
                transform.LookAt(smoothCenter);
                transform.position = smoothCenter + cameraDir * (transform.position - smoothCenter).magnitude;
            }
        } else {
            // 关闭跟随模式,恢复旋转速度
            currentRotateSpeed = Mathf.Lerp(currentRotateSpeed, rotateSpeed, stopSmoothSpeed * Time.deltaTime);
            transform.RotateAround(smoothCenter, Vector3.up, 100 * Time.deltaTime * currentRotateSpeed);
        }
    }

    private Vector3 GetAverageLocation(List<Transform> transforms) {
        Vector3 total = Vector3.zero;
        foreach(var t in transforms) total += t.position;
        return total / transforms.Count;
    }

    private Vector3 GetAverageForward(List<Transform> transforms) {
        Vector3 total = Vector3.zero;
        foreach(var t in transforms) total += t.forward;
        return total;
    }
}

这个脚本的核心逻辑:

  • 用currentRotateSpeed代替直接修改原始rotateSpeed,方便在开关切换时平滑恢复速度;
  • 通过角度差判断是否需要继续旋转,平滑过渡速度避免突变;
  • 停止旋转后,保持相机与群体的距离不变,同时跟随中心移动。

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:04:57