Unity中修改绕目标旋转半径时Transform异常移动问题
问题描述
- 预期功能:运行时调整旋转半径(例如从5改为1)时,物体从当前位置平滑过渡到新半径对应的轨道位置,同时保持绕目标旋转的状态。
- 实际异常:物体先移动到目标对象的原点,再“跳”到新半径对应的固定位置,无法基于当前旋转方位平滑过渡。
问题根源
当前代码中radiusPosition被固定设置为目标Z轴方向的一个固定点(如target.position.z + radius),而非根据物体当前相对于目标的实时方位计算新半径位置。当半径变更时,物体会被强制向这个固定点移动,导致出现先回原点再跳转的异常。
修复方案
核心是基于物体当前相对于目标的方向动态计算新半径位置,步骤如下:
- 计算物体当前相对于目标的方向向量并归一化,保留旋转方位
- 用该方向向量乘以新半径,再加上目标位置,得到匹配当前方位的新半径目标点
- 平滑移动到该动态目标点,同时保留绕目标旋转的逻辑(可选:移动过程中继续旋转,让过渡更自然)
关键代码修改
替换radiusPosition的计算逻辑
删除原固定位置的计算代码,替换为动态方向计算:
if (target != null) { float currentRadius = dc != null ? dc.xRadius : radius; // 计算当前物体相对于目标的方向向量(归一化) Vector3 directionToTarget = (transform.position - target.position).normalized; // 根据新半径计算目标位置,保留当前方位 radiusPosition = target.position + directionToTarget * currentRadius; // 如果使用DrawCircle的yRadius调整高度,叠加高度值 if (dc != null) { radiusPosition.y = target.position.y + dc.yRadius; } }
调整移动时的旋转逻辑(可选)
如果希望移动过程中保持旋转,修改moveToRadius分支的逻辑,允许同时执行旋转和位置平滑:
if (moveToRadius) { if (Vector3.Distance(transform.position, radiusPosition) > 0.01f) // 用距离判断避免浮点误差 { float step = movingToRadiusSpeed * Time.deltaTime; transform.position = Vector3.MoveTowards(transform.position, radiusPosition, step); // 移动过程中继续旋转,保持状态 if (target != null) { transform.RotateAround(target.position, axis, rotatingSpeed * Time.deltaTime); } } else { moveToRadius = false; } }
修改后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RotateAroundTarget : MonoBehaviour { [Header("Target")] public Transform target; [Header("Rotating Speed")] public float rotatingSpeed; [Header("First Move To Radius Settings")] public float movingToRadiusSpeed; public bool moveToRadius = false; [Header("Axis Settings")] public Vector3 axis; [Header("Height Settings")] public bool randomHeight; public float setRandomHeight = 1; [Header("Radius Settings")] public float radius; [Header("Reference To DrawCircle Script")] public DrawCircle dc; [Header("Reference To Linerenderer")] public LineRenderer lineRenderer; private float lastRadius; private float t = 0.0f; public float upperLimit, lowerLimit, delay; private float prevHeight; private Vector3 radiusPosition; private Vector3[] positions; private void Start() { if (dc != null) { lastRadius = dc.xRadius; radius = dc.xRadius; } else { lastRadius = radius; } if (lineRenderer != null) { GetLinePointsInWorldSpace(); } } private void Update() { if (target != null) { float currentRadius = dc != null ? dc.xRadius : radius; // 计算当前物体相对于目标的方向向量(归一化) Vector3 directionToTarget = (transform.position - target.position).normalized; // 根据新半径计算目标位置,保留当前方位 radiusPosition = target.position + directionToTarget * currentRadius; // 如果使用DrawCircle的yRadius调整高度,叠加高度值 if (dc != null) { radiusPosition.y = target.position.y + dc.yRadius; } } if (!moveToRadius) { if (target != null) { transform.RotateAround(target.position, axis, rotatingSpeed * Time.deltaTime); } if (randomHeight) { t += Time.deltaTime; if (t > delay) { prevHeight = setRandomHeight; setRandomHeight = Random.Range(lowerLimit, upperLimit); t = 0; } var tt = transform.position; tt.y = Mathf.Lerp(prevHeight, setRandomHeight, t/delay); // 修正Lerp进度值,避免t超阈值后异常 transform.position = tt; } } // 检测半径变化(增加浮点误差容忍) bool radiusChanged = false; if (dc != null) { if (Mathf.Abs(lastRadius - dc.xRadius) > 0.01f) { radiusChanged = true; lastRadius = dc.xRadius; } } else { if (Mathf.Abs(lastRadius - radius) > 0.01f) { radiusChanged = true; lastRadius = radius; } } if (radiusChanged) { moveToRadius = true; } if (moveToRadius) { if (Vector3.Distance(transform.position, radiusPosition) > 0.01f) // 避免浮点精度问题 { float step = movingToRadiusSpeed * Time.deltaTime; transform.position = Vector3.MoveTowards(transform.position, radiusPosition, step); // 移动过程中保持旋转 if (target != null) { transform.RotateAround(target.position, axis, rotatingSpeed * Time.deltaTime); } } else { moveToRadius = false; } } } Vector3[] GetLinePointsInWorldSpace() { positions = new Vector3[lineRenderer.positionCount]; lineRenderer.GetPositions(positions); return positions; } }
额外优化说明
- 用
Vector3.Distance替代直接的!=判断,避免浮点精度问题导致的逻辑异常 - 修正了随机高度Lerp的进度值(
t/delay),防止t超过delay后Lerp值异常 - 半径变化检测增加了浮点误差容忍,避免因微小数值波动触发不必要的移动
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

