如何制作与加速和减速统一的自由飞行相机脚本?

How to make a free fly camera script in unity with acceleration and decceleration?

我已经编写了一个相机自由飞行脚本,到目前为止,它可以通过围绕 y 轴和 z 轴统一旋转来自由观看。问题是我无法让它根据按下的键相对于它看起来的方向加速。

我已经尝试过使用像 transform.forward 这样的本地变换常量。由于某种原因它不起作用。我还尝试将局部方向矢量应用于加速度并将加速度矢量乘以 transform.rotation。我这样做是出于游戏创意,但也只是出于学习目的。

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

public class Controller : MonoBehaviour
{
    //unity controls and constants input
    [SerializeField] public float accelerationMod;
    [SerializeField] public float xAxisSensitivity;
    [SerializeField] public float yAxisSensitivity;
    [SerializeField] public float deccelerationMod;
    [SerializeField] public string forwards;
    [SerializeField] public string backwards;
    [SerializeField] public string left;
    [SerializeField] public string right;
    [SerializeField] public string up;
    [SerializeField] public string down;

    private Vector3 moveSpeed;
    private bool xVector;
    private bool yVector;
    private bool zVector;

    void Start()
    {
        moveSpeed = new Vector3();
    }

    // Update is called once per frame
    void Update()
    {
        //Debug.Log(moveSpeed);
        //was this axis moved on?
        xVector = false;
        yVector = false;
        zVector = false;
        //acceleration this iteration
        Vector3 acceleration = new Vector3();

        //mouse input
        float rotationHorizontal = xAxisSensitivity * Input.GetAxis("Mouse X");
        float rotationVertical = yAxisSensitivity * Input.GetAxis("Mouse Y");

        //applying mouse rotation
        transform.localEulerAngles = transform.localEulerAngles + new Vector3 (-rotationVertical, rotationHorizontal, 0);

        //key input detection
        if (Input.GetKey(forwards))
        {
            //Debug.Log(forwards);
            xVector = true;
            acceleration += transform.forward;
            Debug.Log(acceleration);
        }

        if (Input.GetKey(left))
        {
            //Debug.Log(left);
            zVector = true;
            acceleration += -transform.right;
        }

        if (Input.GetKey(backwards))
        {
            //Debug.Log(backwards);
            xVector = true;
            acceleration += -transform.forward;
        }

        if (Input.GetKey(right))
        {
            //Debug.Log(right);
            zVector = true;
            acceleration += transform.right;
        }

        if (Input.GetKey(up))
        {
            //Debug.Log(up);
            yVector = true;
            acceleration += transform.up;
        }

        if (Input.GetKey(down))
        {
            //Debug.Log(down);
            yVector = true;
            acceleration += -transform.up;
        }

        //decceleration functionality
        if (!xVector)
        {
            //Debug.Log("xVector");
            if (Math.Abs(moveSpeed.x) < deccelerationMod)
            {
                moveSpeed = new Vector3(0, moveSpeed.y, moveSpeed.z);
            }
            else
            {
                moveSpeed = new Vector3(moveSpeed.x - deccelerationMod * Math.Sign(moveSpeed.x), moveSpeed.y, moveSpeed.z);
            }
        }

        if (!yVector)
        {
            //Debug.Log("yVector");
            if (Math.Abs(moveSpeed.y) < deccelerationMod)
            {
                moveSpeed = new Vector3(moveSpeed.x, 0, moveSpeed.z);
            }
            else
            {
                moveSpeed = new Vector3(moveSpeed.x, moveSpeed.y - deccelerationMod * Math.Sign(moveSpeed.y), moveSpeed.z);
            }
        }

        if (!zVector)
        {
            //Debug.Log("zVector");
            if (Math.Abs(moveSpeed.z) < deccelerationMod)
            {
                moveSpeed = new Vector3(moveSpeed.x, moveSpeed.y, 0);
            }
            else
            {
                moveSpeed = new Vector3(moveSpeed.x, moveSpeed.y, moveSpeed.z - deccelerationMod * Math.Sign(moveSpeed.z));
            }
        }


        Debug.Log(acceleration);
        //processing acceleration and applying it to movementSpeed/Velocity.
        acceleration = transform.TransformVector(acceleration);
        acceleration.Normalize();
        acceleration *= accelerationMod;
        moveSpeed += acceleration;
        Debug.Log(moveSpeed);
        //applying movementSpeed/Velocity
        transform.Translate(moveSpeed);
    }
}


