Unity旋转

前言

对于unity的旋转贼麻烦,用Dotween又要考虑打断,所以还是用最原始的方法在Update生命周期里用插值旋转方便。为了更方便模仿Dotween写了个基类希望大家喜欢

调用案例

using UnityEngine;

public class EnemyController : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float rotateSpeed = 180f;
    
    private void Update()
    {
        // 1. 移动到目标位置
        bool reached = transform.MoveToTransform(target, moveSpeed, Time.deltaTime);
        
        if (reached)
        {
            Debug.Log("已到达目标位置");
        }
        
        // 2. 旋转到目标
        bool rotated = transform.RotateTowardsTarget(target, rotateSpeed, Time.deltaTime);
        
        // 3. 旋转到指定欧拉角
        transform.RotateToEuler(new Vector3(0, 90, 0), rotateSpeed, Time.deltaTime);
        
        // 4. 移动到指定位置
        Vector3 targetPosition = new Vector3(10, 0, 5);
        transform.MoveToPosition(targetPosition, moveSpeed, Time.deltaTime);
        
        // 5. 移动到局部坐标系原点XZ平面
        transform.MoveToOriginXZ(moveSpeed, Time.deltaTime);
    }
}

代码

using UnityEngine;

public static class TransformTool
{
    /// <summary>
    /// 将Transform平滑移动到目标Transform的位置
    /// </summary>
    /// <param name="transform">要移动的Transform</param>
    /// <param name="target">目标Transform</param>
    /// <param name="speed">移动速度(单位/秒)</param>
    /// <param name="deltaTime">时间增量</param>
    /// <param name="lockYAxis">是否锁定Y轴</param>
    /// <returns>是否已到达目标位置</returns>
    public static bool MoveToTransform(this Transform transform, Transform target, 
                                      float speed, float deltaTime, bool lockYAxis = true)
    {
        // 检查目标是否有效
        if (target == null)
        {
            Debug.LogWarning("MoveToTransform: 目标Transform为空");
            return false;
        }
    
        // 获取当前位置和目标位置
        Vector3 currentPos = transform.position;
        Vector3 targetPos = target.position;
    
        // 计算当前位置到目标位置的距离
        float distance = Vector3.Distance(currentPos, targetPos);
    
        // 如果已经在目标位置附近,直接完成
        if (distance < 0.01f)
        {
            transform.position = targetPos; // 精确移动到目标位置
            return true;
        }
    
        // 使用插值移动到目标位置
        Vector3 newPos = Vector3.MoveTowards(currentPos, targetPos, speed * deltaTime);
        
        // 锁定Y轴
        if (lockYAxis)
        {
            newPos.y = transform.position.y;
        }
        
        transform.position = newPos;
    
        // 计算新位置到目标的距离
        float newDistance = Vector3.Distance(newPos, targetPos);
    
        // 判断是否到达(距离小于0.1个单位认为到达)
        return newDistance < 0.1f;
    }
    
    /// <summary>
    /// 将物体插值移动到父物体坐标系的X=0, Z=0位置
    /// 保持Y轴不变
    /// </summary>
    /// <param name="transform">要移动的物体Transform</param>
    /// <param name="speed">移动速度</param>
    /// <param name="deltaTime">时间增量</param>
    /// <returns>是否已到达目标位置</returns>
    public static bool MoveToOriginXZ(this Transform transform, float speed, float deltaTime)
    {
        // 获取当前位置(局部坐标)
        Vector3 localPos = transform.localPosition;
        
        // 如果已经在目标位置,直接返回true
        if (Mathf.Abs(localPos.x) < 0.01f && Mathf.Abs(localPos.z) < 0.01f)
        {
            // 确保精确归零
            transform.localPosition = new Vector3(0, localPos.y, 0);
            return true;
        }
        
        // 计算新位置:X和Z向0移动,Y保持不变
        Vector3 targetPos = new Vector3(0, localPos.y, 0);
        Vector3 newPos = Vector3.MoveTowards(localPos, targetPos, speed * deltaTime);
        
        // 应用新位置
        transform.localPosition = newPos;
        
        // 检查是否到达(使用较小阈值)
        float distance = Vector2.Distance(
            new Vector2(newPos.x, newPos.z), 
            Vector2.zero
        );
        return distance < 0.01f;
    }
    
    /// <summary>
    /// 旋转到指定的欧拉角
    /// </summary>
    /// <param name="transform">物体</param>
    /// <param name="targetEuler">目标欧拉角</param>
    /// <param name="rotateSpeed">旋转速度(度/秒)</param>
    /// <param name="deltaTime">时间增量</param>
    /// <returns>是否已旋转到位</returns>
    public static bool RotateToEuler(this Transform transform, Vector3 targetEuler, 
                                    float rotateSpeed, float deltaTime)
    {
        // 获取当前旋转
        Quaternion currentRotation = transform.rotation;
        Quaternion targetRotation = Quaternion.Euler(targetEuler);
        
        // 如果已经非常接近目标旋转
        if (Quaternion.Angle(currentRotation, targetRotation) < 0.1f)
        {
            transform.rotation = targetRotation; // 直接设置为目标旋转
            return true;
        }
    
        // 使用插值旋转到目标四元数
        Quaternion newRotation = Quaternion.RotateTowards(
            currentRotation, 
            targetRotation, 
            rotateSpeed * deltaTime
        );
    
        // 应用旋转
        transform.rotation = newRotation;
    
        // 返回是否已旋转到位(角度差小于1度认为完成)
        return Quaternion.Angle(newRotation, targetRotation) < 1f;
    }
    
