Created
April 20, 2023 14:17
-
-
Save Monsoonexe/958214c720f4079d6284e49d933707f8 to your computer and use it in GitHub Desktop.
PlayerController
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityEngine.InputSystem; | |
using System.Collections; | |
using UnityEngine.XR.Interaction.Toolkit; | |
using Sirenix.OdinInspector; | |
using ScriptableObjectArchitecture; | |
using HouseOfCards.Player; | |
using RichPackage; | |
using CommonUsages = UnityEngine.XR.CommonUsages; | |
//TODO - add this to Player namespace | |
//TODO - add ETurnType Variable similar to locomotion type | |
/// <summary> | |
/// | |
/// </summary> | |
public class PlayerController : RichMonoBehaviour | |
{ | |
private static PlayerController instance; | |
public static PlayerController Instance => instance; | |
#region Prefab Properties | |
[Title("Prefab Refs")] | |
[SerializeField, Required] | |
private CharacterController characterController; | |
[Title("Locomotion")] | |
[SerializeField, Required] | |
private ContinuousMoveProviderBase movementProvider; | |
[Title("Turners")] | |
[SerializeField, Required] | |
private SnapTurnProviderBase snapTurnProvider; | |
[SerializeField, Required] | |
private ContinuousTurnProviderBase continuousTurnProvider; | |
[Title("Teleporters")] | |
[SerializeField, Required] | |
private XRController leftRayTeleporter; | |
[SerializeField, Required] | |
private XRController rightRayTeleporter; | |
[Title("Grab Interactors")] | |
[SerializeField, Required] | |
private XRController leftHandController; | |
[SerializeField, Required] | |
private XRController rightHandController; | |
[Title("Ray Interactors")] | |
[SerializeField, Required] | |
private XRController leftRayInteractor; | |
[SerializeField, Required] | |
private XRController rightRayInteractor; | |
#endregion | |
#region Option Properties | |
/// <summary> | |
/// How should the Player handle turning. | |
/// </summary> | |
[FoldoutGroup("Locomotion Options")] | |
[Tooltip("How should the Player handle turning.")] | |
[SerializeField, Required] | |
private ETurnTypeVariable TurnType; | |
/// <summary> | |
/// How should the Player handle moving. | |
/// </summary> | |
[FoldoutGroup("Locomotion Options")] | |
[SerializeField, Required] | |
[Tooltip("How should the Player handle moving.")] | |
private ELocomotionTypeVariable locomotionType; | |
/// <summary> | |
/// Is this teleporter allowed to be used? | |
/// </summary> | |
[FoldoutGroup("Locomotion Options")] | |
[ShowInInspector] | |
[PropertyTooltip("Is this teleporter allowed to be used?")] | |
public bool LeftTeleporterEnabled { get; set; } = true; | |
/// <summary> | |
/// Is this teleporter allowed to be used? | |
/// </summary> | |
[FoldoutGroup("Locomotion Options")] | |
[ShowInInspector] | |
[PropertyTooltip("Is this teleporter allowed to be used?")] | |
public bool RightTeleporterEnabled { get; set; } = true; | |
#endregion | |
[Title("Resources")] | |
[SerializeField, Required] | |
private GameEvent toggleDashboardEvent; | |
#region Runtime State | |
//runtime data | |
private LocomotionProvider activeTurnProvider; | |
private bool leftTeleportRayActiveState; //state for "edge" control | |
private bool rightTeleportRayActiveState;//state for "edge" control | |
/// <summary> | |
/// Property backer. Do not interact with this field, rather | |
/// interact with <see cref="TurningEnabled"/>. | |
/// </summary> | |
private bool _turningEnabled = true; //backing state | |
//button states (grosssssss) | |
private bool lastMenuButtonState = false; //TODO button class | |
#endregion | |
private void Reset() | |
{ | |
SetDevDescription("I help control the player!"); | |
characterController = GetComponent<CharacterController>(); | |
movementProvider = GetComponent<ContinuousMoveProviderBase>(); | |
continuousTurnProvider = GetComponent<ContinuousTurnProviderBase>(); | |
snapTurnProvider = GetComponent<SnapTurnProviderBase>(); | |
} | |
protected override void Awake() | |
{ | |
base.Awake(); | |
InitSingleton(this, ref instance, | |
dontDestroyOnLoad: false); | |
//subscribe to events | |
locomotionType.AddListener(SetLocomotionTypeInternal); | |
TurnType.AddListener(SetTurningTypeInternal); | |
//set default states | |
SetLocomotionTypeInternal(locomotionType); | |
continuousTurnProvider.enabled = false; | |
snapTurnProvider.enabled = false; | |
SetTurningTypeInternal(TurnType); | |
} | |
private IEnumerator Start() | |
{ | |
yield return null; //wait a frame to force teleporters off to start with | |
//hide visuals of non-connected controllers | |
TeleportersEnabled = false; //start with inactive. | |
//InitStaticLocomotion(); | |
} | |
private void OnDestroy() | |
{ | |
//unsubscribe from events | |
locomotionType.RemoveListener(SetLocomotionTypeInternal); | |
TurnType.RemoveListener(SetTurningTypeInternal); | |
} | |
private void Update() | |
{ | |
//CheckLocomotionInput(); // Disable in favor of static locomotion system | |
CheckDashboardInput(); | |
} | |
private void CheckLocomotionInput() | |
{ | |
//enable teleport ray behaviour when teleporting is set | |
//as preferred method of locomotion | |
if (locomotionType.Value == ELocomotionType.TELEPORT) | |
{ | |
//handle left stick | |
var input = leftHandController.ReadPrimaryJoystickInput(); | |
var magnitude = input.magnitude; | |
var adjustedMagnitude = GetDeadzoneAdjustedValue(magnitude); | |
//check if teleport is activated | |
if (adjustedMagnitude > 0) | |
{ | |
if (!leftTeleportRayActiveState) | |
{ | |
//update state after edge | |
leftTeleportRayActiveState = true; | |
//disable interact ray | |
leftRayInteractor.gameObject.SetActive(false); | |
//enable teleport ray | |
LeftTeleportRayActive = true; | |
} | |
} | |
else if (leftTeleportRayActiveState) | |
{ | |
//update state after edge | |
leftTeleportRayActiveState = false; | |
//enable teleport ray | |
LeftTeleportRayActive = false; | |
//disable interact ray | |
leftRayInteractor.gameObject.SetActive(true); | |
} | |
//handle right stick | |
input = rightHandController.ReadPrimaryJoystickInput(); | |
magnitude = input.magnitude; | |
adjustedMagnitude = GetDeadzoneAdjustedValue(magnitude); | |
//check if teleport is activated | |
if (adjustedMagnitude > 0) | |
{ | |
if (!rightTeleportRayActiveState) | |
{ | |
//update state after edge | |
rightTeleportRayActiveState = true; | |
//disable interact ray | |
rightRayInteractor.gameObject.SetActive(false); | |
//enable teleport ray | |
RightTeleportRayActive = true; | |
} | |
} | |
else if (rightTeleportRayActiveState) | |
{ | |
//update state after edge | |
rightTeleportRayActiveState = false; | |
//enable teleport ray | |
RightTeleportRayActive = false; | |
//disable interact ray | |
rightRayInteractor.gameObject.SetActive(true); | |
} | |
} | |
} | |
public void InitStaticLocomotion() | |
{ | |
// Enable Left Teleport | |
//leftTeleportRayActiveState = true; //update state after edge | |
leftRayInteractor.gameObject.SetActive(false); //disable interact ray | |
LeftTeleportRayActive = true; //enable teleport ray | |
// Enable Right Interact | |
//rightTeleportRayActiveState = false; //update state after edge | |
RightTeleportRayActive = false; //enable teleport ray | |
rightRayInteractor.gameObject.SetActive(true); //disable interact ray | |
} | |
private void CheckDashboardInput() | |
{ | |
//handle specific button presses | |
bool hasLeftMenu = leftHandController.inputDevice.TryGetFeatureValue( | |
CommonUsages.menuButton, out bool menuButtonState); | |
if (hasLeftMenu && menuButtonState && !lastMenuButtonState) | |
{ | |
RequestToggleMainMenu(default); | |
} | |
lastMenuButtonState = menuButtonState; | |
} | |
#region Turning | |
/// <summary> | |
/// Is turning allowed. | |
/// </summary> | |
[FoldoutGroup("Locomotion Options")] | |
[ShowInInspector] | |
[PropertyTooltip("Is turning allowed.")] | |
public bool TurningEnabled | |
{ | |
get => _turningEnabled; | |
set | |
{ | |
_turningEnabled = value; | |
if (activeTurnProvider != null) | |
activeTurnProvider.enabled = _turningEnabled; | |
} | |
} | |
/// <exception cref="System.NotImplementedException"></exception> | |
private void SetTurningTypeInternal(ETurnType turnType) | |
{ | |
//disable current method | |
if (activeTurnProvider != null) | |
{ | |
activeTurnProvider.enabled = false; | |
activeTurnProvider = null; | |
} | |
//determine new method | |
switch (turnType) | |
{ | |
case ETurnType.None: | |
//do nothing as already disabled | |
break; | |
case ETurnType.Snap: | |
activeTurnProvider = snapTurnProvider; | |
activeTurnProvider.enabled = TurningEnabled; //setting the type doesn't imply enabling | |
break; | |
case ETurnType.Continuous: | |
activeTurnProvider = continuousTurnProvider; | |
activeTurnProvider.enabled = TurningEnabled; //setting the type doesn't imply enabling | |
break; | |
default: | |
throw new System.NotImplementedException($"{turnType} is not implemented!"); | |
} | |
} | |
public void SetTurningType(ETurnType turnType) | |
=> TurnType.Value = turnType; //triggers Internal call | |
public void SetTurningType(ETurnType turnType, bool enabled) | |
{ | |
TurningEnabled = enabled; | |
SetTurningType(turnType); | |
} | |
#endregion | |
public bool MoveEnabled | |
{ | |
get => movementProvider.enabled; | |
set => movementProvider.enabled = value; | |
} | |
private void RequestToggleMainMenu(InputAction.CallbackContext context) | |
{ | |
Debug.Log("Menu button pressed"); | |
toggleDashboardEvent.Raise(); | |
} | |
public void SetLocomotionType(ELocomotionType newType) | |
=> locomotionType.Value = newType; //trigger event | |
private void SetLocomotionTypeInternal(ELocomotionType locomotionType) | |
{ //disable all, then set what is active | |
DisableLocomotion(); | |
switch (locomotionType) | |
{ | |
case ELocomotionType.NONE: | |
//nada, already disabled | |
break; | |
case ELocomotionType.TELEPORT: | |
TeleportersEnabled = true; | |
break; | |
case ELocomotionType.CONTINUOUS: | |
MoveEnabled = true; | |
break; | |
default: | |
Debug.LogError("Case not accounted for: " + locomotionType); | |
break; | |
} | |
} | |
/// <summary> | |
/// Enable turning, moving, and teleporting. | |
/// </summary> | |
public void EnableLocomotion() | |
=> MoveEnabled = TeleportersEnabled = true; | |
/// <summary> | |
/// Disable turning, moving, and teleporting. | |
/// </summary> | |
public void DisableLocomotion() | |
=> MoveEnabled = TeleportersEnabled = false; | |
#region Teleportation State Controllers | |
/// <summary> | |
/// Set turning, moving, and teleporting. | |
/// </summary> | |
public bool TeleportersEnabled | |
{ | |
get => LeftTeleportRayActive;// = rightRayTeleporter.enabled; | |
set | |
{ | |
LeftTeleportRayActive = value; | |
RightTeleportRayActive = value; | |
} | |
} | |
public bool LeftTeleportRayActive | |
{ | |
get => leftRayTeleporter.gameObject.activeSelf && LeftTeleporterEnabled; | |
set | |
{ | |
value = value && LeftTeleporterEnabled; | |
leftRayTeleporter.gameObject.SetActive(value); | |
//force state off if on, otherwise keep same | |
leftTeleportRayActiveState = leftTeleportRayActiveState && value; | |
} | |
} | |
public bool RightTeleportRayActive | |
{ | |
get => rightRayTeleporter.gameObject.activeSelf && RightTeleporterEnabled; | |
set | |
{ | |
value = value && RightTeleporterEnabled; | |
rightRayTeleporter.gameObject.SetActive(value); | |
//force state off if on, otherwise keep same | |
rightTeleportRayActiveState = rightTeleportRayActiveState && value; | |
} | |
} | |
public void EnableTeleportation() | |
=> TeleportersEnabled = true; | |
public void DisableTeleportation() | |
=> TeleportersEnabled = false; | |
#endregion | |
public void TeleportTo(Transform place) | |
=> TeleportTo(place.position); | |
public void TeleportTo(in Vector3 point) | |
{ | |
myTransform.position = point; | |
} | |
/// <summary> | |
/// Step given amount. | |
/// </summary> | |
/// <param name="motion"></param> | |
public void Move(in Vector3 motion) | |
=> characterController.Move(motion); //forward call | |
/// <summary> | |
/// Teleport to position. | |
/// </summary> | |
/// <param name="worldPosition"></param> | |
public void MoveTo(in Vector3 worldPosition) | |
=> myTransform.position = worldPosition; | |
public static Vector2 GetDeadzoneAdjustedValue(Vector2 value) | |
{ | |
var magnitude = value.magnitude; | |
var newMagnitude = GetDeadzoneAdjustedValue(magnitude); | |
if (Mathf.Approximately(newMagnitude, 0f)) | |
value = Vector2.zero; | |
else | |
value *= newMagnitude / magnitude; | |
return value; | |
} | |
public static float GetDeadzoneAdjustedValue(float value) | |
{ | |
var min = 0.125f; | |
var max = 0.925f; | |
var absValue = RichMath.AbsoluteValue(value); | |
if (absValue < min) | |
return 0f; | |
if (absValue > max) | |
return Mathf.Sign(value); | |
return Mathf.Sign(value) * ((absValue - min) / (max - min)); | |
} | |
} | |
public static class Controller_Extensions | |
{ | |
public static Vector2 ReadPrimaryJoystickInput( | |
this XRController device) | |
{ | |
Vector2 input = Vector2.zero; | |
if (device.enableInputActions) | |
device.inputDevice.TryGetFeatureValue( | |
CommonUsages.primary2DAxis, out input); | |
return input; | |
} | |
public static Vector2 ReadSecondaryJoystickInput( | |
this XRController device) | |
{ | |
Vector2 input = Vector2.zero; | |
if (device.enableInputActions) | |
device.inputDevice.TryGetFeatureValue( | |
CommonUsages.secondary2DAxis, out input); | |
return input; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment