Unity3D视角跟随+人称变换

一、视屏演示

Test13D - SampleScene - Windows, Mac, Linux - Unity 2022.3.53f1c1

二、代码说明

1、脚本直接挂载在摄像机上

2、把摄像机对准物体初始位置调整好

三、配置参数说明

分类参数名称类型说明推荐值/示例
位置跟随localOffsetVector3摄像机相对于目标的本地坐标系偏移(目标变换后的位置)默认 (0, 0, 0),第三人称常用 (0, 2, -5)
positionSmoothTimefloat水平方向位置跟随的平滑时间(秒),值越小响应越快0.1~0.3
verticalSmoothTimefloat垂直方向位置跟随的平滑时间(秒),通常比水平更小以实现快速垂直响应0.05~0.15
maxFollowSpeedfloat摄像机最大移动速度限制,防止高速移动时过冲100~200(高速场景可更高)
usePositionPredictionbool是否启用位置预测(根据目标速度提前移动摄像机)true(推荐开启)
predictionFactorfloat位置预测强度,值越大预测距离越远0.1~0.3(高速目标用更高值)
视角控制isTPSbool是否为第三人称视角(TPS)。若为 false,可能切换为其他视角(如第一人称)true(默认第三人称)
TPS_localOffsetVector3第三人称模式下摄像机相对于目标的本地偏移(仅在 isTPS=true 时生效)示例 (0, 1.5, -4)
rotationSmoothTimefloat摄像机旋转的平滑时间(秒),值越小旋转越灵敏0.1~0.3
pitchSpeedfloat俯仰角(上下视角)的移动灵敏度80~150(根据鼠标DPI调整)
minPitchfloat最小俯视角(防止过度仰视)-30(典型值)
maxPitchfloat最大俯视角(防止过度俯视)80(典型值)
目标转向enableTargetRotationFollowbool是否同步旋转目标物体的朝向(例如角色跟随摄像机转向)true(需与角色控制逻辑配合)
targetRotationSmoothTimefloat目标物体旋转的平滑时间(秒)0.1~0.3
maxRotationSpeedfloat目标物体最大旋转速度(度/秒),防止转向过快180~360

四、代码

using System;
using UnityEngine;
using UnityEngine.UIElements;

[System.Serializable]
public class CameraFollowSetting
{
    [Header("位置跟随")]
    public Vector3 localOffset = new Vector3(0, 0, 0);
    public float positionSmoothTime = 0.15f;
    public float verticalSmoothTime = 0.1f;
    public float maxFollowSpeed = 500f;
    public bool usePositionPrediction = true;
    public float predictionFactor = 0.15f;

    [Header("视角控制")]
    public bool isTPS = true;
    public Vector3 TPS_localOffset = new Vector3(0, 0, 0);
    public float rotationSmoothTime = 0.1f;
    public float pitchSpeed = 200f;
    public float minPitch = -30f;
    public float maxPitch = 80f;

    [Header("目标转向")]
    public bool enableTargetRotationFollow = true;
    public float targetRotationSmoothTime = 0.2f;
    public float maxRotationSpeed = 360f;
}

public class ThirthCamera : MonoBehaviour
{
    public Transform target;
    public CameraFollowSetting settings;

    // 位置跟踪
    private Vector3 positionVelocity;
    private Vector3 lastTargetPosition;
    private Vector3 targetVelocity;

    // 旋转跟踪
    private float yRotationVelocity;
    private float targetRotationVelocity;
    private float currentYRotation;
    private float currentPitch;
    private Rigidbody targetRigidbody;

    void Start()
    {
        // 保持编辑器设置的初始位置和旋转
        float offsetX = MathF.Abs(transform.position.x - target.position.x);
        float offsetY = MathF.Abs(transform.position.y - target.position.y);
        float offsetZ = MathF.Abs(transform.position.z - target.position.z);
        if (offsetX > offsetZ)
        {
             settings.localOffset = new Vector3(0,offsetY,-offsetX);
        }
        else if(offsetX < offsetZ)
        {
            settings.localOffset = new Vector3(0, offsetY, -offsetZ);
        }
            
        Debug.Log(offsetX + ":" + offsetY + ":" + offsetZ);
        Debug.Log(transform.position - target.position);
        settings.TPS_localOffset = settings.localOffset;
        currentYRotation = transform.eulerAngles.y;
        currentPitch = transform.eulerAngles.x;

        if (target != null)
        {
            targetRigidbody = target.GetComponent<Rigidbody>();
            if (targetRigidbody != null)
            {
                targetRigidbody.interpolation = RigidbodyInterpolation.Interpolate;
            }
            lastTargetPosition = target.position;
        }
    }

    void LateUpdate()
    {
        if (target == null) return;
        HandlePersonTransformation();
        UpdateTargetVelocity();
        HandleCameraInput();
        UpdateCamera();
    }
    void HandlePersonTransformation()
    {
        if (!settings.isTPS)
        {
            settings.localOffset = new Vector3(0, 0, 0);
        }
        else
        {
            settings.localOffset = settings.TPS_localOffset;
        }
    }
    void UpdateTargetVelocity()
    {
        targetVelocity = (target.position - lastTargetPosition) / Time.deltaTime;
        lastTargetPosition = target.position;
    }

    void HandleCameraInput()
    {
        if (Input.GetMouseButton(0))
        {
            float mouseX = Input.GetAxis("Mouse X");
            float mouseY = Input.GetAxis("Mouse Y");

            currentYRotation += mouseX * settings.pitchSpeed * Time.deltaTime;
            currentPitch = Mathf.Clamp(
                currentPitch - mouseY * settings.pitchSpeed * Time.deltaTime,
                settings.minPitch,
                settings.maxPitch
            );
        }
    }

    void UpdateCamera()
    {
        UpdatePosition();
        UpdateRotation();
        SyncTargetRotation();
    }

    void UpdatePosition()
    {
        if (!settings.isTPS)
        {
            // 第一人称模式下直接设置摄像机位置,禁用平滑和预测
            transform.position = target.TransformPoint(settings.localOffset);
            return;
        }

        Vector3 predictedPosition = GetPredictedPosition();
        ApplyAxisSmoothing(predictedPosition);
    }

    Vector3 GetPredictedPosition()
    {
        Vector3 baseOffset = target.TransformPoint(settings.localOffset);

        if (settings.usePositionPrediction)
        {
            Vector3 effectiveVelocity = targetRigidbody != null ?
                targetRigidbody.velocity : targetVelocity;
            return baseOffset + effectiveVelocity * settings.predictionFactor;
        }
        return baseOffset;
    }

    void ApplyAxisSmoothing(Vector3 targetPos)
    {
        // 水平方向
        Vector3 horizontalTarget = new Vector3(
            targetPos.x,
            transform.position.y,
            targetPos.z
        );

        horizontalTarget = Vector3.SmoothDamp(
            transform.position,
            horizontalTarget,
            ref positionVelocity,
            settings.positionSmoothTime,
            settings.maxFollowSpeed
        );

        // 垂直方向
        Vector3 verticalTarget = new Vector3(
            transform.position.x,
            targetPos.y,
            transform.position.z
        );

        verticalTarget = Vector3.SmoothDamp(
            transform.position,
            verticalTarget,
            ref positionVelocity,
            settings.verticalSmoothTime,
            settings.maxFollowSpeed
        );

        // 合并结果
        transform.position = new Vector3(
            horizontalTarget.x,
            verticalTarget.y,
            horizontalTarget.z
        );
    }

    void UpdateRotation()
    {
        float smoothedY = Mathf.SmoothDampAngle(
            transform.eulerAngles.y,
            currentYRotation,
            ref yRotationVelocity,
            settings.rotationSmoothTime
        );

        transform.rotation = Quaternion.Euler(
            currentPitch,
            smoothedY,
            0
        );
    }

    void SyncTargetRotation()
    {
        if (!settings.enableTargetRotationFollow) return;

        float targetDesiredY = currentYRotation;
        float currentTargetY = target.eulerAngles.y;

        float smoothedTargetY = Mathf.SmoothDampAngle(
            currentTargetY,
            targetDesiredY,
            ref targetRotationVelocity,
            settings.targetRotationSmoothTime,
            settings.maxRotationSpeed
        );

        ApplyTargetRotation(smoothedTargetY);
    }

    void ApplyTargetRotation(float yRotation)
    {
        Quaternion newRotation = Quaternion.Euler(0, yRotation, 0);

        if (targetRigidbody != null)
        {
            targetRigidbody.MoveRotation(newRotation);
        }
        else
        {
            target.rotation = newRotation;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值