using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.XR.Interaction.Toolkit; using jeanf.EventSystem; using jeanf.vrplayer; using jeanf.validationTools; namespace jeanf.vrplayer { public class PlayerActionManager : MonoBehaviour, IValidatable { #region Debug variables public bool isDebug { get => _isDebug; set => _isDebug = value; } [SerializeField] private bool _isDebug = false; public bool IsValid { get; private set; } #endregion #region input action variables //Action listing variables [SerializeField] private List _inputActionList = new List(); [Validation("A reference to InputActionAsset is required.")] [SerializeField] private InputActionAsset m_InputActionAsset; [Validation("A reference to ActionContainerSO is required.")] [SerializeField] private ActionContainerSO _actionContainer; [Validation("A reference to ActionRebindEventChannelSO is required.")] [SerializeField] private ActionRebindEventChannelSO actionRebindedListener; #endregion private void Awake() { ListAllInputs(); } #if UNITY_EDITOR private void OnValidate() { var invalidObjects = new List(); var errorMessages = new List(); var validityCheck = true; invalidObjects.Clear(); if(_actionContainer == null) { invalidObjects.Add(_actionContainer); errorMessages.Add("No action container SO set"); validityCheck = false; } if(actionRebindedListener == null) { invalidObjects.Add(actionRebindedListener); errorMessages.Add("No ActionRebindEventChannelSO set"); validityCheck = false; } if (m_InputActionAsset == null) { invalidObjects.Add(m_InputActionAsset); errorMessages.Add("No InputActionAsset set"); validityCheck = false; } IsValid = validityCheck; if (!IsValid) return; if (IsValid && !Application.isPlaying) return; for (int i = 0; i < invalidObjects.Count; i++) { Debug.LogError($"Error: {errorMessages[i]} ", this.gameObject); } } #endif private void OnEnable() { foreach (var action in _inputActionList) { action.Enable(); action.performed += ctx => ForwardActionToSO(_actionContainer._actions[action], ctx); } actionRebindedListener.OnEventRaised += (input, value) => RebindInput(input, value); } private void OnDisable() => Unsubscribe(); private void OnDestroy() => Unsubscribe(); private void Unsubscribe() { foreach (var action in _inputActionList) { action.performed -= ctx => ForwardActionToSO(_actionContainer._actions[action], ctx); action.Disable(); } _actionContainer._actions.Clear(); _actionContainer._actions.TrimExcess(); _inputActionList.Clear(); _inputActionList.TrimExcess(); actionRebindedListener.OnEventRaised -= (input, value) => RebindInput(input, value); } #if UNITY_EDITOR public void CreatePlayerActions() { foreach (var action in m_InputActionAsset) { ActionSO actionSO = Resources.Load($"Player/Actions/{action.actionMap.name}_{action.name}"); if (actionSO == null) { actionSO = (ActionSO)ScriptableObject.CreateInstance($"ActionSO"); actionSO.name = $"{action.actionMap.name}_{action.name}"; const string path = "Assets/Resources/Player/Actions"; if (!Directory.Exists(path)) { var folderTree = path.Split('/'); var previousTest = ""; for (var i = 0; i < folderTree.Length; i++) { previousTest += $"{folderTree[i]}/"; if (!Directory.Exists(folderTree[i])) { Debug.Log($"Directory {folderTree[i]} does not exist, creating it." ); Directory.CreateDirectory($"{previousTest}"); } else { Debug.Log($"Directory {folderTree[i]} exists, going forward." ); } } } AssetDatabase.CreateAsset(actionSO, $"{path}/{actionSO.name}.asset"); } actionSO.actionName = action.name; actionSO.inputAction = action; actionSO.playerInputComponent = GetComponent(); actionSO.bindings.Clear(); actionSO.bindings.TrimExcess(); foreach(var binding in action.bindings) { actionSO.bindings.Add(binding); } EditorUtility.SetDirty(actionSO); } } #endif private void ListAllInputs() { foreach (var action in m_InputActionAsset) { ActionSO actionSO = Resources.Load($"Player/Actions/{action.actionMap.name}_{action.name}"); if (actionSO == null) { Debug.LogWarning("Did not found actionSO. Press the \"Create Player Actions\" button in the PlayerActionManager component on the Player prefab "); } if (!_actionContainer._actions.ContainsKey(action)) { _actionContainer._actions.Add(action, actionSO); } } } void RebindInput(InputAction inputAction, int index) { if (_actionContainer._actions[inputAction] != null && _actionContainer._actions[inputAction].canRebind) { _actionContainer._actions[inputAction].inputAction.ApplyBindingOverride(index, inputAction.bindings[index].overridePath); } } private Action ForwardActionToSO(ActionSO action, InputAction.CallbackContext ctx) { Action functionToCall = null; if (action.eventChannel == null) return null; var actionType = action.eventChannel.GetType().ToString(); switch (actionType) { case "IntEventChannelSO": IntEventChannelSO intEventChannelSO = (IntEventChannelSO)action.eventChannel; functionToCall = delegate { intEventChannelSO.RaiseEvent(ctx.ReadValue()); }; break; case "VoidEventChannelSO": VoidEventChannelSO voidEventChannelSO = (VoidEventChannelSO)action.eventChannel; functionToCall = delegate { voidEventChannelSO.RaiseEvent(); }; break; case "BoolEventChannelSO": BoolEventChannelSO boolEventChannelSO = (BoolEventChannelSO)action.eventChannel; functionToCall = delegate { boolEventChannelSO.RaiseEvent(ctx.ReadValue()); }; break; case "FloatEventChannelSO": FloatEventChannelSO floatEventChannelSO = (FloatEventChannelSO)action.eventChannel; functionToCall = delegate { floatEventChannelSO.RaiseEvent(ctx.ReadValue()); }; break; case "Vector2EventChannelSO": Vector3EventChannelSO vector2EventChannelSO = (Vector3EventChannelSO)action.eventChannel; functionToCall = delegate { vector2EventChannelSO.RaiseEvent(ctx.ReadValue()); }; break; case "Vector3EventChannelSO": Vector3EventChannelSO vector3EventChannelSO = (Vector3EventChannelSO)action.eventChannel; functionToCall = delegate { vector3EventChannelSO.RaiseEvent(ctx.ReadValue()); }; break; case "QuaternionEventChannelSO": QuaternionEventChannelSO quaternionEventChannelSO = (QuaternionEventChannelSO)action.eventChannel; functionToCall = delegate { quaternionEventChannelSO.RaiseEvent(ctx.ReadValue()); }; break; //case "XRBaseInteractorEventChannelSO": // XRBaseInteractorEventChannelSO xRBaseInteractorEventChannelSO = (XRBaseInteractorEventChannelSO)action.eventChannel; // XRBaseInteractor xrBaseInteractor = xRBaseInteractorEventChannelSO.OnEventRaised(ctx.ReadValue()); // functionToCall = delegate { xRBaseInteractorEventChannelSO.RaiseEvent(xrBaseInteractor); }; // break; default: Debug.Log("Null"); break; } return functionToCall; } } }