204 lines
7.8 KiB
C#
204 lines
7.8 KiB
C#
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
namespace FloodSWE.Drone
|
|
{
|
|
/// <summary>
|
|
/// Action-based XR drone movement for OpenXR/Input System.
|
|
/// Attach to XR Origin root and wire actions from an InputActionAsset.
|
|
/// </summary>
|
|
public sealed class SimpleXrDroneController : MonoBehaviour
|
|
{
|
|
[Header("References")]
|
|
[SerializeField] private Transform movementReference;
|
|
|
|
[Header("Actions")]
|
|
[SerializeField] private InputActionReference moveAction;
|
|
[SerializeField] private InputActionReference liftYawAction;
|
|
[SerializeField] private InputActionReference liftUpButtonAction;
|
|
[SerializeField] private InputActionReference liftDownButtonAction;
|
|
[SerializeField] private InputActionReference boostAction;
|
|
[SerializeField] private bool autoEnableActions = true;
|
|
[SerializeField] private bool useLiftAxis = true;
|
|
[SerializeField] private bool useLiftButtons = true;
|
|
|
|
[Header("Flight")]
|
|
[SerializeField] private float moveSpeedMps = 8.0f;
|
|
[SerializeField] private float liftSpeedMps = 5.0f;
|
|
[SerializeField] private float yawSpeedDegPerSec = 75.0f;
|
|
[SerializeField] private float boostMultiplier = 2.0f;
|
|
[SerializeField] private float axisDeadzone = 0.15f;
|
|
[SerializeField] private bool flattenReferenceForward = true;
|
|
|
|
private bool moveEnabledByThis;
|
|
private bool liftYawEnabledByThis;
|
|
private bool liftUpEnabledByThis;
|
|
private bool liftDownEnabledByThis;
|
|
private bool boostEnabledByThis;
|
|
private bool boostActive;
|
|
private float currentHorizontalSpeedMps;
|
|
private float currentVerticalSpeedMps;
|
|
private float currentCombinedSpeedMps;
|
|
private float currentMaxSpeedMps;
|
|
|
|
public float MoveSpeedMps => Mathf.Max(0.0f, moveSpeedMps);
|
|
public float LiftSpeedMps => Mathf.Max(0.0f, liftSpeedMps);
|
|
public float YawSpeedDegPerSec => Mathf.Max(0.0f, yawSpeedDegPerSec);
|
|
public float BoostMultiplier => Mathf.Max(1.0f, boostMultiplier);
|
|
public bool BoostActive => boostActive;
|
|
public float CurrentHorizontalSpeedMps => currentHorizontalSpeedMps;
|
|
public float CurrentVerticalSpeedMps => currentVerticalSpeedMps;
|
|
public float CurrentCombinedSpeedMps => currentCombinedSpeedMps;
|
|
public float CurrentMaxSpeedMps => currentMaxSpeedMps;
|
|
public float ConfiguredBaseMaxSpeedMps => Mathf.Sqrt(MoveSpeedMps * MoveSpeedMps + LiftSpeedMps * LiftSpeedMps);
|
|
public float ConfiguredBoostedMaxSpeedMps => ConfiguredBaseMaxSpeedMps * BoostMultiplier;
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (!autoEnableActions)
|
|
{
|
|
return;
|
|
}
|
|
|
|
moveEnabledByThis = TryEnable(moveAction);
|
|
liftYawEnabledByThis = TryEnable(liftYawAction);
|
|
liftUpEnabledByThis = TryEnable(liftUpButtonAction);
|
|
liftDownEnabledByThis = TryEnable(liftDownButtonAction);
|
|
boostEnabledByThis = TryEnable(boostAction);
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (!autoEnableActions)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TryDisable(moveAction, moveEnabledByThis);
|
|
TryDisable(liftYawAction, liftYawEnabledByThis);
|
|
TryDisable(liftUpButtonAction, liftUpEnabledByThis);
|
|
TryDisable(liftDownButtonAction, liftDownEnabledByThis);
|
|
TryDisable(boostAction, boostEnabledByThis);
|
|
moveEnabledByThis = false;
|
|
liftYawEnabledByThis = false;
|
|
liftUpEnabledByThis = false;
|
|
liftDownEnabledByThis = false;
|
|
boostEnabledByThis = false;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
Vector2 move = ReadVector2(moveAction);
|
|
Vector2 liftYaw = ReadVector2(liftYawAction);
|
|
bool boost = ReadButton(boostAction);
|
|
|
|
move = ApplyDeadzone(move, axisDeadzone);
|
|
liftYaw = ApplyDeadzone(liftYaw, axisDeadzone);
|
|
|
|
float liftInput = 0.0f;
|
|
if (useLiftAxis)
|
|
liftInput += liftYaw.y;
|
|
if (useLiftButtons)
|
|
{
|
|
if (ReadButton(liftUpButtonAction))
|
|
liftInput += 1.0f;
|
|
if (ReadButton(liftDownButtonAction))
|
|
liftInput -= 1.0f;
|
|
}
|
|
liftInput = Mathf.Clamp(liftInput, -1.0f, 1.0f);
|
|
|
|
float multiplier = boost ? Mathf.Max(1.0f, boostMultiplier) : 1.0f;
|
|
boostActive = boost;
|
|
Transform reference = movementReference != null ? movementReference : transform;
|
|
Vector3 forward = reference.forward;
|
|
Vector3 right = reference.right;
|
|
|
|
if (flattenReferenceForward)
|
|
{
|
|
forward.y = 0.0f;
|
|
right.y = 0.0f;
|
|
forward = forward.sqrMagnitude > 1e-6f ? forward.normalized : Vector3.forward;
|
|
right = right.sqrMagnitude > 1e-6f ? right.normalized : Vector3.right;
|
|
}
|
|
|
|
Vector3 planarVelocity =
|
|
(forward * move.y + right * move.x) * Mathf.Max(0.0f, moveSpeedMps) * multiplier;
|
|
Vector3 liftVelocity = Vector3.up * liftInput * Mathf.Max(0.0f, liftSpeedMps) * multiplier;
|
|
transform.position += (planarVelocity + liftVelocity) * Time.deltaTime;
|
|
currentHorizontalSpeedMps = planarVelocity.magnitude;
|
|
currentVerticalSpeedMps = Mathf.Abs(liftVelocity.y);
|
|
currentCombinedSpeedMps = (planarVelocity + liftVelocity).magnitude;
|
|
float maxMove = Mathf.Max(0.0f, moveSpeedMps) * multiplier;
|
|
float maxLift = Mathf.Max(0.0f, liftSpeedMps) * multiplier;
|
|
currentMaxSpeedMps = Mathf.Sqrt(maxMove * maxMove + maxLift * maxLift);
|
|
|
|
float yawDelta = liftYaw.x * Mathf.Max(0.0f, yawSpeedDegPerSec) * Time.deltaTime;
|
|
transform.Rotate(0.0f, yawDelta, 0.0f, Space.World);
|
|
}
|
|
|
|
public void SetMoveSpeed(float value)
|
|
{
|
|
moveSpeedMps = Mathf.Max(0.0f, value);
|
|
}
|
|
|
|
public void SetLiftSpeed(float value)
|
|
{
|
|
liftSpeedMps = Mathf.Max(0.0f, value);
|
|
}
|
|
|
|
public void SetYawSpeed(float value)
|
|
{
|
|
yawSpeedDegPerSec = Mathf.Max(0.0f, value);
|
|
}
|
|
|
|
public void SetBoostMultiplier(float value)
|
|
{
|
|
boostMultiplier = Mathf.Max(1.0f, value);
|
|
}
|
|
|
|
private static bool TryEnable(InputActionReference actionRef)
|
|
{
|
|
InputAction action = actionRef != null ? actionRef.action : null;
|
|
if (action == null || action.enabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
action.Enable();
|
|
return true;
|
|
}
|
|
|
|
private static void TryDisable(InputActionReference actionRef, bool enabledByThis)
|
|
{
|
|
if (!enabledByThis)
|
|
{
|
|
return;
|
|
}
|
|
|
|
InputAction action = actionRef != null ? actionRef.action : null;
|
|
if (action != null && action.enabled)
|
|
{
|
|
action.Disable();
|
|
}
|
|
}
|
|
|
|
private static Vector2 ReadVector2(InputActionReference actionRef)
|
|
{
|
|
InputAction action = actionRef != null ? actionRef.action : null;
|
|
return action != null ? action.ReadValue<Vector2>() : Vector2.zero;
|
|
}
|
|
|
|
private static bool ReadButton(InputActionReference actionRef)
|
|
{
|
|
InputAction action = actionRef != null ? actionRef.action : null;
|
|
return action != null && action.IsPressed();
|
|
}
|
|
|
|
private static Vector2 ApplyDeadzone(Vector2 value, float deadzone)
|
|
{
|
|
float threshold = Mathf.Clamp(deadzone, 0.0f, 0.99f);
|
|
return value.sqrMagnitude >= threshold * threshold ? value : Vector2.zero;
|
|
}
|
|
}
|
|
}
|