我最终想要一个具有 3 轴旋转控制、加速和减速功能的运动控制器。如果您熟悉游戏 "Space Engineers",我正在尝试复制他们的移动系统。目前运动似乎是随机的,但我怀疑这是因为我应用了错误的旋转或没有转换为正确的参考 space.

  1. 通过直接应用这些,您可以在旋转中获得万向节锁定。您应该在 全局 坐标中设置 Y 旋转,并且仅在 space.

  2. 中设置 X 旋转
  3. 然后你混淆了 leftbackwards 的情况 Input.GetKey(left) (应该使用 X-Axis)和 Input.GetKey(backwards) (应该使用 Z-Axis).

  4. 然后你在加速上使用了TransformVector

    Transforms vector from local space to world space.

    但是,transform.Translate(moveSpeed) 默认移动到 local space!因此 moveSpeedacceleration 应该留在本地 space.

我还稍微重构了你的代码:

  • 我宁愿使用 KeyCode 而不是 string 作为控件,所以你只能从现有的选项中 select ,这样不容易出错

  • 我个人总是会为垂直旋转添加一个夹紧,这样你就不会结束 upside-down

    • 你 could/should 也将 moveSpeed 限制在最大幅度以避免用户只是加速到无穷大而迷失在 space ;)

因此代码可能如下所示

public class Controller : MonoBehaviour
{
    [Header("Constants")]

    //unity controls and constants input
    public float AccelerationMod;
    public float XAxisSensitivity;
    public float YAxisSensitivity;
    public float DecelerationMod;

    [Space]

    [Range(0, 89)] public float MaxXAngle = 60f;

    [Space]

    public float MaximumMovementSpeed = 1f;

    [Header("Controls")]

    public KeyCode Forwards = KeyCode.W;
    public KeyCode Backwards = KeyCode.S;
    public KeyCode Left = KeyCode.A;
    public KeyCode Right = KeyCode.D;
    public KeyCode Up = KeyCode.Q;
    public KeyCode Down = KeyCode.E;

    private Vector3 _moveSpeed;


    private void Start()
    {
        _moveSpeed = Vector3.zero;
    }

    // Update is called once per frame
    private void Update()
    {
        HandleMouseRotation();

        var acceleration = HandleKeyInput();

        _moveSpeed += acceleration;

        HandleDeceleration(acceleration);

        // clamp the move speed
        if(_moveSpeed.magnitude > MaximumMovementSpeed)
        {
            _moveSpeed = _moveSpeed.normalized * MaximumMovementSpeed;
        }

        transform.Translate(_moveSpeed);
    }

    private Vector3 HandleKeyInput()
    {
        var acceleration = Vector3.zero;

        //key input detection
        if (Input.GetKey(Forwards))
        {
            acceleration.z += 1;
        }

        if (Input.GetKey(Backwards))
        {
            acceleration.z -= 1;
        }

        if (Input.GetKey(Left))
        {
            acceleration.x -= 1;
        }

        if (Input.GetKey(Right))
        {
            acceleration.x += 1;
        }

        if (Input.GetKey(Up))
        {
            acceleration.y += 1;
        }

        if (Input.GetKey(Down))
        {
            acceleration.y -= 1;
        }

        return acceleration.normalized * AccelerationMod;
    }

    private float _rotationX;

    private void HandleMouseRotation()
    {
        //mouse input
        var rotationHorizontal = XAxisSensitivity * Input.GetAxis("Mouse X");
        var rotationVertical = YAxisSensitivity * Input.GetAxis("Mouse Y");

        //applying mouse rotation
        // always rotate Y in global world space to avoid gimbal lock
        transform.Rotate(Vector3.up * rotationHorizontal, Space.World);

        var rotationY = transform.localEulerAngles.y;

        _rotationX += rotationVertical;
        _rotationX = Mathf.Clamp(_rotationX, -MaxXAngle, MaxXAngle);

        transform.localEulerAngles = new Vector3(-_rotationX, rotationY, 0);
    }

    private void HandleDeceleration(Vector3 acceleration)
    {
        //deceleration functionality
        if (Mathf.Approximately(Mathf.Abs(acceleration.x), 0))
        {
            if (Mathf.Abs(_moveSpeed.x) < DecelerationMod)
            {
                _moveSpeed.x = 0;
            }
            else
            {
                _moveSpeed.x -= DecelerationMod * Mathf.Sign(_moveSpeed.x);
            }
        }

        if (Mathf.Approximately(Mathf.Abs(acceleration.y), 0))
        {
            if (Mathf.Abs(_moveSpeed.y) < DecelerationMod)
            {
                _moveSpeed.y = 0;
            }
            else
            {
                _moveSpeed.y -= DecelerationMod * Mathf.Sign(_moveSpeed.y);
            }
        }

        if (Mathf.Approximately(Mathf.Abs(acceleration.z), 0))
        {
            if (Mathf.Abs(_moveSpeed.z) < DecelerationMod)
            {
                _moveSpeed.z = 0;
            }
            else
            {
                _moveSpeed.z -= DecelerationMod * Mathf.Sign(_moveSpeed.z);
            }
        }
    }
}

这应该会给您想要的行为。

Here is what it looks like (gif too big for adding here directly)