如何制作与加速和减速统一的自由飞行相机脚本?
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.
通过直接应用这些,您可以在旋转中获得万向节锁定。您应该在 全局 坐标中设置 Y
旋转,并且仅在 space.
中设置 X
旋转
然后你混淆了 left
和 backwards
的情况 Input.GetKey(left)
(应该使用 X-Axis)和 Input.GetKey(backwards)
(应该使用 Z-Axis).
然后你在加速上使用了TransformVector
Transforms vector from local space to world space.
但是,transform.Translate(moveSpeed)
默认移动到 local space!因此 moveSpeed
和 acceleration
应该留在本地 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)
我已经编写了一个相机自由飞行脚本,到目前为止,它可以通过围绕 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.
通过直接应用这些,您可以在旋转中获得万向节锁定。您应该在 全局 坐标中设置
Y
旋转,并且仅在 space. 中设置 然后你混淆了
left
和backwards
的情况Input.GetKey(left)
(应该使用 X-Axis)和Input.GetKey(backwards)
(应该使用 Z-Axis).然后你在加速上使用了
TransformVector
Transforms vector from local space to world space.
但是,
transform.Translate(moveSpeed)
默认移动到 local space!因此moveSpeed
和acceleration
应该留在本地 space.
X
旋转
我还稍微重构了你的代码:
我宁愿使用
KeyCode
而不是string
作为控件,所以你只能从现有的选项中 select ,这样不容易出错我个人总是会为垂直旋转添加一个夹紧,这样你就不会结束 upside-down
- 你 could/should 也将
moveSpeed
限制在最大幅度以避免用户只是加速到无穷大而迷失在 space ;)
- 你 could/should 也将
因此代码可能如下所示
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)