    /// <summary>
    /// 旋转到指定的四元数
    /// </summary>
    /// <param name="transform">物体</param>
    /// <param name="targetRotation">目标四元数</param>
    /// <param name="rotateSpeed">旋转速度(度/秒)</param>
    /// <param name="deltaTime">时间增量</param>
    /// <returns>是否已旋转到位</returns>
    public static bool RotateToQuaternion(this Transform transform, Quaternion targetRotation, 
                                         float rotateSpeed, float deltaTime)
    {
        // 获取当前旋转
        Quaternion currentRotation = transform.rotation;
        
        // 如果已经非常接近目标旋转
        if (Quaternion.Angle(currentRotation, targetRotation) < 0.1f)
        {
            transform.rotation = targetRotation; // 直接设置为目标旋转
            return true;
        }
    
        // 使用插值旋转到目标四元数
        Quaternion newRotation = Quaternion.RotateTowards(
            currentRotation, 
            targetRotation, 
            rotateSpeed * deltaTime
        );
    
        // 应用旋转
        transform.rotation = newRotation;
    
        // 返回是否已旋转到位(角度差小于1度认为完成)
        return Quaternion.Angle(newRotation, targetRotation) < 1f;
    }
    
    /// <summary>
    /// 插值转向指定目标
    /// </summary>
    /// <param name="transform">需要旋转的Transform</param>
    /// <param name="target">目标Transform</param>
    /// <param name="rotationSpeed">旋转速度(度/秒)</param>
    /// <param name="deltaTime">时间增量</param>
    /// <param name="offsetEuler">旋转偏移量(欧拉角)</param>
    /// <param name="ignoreYAxis">是否忽略Y轴(水平旋转)</param>
    /// <returns>是否已旋转到目标方向</returns>
    public static bool RotateTowardsTarget(this Transform transform, Transform target, 
                                         float rotationSpeed, float deltaTime, 
                                         Vector3 offsetEuler = default, bool ignoreYAxis = true)
    {
        if (transform == null || target == null)
        {
            Debug.LogWarning("RotateTowardsTarget: 自身或目标Transform为空");
            return false;
        }
        
        // 计算目标方向
        Vector3 direction = target.position - transform.position;
        
        // 是否忽略Y轴
        if (ignoreYAxis)
        {
            direction.y = 0; // 锁定Y轴,确保在水平面旋转
            
            // 如果水平方向几乎为零,则不旋转
            if (direction.sqrMagnitude < 0.0001f)
            {
                return true; // 可以认为已经面向目标
            }
        }
        
        // 计算目标旋转
        Quaternion targetRotation = Quaternion.LookRotation(direction.normalized, Vector3.up);
        
        // 应用偏移
        if (offsetEuler != Vector3.zero)
        {
            targetRotation *= Quaternion.Euler(offsetEuler);
        }
        
        // 获取当前旋转
        Quaternion currentRotation = transform.rotation;
        
        // 计算当前角度差
        float currentAngle = Quaternion.Angle(currentRotation, targetRotation);
        
        // 如果已经接近目标旋转
        if (currentAngle <= 0.1f)
        {
            transform.rotation = targetRotation; // 精确设置为目标旋转
            return true;
        }
        
        // 使用插值平滑旋转
        transform.rotation = Quaternion.RotateTowards(
            currentRotation,
            targetRotation,
            rotationSpeed * deltaTime
        );
        
        // 计算新的角度差
        float newAngle = Quaternion.Angle(transform.rotation, targetRotation);
        
        // 返回是否已旋转到位(角度差小于1度认为完成)
        return newAngle < 1f;
    }
    
    /// <summary>
    /// 平滑移动到指定位置
    /// </summary>
    /// <param name="transform">要移动的Transform</param>
    /// <param name="targetPosition">目标位置</param>
    /// <param name="speed">移动速度(单位/秒)</param>
    /// <param name="deltaTime">时间增量</param>
    /// <param name="lockYAxis">是否锁定Y轴</param>
    /// <returns>是否已到达目标位置</returns>
    public static bool MoveToPosition(this Transform transform, Vector3 targetPosition, 
                                     float speed, float deltaTime, bool lockYAxis = true)
    {
        // 获取当前位置
        Vector3 currentPos = transform.position;
    
        // 计算当前位置到目标位置的距离
        float distance = Vector3.Distance(currentPos, targetPosition);
    
        // 如果已经在目标位置附近,直接完成
        if (distance < 0.01f)
        {
            transform.position = targetPosition; // 精确移动到目标位置
            return true;
        }
    
        // 使用插值移动到目标位置
        Vector3 newPos = Vector3.MoveTowards(currentPos, targetPosition, speed * deltaTime);
        
        // 锁定Y轴
        if (lockYAxis)
        {
            newPos.y = transform.position.y;
        }
        
        transform.position = newPos;
    
        // 计算新位置到目标的距离
        float newDistance = Vector3.Distance(newPos, targetPosition);
    
        // 判断是否到达(距离小于0.1个单位认为到达)
        return newDistance < 0.1f;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值