Last active
August 5, 2025 04:22
-
-
Save thsbrown/af0be2c7193a7b5ffeca09ad020ff9e6 to your computer and use it in GitHub Desktop.
Example of a Service in Command Center Earth (Singleton)
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 System; | |
| using System.Linq; | |
| using _StudioName.Scripts.Runtime.Attributes; | |
| using Sirenix.OdinInspector; | |
| using Sirenix.Serialization; | |
| using StudioName.Runtime; | |
| using UnityEngine; | |
| using UnityEngine.InputSystem; | |
| using UnityEngine.InputSystem.LowLevel; | |
| namespace _Game_Assets.Scripts.Systems | |
| { | |
| [ShowOdinSerializedPropertiesInInspector] | |
| [Singleton("",false)] | |
| public class ControlsService : Singleton<ControlsService>, DefaultControls.IMissileActions, ISerializationCallbackReceiver | |
| { | |
| /// <summary> | |
| /// The parent gameobject that contains our mobile button basic layout. <see cref="MobileButtonLayout.Basic"/> | |
| /// </summary> | |
| [Tooltip("The parent gameobject that contains our mobile button basic layout. See MobileButtonLayout.Basic")] | |
| public GameObject mobileBasicButtonLayout; | |
| /// <summary> | |
| /// The canvas we will use to render our controls on screen. | |
| /// </summary> | |
| public Canvas controlsCanvas; | |
| /// <summary> | |
| /// Settings that will be used to render our controls on screen. | |
| /// </summary> | |
| public ControlsScreenSpaceRenderModeSettings controlsScreenSpaceRenderModeSettings; | |
| /// <summary> | |
| /// Fires when <see cref="IsLeftThrustControlActivated"/> value has been changed from true to false or vice versa | |
| /// <remarks>The value returned is the new value of the property</remarks> | |
| /// </summary> | |
| public event Action<bool> OnLeftThrustControlActivatedChanged; | |
| /// <summary> | |
| /// Fires when <see cref="IsRightThrustControlActivated"/> value has been changed from true to false or vice versa | |
| /// <remarks>The value returned is the new value of the property</remarks> | |
| /// </summary> | |
| public event Action<bool> OnRightThrustControlActivatedChanged; | |
| /// <summary> | |
| /// Fires when <see cref="IsBoostControlActivated"/> value has been changed from true to false or vice versa | |
| /// <remarks>The value returned is the new value of the property</remarks> | |
| /// </summary> | |
| public event Action<bool> OnBoostControlActivatedChanged; | |
| /// <summary> | |
| /// Fires when <see cref="isSelfDestructControlActivated"/> value has been changed from true to false or vice versa | |
| /// <remarks>The value returned is the new value of the property</remarks> | |
| /// </summary> | |
| public event Action<bool> OnSelfDestructControlActivatedChanged; | |
| /// <summary> | |
| /// Fires when the control scheme updates. | |
| /// <remarks>The value returned is the new control scheme set. See <see cref="DEFAULT_MOBILE_CONTROL_SCHEME"/> <see cref="DEFAULT_PC_CONTROL_SCHEME"/>, <see cref="DEFAULT_GAMEPAD_CONTROL_SCHEME"/></remarks> | |
| /// </summary> | |
| public event Action<string> OnControlSchemeChanged; | |
| /// <summary> | |
| /// The basic control scheme that will be used for mobile . | |
| /// </summary> | |
| public const string DEFAULT_MOBILE_CONTROL_SCHEME = "Touch - Basic"; | |
| /// <summary> | |
| /// The controls scheme that will be used for mouse and keybaord. | |
| /// </summary> | |
| public const string DEFAULT_PC_CONTROL_SCHEME = "Keyboard&Mouse"; | |
| /// <summary> | |
| /// The controls scheme that will be used for gamepad. | |
| /// </summary> | |
| public const string DEFAULT_GAMEPAD_CONTROL_SCHEME = "Gamepad"; | |
| /// <summary> | |
| /// The default controls input action collection used in our game | |
| /// </summary> | |
| private DefaultControls defaultControls; | |
| private bool isLeftThrustControlActivated; | |
| private bool isRightThrustControlActivated; | |
| private bool isBoostControlActivated; | |
| private bool isSelfDestructControlActivated; | |
| private bool isLeftThrustControlDisabled; | |
| private bool isRightThrustControlDisabled; | |
| private bool isBoostControlDisabled; | |
| private bool isSelfDestructControlDisabled; | |
| private bool callTerminateOnDestroy; | |
| private void OnEnable() | |
| { | |
| DefaultControls.Missile.AddCallbacks(this); | |
| } | |
| private void OnDisable() | |
| { | |
| DefaultControls.Missile.RemoveCallbacks(this); | |
| } | |
| protected override void SingletonOnDestroy() | |
| { | |
| if (!callTerminateOnDestroy) | |
| { | |
| return; | |
| } | |
| Terminate(); | |
| } | |
| /// <summary> | |
| /// Initializes our control service | |
| /// </summary> | |
| /// <param name="controlsRenderCamera">The camera that we will use to render our controls.</param> | |
| /// <param name="callTerminateOnDestroy"> | |
| /// If true, will call <see cref="Terminate"/> when the singleton is destroyed. Set false if you want to call <see cref="Terminate"/> manually. | |
| /// </param> | |
| public void Initialize(Camera controlsRenderCamera, bool callTerminateOnDestroy = false) | |
| { | |
| controlsCanvas.renderMode = RenderMode.ScreenSpaceCamera; | |
| controlsCanvas.worldCamera = controlsRenderCamera; | |
| controlsCanvas.planeDistance = controlsScreenSpaceRenderModeSettings.planeDistance; | |
| controlsCanvas.sortingLayerID = controlsScreenSpaceRenderModeSettings.sortingLayer; | |
| controlsCanvas.sortingOrder = controlsScreenSpaceRenderModeSettings.sortingOrder; | |
| this.callTerminateOnDestroy = callTerminateOnDestroy; | |
| //ensure all controls are disabled by default | |
| ToggleControls(false); | |
| //in editor or on pc, mac, or linux enable basic pc button layout by default | |
| #if UNITY_EDITOR || UNITY_STANDALONE | |
| DefaultControls.bindingMask = new InputBinding | |
| { | |
| groups = DEFAULT_PC_CONTROL_SCHEME | |
| }; | |
| //on mobile enable basic mobile button layout by default | |
| #elif UNITY_ANDROID || UNITY_IOS | |
| DefaultControls.bindingMask = new InputBinding | |
| { | |
| groups = DEFAULT_MOBILE_CONTROL_SCHEME | |
| }; | |
| #endif | |
| InputSystem.onEvent += OnInputSystemOnEventUpdateControlSchemeAndMobileButtonsHandler; | |
| OnControlSchemeChanged += OnControlSchemeChangedHideCursorHandler; | |
| //hide or show our cursor immediately depending on our current control scheme | |
| OnControlSchemeChangedHideCursorHandler(ActiveControlScheme); | |
| } | |
| /// <summary> | |
| /// Terminates our control service. | |
| /// </summary> | |
| public void Terminate() | |
| { | |
| InputSystem.onEvent -= OnInputSystemOnEventUpdateControlSchemeAndMobileButtonsHandler; | |
| OnControlSchemeChanged -= OnControlSchemeChangedHideCursorHandler; | |
| } | |
| /// <summary> | |
| /// Turns all controls on or off. If any controls were toggled using ToggleBlankControl will reset to isEnabled value | |
| /// </summary> | |
| /// <param name="isEnabled">true to enable all controls, false to disable all controls</param> | |
| public void ToggleControls(bool isEnabled) | |
| { | |
| ToggleLeftThrustControl(isEnabled); | |
| ToggleRightThrustControl(isEnabled); | |
| ToggleBoostControl(isEnabled); | |
| ToggleSelfDestructControl(isEnabled); | |
| } | |
| /// <summary> | |
| /// Enable / Disables the left thrust control | |
| /// </summary> | |
| /// <param name="isEnabled">true if left thrust control is enabled, false to disable it</param> | |
| /// <returns>The control service to allow chaining</returns> | |
| /// TODO given that most logic is handled in the property setter we should consider if replacing this with public property is better | |
| public ControlsService ToggleLeftThrustControl(bool isEnabled) | |
| { | |
| IsLeftThrustControlDisabled = !isEnabled; | |
| return this; | |
| } | |
| /// <summary> | |
| /// Enable / Disables the right thrust control | |
| /// </summary> | |
| /// <param name="isEnabled">true if right thrust control is enabled, false to disable it</param> | |
| /// <returns>The control service to allow chaining</returns> | |
| /// TODO given that most logic is handled in the property setter we should consider if replacing this with public property is better | |
| public ControlsService ToggleRightThrustControl(bool isEnabled) | |
| { | |
| IsRightThrustControlDisabled = !isEnabled; | |
| return this; | |
| } | |
| /// <summary> | |
| /// Enable / Disabled the boost control | |
| /// </summary> | |
| /// <param name="isEnabled">true if boost control is enabled, false to disable it</param> | |
| /// <returns>The control service to allow chaining</returns> | |
| /// TODO given that most logic is handled in the property setter we should consider if replacing this with public property is better | |
| public ControlsService ToggleBoostControl(bool isEnabled) | |
| { | |
| IsBoostControlDisabled = !isEnabled; | |
| return this; | |
| } | |
| /// <summary> | |
| /// Enable / Disabled the self destruct control | |
| /// </summary> | |
| /// <param name="isEnabled">true if self destruct control is enabled, false to disable it</param> | |
| /// <returns>The control service to allow chaining</returns> | |
| /// TODO given that most logic is handled in the property setter we should consider if replacing this with public property is better | |
| public ControlsService ToggleSelfDestructControl(bool isEnabled) | |
| { | |
| IsSelfDestructControlDisabled = !isEnabled; | |
| return this; | |
| } | |
| /// <summary> | |
| /// Will immediately enable our on-screen mobile button controls if the current control scheme is a touch control scheme. | |
| /// Additionally will enable <see cref="OnScreenControlsDisplaySessionIsActive"/> to ensure on screen controls | |
| /// are toggled on and off depending on the current state of our control scheme. | |
| /// <remarks> | |
| /// Control Scheme is always auto updated depending on the state of our most recent input. | |
| /// This means when our sessions is active on-screen controls will be enabled when a touch control scheme is active | |
| /// and disabled when any other type of control scheme is active. | |
| /// </remarks> | |
| /// </summary> | |
| /// TODO if this method proves troublesome we can tie auto updating on-screen controls visibility to | |
| /// TODO DefaultControls.Missile.enabled. Meaning that we would just automatically show or hide our on-screen | |
| /// TODO controls when our missile controls are enabled or disabled. | |
| public void BeginOnScreenControlsDisplaySession() | |
| { | |
| //TODO update this when we do our advanced mobile controls. | |
| if(ActiveControlScheme == DEFAULT_MOBILE_CONTROL_SCHEME) | |
| { | |
| SetMobileButtonLayout(MobileButtonLayout.Basic); | |
| } | |
| OnScreenControlsDisplaySessionIsActive = true; | |
| } | |
| /// <summary> | |
| /// Will immediately disable our on-screen mobile button controls if they are visible and end | |
| /// our display session by disabling <see cref="OnScreenControlsDisplaySessionIsActive"/> | |
| /// </summary> | |
| public void EndOnScreenControlsDisplaySession() | |
| { | |
| OnScreenControlsDisplaySessionIsActive = false; | |
| SetMobileButtonLayout(MobileButtonLayout.Disabled); | |
| } | |
| public void OnSelfDestruct(InputAction.CallbackContext context) | |
| { | |
| IsSelfDestructControlActivated = context.phase switch | |
| { | |
| InputActionPhase.Performed => true, | |
| InputActionPhase.Canceled => false, | |
| _ => IsSelfDestructControlActivated | |
| }; | |
| } | |
| public void OnBoost(InputAction.CallbackContext context) | |
| { | |
| IsBoostControlActivated = context.phase switch | |
| { | |
| InputActionPhase.Performed => true, | |
| InputActionPhase.Canceled => false, | |
| _ => IsBoostControlActivated | |
| }; | |
| } | |
| public void OnHorizontalThrust(InputAction.CallbackContext context) | |
| { | |
| var value = context.ReadValue<float>(); | |
| switch (value) | |
| { | |
| case < 0: | |
| IsLeftThrustControlActivated = true; | |
| IsRightThrustControlActivated = false; | |
| break; | |
| case > 0: | |
| IsLeftThrustControlActivated = false; | |
| IsRightThrustControlActivated = true; | |
| break; | |
| case 0: | |
| IsLeftThrustControlActivated = false; | |
| IsRightThrustControlActivated = false; | |
| break; | |
| } | |
| } | |
| /// <summary> | |
| /// Sets the mobile button layout that will be active when our control service is active | |
| /// </summary> | |
| /// TODO update this when we do our advanced mobile controls. | |
| private void SetMobileButtonLayout(MobileButtonLayout mobileButtonLayout) | |
| { | |
| switch (mobileButtonLayout) | |
| { | |
| case MobileButtonLayout.Disabled: | |
| mobileBasicButtonLayout.SetActive(false); | |
| break; | |
| case MobileButtonLayout.Basic: | |
| mobileBasicButtonLayout.SetActive(true); | |
| break; | |
| } | |
| } | |
| /// <summary> | |
| /// If we are on <see cref="DEFAULT_PC_CONTROL_SCHEME"/> show our cursor, otherwise hide it. | |
| /// </summary> | |
| /// <param name="controlScheme">The control scheme we just switched to</param> | |
| private void OnControlSchemeChangedHideCursorHandler(string controlScheme) | |
| { | |
| switch (controlScheme) | |
| { | |
| case DEFAULT_PC_CONTROL_SCHEME: | |
| Cursor.visible = true; | |
| break; | |
| default: | |
| Cursor.visible = false; | |
| break; | |
| } | |
| } | |
| //TODO probably best to make this private as our controls service should manage our controls and not let anything else do so | |
| public DefaultControls DefaultControls => defaultControls ??= new DefaultControls(); | |
| /// <summary> | |
| /// Returns true if the left thrust control is considered activated after input processing. | |
| /// <remarks>If <see cref="IsLeftThrustControlDisabled"/> is true false will always be returned!</remarks> | |
| /// </summary> | |
| [Title("Debugging")] | |
| [ToggleLeft] | |
| [LabelText("Left Pressed")] | |
| [ShowInInspector,ReadOnly] | |
| public bool IsLeftThrustControlActivated | |
| { | |
| get => isLeftThrustControlActivated && !IsLeftThrustControlDisabled; | |
| private set | |
| { | |
| //when left thrust control is disabled don't set or fire any events on set | |
| if (IsLeftThrustControlDisabled) | |
| { | |
| return; | |
| } | |
| //only fire event on first move to true | |
| if (value && !isLeftThrustControlActivated) | |
| { | |
| OnLeftThrustControlActivatedChanged?.Invoke(true); | |
| } | |
| //only fire vent on first move to false | |
| if (!value && isLeftThrustControlActivated) | |
| { | |
| OnLeftThrustControlActivatedChanged?.Invoke(false); | |
| } | |
| isLeftThrustControlActivated = value; | |
| } | |
| } | |
| /// <summary> | |
| /// Returns true if the right thrust control is considered activated after input processing | |
| /// <remarks>If <see cref="IsRightThrustControlDisabled"/> is true false will always be returned!</remarks> | |
| /// </summary> | |
| [ToggleLeft] | |
| [LabelText("Right Pressed")] | |
| [ShowInInspector, ReadOnly] | |
| public bool IsRightThrustControlActivated | |
| { | |
| get => isRightThrustControlActivated && !IsRightThrustControlDisabled; | |
| private set | |
| { | |
| //when right thrust control is disabled don't set or fire any events on set | |
| if (IsRightThrustControlDisabled) | |
| { | |
| return; | |
| } | |
| //only fire event on first move to true | |
| if (value && !isRightThrustControlActivated) | |
| { | |
| OnRightThrustControlActivatedChanged?.Invoke(true); | |
| } | |
| //only fire event on first move to false | |
| if (!value && isRightThrustControlActivated) | |
| { | |
| OnRightThrustControlActivatedChanged?.Invoke(false); | |
| } | |
| isRightThrustControlActivated = value; | |
| } | |
| } | |
| /// <summary> | |
| /// Returns true if the boost control is considered activated after input processing | |
| /// </summary> | |
| [LabelText("Both Pressed")] | |
| [ToggleLeft] | |
| [ShowInInspector, ReadOnly] | |
| public bool IsBoostControlActivated | |
| { | |
| get => isBoostControlActivated; | |
| private set { | |
| //when boost control is disabled don't set or fire any events on set | |
| //TODO technically this shouldn't be possible with boost because our boost input action is tied directly | |
| //TODO to our boost control disabled state. However doing this to be safe just in case things change in the future | |
| if (IsBoostControlDisabled) | |
| { | |
| return; | |
| } | |
| //only fire event on first move to true | |
| if (value && !isBoostControlActivated) | |
| { | |
| OnBoostControlActivatedChanged?.Invoke(true); | |
| } | |
| //only fire event on first move to false | |
| if (!value && isBoostControlActivated) | |
| { | |
| OnBoostControlActivatedChanged?.Invoke(false); | |
| } | |
| isBoostControlActivated = value; | |
| } | |
| } | |
| /// <summary> | |
| /// Returns true if the self destruct control is considered activated after input processing | |
| /// </summary> | |
| [LabelText("Self Destruct Pressed")] | |
| [ToggleLeft] | |
| [ShowInInspector, ReadOnly] | |
| public bool IsSelfDestructControlActivated | |
| { | |
| get => isSelfDestructControlActivated; | |
| private set { | |
| //when boost control is disabled don't set or fire any events on set | |
| //TODO technically this shouldn't be possible with self destruct because our boost input action is tied directly | |
| //TODO to our self destruct control disabled state. However doing this to be safe just in case things change in the future | |
| if (IsSelfDestructControlDisabled) | |
| { | |
| return; | |
| } | |
| //only fire event on first move to true | |
| if (value && !isSelfDestructControlActivated) | |
| { | |
| OnSelfDestructControlActivatedChanged?.Invoke(true); | |
| } | |
| //only fire event on first move to false | |
| if (!value && isSelfDestructControlActivated) | |
| { | |
| OnSelfDestructControlActivatedChanged?.Invoke(false); | |
| } | |
| isSelfDestructControlActivated = value; | |
| } | |
| } | |
| /// <summary> | |
| /// If true will disable <see cref="IsLeftThrustControlActivated"/> | |
| /// </summary> | |
| //TODO I wonder if it would make more sense to handle this type of thing in the input system itself (processor?) | |
| [LabelText("Left Disabled")] | |
| [ToggleLeft] | |
| [ShowInInspector, ReadOnly] | |
| [PropertyTooltip("If true will disable IsLeftThrustControlActivated")] | |
| public bool IsLeftThrustControlDisabled | |
| { | |
| get => isLeftThrustControlDisabled; | |
| private set | |
| { | |
| //TODO We may want to move this back to the ToggleLeftThrustControl() as this side effect might produce unwanted unexpected behavior | |
| //if we are enabling the left thrust control and our horizontal thrust is off ensure we turn it on | |
| if (!value && !DefaultControls.Missile.HorizontalThrust.enabled) | |
| { | |
| DefaultControls.Missile.HorizontalThrust.Enable(); | |
| } | |
| //if we disable our left thrust control and it is currently activated ensure we deactivate it | |
| if (value && IsLeftThrustControlActivated) | |
| { | |
| isLeftThrustControlActivated = false; | |
| } | |
| isLeftThrustControlDisabled = value; | |
| } | |
| } | |
| /// <summary> | |
| /// If true will disable <see cref="IsRightThrustControlActivated"/> | |
| /// </summary> | |
| //TODO I wonder if it would make more sense to handle this type of thing in the input system itself (processor?) | |
| [LabelText("Right Disabled")] | |
| [ToggleLeft] | |
| [ShowInInspector, ReadOnly] | |
| [PropertyTooltip("If true will disable IsRightThrustControlDisabled")] | |
| public bool IsRightThrustControlDisabled | |
| { | |
| get => isRightThrustControlDisabled; | |
| private set | |
| { | |
| //TODO We may want to move this back to the ToggleRightThrustControl() as this side effect might produce unwanted unexpected behavior | |
| //if we are enabling the right thrust control and our horizontal thrust is off ensure we turn it on | |
| if (!value && !DefaultControls.Missile.HorizontalThrust.enabled) | |
| { | |
| DefaultControls.Missile.HorizontalThrust.Enable(); | |
| } | |
| //if we disable our right thrust control and it is currently activated ensure we deactivate it | |
| if(value && IsRightThrustControlActivated) | |
| { | |
| isRightThrustControlActivated = false; | |
| } | |
| isRightThrustControlDisabled = value; | |
| } | |
| } | |
| /// <summary> | |
| /// If true will disable <see cref="IsBoostControlActivated"/> | |
| /// </summary> | |
| [LabelText("Boost Disabled")] | |
| [ToggleLeft] | |
| [ShowInInspector, ReadOnly] | |
| [PropertyTooltip("If true will disable IsBoostControlDisabled")] | |
| public bool IsBoostControlDisabled | |
| { | |
| get => isBoostControlDisabled; | |
| private set | |
| { | |
| //TODO We may want to move this back to the ToggleBoostControl() as this side effect might produce unwanted unexpected behavior | |
| //our field is tied directly to the input system action so match the input action enabled state to our field | |
| if (!value) | |
| { | |
| DefaultControls.Missile.Boost.Enable(); | |
| } | |
| else | |
| { | |
| DefaultControls.Missile.Boost.Disable(); | |
| } | |
| //if we disable our boost control and it is currently activated ensure we deactivate it | |
| if (value && isBoostControlActivated) | |
| { | |
| isBoostControlActivated = false; | |
| } | |
| isBoostControlDisabled = value; | |
| } | |
| } | |
| /// <summary> | |
| /// If true will disable <see cref="IsSelfDestructControlActivated"/> | |
| /// </summary> | |
| [LabelText("Self Destruct Disabled")] | |
| [ToggleLeft] | |
| [ShowInInspector, ReadOnly] | |
| [PropertyTooltip("If true will disable IsSelfDestructControlDisabled")] | |
| public bool IsSelfDestructControlDisabled | |
| { | |
| get => isSelfDestructControlDisabled; | |
| private set | |
| { | |
| //TODO We may want to move this back to the ToggleSelfDestructControl() as this side effect might produce unwanted unexpected behavior | |
| //our field is tied directly to the input system action so match the input action enabled state to our field | |
| if (!value) | |
| { | |
| DefaultControls.Missile.SelfDestruct.Enable(); | |
| } | |
| else | |
| { | |
| DefaultControls.Missile.SelfDestruct.Disable(); | |
| } | |
| //if we disable our self destruct control and it is currently activated ensure we deactivate it | |
| if (value && isSelfDestructControlActivated) | |
| { | |
| isSelfDestructControlActivated = false; | |
| } | |
| isSelfDestructControlDisabled = value; | |
| } | |
| } | |
| /// <summary> | |
| /// The control scheme that is currently being utilized by our control service and input system. | |
| /// </summary> | |
| [ValueDropdown("GetDefinedControlSchemes")] | |
| [ShowInInspector] | |
| [PropertyTooltip("The control scheme that is currently being utilized by our control service and input system.")] | |
| public string ActiveControlScheme | |
| { | |
| get => DefaultControls.bindingMask.GetValueOrDefault().groups; | |
| private set => DefaultControls.bindingMask = new InputBinding { groups = value }; | |
| } | |
| /// <summary> | |
| /// True if we have started an on screen controls displays session using <see cref="BeginOnScreenControlsDisplaySession"/>. | |
| /// This means that our on-screen controls will be automatically toggled on and off depending on whether or not any of our | |
| /// mobile controls schemes are active or not. | |
| /// </summary> | |
| public bool OnScreenControlsDisplaySessionIsActive { get; private set;} | |
| /// <summary> | |
| /// Handles the <see cref="InputSystem.onEvent"/> to ensure we update our control scheme and mobile buttons | |
| /// based on the most recent device we are using. | |
| /// </summary> | |
| /// <example> | |
| /// 1. Keyboard or mouse are moved or pressed, we should switch to our pc control scheme | |
| /// 2. Gamepad sticks moved or buttons pressed, we should switch to our gamepad control scheme | |
| /// 3. Touchscreen is touched, we should switch to our mobile control scheme and enable our mobile on-screen buttons. | |
| /// </example> | |
| /// <param name="inputEventPtr"></param> | |
| /// <param name="inputDevice"></param> | |
| private void OnInputSystemOnEventUpdateControlSchemeAndMobileButtonsHandler(InputEventPtr inputEventPtr, InputDevice inputDevice) | |
| { | |
| // Ignore anything that isn't a state event. These events signal that some input has changed | |
| if (!inputEventPtr.IsA<StateEvent>() && !inputEventPtr.IsA<DeltaStateEvent>()) | |
| { | |
| return; | |
| } | |
| //ignore input from on screen controls. See SetupInputControl() method in OnScreenControls.cs for more info | |
| //on the usage we are filtering out here | |
| if (inputDevice.usages.Any(x => x == "OnScreen")) | |
| { | |
| return; | |
| } | |
| //instead of finding our controls scheme manually with this method we should also be able to do it using | |
| //var scheme = InputControlScheme.FindControlSchemeForDevices(currentInputDevice, inputActionMap.controlSchemes); | |
| //inputActionAsset.bindingMask = new InputBinding { groups = scheme.bindingGroup }; | |
| //we would need to ensure that required devices + usages are set appropriately in our input action asset | |
| var bindingMaskGroups = inputDevice switch | |
| { | |
| //note to return multiple control schemes for a binding mask you can do the following: | |
| //${DEFAULT_PC_CONTROL_SCHEME};${DEFAULT_GAMEPAD_CONTROL_SCHEME} where the ; is the separator between multiple control schemes | |
| Keyboard or Mouse => DEFAULT_PC_CONTROL_SCHEME, | |
| Gamepad => DEFAULT_GAMEPAD_CONTROL_SCHEME, | |
| Touchscreen => DEFAULT_MOBILE_CONTROL_SCHEME, | |
| _ => null | |
| }; | |
| //if we have no control scheme for this device then we can ignore it | |
| if (bindingMaskGroups == null) | |
| { | |
| return; | |
| } | |
| //if our binding mask is already set to the same value then we can ignore it | |
| if (bindingMaskGroups == DefaultControls.bindingMask?.groups) | |
| { | |
| return; | |
| } | |
| //TODO re-evaluate if this is the best place to do this. | |
| //during an active on screen controls display session we need to ensure we auto toggle on-screen controls on | |
| //when a touch control scheme is active and off when one is not. | |
| if (OnScreenControlsDisplaySessionIsActive) | |
| { | |
| SetMobileButtonLayout(bindingMaskGroups == DEFAULT_MOBILE_CONTROL_SCHEME? MobileButtonLayout.Basic :MobileButtonLayout.Disabled); | |
| } | |
| DefaultControls.bindingMask = new InputBinding | |
| { | |
| groups = bindingMaskGroups | |
| }; | |
| OnControlSchemeChanged?.Invoke(bindingMaskGroups); | |
| } | |
| /// <summary> | |
| /// Defines the types of mobile button layouts available | |
| /// </summary> | |
| public enum MobileButtonLayout | |
| { | |
| /// <summary> | |
| /// Disables mobile button layout | |
| /// </summary> | |
| Disabled, | |
| /// <summary> | |
| /// Two button layout that that DOES NOT allow for boosting while turning. | |
| /// </summary> | |
| Basic, | |
| //TODO uncomment this section when we add our advanced layout! | |
| /// <summary> | |
| /// Three button layout that DOES allow for boosting while turning. | |
| /// </summary> | |
| //Advanced | |
| } | |
| [Serializable] | |
| public class ControlsScreenSpaceRenderModeSettings | |
| { | |
| [SortingLayer] | |
| public int sortingLayer; | |
| public int sortingOrder; | |
| public int planeDistance; | |
| } | |
| #region Editor Code | |
| #if UNITY_EDITOR | |
| [OnInspectorGUI] | |
| private void Repaint() | |
| { | |
| Sirenix.Utilities.Editor.GUIHelper.RequestRepaint(); | |
| } | |
| /// <summary> | |
| /// Gets the control schemes we have defined in our <see cref="DefaultControls"/> input actions. | |
| /// <remarks>Primarily used in <see cref="ActiveControlScheme"/> to allow changing control scheme in the inspector for easier debugging.</remarks> | |
| /// </summary> | |
| /// <returns>A list of the controls scheme names defined in our <see cref="DefaultControls"/></returns> | |
| private ValueDropdownList<string> GetDefinedControlSchemes() | |
| { | |
| var result = new ValueDropdownList<string>(); | |
| foreach (var scheme in DefaultControls.controlSchemes) | |
| { | |
| result.Add(scheme.name); | |
| } | |
| return result; | |
| } | |
| #endif | |
| #endregion | |
| #region Serialization | |
| [SerializeField, HideInInspector] | |
| private SerializationData serializationData; | |
| void ISerializationCallbackReceiver.OnAfterDeserialize() | |
| { | |
| UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData); | |
| } | |
| void ISerializationCallbackReceiver.OnBeforeSerialize() | |
| { | |
| UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData); | |
| } | |
| public SerializationData SerializationData | |
| { | |
| get => serializationData; | |
| set => serializationData = value; | |
| } | |
| #endregion | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment