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

Unity中修改绕目标旋转半径时Transform异常移动问题

问题描述
  • 预期功能:运行时调整旋转半径(例如从5改为1)时,物体从当前位置平滑过渡到新半径对应的轨道位置,同时保持绕目标旋转的状态。
  • 实际异常:物体先移动到目标对象的原点,再“跳”到新半径对应的固定位置,无法基于当前旋转方位平滑过渡。
问题根源

当前代码中radiusPosition被固定设置为目标Z轴方向的一个固定点(如target.position.z + radius),而非根据物体当前相对于目标的实时方位计算新半径位置。当半径变更时,物体会被强制向这个固定点移动,导致出现先回原点再跳转的异常。

修复方案

核心是基于物体当前相对于目标的方向动态计算新半径位置,步骤如下:

  1. 计算物体当前相对于目标的方向向量并归一化,保留旋转方位
  2. 用该方向向量乘以新半径,再加上目标位置,得到匹配当前方位的新半径目标点
  3. 平滑移动到该动态目标点,同时保留绕目标旋转的逻辑(可选:移动过程中继续旋转,让过渡更自然)

关键代码修改

替换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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 15:02:01