// Copyright (c) 2015 Augie R. Maddox, Guavaman Enterprises. All rights reserved. #pragma warning disable 0219 #pragma warning disable 0618 #pragma warning disable 0649 namespace Rewired.UI.ControlMapper { using UnityEngine; using UnityEngine.UI; using UnityEngine.Events; using System.Collections.Generic; using UnityEngine.EventSystems; using Rewired; using Rewired.Utils; [AddComponentMenu("")] public partial class ControlMapper : MonoBehaviour { #region Consts private const float blockInputOnFocusTimeout = 0.1f; // a small delay after main screen receives focus to filter out button down events during new assignments private const string buttonIdentifier_playerSelection = "PlayerSelection"; private const string buttonIdentifier_removeController = "RemoveController"; private const string buttonIdentifier_assignController = "AssignController"; private const string buttonIdentifier_calibrateController = "CalibrateController"; private const string buttonIdentifier_editInputBehaviors = "EditInputBehaviors"; private const string buttonIdentifier_mapCategorySelection = "MapCategorySelection"; private const string buttonIdentifier_assignedControllerSelection = "AssignedControllerSelection"; private const string buttonIdentifier_done = "Done"; private const string buttonIdentifier_restoreDefaults = "RestoreDefaults"; #endregion #region Inspector Vars [SerializeField] [Tooltip("Must be assigned a Rewired Input Manager scene object or prefab.")] private InputManager _rewiredInputManager; [SerializeField] [Tooltip("Set to True to prevent the Game Object from being destroyed when a new scene is loaded.\n\nNOTE: Changing this value from True to False at runtime will have no effect because Object.DontDestroyOnLoad cannot be undone once set.")] private bool _dontDestroyOnLoad; [SerializeField] [Tooltip("Open the control mapping screen immediately on start. Mainly used for testing.")] private bool _openOnStart = false; [SerializeField] [Tooltip("The Layout of the Keyboard Maps to be displayed.")] private int _keyboardMapDefaultLayout = 0; [SerializeField] [Tooltip("The Layout of the Mouse Maps to be displayed.")] private int _mouseMapDefaultLayout = 0; [SerializeField] [Tooltip("The Layout of the Mouse Maps to be displayed.")] private int _joystickMapDefaultLayout = 0; [SerializeField] private MappingSet[] _mappingSets = new MappingSet[1] { MappingSet.Default }; [SerializeField] [Tooltip("Display a selectable list of Players. If your game only supports 1 player, you can disable this.")] private bool _showPlayers = true; [SerializeField] [Tooltip("Display the Controller column for input mapping.")] private bool _showControllers = true; [SerializeField] [Tooltip("Display the Keyboard column for input mapping.")] private bool _showKeyboard = true; [SerializeField] [Tooltip("Display the Mouse column for input mapping.")] private bool _showMouse = true; [SerializeField] [Tooltip( "The maximum number of controllers allowed to be assigned to a Player. " + "If set to any value other than 1, a selectable list of currently-assigned controller will be displayed to the user. " + "[0 = infinite]" )] private int _maxControllersPerPlayer = 1; [SerializeField] [Tooltip("Display section labels for each Action Category in the input field grid. Only applies if Action Categories are used to display the Action list.")] private bool _showActionCategoryLabels = false; [SerializeField] [Tooltip("The number of input fields to display for the keyboard. If you want to support alternate mappings on the same device, set this to 2 or more.")] private int _keyboardInputFieldCount = 2; [SerializeField] [Tooltip("The number of input fields to display for the mouse. If you want to support alternate mappings on the same device, set this to 2 or more.")] private int _mouseInputFieldCount = 1; [SerializeField] [Tooltip("The number of input fields to display for joysticks. If you want to support alternate mappings on the same device, set this to 2 or more.")] private int _controllerInputFieldCount = 1; [SerializeField] [Tooltip( "Display a full-axis input assignment field for every axis-type Action in the input field grid. Also displays an invert toggle for the user " + " to invert the full-axis assignment direction." + "\n\n*IMPORTANT*: This field is required if you have made any full-axis assignments in the Rewired Input Manager or in saved XML user data. " + "Disabling this field when you have full-axis assignments will result in the inability for the user to view, remove, or modify these " + "full-axis assignments. In addition, these assignments may cause conflicts when trying to remap the same axes to Actions." )] private bool _showFullAxisInputFields = true; [SerializeField] [Tooltip( "Display a positive and negative input assignment field for every axis-type Action in the input field grid." + "\n\n*IMPORTANT*: These fields are required to assign buttons, keyboard keys, and hat or D-Pad directions to axis-type Actions. " + "If you have made any split-axis assignments or button/key/D-pad assignments to axis-type Actions in the Rewired Input Manager or " + "in saved XML user data, disabling these fields will result in the inability for the user to view, remove, or modify these assignments. " + "In addition, these assignments may cause conflicts when trying to remap the same elements to Actions." )] private bool _showSplitAxisInputFields = true; [SerializeField] [Tooltip("If enabled, when an element assignment conflict is found, an option will be displayed that allows the user to make the conflicting assignment anyway.")] private bool _allowElementAssignmentConflicts = false; [SerializeField] [Tooltip("The width in relative pixels of the Action label column.")] private int _actionLabelWidth = 200; [SerializeField] [Tooltip("The width in relative pixels of the Keyboard column.")] private int _keyboardColMaxWidth = 360; [SerializeField] [Tooltip("The width in relative pixels of the Mouse column.")] private int _mouseColMaxWidth = 200; [SerializeField] [Tooltip("The width in relative pixels of the Controller column.")] private int _controllerColMaxWidth = 200; [SerializeField] [Tooltip("The height in relative pixels of the input grid button rows.")] private int _inputRowHeight = 40; [SerializeField] [Tooltip("The width in relative pixels of spacing between columns.")] private int _inputColumnSpacing = 40; [SerializeField] [Tooltip("The height in relative pixels of the space between Action Category sections. Only applicable if Show Action Category Labels is checked.")] private int _inputRowCategorySpacing = 20; [SerializeField] [Tooltip("The width in relative pixels of the invert toggle buttons.")] private int _invertToggleWidth = 40; [SerializeField] [Tooltip("The width in relative pixels of generated popup windows.")] private int _defaultWindowWidth = 500; [SerializeField] [Tooltip("The height in relative pixels of generated popup windows.")] private int _defaultWindowHeight = 400; [SerializeField] [Tooltip("The time in seconds the user has to press an element on a controller when assigning a controller to a Player. If this time elapses with no user input a controller, the assignment will be canceled.")] private float _controllerAssignmentTimeout = 5.0f; [SerializeField] [Tooltip("The time in seconds the user has to press an element on a controller while waiting for axes to be centered before assigning input.")] private float _preInputAssignmentTimeout = 5.0f; [SerializeField] [Tooltip("The time in seconds the user has to press an element on a controller when assigning input. If this time elapses with no user input on the target controller, the assignment will be canceled.")] private float _inputAssignmentTimeout = 5.0f; [SerializeField] [Tooltip("The time in seconds the user has to press an element on a controller during calibration.")] private float _axisCalibrationTimeout = 5.0f; [SerializeField] [Tooltip("If checked, mouse X-axis movement will always be ignored during input assignment. Check this if you don't want the horizontal mouse axis to be user-assignable to any Actions.")] private bool _ignoreMouseXAxisAssignment = true; [SerializeField] [Tooltip("If checked, mouse Y-axis movement will always be ignored during input assignment. Check this if you don't want the vertical mouse axis to be user-assignable to any Actions.")] private bool _ignoreMouseYAxisAssignment = true; [SerializeField] [Tooltip("An Action that when activated will alternately close or open the main screen as long as no popup windows are open.")] private int _screenToggleAction = -1; [SerializeField] [Tooltip("An Action that when activated will open the main screen if it is closed.")] private int _screenOpenAction = -1; [SerializeField] [Tooltip("An Action that when activated will close the main screen as long as no popup windows are open.")] private int _screenCloseAction = -1; [SerializeField] [Tooltip("An Action that when activated will cancel and close any open popup window. Use with care because the element assigned to this Action can never be mapped by the user (because it would just cancel his assignment).")] private int _universalCancelAction = -1; [SerializeField] [Tooltip("If enabled, Universal Cancel will also close the main screen if pressed when no windows are open.")] private bool _universalCancelClosesScreen = true; [SerializeField] [Tooltip("If checked, controls will be displayed which will allow the user to customize certain Input Behavior settings.")] private bool _showInputBehaviorSettings; [SerializeField] [Tooltip("Customizable settings for user-modifiable Input Behaviors. This can be used for settings like Mouse Look Sensitivity.")] private InputBehaviorSettings[] _inputBehaviorSettings; [SerializeField] [Tooltip("If enabled, UI elements will be themed based on the settings in Theme Settings.")] private bool _useThemeSettings = true; [SerializeField] [Tooltip("Must be assigned a ThemeSettings object. Used to theme UI elements.")] private ThemeSettings _themeSettings; [SerializeField] [Tooltip("Must be assigned a LanguageData object. Used to retrieve language entries for UI elements.")] private UI.ControlMapper.LanguageData _language; [SerializeField] [Tooltip("A list of prefabs. You should not have to modify this.")] private Prefabs prefabs; [SerializeField] [Tooltip("A list of references to elements in the hierarchy. You should not have to modify this.")] private References references; [SerializeField] [Tooltip("Show the label for the Players button group?")] private bool _showPlayersGroupLabel = true; [SerializeField] [Tooltip("Show the label for the Controller button group?")] private bool _showControllerGroupLabel = true; [SerializeField] [Tooltip("Show the label for the Assigned Controllers button group?")] private bool _showAssignedControllersGroupLabel = true; [SerializeField] [Tooltip("Show the label for the Settings button group?")] private bool _showSettingsGroupLabel = true; [SerializeField] [Tooltip("Show the label for the Map Categories button group?")] private bool _showMapCategoriesGroupLabel = true; [SerializeField] [Tooltip("Show the label for the current controller name?")] private bool _showControllerNameLabel = true; [SerializeField] [Tooltip("Show the Assigned Controllers group? If joystick auto-assignment is enabled in the Rewired Input Manager and the max joysticks per player is set to any value other than 1, the Assigned Controllers group will always be displayed.")] private bool _showAssignedControllers = true; #endregion #region Events // .NET events private System.Action _ScreenClosedEvent; private System.Action _ScreenOpenedEvent; private System.Action _PopupWindowOpenedEvent; private System.Action _PopupWindowClosedEvent; private System.Action _InputPollingStartedEvent; private System.Action _InputPollingEndedEvent; /// /// Event sent when the UI is closed. /// This is a .NET event. If you are using Unity Events, /// use the onScreenClosed event. /// public event System.Action ScreenClosedEvent { add { _ScreenClosedEvent += value; } remove { _ScreenClosedEvent -= value; } } /// /// Event sent when the UI is opened. /// This is a .NET event. If you are using Unity Events, /// use the onScreenOpened event. /// public event System.Action ScreenOpenedEvent { add { _ScreenOpenedEvent += value; } remove { _ScreenOpenedEvent -= value; } } /// /// Event sent when a popup window is closed. /// This is a .NET event. If you are using Unity Events, /// use the onPopupWindowClosed event. /// public event System.Action PopupWindowClosedEvent { add { _PopupWindowClosedEvent += value; } remove { _PopupWindowClosedEvent -= value; } } /// /// Event sent when a popup window is opened. /// This is a .NET event. If you are using Unity Events, /// use the onPopupWindowOpened event. /// public event System.Action PopupWindowOpenedEvent { add { _PopupWindowOpenedEvent += value; } remove { _PopupWindowOpenedEvent -= value; } } /// /// Event sent when polling for input has started. /// This is a .NET event. If you are using Unity Events, /// use the onInputPollingStarted event. /// public event System.Action InputPollingStartedEvent { add { _InputPollingStartedEvent += value; } remove { _InputPollingStartedEvent -= value; } } /// /// Event sent when polling for input has ended. /// This is a .NET event. If you are using Unity Events, /// use the onInputPollingStopped event. /// public event System.Action InputPollingEndedEvent { add { _InputPollingEndedEvent += value; } remove { _InputPollingEndedEvent -= value; } } // Unity events [SerializeField] [Tooltip("Event sent when the UI is closed.")] private UnityEvent _onScreenClosed; [SerializeField] [Tooltip("Event sent when the UI is opened.")] private UnityEvent _onScreenOpened; [SerializeField] [Tooltip("Event sent when a popup window is closed.")] private UnityEvent _onPopupWindowClosed; [SerializeField] [Tooltip("Event sent when a popup window is opened.")] private UnityEvent _onPopupWindowOpened; [SerializeField] [Tooltip("Event sent when polling for input has started.")] private UnityEvent _onInputPollingStarted; [SerializeField] [Tooltip("Event sent when polling for input has ended.")] private UnityEvent _onInputPollingEnded; /// /// Unity event sent when the UI is closed. /// This is a Unity event. For the .NET event, /// use ScreenClosedEvent. /// public event UnityAction onScreenClosed { add { _onScreenClosed.AddListener(value); } remove { _onScreenClosed.RemoveListener(value); } } /// /// Unity event sent when the UI is opened. /// This is a Unity event. For the .NET event, /// use ScreenOpenedEvent. /// public event UnityAction onScreenOpened { add { _onScreenOpened.AddListener(value); } remove { _onScreenOpened.RemoveListener(value); } } /// /// Unity event sent when a popup window is closed. /// This is a Unity event. For the .NET event, /// use PopupWindowClosedEvent. /// public event UnityAction onPopupWindowClosed { add { _onPopupWindowClosed.AddListener(value); } remove { _onPopupWindowClosed.RemoveListener(value); } } /// /// Unity event sent when a popup window is opened. /// This is a Unity event. For the .NET event, /// use PopupWindowOpenedEvent. /// public event UnityAction onPopupWindowOpened { add { _onPopupWindowOpened.AddListener(value); } remove { _onPopupWindowOpened.RemoveListener(value); } } /// /// Unity event sent when polling for input has started. /// This is a Unity event. For the .NET event, /// use InputPollingStartedEvent. /// public event UnityAction onInputPollingStarted { add { _onInputPollingStarted.AddListener(value); } remove { _onInputPollingStarted.RemoveListener(value); } } /// /// Unity event sent when polling for input has ended. /// This is a Unity event. For the .NET event, /// use InputPollingEndedEvent. /// public event UnityAction onInputPollingEnded { add { _onInputPollingEnded.AddListener(value); } remove { _onInputPollingEnded.RemoveListener(value); } } #endregion #region Working vars private static ControlMapper Instance; private bool initialized; private int playerCount; private InputGrid inputGrid; private WindowManager windowManager; private int currentPlayerId; private int currentMapCategoryId; private List playerButtons; private List mapCategoryButtons; private List assignedControllerButtons; private GUIButton assignedControllerButtonsPlaceholder; private List miscInstantiatedObjects; private GameObject canvas; private GameObject lastUISelection; private int currentJoystickId = -1; private float blockInputOnFocusEndTime; private bool isPollingForInput; private InputMapping pendingInputMapping; private AxisCalibrator pendingAxisCalibration; private System.Action inputFieldActivatedDelegate; private System.Action inputFieldInvertToggleStateChangedDelegate; private System.Action _restoreDefaultsDelegate; #endregion #region Properties #region Inspector Properties // Public access to certain settings which can be changed at runtime before initialization or after initialization provided Reset() is called afterwards. // This is useful if you want to show only certain settings based on the current platform. // Not all properties are runtime modifiable. public InputManager rewiredInputManager { get { return _rewiredInputManager; } set { _rewiredInputManager = value; InspectorPropertyChanged(true); } } public bool dontDestroyOnLoad { get { return _dontDestroyOnLoad; } set { if(value != _dontDestroyOnLoad) { if(value) DontDestroyOnLoad(transform.gameObject); } _dontDestroyOnLoad = value; } } public int keyboardMapDefaultLayout { get { return _keyboardMapDefaultLayout; } set { _keyboardMapDefaultLayout = value; InspectorPropertyChanged(true); } } public int mouseMapDefaultLayout { get { return _mouseMapDefaultLayout; } set { _mouseMapDefaultLayout = value; InspectorPropertyChanged(true); } } public int joystickMapDefaultLayout { get { return _joystickMapDefaultLayout; } set { _joystickMapDefaultLayout = value; InspectorPropertyChanged(true); } } //public MappingSet[] mappingSets { get { return _mappingSets; } set { _mappingSets = value; InspectorPropertyChanged(); } } public bool showPlayers { get { return _showPlayers && ReInput.players.playerCount > 1; } set { _showPlayers = value; InspectorPropertyChanged(true); } } public bool showControllers { get { return _showControllers; } set { _showControllers = value; InspectorPropertyChanged(true); } } public bool showKeyboard { get { return _showKeyboard; } set { _showKeyboard = value; InspectorPropertyChanged(true); } } public bool showMouse { get { return _showMouse; } set { _showMouse = value; InspectorPropertyChanged(true); } } public int maxControllersPerPlayer { get { return _maxControllersPerPlayer; } set { _maxControllersPerPlayer = value; InspectorPropertyChanged(true); } } public bool showActionCategoryLabels { get { return _showActionCategoryLabels; } set { _showActionCategoryLabels = value; InspectorPropertyChanged(true); } } public int keyboardInputFieldCount { get { return _keyboardInputFieldCount; } set { _keyboardInputFieldCount = value; InspectorPropertyChanged(true); } } public int mouseInputFieldCount { get { return _mouseInputFieldCount; } set { _mouseInputFieldCount = value; InspectorPropertyChanged(true); } } public int controllerInputFieldCount { get { return _controllerInputFieldCount; } set { _controllerInputFieldCount = value; InspectorPropertyChanged(true); } } public bool showFullAxisInputFields { get { return _showFullAxisInputFields; } set { _showFullAxisInputFields = value; InspectorPropertyChanged(true); } } public bool showSplitAxisInputFields { get { return _showSplitAxisInputFields; } set { _showSplitAxisInputFields = value; InspectorPropertyChanged(true); } } public bool allowElementAssignmentConflicts { get { return _allowElementAssignmentConflicts; } set { _allowElementAssignmentConflicts = value; InspectorPropertyChanged(); } } public int actionLabelWidth { get { return _actionLabelWidth; } set { _actionLabelWidth = value; InspectorPropertyChanged(true); } } public int keyboardColMaxWidth { get { return _keyboardColMaxWidth; } set { _keyboardColMaxWidth = value; InspectorPropertyChanged(true); } } public int mouseColMaxWidth { get { return _mouseColMaxWidth; } set { _mouseColMaxWidth = value; InspectorPropertyChanged(true); } } public int controllerColMaxWidth { get { return _controllerColMaxWidth; } set { _controllerColMaxWidth = value; InspectorPropertyChanged(true); } } public int inputRowHeight { get { return _inputRowHeight; } set { _inputRowHeight = value; InspectorPropertyChanged(true); } } public int inputColumnSpacing { get { return _inputColumnSpacing; } set { _inputColumnSpacing = value; InspectorPropertyChanged(true); } } public int inputRowCategorySpacing { get { return _inputRowCategorySpacing; } set { _inputRowCategorySpacing = value; InspectorPropertyChanged(true); } } public int invertToggleWidth { get { return _invertToggleWidth; } set { _invertToggleWidth = value; InspectorPropertyChanged(true); } } public int defaultWindowWidth { get { return _defaultWindowWidth; } set { _defaultWindowWidth = value; InspectorPropertyChanged(true); } } public int defaultWindowHeight { get { return _defaultWindowHeight; } set { _defaultWindowHeight = value; InspectorPropertyChanged(true); } } public float controllerAssignmentTimeout { get { return _controllerAssignmentTimeout; } set { _controllerAssignmentTimeout = value; InspectorPropertyChanged(); } } public float preInputAssignmentTimeout { get { return _preInputAssignmentTimeout; } set { _preInputAssignmentTimeout = value; InspectorPropertyChanged(); } } public float inputAssignmentTimeout { get { return _inputAssignmentTimeout; } set { _inputAssignmentTimeout = value; InspectorPropertyChanged(); } } public float axisCalibrationTimeout { get { return _axisCalibrationTimeout; } set { _axisCalibrationTimeout = value; InspectorPropertyChanged(); } } public bool ignoreMouseXAxisAssignment { get { return _ignoreMouseXAxisAssignment; } set { _ignoreMouseXAxisAssignment = value; InspectorPropertyChanged(); } } public bool ignoreMouseYAxisAssignment { get { return _ignoreMouseYAxisAssignment; } set { _ignoreMouseYAxisAssignment = value; InspectorPropertyChanged(); } } //public int screenToggleAction { get { return _screenToggleAction; } set { _screenToggleAction = value; InspectorPropertyChanged(); } } //public int screenOpenAction { get { return _screenOpenAction; } set { _screenOpenAction = value; InspectorPropertyChanged(); } } //public int screenCloseAction { get { return _screenCloseAction; } set { _screenCloseAction = value; InspectorPropertyChanged(); } } //public int universalCancelAction { get { return _universalCancelAction; } set { _universalCancelAction = value; InspectorPropertyChanged(); } } public bool universalCancelClosesScreen { get { return _universalCancelClosesScreen; } set { _universalCancelClosesScreen = value; InspectorPropertyChanged(); } } public bool showInputBehaviorSettings { get { return _showInputBehaviorSettings; } set { _showInputBehaviorSettings = value; InspectorPropertyChanged(true); } } public bool useThemeSettings { get { return _useThemeSettings; } set { _useThemeSettings = value; InspectorPropertyChanged(true); } } public LanguageData language { get { return _language; } set { _language = value; if(_language != null) _language.Initialize(); InspectorPropertyChanged(true); } } public bool showPlayersGroupLabel { get { return _showPlayersGroupLabel; } set { _showPlayersGroupLabel = value; InspectorPropertyChanged(true); } } public bool showControllerGroupLabel { get { return _showControllerGroupLabel; } set { _showControllerGroupLabel = value; InspectorPropertyChanged(true); } } public bool showAssignedControllersGroupLabel { get { return _showAssignedControllersGroupLabel; } set { _showAssignedControllersGroupLabel = value; InspectorPropertyChanged(true); } } public bool showSettingsGroupLabel { get { return _showSettingsGroupLabel; } set { _showSettingsGroupLabel = value; InspectorPropertyChanged(true); } } public bool showMapCategoriesGroupLabel { get { return _showMapCategoriesGroupLabel; } set { _showMapCategoriesGroupLabel = value; InspectorPropertyChanged(true); } } public bool showControllerNameLabel { get { return _showControllerNameLabel; } set { _showControllerNameLabel = value; InspectorPropertyChanged(true); } } public bool showAssignedControllers { get { return _showAssignedControllers; } set { _showAssignedControllers = value; InspectorPropertyChanged(true); } } public System.Action restoreDefaultsDelegate { get { return _restoreDefaultsDelegate; } set { _restoreDefaultsDelegate = value; } } #endregion public bool isOpen { get { if(!initialized) return references.canvas != null ? references.canvas.gameObject.activeInHierarchy : false; return canvas.activeInHierarchy; } } private bool isFocused { get { if(!initialized) return false; return !windowManager.isWindowOpen; } } private bool inputAllowed { get { if(blockInputOnFocusEndTime > Time.unscaledTime) return false; return true; } } private int inputGridColumnCount { get { int count = 1; // action label is always displayed if(_showKeyboard) count++; if(_showMouse) count++; if(_showControllers) count++; return count; } } private int inputGridWidth => _actionLabelWidth + (_showKeyboard ? _keyboardColMaxWidth : 0) + (_showMouse ? _mouseColMaxWidth : 0) + (_showControllers ? _controllerColMaxWidth : 0) + ((inputGridColumnCount - 1) * _inputColumnSpacing); private Player currentPlayer => ReInput.players.GetPlayer(currentPlayerId); private InputCategory currentMapCategory => ReInput.mapping.GetMapCategory(currentMapCategoryId); private MappingSet currentMappingSet { get { if(currentMapCategoryId < 0) return null; for(int i = 0; i < _mappingSets.Length; i++) { if(_mappingSets[i].mapCategoryId == currentMapCategoryId) return _mappingSets[i]; } return null; } } private Joystick currentJoystick => ReInput.controllers.GetJoystick(currentJoystickId); private bool isJoystickSelected => currentJoystickId >= 0; private GameObject currentUISelection => EventSystem.current != null ? EventSystem.current.currentSelectedGameObject : null; private bool showSettings => _showInputBehaviorSettings && _inputBehaviorSettings.Length > 0; private bool showMapCategories { get { if(_mappingSets == null) return false; if(_mappingSets.Length <= 1) return false; return true; } } #endregion #region MonoBehaviour Events void Awake() { if(_dontDestroyOnLoad) { DontDestroyOnLoad(transform.gameObject); } PreInitialize(); // Open immediately if instantiated with canvas active if(isOpen) { Initialize(); Open(true); } } void Start() { if(_openOnStart) Open(false); } void Update() { #if UNITY_EDITOR CheckEditorRecompile(); #endif if(!isOpen) return; if(!initialized) return; // Make sure the selection is never totally lost for joystick only users CheckUISelection(); } void OnDestroy() { // Remove all events ReInput.ControllerConnectedEvent -= OnJoystickConnected; ReInput.ControllerDisconnectedEvent -= OnJoystickDisconnected; ReInput.ControllerPreDisconnectEvent -= OnJoystickPreDisconnect; #if UNITY_EDITOR ReInput.EditorRecompileEvent -= OnEditorRecompile; #endif UnsubscribeMenuControlInputEvents(); } #endregion #region Initialization private void PreInitialize() { if(!ReInput.isReady) { Debug.LogError("Rewired Control Mapper: Rewired has not been initialized! Are you missing a Rewired Input Manager in your scene?"); return; } // Set up menu control input events SubscribeMenuControlInputEvents(); } private void Initialize() { if(initialized) return; if(!ReInput.isReady) return; if(_rewiredInputManager == null) { _rewiredInputManager = Object.FindObjectOfType(); if(_rewiredInputManager == null) { Debug.LogError("Rewired Control Mapper: A Rewired Input Manager was not assigned in the inspector or found in the current scene! Control Mapper will not function."); return; } } // Set up singleton if(ControlMapper.Instance != null) { Debug.LogError("Rewired Control Mapper: Only one ControlMapper can exist at one time!"); return; } ControlMapper.Instance = this; // Check inspector vars if(prefabs == null || !prefabs.Check()) { Debug.LogError("Rewired Control Mapper: All prefabs must be assigned in the inspector!"); return; } if(references == null || !references.Check()) { Debug.LogError("Rewired Control Mapper: All references must be assigned in the inspector!"); return; } references.inputGridLayoutElement = references.inputGridContainer.GetComponent(); if(references.inputGridLayoutElement == null) { Debug.LogError("Rewired Control Mapper: InputGridContainer is missing LayoutElement component!"); return; } if(_showKeyboard && _keyboardInputFieldCount < 1) { Debug.LogWarning("Rewired Control Mapper: Keyboard Input Fields must be at least 1!"); _keyboardInputFieldCount = 1; } if(_showMouse && _mouseInputFieldCount < 1) { Debug.LogWarning("Rewired Control Mapper: Mouse Input Fields must be at least 1!"); _mouseInputFieldCount = 1; } if(_showControllers && _controllerInputFieldCount < 1) { Debug.LogWarning("Rewired Control Mapper: Controller Input Fields must be at least 1!"); _controllerInputFieldCount = 1; } if(_maxControllersPerPlayer < 0) { Debug.LogWarning("Rewired Control Mapper: Max Controllers Per Player must be at least 0 (no limit)!"); _maxControllersPerPlayer = 0; } if(_useThemeSettings && _themeSettings == null) { Debug.LogWarning("Rewired Control Mapper: To use theming, Theme Settings must be set in the inspector! Theming has been disabled."); _useThemeSettings = false; } if(_language == null) { Debug.LogError("Rawired UI: Language must be set in the inspector!"); return; } // Initialize objects _language.Initialize(); // Create delegates inputFieldActivatedDelegate = OnInputFieldActivated; inputFieldInvertToggleStateChangedDelegate = OnInputFieldInvertToggleStateChanged; // Register for events ReInput.ControllerConnectedEvent += OnJoystickConnected; ReInput.ControllerDisconnectedEvent += OnJoystickDisconnected; ReInput.ControllerPreDisconnectEvent += OnJoystickPreDisconnect; #if UNITY_EDITOR ReInput.EditorRecompileEvent += OnEditorRecompile; #endif // Get the number of players playerCount = ReInput.players.playerCount; // Store references canvas = references.canvas.gameObject; // Create objects windowManager = new WindowManager(prefabs.window, prefabs.fader, references.canvas.transform); playerButtons = new List(); mapCategoryButtons = new List(); assignedControllerButtons = new List(); miscInstantiatedObjects = new List(); // Set default values currentMapCategoryId = _mappingSets[0].mapCategoryId; // Draw generated UI items Draw(); // Create input grid CreateInputGrid(); // Create the layout CreateLayout(); // Set up events in fixed UI elements SubscribeFixedUISelectionEvents(); initialized = true; } #endregion #region Controller Connect / Disconnect Event Handlers private void OnJoystickConnected(ControllerStatusChangedEventArgs args) { if(!initialized) return; if(!_showControllers) return; ClearVarsOnJoystickChange(); ForceRefresh(); } private void OnJoystickDisconnected(ControllerStatusChangedEventArgs args) { if(!initialized) return; if(!_showControllers) return; ClearVarsOnJoystickChange(); ForceRefresh(); } private void OnJoystickPreDisconnect(ControllerStatusChangedEventArgs args) { if(!initialized) return; if(!_showControllers) return; } #endregion #region Main Window Event Handlers // Public - UI Buttons reference and call these directly public void OnButtonActivated(ButtonInfo buttonInfo) { if(!initialized) return; if(!inputAllowed) return; switch(buttonInfo.identifier) { case buttonIdentifier_playerSelection: // player selection OnPlayerSelected(buttonInfo.intData, true); break; case buttonIdentifier_assignedControllerSelection: OnControllerSelected(buttonInfo.intData); break; case buttonIdentifier_removeController: OnRemoveCurrentController(); break; case buttonIdentifier_assignController: ShowAssignControllerWindow(); break; case buttonIdentifier_calibrateController: ShowCalibrateControllerWindow(); break; case buttonIdentifier_editInputBehaviors: ShowEditInputBehaviorsWindow(); break; case buttonIdentifier_mapCategorySelection: OnMapCategorySelected(buttonInfo.intData, true); break; case buttonIdentifier_done: Close(true); break; case buttonIdentifier_restoreDefaults: OnRestoreDefaults(); break; } } public void OnInputFieldActivated(InputFieldInfo fieldInfo) { if(!initialized) return; if(!inputAllowed) return; if(currentPlayer == null) return; InputAction action = ReInput.mapping.GetAction(fieldInfo.actionId); if(action == null) return; string actionName; if(action.type == InputActionType.Button) { actionName = action.descriptiveName; } else if(action.type == InputActionType.Axis) { if(fieldInfo.axisRange == AxisRange.Full) actionName = action.descriptiveName; else if(fieldInfo.axisRange == AxisRange.Positive) { if(string.IsNullOrEmpty(action.positiveDescriptiveName)) actionName = action.descriptiveName + " +"; else actionName = action.positiveDescriptiveName; } else if(fieldInfo.axisRange == AxisRange.Negative) { if(string.IsNullOrEmpty(action.negativeDescriptiveName)) actionName = action.descriptiveName + " -"; else actionName = action.negativeDescriptiveName; } else throw new System.NotImplementedException(); } else throw new System.NotImplementedException(); ControllerMap map = GetControllerMap(fieldInfo.controllerType); if(map == null) return; ActionElementMap aem = fieldInfo.actionElementMapId >= 0 ? map.GetElementMap(fieldInfo.actionElementMapId) : null; if(aem != null) { // element replacement dialog ShowBeginElementAssignmentReplacementWindow(fieldInfo, action, map, aem, actionName); } else { // create new element dialog ShowCreateNewElementAssignmentWindow(fieldInfo, action, map, actionName); } } public void OnInputFieldInvertToggleStateChanged(ToggleInfo toggleInfo, bool newState) { if(!initialized) return; if(!inputAllowed) return; SetActionAxisInverted(newState, toggleInfo.controllerType, toggleInfo.actionElementMapId); } // Private private void OnPlayerSelected(int playerId, bool redraw) { if(!initialized) return; currentPlayerId = playerId; ClearVarsOnPlayerChange(); if(redraw) Redraw(true, true); } private void OnControllerSelected(int joystickId) { if(!initialized) return; currentJoystickId = joystickId; Redraw(true, true); } private void OnRemoveCurrentController() { if(currentPlayer == null) return; if(currentJoystickId < 0) return; RemoveController(currentPlayer, currentJoystickId); ClearVarsOnJoystickChange(); Redraw(false, false); } private void OnMapCategorySelected(int id, bool redraw) { if(!initialized) return; currentMapCategoryId = id; if(redraw) Redraw(true, true); } private void OnRestoreDefaults() { if(!initialized) return; ShowRestoreDefaultsWindow(); } private void OnScreenToggleActionPressed(InputActionEventData data) { if(!isOpen) { // mapper is not open, open it Open(); return; } if(!initialized) return; if(!isFocused) return; // a window is open, so do nothing Close(true); // no window is open, close the mapper } private void OnScreenOpenActionPressed(InputActionEventData data) { Open(); } private void OnScreenCloseActionPressed(InputActionEventData data) { if(!initialized) return; if(!isOpen) return; if(!isFocused) return; // a window is open, so do nothing Close(true); // no window is open, close the mapper } private void OnUniversalCancelActionPressed(InputActionEventData data) { if(!initialized) return; if(!isOpen) return; if(_universalCancelClosesScreen) { if(isFocused) { Close(true); // close the main screen return; } } else if(isFocused) { // no window is open, so do nothing return; } CloseAllWindows(); } #endregion #region Popup Window Button Callbacks private void OnWindowCancel(int windowId) { if(!initialized) return; if(windowId < 0) return; CloseWindow(windowId); } private void OnRemoveElementAssignment(int windowId, ControllerMap map, ActionElementMap aem) { if(map == null || aem == null) return; map.DeleteElementMap(aem.id); CloseWindow(windowId); } private void OnBeginElementAssignment(InputFieldInfo fieldInfo, ControllerMap map, ActionElementMap aem, string actionName) { if(fieldInfo == null || map == null) return; // Store the mapping information pendingInputMapping = new InputMapping(actionName, fieldInfo, map, aem, fieldInfo.controllerType, fieldInfo.controllerId); switch(fieldInfo.controllerType) { case ControllerType.Joystick: ShowElementAssignmentPrePollingWindow(); break; case ControllerType.Keyboard: ShowElementAssignmentPollingWindow(); break; case ControllerType.Mouse: ShowElementAssignmentPollingWindow(); break; default: throw new System.NotImplementedException(); } } private void OnControllerAssignmentConfirmed(int windowId, Player player, int controllerId) { if(windowId < 0 || player == null || controllerId < 0) return; // Assign the joystick AssignController(player, controllerId); // Close the window CloseWindow(windowId); } private void OnMouseAssignmentConfirmed(int windowId, Player player) { if(windowId < 0 || player == null) return; // Remove mouse from all normal Players IList playes = ReInput.players.Players; for(int i = 0; i < playes.Count; i++) { if(playes[i] == player) continue; // skip self playes[i].controllers.hasMouse = false; } // Assign the controller player.controllers.hasMouse = true; CloseWindow(windowId); } private void OnElementAssignmentConflictReplaceConfirmed(int windowId, InputMapping mapping, ElementAssignment assignment, bool skipOtherPlayers) { if(currentPlayer == null || mapping == null) return; ElementAssignmentConflictCheck conflictCheck; if(!CreateConflictCheck(mapping, assignment, out conflictCheck)) { Debug.LogError("Rewired Control Mapper: Error creating conflict check!"); CloseWindow(windowId); return; } // Remove conflicting mappings if(skipOtherPlayers) { // skip other players, only remove from system and self ReInput.players.SystemPlayer.controllers.conflictChecking.RemoveElementAssignmentConflicts(conflictCheck); currentPlayer.controllers.conflictChecking.RemoveElementAssignmentConflicts(conflictCheck); } else { // remove from all players ReInput.controllers.conflictChecking.RemoveElementAssignmentConflicts(conflictCheck); } // Create the new mapping or replace existing mapping.map.ReplaceOrCreateElementMap(assignment); CloseWindow(windowId); } private void OnElementAssignmentAddConfirmed(int windowId, InputMapping mapping, ElementAssignment assignment) { if(currentPlayer == null || mapping == null) return; // Create the new mapping or replace existing mapping.map.ReplaceOrCreateElementMap(assignment); CloseWindow(windowId); } private void OnRestoreDefaultsConfirmed(int windowId) { if(_restoreDefaultsDelegate == null) { IList players = ReInput.players.Players; for(int i = 0; i < players.Count; i++) { Player player = players[i]; if(_showControllers) player.controllers.maps.LoadDefaultMaps(ControllerType.Joystick); if(_showKeyboard) player.controllers.maps.LoadDefaultMaps(ControllerType.Keyboard); if(_showMouse) player.controllers.maps.LoadDefaultMaps(ControllerType.Mouse); } } CloseWindow(windowId); if(_restoreDefaultsDelegate != null) { _restoreDefaultsDelegate(); } } #endregion #region Popup Window Update Callbacks private void OnAssignControllerWindowUpdate(int windowId) { if(currentPlayer == null) return; Window window = windowManager.GetWindow(windowId); if(windowId < 0) return; InputPollingStarted(); // Check the close window timer if(window.timer.finished) { // timer expired InputPollingStopped(); CloseWindow(windowId); // close the window return; } // Poll for controller element down ControllerPollingInfo info = ReInput.controllers.polling.PollAllControllersOfTypeForFirstElementDown(ControllerType.Joystick); if(info.success) { InputPollingStopped(); // Check if another Player has this controller already if(ReInput.controllers.IsControllerAssigned(ControllerType.Joystick, info.controllerId) && !currentPlayer.controllers.ContainsController(ControllerType.Joystick, info.controllerId)) { // another player has the controller ShowControllerAssignmentConflictWindow(info.controllerId); return; } // Assign the controller to this Player OnControllerAssignmentConfirmed(windowId, currentPlayer, info.controllerId); return; } // Show the window close timer window.SetContentText(Mathf.CeilToInt(window.timer.remaining).ToString(), 1); } private void OnElementAssignmentPrePollingWindowUpdate(int windowId) { if(currentPlayer == null) return; Window window = windowManager.GetWindow(windowId); if(windowId < 0) return; if(pendingInputMapping == null) return; InputPollingStarted(); // Check the close window timer if(window.timer.finished) { // timer expired goto Success; // when timer expires, go to next step to support no-button controllers like pedals } // Show the window close timer window.SetContentText(Mathf.CeilToInt(window.timer.remaining).ToString(), 1); ControllerPollingInfo pollingInfo; switch(pendingInputMapping.controllerType) { case ControllerType.Joystick: if(currentPlayer.controllers.joystickCount == 0) return; pollingInfo = ReInput.controllers.polling.PollControllerForFirstButtonDown(pendingInputMapping.controllerType, currentJoystick.id); break; case ControllerType.Keyboard: case ControllerType.Mouse: pollingInfo = ReInput.controllers.polling.PollControllerForFirstButtonDown(pendingInputMapping.controllerType, 0); break; default: throw new System.NotImplementedException(); } if(!pollingInfo.success) return; Success: ShowElementAssignmentPollingWindow(); } private void OnJoystickElementAssignmentPollingWindowUpdate(int windowId) { if(currentPlayer == null) return; Window window = windowManager.GetWindow(windowId); if(windowId < 0) return; if(pendingInputMapping == null) return; InputPollingStarted(); // Check the close window timer if(window.timer.finished) { // timer expired InputPollingStopped(); CloseWindow(windowId); // close the window return; } // Show the window close timer window.SetContentText(Mathf.CeilToInt(window.timer.remaining).ToString(), 1); if(currentPlayer.controllers.joystickCount == 0) return; ControllerPollingInfo pollingInfo = ReInput.controllers.polling.PollControllerForFirstElementDown(ControllerType.Joystick, currentJoystick.id); if(!pollingInfo.success) return; // Verify that this assignment is allowed if(!IsAllowedAssignment(pendingInputMapping, pollingInfo)) return; ElementAssignment assignment = pendingInputMapping.ToElementAssignment(pollingInfo); if(!HasElementAssignmentConflicts(currentPlayer, pendingInputMapping, assignment, false)) { pendingInputMapping.map.ReplaceOrCreateElementMap(assignment); InputPollingStopped(); CloseWindow(windowId); } else { InputPollingStopped(); ShowElementAssignmentConflictWindow(assignment, false); } } private void OnKeyboardElementAssignmentPollingWindowUpdate(int windowId) { if(currentPlayer == null) return; Window window = windowManager.GetWindow(windowId); if(windowId < 0) return; if(pendingInputMapping == null) return; InputPollingStarted(); // Check the close window timer if(window.timer.finished) { // timer expired InputPollingStopped(); CloseWindow(windowId); // close the window return; } ControllerPollingInfo pollingInfo; ModifierKeyFlags modifierFlags; bool modifierKeyPressed; string label; PollKeyboardForAssignment(out pollingInfo, out modifierKeyPressed, out modifierFlags, out label); if(modifierKeyPressed) window.timer.Start(_inputAssignmentTimeout); // reset close timer if a modifier key is pressed // Show the window close timer window.SetContentText(modifierKeyPressed ? string.Empty : Mathf.CeilToInt(window.timer.remaining).ToString(), 2); // Show the modifier key label window.SetContentText(label, 1); if(!pollingInfo.success) return; // Verify that this assignment is allowed if(!IsAllowedAssignment(pendingInputMapping, pollingInfo)) return; ElementAssignment assignment = pendingInputMapping.ToElementAssignment(pollingInfo, modifierFlags); if(!HasElementAssignmentConflicts(currentPlayer, pendingInputMapping, assignment, false)) { pendingInputMapping.map.ReplaceOrCreateElementMap(assignment); InputPollingStopped(); CloseWindow(windowId); } else { InputPollingStopped(); ShowElementAssignmentConflictWindow(assignment, false); } } private void OnMouseElementAssignmentPollingWindowUpdate(int windowId) { if(currentPlayer == null) return; Window window = windowManager.GetWindow(windowId); if(windowId < 0) return; if(pendingInputMapping == null) return; InputPollingStarted(); // Check the close window timer if(window.timer.finished) { // timer expired InputPollingStopped(); CloseWindow(windowId); // close the window return; } // Show the window close timer window.SetContentText(Mathf.CeilToInt(window.timer.remaining).ToString(), 1); ControllerPollingInfo pollingInfo; if(_ignoreMouseXAxisAssignment || _ignoreMouseYAxisAssignment) { pollingInfo = new ControllerPollingInfo(); foreach(ControllerPollingInfo p in ReInput.controllers.polling.PollControllerForAllElementsDown(ControllerType.Mouse, 0)) { if(p.elementType == ControllerElementType.Axis) { if(_ignoreMouseXAxisAssignment && p.elementIndex == 0) continue; // skip X else if(_ignoreMouseYAxisAssignment && p.elementIndex == 1) continue; // skip Y } pollingInfo = p; break; } } else { pollingInfo = ReInput.controllers.polling.PollControllerForFirstElementDown(ControllerType.Mouse, 0); } if(!pollingInfo.success) return; // Verify that the assignment is allowed if(!IsAllowedAssignment(pendingInputMapping, pollingInfo)) return; ElementAssignment assignment = pendingInputMapping.ToElementAssignment(pollingInfo); if(!HasElementAssignmentConflicts(currentPlayer, pendingInputMapping, assignment, true)) { pendingInputMapping.map.ReplaceOrCreateElementMap(assignment); InputPollingStopped(); CloseWindow(windowId); } else { InputPollingStopped(); ShowElementAssignmentConflictWindow(assignment, true); } } private void OnCalibrateAxisStep1WindowUpdate(int windowId) { if(currentPlayer == null) return; Window window = windowManager.GetWindow(windowId); if(windowId < 0) return; if(pendingAxisCalibration == null || !pendingAxisCalibration.isValid) return; InputPollingStarted(); // Check the close window timer if(window.timer.finished) { // timer expired goto Success; // when timer expires, go to next step to support no-button controllers like pedals } // Show the window close timer window.SetContentText(Mathf.CeilToInt(window.timer.remaining).ToString(), 1); // Poll for button press for confirmation if(currentPlayer.controllers.joystickCount == 0) return; ControllerPollingInfo pollingInfo = pendingAxisCalibration.joystick.PollForFirstButtonDown(); if(!pollingInfo.success) return; Success: pendingAxisCalibration.RecordZero(); // record zero value CloseWindow(windowId); // close this window ShowCalibrateAxisStep2Window(); } private void OnCalibrateAxisStep2WindowUpdate(int windowId) { if(currentPlayer == null) return; Window window = windowManager.GetWindow(windowId); if(windowId < 0) return; if(pendingAxisCalibration == null || !pendingAxisCalibration.isValid) return; // Check the close window timer if(window.timer.finished) { // timer expired goto Success; // when timer expires, go to next step to support no-button controllers like pedals } // Show the window close timer window.SetContentText(Mathf.CeilToInt(window.timer.remaining).ToString(), 1); // Record min/max values as user moves the axis pendingAxisCalibration.RecordMinMax(); // Poll for button press for confirmation if(currentPlayer.controllers.joystickCount == 0) return; ControllerPollingInfo pollingInfo = pendingAxisCalibration.joystick.PollForFirstButtonDown(); if(!pollingInfo.success) return; Success: EndAxisCalibration(); // commit the calibration InputPollingStopped(); CloseWindow(windowId); // close this window } #endregion #region Show Specific Popup Windows private void ShowAssignControllerWindow() { if(currentPlayer == null) return; if(ReInput.controllers.joystickCount == 0) return; Window window = OpenWindow(true); if(window == null) return; window.SetUpdateCallback(OnAssignControllerWindowUpdate); window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, _language.assignControllerWindowTitle); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), _language.assignControllerWindowMessage); window.AddContentText(prefabs.windowContentText, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomHStretch, Vector2.zero, ""); window.timer.Start(_controllerAssignmentTimeout); windowManager.Focus(window); } private void ShowControllerAssignmentConflictWindow(int controllerId) { if(currentPlayer == null) return; if(ReInput.controllers.joystickCount == 0) return; Window window = OpenWindow(true); if(window == null) return; string otherPlayer = string.Empty; IList players = ReInput.players.Players; for(int i = 0; i < players.Count; i++) { if(players[i] == currentPlayer) continue; // skip self if(!players[i].controllers.ContainsController(ControllerType.Joystick, controllerId)) continue; otherPlayer = players[i].descriptiveName; break; } Joystick joystick = ReInput.controllers.GetJoystick(controllerId); window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, _language.controllerAssignmentConflictWindowTitle); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), _language.GetControllerAssignmentConflictWindowMessage(joystick.name, otherPlayer, currentPlayer.descriptiveName)); UnityAction cancelCallback = () => { OnWindowCancel(window.id); }; window.cancelCallback = cancelCallback; window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomLeft, UI.UIAnchor.BottomLeft, Vector2.zero, _language.yes, () => { OnControllerAssignmentConfirmed(window.id, currentPlayer, controllerId); }, cancelCallback, true); window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomRight, UI.UIAnchor.BottomRight, Vector2.zero, _language.no, cancelCallback, cancelCallback, false); windowManager.Focus(window); } private void ShowBeginElementAssignmentReplacementWindow(InputFieldInfo fieldInfo, InputAction action, ControllerMap map, ActionElementMap aem, string actionName) { GUIInputField field = inputGrid.GetGUIInputField(currentMapCategoryId, action.id, fieldInfo.axisRange, fieldInfo.controllerType, fieldInfo.intData); if(field == null) return; // Open window and set properties Window window = OpenWindow(true); if(window == null) return; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, actionName); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), field.GetLabel()); UnityAction cancelCallback = () => { OnWindowCancel(window.id); }; window.cancelCallback = cancelCallback; window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomLeft, UI.UIAnchor.BottomLeft, Vector2.zero, _language.replace, () => { OnBeginElementAssignment(fieldInfo, map, aem, actionName); }, cancelCallback, true); window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomCenter, Vector2.zero, _language.remove, () => { OnRemoveElementAssignment(window.id, map, aem); }, cancelCallback, false); window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomRight, UI.UIAnchor.BottomRight, Vector2.zero, _language.cancel, cancelCallback, cancelCallback, false); windowManager.Focus(window); } private void ShowCreateNewElementAssignmentWindow(InputFieldInfo fieldInfo, InputAction action, ControllerMap map, string actionName) { GUIInputField field = inputGrid.GetGUIInputField(currentMapCategoryId, action.id, fieldInfo.axisRange, fieldInfo.controllerType, fieldInfo.intData); if(field == null) return; OnBeginElementAssignment(fieldInfo, map, null, actionName); } private void ShowElementAssignmentPrePollingWindow() { if(pendingInputMapping == null) return; // Open window and set properties Window window = OpenWindow(true); if(window == null) return; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, pendingInputMapping.actionName); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), _language.elementAssignmentPrePollingWindowMessage); if(prefabs.centerStickGraphic != null) window.AddContentImage(prefabs.centerStickGraphic, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomCenter, new Vector2(0, 40)); window.AddContentText(prefabs.windowContentText, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomHStretch, Vector2.zero, ""); window.SetUpdateCallback(OnElementAssignmentPrePollingWindowUpdate); window.timer.Start(_preInputAssignmentTimeout); windowManager.Focus(window); } private void ShowElementAssignmentPollingWindow() { if(pendingInputMapping == null) return; switch(pendingInputMapping.controllerType) { case ControllerType.Joystick: ShowJoystickElementAssignmentPollingWindow(); break; case ControllerType.Keyboard: ShowKeyboardElementAssignmentPollingWindow(); break; case ControllerType.Mouse: if(currentPlayer.controllers.hasMouse) ShowMouseElementAssignmentPollingWindow(); else ShowMouseAssignmentConflictWindow(); break; default: throw new System.NotImplementedException(); } } private void ShowJoystickElementAssignmentPollingWindow() { if(pendingInputMapping == null) return; // Open window and set properties Window window = OpenWindow(true); if(window == null) return; string message = pendingInputMapping.axisRange == AxisRange.Full && _showFullAxisInputFields && !_showSplitAxisInputFields ? _language.GetJoystickElementAssignmentPollingWindowMessage_FullAxisFieldOnly(pendingInputMapping.actionName) : _language.GetJoystickElementAssignmentPollingWindowMessage(pendingInputMapping.actionName); window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, pendingInputMapping.actionName); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), message); window.AddContentText(prefabs.windowContentText, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomHStretch, Vector2.zero, ""); window.SetUpdateCallback(OnJoystickElementAssignmentPollingWindowUpdate); window.timer.Start(_inputAssignmentTimeout); windowManager.Focus(window); } private void ShowKeyboardElementAssignmentPollingWindow() { if(pendingInputMapping == null) return; // Open window and set properties Window window = OpenWindow(true); if(window == null) return; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, pendingInputMapping.actionName); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), _language.GetKeyboardElementAssignmentPollingWindowMessage(pendingInputMapping.actionName)); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -(window.GetContentTextHeight(0) + 50.0f)), ""); window.AddContentText(prefabs.windowContentText, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomHStretch, Vector2.zero, ""); window.SetUpdateCallback(OnKeyboardElementAssignmentPollingWindowUpdate); window.timer.Start(_inputAssignmentTimeout); windowManager.Focus(window); } private void ShowMouseElementAssignmentPollingWindow() { if(pendingInputMapping == null) return; // Open window and set properties Window window = OpenWindow(true); if(window == null) return; string message = pendingInputMapping.axisRange == AxisRange.Full && _showFullAxisInputFields && !_showSplitAxisInputFields ? _language.GetMouseElementAssignmentPollingWindowMessage_FullAxisFieldOnly(pendingInputMapping.actionName) : _language.GetMouseElementAssignmentPollingWindowMessage(pendingInputMapping.actionName); window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, pendingInputMapping.actionName); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), message); window.AddContentText(prefabs.windowContentText, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomHStretch, Vector2.zero, ""); window.SetUpdateCallback(OnMouseElementAssignmentPollingWindowUpdate); window.timer.Start(_inputAssignmentTimeout); windowManager.Focus(window); } private void ShowElementAssignmentConflictWindow(ElementAssignment assignment, bool skipOtherPlayers) { if(pendingInputMapping == null) return; // Determine what kind of conflict we had bool blocked = IsBlockingAssignmentConflict(pendingInputMapping, assignment, skipOtherPlayers); string message = blocked ? _language.GetElementAlreadyInUseBlocked(pendingInputMapping.elementName) : _language.GetElementAlreadyInUseCanReplace(pendingInputMapping.elementName, _allowElementAssignmentConflicts); // Open window and set properties Window window = OpenWindow(true); if(window == null) return; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, _language.elementAssignmentConflictWindowMessage); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), message); UnityAction cancelCallback = () => { OnWindowCancel(window.id); }; window.cancelCallback = cancelCallback; if(blocked) { window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomCenter, Vector2.zero, _language.okay, cancelCallback, cancelCallback, true); } else { window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomLeft, UI.UIAnchor.BottomLeft, Vector2.zero, _language.replace, () => { OnElementAssignmentConflictReplaceConfirmed(window.id, pendingInputMapping, assignment, skipOtherPlayers); }, cancelCallback, true); if(_allowElementAssignmentConflicts) { window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomCenter, Vector2.zero, _language.add, () => { OnElementAssignmentAddConfirmed(window.id, pendingInputMapping, assignment); }, cancelCallback, false); } window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomRight, UI.UIAnchor.BottomRight, Vector2.zero, _language.cancel, cancelCallback, cancelCallback, false); } windowManager.Focus(window); } private void ShowMouseAssignmentConflictWindow() { if(currentPlayer == null) return; Window window = OpenWindow(true); if(window == null) return; string otherPlayer = string.Empty; IList players = ReInput.players.Players; for(int i = 0; i < players.Count; i++) { if(players[i] == currentPlayer) continue; // skip self if(!players[i].controllers.hasMouse) continue; otherPlayer = players[i].descriptiveName; break; } window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, _language.mouseAssignmentConflictWindowTitle); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), _language.GetMouseAssignmentConflictWindowMessage(otherPlayer, currentPlayer.descriptiveName)); UnityAction cancelCallback = () => { OnWindowCancel(window.id); }; window.cancelCallback = cancelCallback; window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomLeft, UI.UIAnchor.BottomLeft, Vector2.zero, _language.yes, () => { OnMouseAssignmentConfirmed(window.id, currentPlayer); }, cancelCallback, true); window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomRight, UI.UIAnchor.BottomRight, Vector2.zero, _language.no, cancelCallback, cancelCallback, false); windowManager.Focus(window); } private void ShowCalibrateControllerWindow() { if(currentPlayer == null) return; if(currentPlayer.controllers.joystickCount == 0) return; CalibrationWindow window = OpenWindow(prefabs.calibrationWindow, "CalibrationWindow", true) as CalibrationWindow; if(window == null) return; Joystick joystick = currentJoystick; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, _language.calibrateControllerWindowTitle); window.SetJoystick(currentPlayer.id, joystick); window.SetButtonCallback(CalibrationWindow.ButtonIdentifier.Done, CloseWindow); window.SetButtonCallback(CalibrationWindow.ButtonIdentifier.Calibrate, StartAxisCalibration); window.SetButtonCallback(CalibrationWindow.ButtonIdentifier.Cancel, CloseWindow); windowManager.Focus(window); } private void ShowCalibrateAxisStep1Window() { if(currentPlayer == null) return; Window window = OpenWindow(false); if(window == null) return; if(pendingAxisCalibration == null) return; Joystick joystick = pendingAxisCalibration.joystick; if(joystick.axisCount == 0) return; int axisIndex = pendingAxisCalibration.axisIndex; if(axisIndex < 0 || axisIndex >= joystick.axisCount) return; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, _language.calibrateAxisStep1WindowTitle); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), _language.GetCalibrateAxisStep1WindowMessage(joystick.AxisElementIdentifiers[axisIndex].name)); if(prefabs.centerStickGraphic != null) window.AddContentImage(prefabs.centerStickGraphic, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomCenter, new Vector2(0, 40)); window.AddContentText(prefabs.windowContentText, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomHStretch, Vector2.zero, ""); window.SetUpdateCallback(OnCalibrateAxisStep1WindowUpdate); window.timer.Start(_axisCalibrationTimeout); windowManager.Focus(window); } private void ShowCalibrateAxisStep2Window() { if(currentPlayer == null) return; Window window = OpenWindow(false); if(window == null) return; if(pendingAxisCalibration == null) return; Joystick joystick = pendingAxisCalibration.joystick; if(joystick.axisCount == 0) return; int axisIndex = pendingAxisCalibration.axisIndex; if(axisIndex < 0 || axisIndex >= joystick.axisCount) return; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, _language.calibrateAxisStep2WindowTitle); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), _language.GetCalibrateAxisStep2WindowMessage(joystick.AxisElementIdentifiers[axisIndex].name)); if(prefabs.moveStickGraphic != null) window.AddContentImage(prefabs.moveStickGraphic, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomCenter, new Vector2(0, 40)); window.AddContentText(prefabs.windowContentText, UI.UIPivot.BottomCenter, UI.UIAnchor.BottomHStretch, Vector2.zero, ""); window.SetUpdateCallback(OnCalibrateAxisStep2WindowUpdate); window.timer.Start(_axisCalibrationTimeout); windowManager.Focus(window); } private void ShowEditInputBehaviorsWindow() { if(currentPlayer == null) return; if(_inputBehaviorSettings == null) return; InputBehaviorWindow window = OpenWindow(prefabs.inputBehaviorsWindow, "EditInputBehaviorsWindow", true) as InputBehaviorWindow; if(window == null) return; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, _language.inputBehaviorSettingsWindowTitle); window.SetData(currentPlayer.id, _inputBehaviorSettings); window.SetButtonCallback(InputBehaviorWindow.ButtonIdentifier.Done, CloseWindow); window.SetButtonCallback(InputBehaviorWindow.ButtonIdentifier.Cancel, CloseWindow); windowManager.Focus(window); } private void ShowRestoreDefaultsWindow() { if(currentPlayer == null) return; OpenModal( _language.restoreDefaultsWindowTitle, _language.restoreDefaultsWindowMessage, _language.yes, OnRestoreDefaultsConfirmed, _language.no, OnWindowCancel, true ); } #endregion #region Input Field Grid private void CreateInputGrid() { InitializeInputGrid(); // Create the column groups CreateHeaderLabels(); CreateActionLabelColumn(); CreateKeyboardInputFieldColumn(); CreateMouseInputFieldColumn(); CreateControllerInputFieldColumn(); // Create GUI Elements CreateInputActionLabels(); CreateInputFields(); // Disable all GUI elements initially inputGrid.HideAll(); // Fix Unity scroll bar bug ResetInputGridScrollBar(); } private void InitializeInputGrid() { if(inputGrid == null) inputGrid = new InputGrid(); else inputGrid.ClearAll(); // Add all user-assignable actions to the grid for(int i = 0; i < _mappingSets.Length; i++) { MappingSet set = _mappingSets[i]; if(set == null || !set.isValid) continue; InputMapCategory mapCat = ReInput.mapping.GetMapCategory(set.mapCategoryId); if(mapCat == null) continue; if(!mapCat.userAssignable) continue; inputGrid.AddMapCategory(set.mapCategoryId); if(set.actionListMode == MappingSet.ActionListMode.ActionCategory) { // list actions in action categories IList actionCategoryIds = set.actionCategoryIds; for(int j = 0; j < actionCategoryIds.Count; j++) { int actionCatId = actionCategoryIds[j]; InputCategory actionCat = ReInput.mapping.GetActionCategory(actionCatId); if(actionCat == null) continue; if(!actionCat.userAssignable) continue; inputGrid.AddActionCategory(set.mapCategoryId, actionCatId); // add the action category foreach(InputAction action in ReInput.mapping.UserAssignableActionsInCategory(actionCatId)) { if(action.type == InputActionType.Axis) { if(_showFullAxisInputFields) inputGrid.AddAction(set.mapCategoryId, action, AxisRange.Full); if(_showSplitAxisInputFields) { inputGrid.AddAction(set.mapCategoryId, action, AxisRange.Positive); inputGrid.AddAction(set.mapCategoryId, action, AxisRange.Negative); } } else if(action.type == InputActionType.Button) { inputGrid.AddAction(set.mapCategoryId, action, AxisRange.Positive); } } } } else { // list individual user-assigned actions IList actionIds = set.actionIds; for(int j = 0; j < actionIds.Count; j++) { InputAction action = ReInput.mapping.GetAction(actionIds[j]); if(action == null) continue; if(action.type == InputActionType.Axis) { if(_showFullAxisInputFields) inputGrid.AddAction(set.mapCategoryId, action, AxisRange.Full); if(_showSplitAxisInputFields) { inputGrid.AddAction(set.mapCategoryId, action, AxisRange.Positive); inputGrid.AddAction(set.mapCategoryId, action, AxisRange.Negative); } } else if(action.type == InputActionType.Button) { inputGrid.AddAction(set.mapCategoryId, action, AxisRange.Positive); } } } } // Set column H-spacing references.inputGridInnerGroup.GetComponent().spacing = _inputColumnSpacing; // Set grid layout width references.inputGridLayoutElement.flexibleWidth = 0; // disable flexible width references.inputGridLayoutElement.preferredWidth = inputGridWidth; } private void RefreshInputGridStructure() { if(currentMappingSet == null) return; inputGrid.HideAll(); inputGrid.Show(currentMappingSet.mapCategoryId); // Resize the main container to fit the elements references.inputGridInnerGroup.GetComponent().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, inputGrid.GetColumnHeight(currentMappingSet.mapCategoryId)); } // Layout private void CreateHeaderLabels() { GUILabel label; // Actions column header references.inputGridHeader1 = CreateNewColumnGroup("ActionsHeader", references.inputGridHeadersGroup, _actionLabelWidth).transform; CreateLabel(prefabs.inputGridHeaderLabel, _language.actionColumnLabel, references.inputGridHeader1, Vector2.zero); // Keyboard column header if(_showKeyboard) { references.inputGridHeader2 = CreateNewColumnGroup("KeybordHeader", references.inputGridHeadersGroup, _keyboardColMaxWidth).transform; label = CreateLabel(prefabs.inputGridHeaderLabel, _language.keyboardColumnLabel, references.inputGridHeader2, Vector2.zero); label.SetTextAlignment(TextAnchor.MiddleCenter); } // Mouse column header if(_showMouse) { references.inputGridHeader3 = CreateNewColumnGroup("MouseHeader", references.inputGridHeadersGroup, _mouseColMaxWidth).transform; label = CreateLabel(prefabs.inputGridHeaderLabel, _language.mouseColumnLabel, references.inputGridHeader3, Vector2.zero); label.SetTextAlignment(TextAnchor.MiddleCenter); } // Controller column header if(_showControllers) { references.inputGridHeader4 = CreateNewColumnGroup("ControllerHeader", references.inputGridHeadersGroup, _controllerColMaxWidth).transform; label = CreateLabel(prefabs.inputGridHeaderLabel, _language.controllerColumnLabel, references.inputGridHeader4, Vector2.zero); label.SetTextAlignment(TextAnchor.MiddleCenter); } } private void CreateActionLabelColumn() { Transform columnXform = CreateNewColumnGroup("ActionLabelColumn", references.inputGridInnerGroup, _actionLabelWidth).transform; references.inputGridActionColumn = columnXform; } private void CreateKeyboardInputFieldColumn() { if(!_showKeyboard) return; CreateInputFieldColumn("KeyboardColumn", ControllerType.Keyboard, _keyboardColMaxWidth, _keyboardInputFieldCount, true); } private void CreateMouseInputFieldColumn() { if(!_showMouse) return; CreateInputFieldColumn("MouseColumn", ControllerType.Mouse, _mouseColMaxWidth, _mouseInputFieldCount, false); } private void CreateControllerInputFieldColumn() { if(!_showControllers) return; CreateInputFieldColumn("ControllerColumn", ControllerType.Joystick, _controllerColMaxWidth, _controllerInputFieldCount, false); } private void CreateInputFieldColumn(string name, ControllerType controllerType, int maxWidth, int cols, bool disableFullAxis) { Transform columnXform = CreateNewColumnGroup(name, references.inputGridInnerGroup, maxWidth).transform; switch(controllerType) { case ControllerType.Joystick: references.inputGridControllerColumn = columnXform; break; case ControllerType.Keyboard: references.inputGridKeyboardColumn = columnXform; break; case ControllerType.Mouse: references.inputGridMouseColumn = columnXform; break; default: throw new System.NotImplementedException(); } } // Create the individual elements private void CreateInputActionLabels() { Transform columnXform = references.inputGridActionColumn; for(int mappingSetIndex = 0; mappingSetIndex < _mappingSets.Length; mappingSetIndex++) { MappingSet set = _mappingSets[mappingSetIndex]; if(set == null || !set.isValid) continue; int yPos = 0; // Create the labels if(set.actionListMode == MappingSet.ActionListMode.ActionCategory) { // list all actions in categories int categoryCount = 0; IList actionCategoryIds = set.actionCategoryIds; for(int i = 0; i < actionCategoryIds.Count; i++) { InputCategory category = ReInput.mapping.GetActionCategory(actionCategoryIds[i]); if(category == null) continue; if(!category.userAssignable) continue; // action category isn't user assignable // Count actions first int count = CountIEnumerable(ReInput.mapping.UserAssignableActionsInCategory(category.id)); if(count == 0) continue; // no actions so skip category // Draw category label if(_showActionCategoryLabels) { if(categoryCount > 0) yPos -= _inputRowCategorySpacing; // extra space above category GUILabel label = CreateLabel(category.descriptiveName, columnXform, new Vector2(0, yPos)); label.SetFontStyle(FontStyle.Bold); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionCategoryLabel(set.mapCategoryId, category.id, label); yPos -= _inputRowHeight; } // Draw Action labels foreach(InputAction action in ReInput.mapping.UserAssignableActionsInCategory(category.id, true)) { GUILabel label; if(action.type == InputActionType.Axis) { if(_showFullAxisInputFields) { label = CreateLabel(action.descriptiveName, columnXform, new Vector2(0, yPos)); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionLabel(set.mapCategoryId, action.id, AxisRange.Full, label); yPos -= _inputRowHeight; } if(_showSplitAxisInputFields) { string positiveDescriptiveName = !string.IsNullOrEmpty(action.positiveDescriptiveName) ? action.positiveDescriptiveName : action.descriptiveName + " +"; label = CreateLabel(positiveDescriptiveName, columnXform, new Vector2(0, yPos)); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionLabel(set.mapCategoryId, action.id, AxisRange.Positive, label); yPos -= _inputRowHeight; string negativeDescriptiveName = !string.IsNullOrEmpty(action.negativeDescriptiveName) ? action.negativeDescriptiveName : action.descriptiveName + " -"; label = CreateLabel(negativeDescriptiveName, columnXform, new Vector2(0, yPos)); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionLabel(set.mapCategoryId, action.id, AxisRange.Negative, label); yPos -= _inputRowHeight; } } else if(action.type == InputActionType.Button) { label = CreateLabel(action.descriptiveName, columnXform, new Vector2(0, yPos)); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionLabel(set.mapCategoryId, action.id, AxisRange.Positive, label); yPos -= _inputRowHeight; } } categoryCount++; } } else { // list only individual actions defined by the user IList actionIds = set.actionIds; for(int i = 0; i < actionIds.Count; i++) { InputAction action = ReInput.mapping.GetAction(actionIds[i]); if(action == null) continue; if(!action.userAssignable) continue; // action isn't user assignable InputCategory category = ReInput.mapping.GetActionCategory(action.categoryId); if(category == null) continue; if(!category.userAssignable) continue; // action category isn't user assignable // Draw Action labels GUILabel label; if(action.type == InputActionType.Axis) { if(_showFullAxisInputFields) { label = CreateLabel(action.descriptiveName, columnXform, new Vector2(0, yPos)); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionLabel(set.mapCategoryId, action.id, AxisRange.Full, label); yPos -= _inputRowHeight; } if(_showSplitAxisInputFields) { label = CreateLabel(action.positiveDescriptiveName, columnXform, new Vector2(0, yPos)); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionLabel(set.mapCategoryId, action.id, AxisRange.Positive, label); yPos -= _inputRowHeight; label = CreateLabel(action.negativeDescriptiveName, columnXform, new Vector2(0, yPos)); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionLabel(set.mapCategoryId, action.id, AxisRange.Negative, label); yPos -= _inputRowHeight; } } else if(action.type == InputActionType.Button) { label = CreateLabel(action.descriptiveName, columnXform, new Vector2(0, yPos)); label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); inputGrid.AddActionLabel(set.mapCategoryId, action.id, AxisRange.Positive, label); yPos -= _inputRowHeight; } } } inputGrid.SetColumnHeight(set.mapCategoryId, -yPos); // store the column height for this map category } } private void CreateInputFields() { if(_showControllers) CreateInputFields(references.inputGridControllerColumn, ControllerType.Joystick, _controllerColMaxWidth, _controllerInputFieldCount, false); if(_showKeyboard) CreateInputFields(references.inputGridKeyboardColumn, ControllerType.Keyboard, _keyboardColMaxWidth, _keyboardInputFieldCount, true); if(_showMouse) CreateInputFields(references.inputGridMouseColumn, ControllerType.Mouse, _mouseColMaxWidth, _mouseInputFieldCount, false); } private void CreateInputFields(Transform columnXform, ControllerType controllerType, int maxWidth, int cols, bool disableFullAxis) { for(int mappingSetIndex = 0; mappingSetIndex < _mappingSets.Length; mappingSetIndex++) { MappingSet set = _mappingSets[mappingSetIndex]; if(set == null || !set.isValid) continue; // Create input field buttons int fieldWidth = maxWidth / cols; int yPos = 0; int categoryCount = 0; // Create the labels if(set.actionListMode == MappingSet.ActionListMode.ActionCategory) { // list all actions in categories IList actionCategoryIds = set.actionCategoryIds; for(int i = 0; i < actionCategoryIds.Count; i++) { InputCategory category = ReInput.mapping.GetActionCategory(actionCategoryIds[i]); if(category == null) continue; if(!category.userAssignable) continue; // action category isn't user assignable // Count actions first int count = CountIEnumerable(ReInput.mapping.UserAssignableActionsInCategory(category.id)); if(count == 0) continue; // no actions so skip category // Add spaces for category labels if(_showActionCategoryLabels) { yPos -= categoryCount > 0 ? _inputRowHeight + _inputRowCategorySpacing : _inputRowHeight; } // Draw input field buttons foreach(InputAction action in ReInput.mapping.UserAssignableActionsInCategory(category.id, true)) { if(action.type == InputActionType.Axis) { if(_showFullAxisInputFields) CreateInputFieldSet(columnXform, set.mapCategoryId, action, AxisRange.Full, controllerType, cols, fieldWidth, ref yPos, disableFullAxis); if(_showSplitAxisInputFields) { CreateInputFieldSet(columnXform, set.mapCategoryId, action, AxisRange.Positive, controllerType, cols, fieldWidth, ref yPos, false); CreateInputFieldSet(columnXform, set.mapCategoryId, action, AxisRange.Negative, controllerType, cols, fieldWidth, ref yPos, false); } } else if(action.type == InputActionType.Button) { CreateInputFieldSet(columnXform, set.mapCategoryId, action, AxisRange.Positive, controllerType, cols, fieldWidth, ref yPos, false); } categoryCount++; } } } else { // list only individual actins defined by the user IList actionIds = set.actionIds; for(int i = 0; i < actionIds.Count; i++) { InputAction action = ReInput.mapping.GetAction(actionIds[i]); if(action == null) continue; if(!action.userAssignable) continue; InputCategory category = ReInput.mapping.GetActionCategory(action.categoryId); if(category == null) continue; if(!category.userAssignable) continue; // action category isn't user assignable // Draw input field buttons if(action.type == InputActionType.Axis) { if(_showFullAxisInputFields) CreateInputFieldSet(columnXform, set.mapCategoryId, action, AxisRange.Full, controllerType, cols, fieldWidth, ref yPos, disableFullAxis); if(_showSplitAxisInputFields) { CreateInputFieldSet(columnXform, set.mapCategoryId, action, AxisRange.Positive, controllerType, cols, fieldWidth, ref yPos, false); CreateInputFieldSet(columnXform, set.mapCategoryId, action, AxisRange.Negative, controllerType, cols, fieldWidth, ref yPos, false); } } else if(action.type == InputActionType.Button) { CreateInputFieldSet(columnXform, set.mapCategoryId, action, AxisRange.Positive, controllerType, cols, fieldWidth, ref yPos, false); } } } } } private void CreateInputFieldSet(Transform parent, int mapCategoryId, InputAction action, AxisRange axisRange, ControllerType controllerType, int cols, int fieldWidth, ref int yPos, bool disableFullAxis) { // Create horizontal layout group to hold fields GameObject layoutGroup = CreateNewGUIObject("FieldLayoutGroup", parent, new Vector2(0, yPos)); HorizontalLayoutGroup hLayoutGroup = layoutGroup.AddComponent(); RectTransform rt = layoutGroup.GetComponent(); rt.anchorMin = new Vector2(0, 1); rt.anchorMax = new Vector2(1, 1); rt.pivot = new Vector2(0, 1); rt.sizeDelta = Vector2.zero; rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _inputRowHeight); // Create the field set inputGrid.AddInputFieldSet(mapCategoryId, action, axisRange, controllerType, layoutGroup); // Create the fields for(int fieldIndex = 0; fieldIndex < cols; fieldIndex++) { int toggleWidth = axisRange == AxisRange.Full ? _invertToggleWidth : 0; GUIInputField field = CreateInputField(hLayoutGroup.transform, Vector2.zero, "", action.id, axisRange, controllerType, fieldIndex); field.SetFirstChildObjectWidth(LayoutElementSizeType.PreferredSize, fieldWidth - toggleWidth); // set max width so buttons will stay equal widths inputGrid.AddInputField(mapCategoryId, action, axisRange, controllerType, fieldIndex, field); // add to the system if(axisRange == AxisRange.Full) { // Create invert toggle field if(!disableFullAxis) { GUIToggle toggle = CreateToggle(prefabs.inputGridFieldInvertToggle, hLayoutGroup.transform, Vector2.zero, "", action.id, axisRange, controllerType, fieldIndex); toggle.SetFirstChildObjectWidth(LayoutElementSizeType.MinSize, toggleWidth); // set max width so buttons will stay equal widths field.AddToggle(toggle); } else { field.SetInteractible(false, false, true); } } } yPos -= _inputRowHeight; } private void PopulateInputFields() { // Clear inputGrid.InitializeFields(currentMapCategoryId); // Get the player if(currentPlayer == null) return; inputGrid.SetFieldsActive(currentMapCategoryId, true); foreach(InputActionSet actionSet in inputGrid.GetActionSets(currentMapCategoryId)) { ControllerType controllerType; int controllerId; int layoutId; int fieldCount; // Draw keyboard and mouse fields if(_showKeyboard) { controllerType = ControllerType.Keyboard; controllerId = 0; layoutId = _keyboardMapDefaultLayout; fieldCount = _keyboardInputFieldCount; ControllerMap map = GetControllerMapOrCreateNew(controllerType, controllerId, layoutId); PopulateInputFieldGroup(actionSet, map, controllerType, controllerId, fieldCount); } if(_showMouse) { controllerType = ControllerType.Mouse; controllerId = 0; layoutId = _mouseMapDefaultLayout; fieldCount = _mouseInputFieldCount; ControllerMap map = GetControllerMapOrCreateNew(controllerType, controllerId, layoutId); if(currentPlayer.controllers.hasMouse) PopulateInputFieldGroup(actionSet, map, controllerType, controllerId, fieldCount); // only populate if mouse is assigned, hide maps if not // Do not disable fields if player doesn't have mouse so player can assign it by clicking on the fields } // Draw joystick fields if(isJoystickSelected && currentPlayer.controllers.joystickCount > 0) { controllerType = ControllerType.Joystick; controllerId = currentJoystick.id; layoutId = _joystickMapDefaultLayout; fieldCount = _controllerInputFieldCount; ControllerMap map = GetControllerMapOrCreateNew(controllerType, controllerId, layoutId); PopulateInputFieldGroup(actionSet, map, controllerType, controllerId, fieldCount); } else { // no controller assigned to this player DisableInputFieldGroup(actionSet, ControllerType.Joystick, _controllerInputFieldCount); } } } private void PopulateInputFieldGroup(InputActionSet actionSet, ControllerMap controllerMap, ControllerType controllerType, int controllerId, int maxFields) { if(controllerMap == null) return; int count = 0; // First populate fixed data in all fields including blanks inputGrid.SetFixedFieldData(currentMapCategoryId, actionSet.actionId, actionSet.axisRange, controllerType, controllerId); // Populate data in fields that have an ActionElementMap foreach(ActionElementMap aem in controllerMap.ElementMapsWithAction(actionSet.actionId)) { if(aem.elementType == ControllerElementType.Button) { if(actionSet.axisRange == AxisRange.Full) { // Not implemented continue; } else if(actionSet.axisRange == AxisRange.Positive) { if(aem.axisContribution == Pole.Negative) continue; } else if(actionSet.axisRange == AxisRange.Negative) { if(aem.axisContribution == Pole.Positive) continue; } inputGrid.PopulateField(currentMapCategoryId, actionSet.actionId, actionSet.axisRange, controllerType, controllerId, count, aem.id, aem.elementIdentifierName, false); } else if(aem.elementType == ControllerElementType.Axis) { if(actionSet.axisRange == AxisRange.Full) { if(aem.axisRange != AxisRange.Full) continue; inputGrid.PopulateField(currentMapCategoryId, actionSet.actionId, actionSet.axisRange, controllerType, controllerId, count, aem.id, aem.elementIdentifierName, aem.invert); } else if(actionSet.axisRange == AxisRange.Positive) { if(aem.axisRange == AxisRange.Full) continue; if(aem.axisContribution == Pole.Negative) continue; inputGrid.PopulateField(currentMapCategoryId, actionSet.actionId, actionSet.axisRange, controllerType, controllerId, count, aem.id, aem.elementIdentifierName, false); } else if(actionSet.axisRange == AxisRange.Negative) { if(aem.axisRange == AxisRange.Full) continue; if(aem.axisContribution == Pole.Positive) continue; inputGrid.PopulateField(currentMapCategoryId, actionSet.actionId, actionSet.axisRange, controllerType, controllerId, count, aem.id, aem.elementIdentifierName, false); } } count++; if(count > maxFields) break; // no more room } } private void DisableInputFieldGroup(InputActionSet actionSet, ControllerType controllerType, int fieldCount) { // Disable the fields for(int i = 0; i < fieldCount; i++) { GUIInputField field = inputGrid.GetGUIInputField(currentMapCategoryId, actionSet.actionId, actionSet.axisRange, controllerType, i); if(field == null) continue; field.SetInteractible(false, false); } } private void ResetInputGridScrollBar() { references.inputGridInnerGroup.GetComponent().anchoredPosition = Vector2.zero; // fix Unity scrollbar bug references.inputGridVScrollbar.value = 1.0f; } #endregion #region Drawing (UI Layout) // Draw the layout initially private void CreateLayout() { // Enable all groups that are used by the current settings. // Player section references.playersGroup.gameObject.SetActive(showPlayers); // Controller section references.controllerGroup.gameObject.SetActive(_showControllers); references.assignedControllersGroup.gameObject.SetActive(_showControllers && ShowAssignedControllers()); // Settings and Map Categories section references.settingsAndMapCategoriesGroup.gameObject.SetActive(showSettings || showMapCategories); // Settings section references.settingsGroup.gameObject.SetActive(showSettings); // Map categories section references.mapCategoriesGroup.gameObject.SetActive(showMapCategories); } private void Draw() { DrawPlayersGroup(); DrawControllersGroup(); DrawSettingsGroup(); DrawMapCategoriesGroup(); DrawWindowButtonsGroup(); } private void DrawPlayersGroup() { if(!showPlayers) return; // Set label references.playersGroup.labelText = _language.playersGroupLabel; references.playersGroup.SetLabelActive(_showPlayersGroupLabel); for(int i = 0; i < playerCount; i++) { Player player = ReInput.players.GetPlayer(i); if(player == null) continue; GameObject instance = UI.ControlMapper.UITools.InstantiateGUIObject(prefabs.button, references.playersGroup.content, "Player" + i + "Button"); GUIButton button = new GUIButton(instance); button.SetLabel(player.descriptiveName); button.SetButtonInfoData(buttonIdentifier_playerSelection, player.id); button.SetOnClickCallback(OnButtonActivated); button.buttonInfo.OnSelectedEvent += OnUIElementSelected; playerButtons.Add(button); } } private void DrawControllersGroup() { if(!_showControllers) return; // Set group labels references.controllerSettingsGroup.labelText = _language.controllerSettingsGroupLabel; references.controllerSettingsGroup.SetLabelActive(_showControllerGroupLabel); references.controllerNameLabel.gameObject.SetActive(_showControllerNameLabel); references.controllerGroupLabelGroup.gameObject.SetActive(_showControllerGroupLabel || _showControllerNameLabel); if(ShowAssignedControllers()) { references.assignedControllersGroup.labelText = _language.assignedControllersGroupLabel; references.assignedControllersGroup.SetLabelActive(_showAssignedControllersGroupLabel); } ButtonInfo buttonInfo; // Set up button labels in static buttons buttonInfo = references.removeControllerButton.GetComponent(); buttonInfo.text.text = _language.removeControllerButtonLabel; buttonInfo = references.calibrateControllerButton.GetComponent(); buttonInfo.text.text = _language.calibrateControllerButtonLabel; buttonInfo = references.assignControllerButton.GetComponent(); buttonInfo.text.text = _language.assignControllerButtonLabel; // Create None placeholder button GUIButton guiButton = CreateButton(_language.none, references.assignedControllersGroup.content, Vector2.zero); guiButton.SetInteractible(false, false, true); assignedControllerButtonsPlaceholder = guiButton; } private void DrawSettingsGroup() { if(!showSettings) return; // Set label on group references.settingsGroup.labelText = _language.settingsGroupLabel; references.settingsGroup.SetLabelActive(_showSettingsGroupLabel); // Create Input Behavior Settings button GUIButton button = CreateButton(_language.inputBehaviorSettingsButtonLabel, references.settingsGroup.content, Vector2.zero); miscInstantiatedObjects.Add(button.gameObject); button.buttonInfo.OnSelectedEvent += OnUIElementSelected; button.SetButtonInfoData(buttonIdentifier_editInputBehaviors, 0); button.SetOnClickCallback(OnButtonActivated); } private void DrawMapCategoriesGroup() { if(!showMapCategories) return; if(_mappingSets == null) return; // Set label on group references.mapCategoriesGroup.labelText = _language.mapCategoriesGroupLabel; references.mapCategoriesGroup.SetLabelActive(_showMapCategoriesGroupLabel); for(int i = 0; i < _mappingSets.Length; i++) { MappingSet set = _mappingSets[i]; if(set == null) continue; InputMapCategory cat = ReInput.mapping.GetMapCategory(set.mapCategoryId); if(cat == null) continue; // invalid map category id GameObject instance = UI.ControlMapper.UITools.InstantiateGUIObject(prefabs.button, references.mapCategoriesGroup.content, cat.name + "Button"); GUIButton button = new GUIButton(instance); button.SetLabel(cat.descriptiveName); button.SetButtonInfoData(buttonIdentifier_mapCategorySelection, cat.id); button.SetOnClickCallback(OnButtonActivated); button.buttonInfo.OnSelectedEvent += OnUIElementSelected; mapCategoryButtons.Add(button); } } private void DrawWindowButtonsGroup() { references.doneButton.GetComponent().text.text = _language.doneButtonLabel; references.restoreDefaultsButton.GetComponent().text.text = _language.restoreDefaultsButtonLabel; } // Redraw / Refresh the layout private void Redraw(bool listsChanged, bool playTransitions) { RedrawPlayerGroup(playTransitions); RedrawControllerGroup(); RedrawMapCategoriesGroup(playTransitions); RedrawInputGrid(listsChanged); // Make sure something is always selected so UI navigation is never lost with only joysticks if(currentUISelection == null || !currentUISelection.activeInHierarchy) RestoreLastUISelection(); } private void RedrawPlayerGroup(bool playTransitions) { if(!showPlayers) return; for(int i = 0; i < playerButtons.Count; i++) { bool state = currentPlayerId != playerButtons[i].buttonInfo.intData; playerButtons[i].SetInteractible(state, playTransitions); } } private void RedrawControllerGroup() { int prevSelectedButtonJoyId = -1; // Reset config group references.controllerNameLabel.text = _language.none; UI.ControlMapper.UITools.SetInteractable(references.removeControllerButton, false, false); UI.ControlMapper.UITools.SetInteractable(references.assignControllerButton, false, false); UI.ControlMapper.UITools.SetInteractable(references.calibrateControllerButton, false, false); // Reset assigned controllers group if(ShowAssignedControllers()) { // Clear assigned controller buttons foreach(GUIButton button in assignedControllerButtons) { if(button.gameObject == null) continue; if(currentUISelection == button.gameObject) prevSelectedButtonJoyId = button.buttonInfo.intData; // store selection if this was the active UI element Object.Destroy(button.gameObject); } assignedControllerButtons.Clear(); // Enable placeholder assignedControllerButtonsPlaceholder.SetActive(true); } // Get the current player Player player = ReInput.players.GetPlayer(currentPlayerId); if(player == null) return; // Assigned controller buttons if(ShowAssignedControllers()) { if(player.controllers.joystickCount > 0) assignedControllerButtonsPlaceholder.SetActive(false); // disable the None placeholder // Create buttons for the assigned controllers foreach(Joystick joystick in player.controllers.Joysticks) { GUIButton button = CreateButton(joystick.name, references.assignedControllersGroup.content, Vector2.zero); button.SetButtonInfoData(buttonIdentifier_assignedControllerSelection, joystick.id); button.SetOnClickCallback(OnButtonActivated); button.buttonInfo.OnSelectedEvent += OnUIElementSelected; assignedControllerButtons.Add(button); if(joystick.id == currentJoystickId) button.SetInteractible(false, true); // disable the selected joystick } // Auto-select the first controller if none selected if(player.controllers.joystickCount > 0 && !isJoystickSelected) { currentJoystickId = player.controllers.Joysticks[0].id; assignedControllerButtons[0].SetInteractible(false, false); } // Restore UI selection if selected button was destroyed if(prevSelectedButtonJoyId >= 0) { foreach(GUIButton button in assignedControllerButtons) { if(button.buttonInfo.intData == prevSelectedButtonJoyId) { SetUISelection(button.gameObject); break; } } } } else { // just one controller per player // Auto-select the first controller if none selected if(player.controllers.joystickCount > 0 && !isJoystickSelected) { currentJoystickId = player.controllers.Joysticks[0].id; } } // Set ineractible on controller config buttons if a controller is selected/assigned if(isJoystickSelected && player.controllers.joystickCount > 0) { // Remove Controller button references.removeControllerButton.interactable = true; // Controller name label references.controllerNameLabel.text = currentJoystick.name; // Calibrate Controller button if(currentJoystick.axisCount > 0) references.calibrateControllerButton.interactable = true; } // Assign Controller button int playerJoystickCount = player.controllers.joystickCount; int totalJoystickCount = ReInput.controllers.joystickCount; int maxJoysticksPerPlayer = GetMaxControllersPerPlayer(); bool infiniteJoysticksPerPlayer = maxJoysticksPerPlayer == 0; if(totalJoystickCount > 0 && playerJoystickCount < totalJoystickCount && (maxJoysticksPerPlayer == 1 || infiniteJoysticksPerPlayer || playerJoystickCount < maxJoysticksPerPlayer) ) { UI.ControlMapper.UITools.SetInteractable(references.assignControllerButton, true, false); } } private void RedrawMapCategoriesGroup(bool playTransitions) { if(!showMapCategories) return; for(int i = 0; i < mapCategoryButtons.Count; i++) { bool state = currentMapCategoryId != mapCategoryButtons[i].buttonInfo.intData; mapCategoryButtons[i].SetInteractible(state, playTransitions); } } private void RedrawInputGrid(bool listsChanged) { if(listsChanged) RefreshInputGridStructure(); PopulateInputFields(); if(listsChanged) ResetInputGridScrollBar(); } private void ForceRefresh() { if(windowManager.isWindowOpen) CloseAllWindows(); else Redraw(false, false); } #endregion #region Create UI Objects private void CreateInputCategoryRow(ref int rowCount, InputCategory category) { CreateLabel(category.descriptiveName, references.inputGridActionColumn, new Vector2(0, rowCount * _inputRowHeight * -1.0f)); rowCount++; } private GUILabel CreateLabel(string labelText, Transform parent, Vector2 offset) { return CreateLabel(prefabs.inputGridLabel, labelText, parent, offset); } private GUILabel CreateLabel(GameObject prefab, string labelText, Transform parent, Vector2 offset) { GameObject instance = InstantiateGUIObject(prefab, parent, offset); Text text = UnityTools.GetComponentInSelfOrChildren(instance); if(text == null) { Debug.LogError("Rewired Control Mapper: Label prefab is missing Text component!"); return null; } text.text = labelText; return new GUILabel(instance); } private GUIButton CreateButton(string labelText, Transform parent, Vector2 offset) { GUIButton button = new GUIButton(InstantiateGUIObject(prefabs.button, parent, offset)); button.SetLabel(labelText); return button; } private GUIButton CreateFitButton(string labelText, Transform parent, Vector2 offset) { GUIButton button = new GUIButton(InstantiateGUIObject(prefabs.fitButton, parent, offset)); button.SetLabel(labelText); return button; } private GUIInputField CreateInputField(Transform parent, Vector2 offset, string label, int actionId, AxisRange axisRange, ControllerType controllerType, int fieldIndex) { GUIInputField field = CreateInputField(parent, offset); field.SetLabel(""); field.SetFieldInfoData(actionId, axisRange, controllerType, fieldIndex); field.SetOnClickCallback(inputFieldActivatedDelegate); field.fieldInfo.OnSelectedEvent += OnUIElementSelected; return field; } private GUIInputField CreateInputField(Transform parent, Vector2 offset) { return new GUIInputField(InstantiateGUIObject(prefabs.inputGridFieldButton, parent, offset)); } private GUIToggle CreateToggle(GameObject prefab, Transform parent, Vector2 offset, string label, int actionId, AxisRange axisRange, ControllerType controllerType, int fieldIndex) { GUIToggle toggle = CreateToggle(prefab, parent, offset); toggle.SetToggleInfoData(actionId, axisRange, controllerType, fieldIndex); toggle.SetOnSubmitCallback(inputFieldInvertToggleStateChangedDelegate); toggle.toggleInfo.OnSelectedEvent += OnUIElementSelected; return toggle; } private GUIToggle CreateToggle(GameObject prefab, Transform parent, Vector2 offset) { return new GUIToggle(InstantiateGUIObject(prefab, parent, offset)); } private GameObject InstantiateGUIObject(GameObject prefab, Transform parent, Vector2 offset) { if(prefab == null) { Debug.LogError("Rewired Control Mapper: Prefab is null!"); return null; } GameObject instance = (GameObject)Object.Instantiate(prefab); return InitializeNewGUIGameObject(instance, parent, offset); } private GameObject CreateNewGUIObject(string name, Transform parent, Vector2 offset) { GameObject instance = new GameObject(); instance.name = name; instance.AddComponent(); return InitializeNewGUIGameObject(instance, parent, offset); } private GameObject InitializeNewGUIGameObject(GameObject gameObject, Transform parent, Vector2 offset) { if(gameObject == null) { Debug.LogError("Rewired Control Mapper: GameObject is null!"); return null; } RectTransform instanceXform = gameObject.GetComponent(); if(instanceXform == null) { Debug.LogError("Rewired Control Mapper: GameObject does not have a RectTransform component!"); return gameObject; } if(parent != null) { instanceXform.SetParent(parent, false); } //instanceXform.localScale = Vector3.one; //instanceXform.localPosition = Vector3.zero; instanceXform.anchoredPosition = offset; return gameObject; } private GameObject CreateNewColumnGroup(string name, Transform parent, int maxWidth) { GameObject group = CreateNewGUIObject(name, parent, Vector2.zero); inputGrid.AddGroup(group); LayoutElement layout = group.AddComponent(); if(maxWidth >= 0) layout.preferredWidth = maxWidth; RectTransform rectXForm = group.GetComponent(); rectXForm.anchorMin = new Vector2(0, 0); rectXForm.anchorMax = new Vector2(1, 0); return group; } #endregion #region Popup Window Management private Window OpenWindow(bool closeOthers) { return OpenWindow(string.Empty, closeOthers); } private Window OpenWindow(string name, bool closeOthers) { if(closeOthers) windowManager.CancelAll(); Window window = windowManager.OpenWindow(name, _defaultWindowWidth, _defaultWindowHeight); if(window == null) return null; ChildWindowOpened(); return window; } private Window OpenWindow(GameObject windowPrefab, bool closeOthers) { return OpenWindow(windowPrefab, string.Empty, closeOthers); } private Window OpenWindow(GameObject windowPrefab, string name, bool closeOthers) { if(closeOthers) windowManager.CancelAll(); Window window = windowManager.OpenWindow(windowPrefab, name); if(window == null) return null; ChildWindowOpened(); return window; } private void OpenModal(string title, string message, string confirmText, System.Action confirmAction, string cancelText, System.Action cancelAction, bool closeOthers) { Window window = OpenWindow(closeOthers); if(window == null) return; window.CreateTitleText(prefabs.windowTitleText, Vector2.zero, title); window.AddContentText(prefabs.windowContentText, UI.UIPivot.TopCenter, UI.UIAnchor.TopHStretch, new Vector2(0, -100), message); UnityAction cancelCallback = () => { OnWindowCancel(window.id); }; window.cancelCallback = cancelCallback; window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomLeft, UI.UIAnchor.BottomLeft, Vector2.zero, confirmText, () => { OnRestoreDefaultsConfirmed(window.id); }, cancelCallback, false); window.CreateButton(prefabs.fitButton, UI.UIPivot.BottomRight, UI.UIAnchor.BottomRight, Vector2.zero, cancelText, cancelCallback, cancelCallback, true); windowManager.Focus(window); } private void CloseWindow(int windowId) { if(!windowManager.isWindowOpen) return; windowManager.CloseWindow(windowId); ChildWindowClosed(); } private void CloseTopWindow() { if(!windowManager.isWindowOpen) return; windowManager.CloseTop(); ChildWindowClosed(); } private void CloseAllWindows() { if(!windowManager.isWindowOpen) return; windowManager.CancelAll(); ChildWindowClosed(); InputPollingStopped(); } private void ChildWindowOpened() { if(!windowManager.isWindowOpen) return; // do nothing if a window is not open SetIsFocused(false); if(_PopupWindowOpenedEvent != null) _PopupWindowOpenedEvent(); if(_onPopupWindowOpened != null) _onPopupWindowOpened.Invoke(); } private void ChildWindowClosed() { if(windowManager.isWindowOpen) return; // do nothing if a window is still open SetIsFocused(true); if(_PopupWindowClosedEvent != null) _PopupWindowClosedEvent(); if(_onPopupWindowClosed != null) _onPopupWindowClosed.Invoke(); } #endregion #region Element Assignment private bool HasElementAssignmentConflicts(Player player, InputMapping mapping, ElementAssignment assignment, bool skipOtherPlayers) { if(player == null || mapping == null) return false; ElementAssignmentConflictCheck conflictCheck; if(!CreateConflictCheck(mapping, assignment, out conflictCheck)) return false; if(skipOtherPlayers) { // skip other players, only check system and self if(ReInput.players.SystemPlayer.controllers.conflictChecking.DoesElementAssignmentConflict(conflictCheck)) return true; if(player.controllers.conflictChecking.DoesElementAssignmentConflict(conflictCheck)) return true; return false; } else { // check all players return ReInput.controllers.conflictChecking.DoesElementAssignmentConflict(conflictCheck); } } private bool IsBlockingAssignmentConflict(InputMapping mapping, ElementAssignment assignment, bool skipOtherPlayers) { ElementAssignmentConflictCheck conflictCheck; if(!CreateConflictCheck(mapping, assignment, out conflictCheck)) return false; if(skipOtherPlayers) { // skip other players, only check system and self foreach(ElementAssignmentConflictInfo conflict in ReInput.players.SystemPlayer.controllers.conflictChecking.ElementAssignmentConflicts(conflictCheck)) { if(!conflict.isUserAssignable) return true; } foreach(ElementAssignmentConflictInfo conflict in currentPlayer.controllers.conflictChecking.ElementAssignmentConflicts(conflictCheck)) { if(!conflict.isUserAssignable) return true; } } else { // check all players foreach(ElementAssignmentConflictInfo conflict in ReInput.controllers.conflictChecking.ElementAssignmentConflicts(conflictCheck)) { if(!conflict.isUserAssignable) return true; } } return false; } private IEnumerable ElementAssignmentConflicts(Player player, InputMapping mapping, ElementAssignment assignment, bool skipOtherPlayers) { if(player == null || mapping == null) yield break; ElementAssignmentConflictCheck conflictCheck; if(!CreateConflictCheck(mapping, assignment, out conflictCheck)) yield break; if(skipOtherPlayers) { // skip other players, only check system and self foreach(ElementAssignmentConflictInfo conflict in ReInput.players.SystemPlayer.controllers.conflictChecking.ElementAssignmentConflicts(conflictCheck)) { if(!conflict.isUserAssignable) yield return conflict; } foreach(ElementAssignmentConflictInfo conflict in player.controllers.conflictChecking.ElementAssignmentConflicts(conflictCheck)) { if(!conflict.isUserAssignable) yield return conflict; } } else { // check all players foreach(ElementAssignmentConflictInfo conflict in ReInput.controllers.conflictChecking.ElementAssignmentConflicts(conflictCheck)) { if(!conflict.isUserAssignable) yield return conflict; } } } private bool CreateConflictCheck(InputMapping mapping, ElementAssignment assignment, out ElementAssignmentConflictCheck conflictCheck) { if(mapping == null || currentPlayer == null) { conflictCheck = new ElementAssignmentConflictCheck(); return false; } conflictCheck = assignment.ToElementAssignmentConflictCheck(); conflictCheck.playerId = currentPlayer.id; conflictCheck.controllerType = mapping.controllerType; conflictCheck.controllerId = mapping.controllerId; conflictCheck.controllerMapId = mapping.map.id; conflictCheck.controllerMapCategoryId = mapping.map.categoryId; if(mapping.aem != null) conflictCheck.elementMapId = mapping.aem.id; return true; } private void PollKeyboardForAssignment(out ControllerPollingInfo pollingInfo, out bool modifierKeyPressed, out ModifierKeyFlags modifierFlags, out string label) { pollingInfo = new ControllerPollingInfo(); label = string.Empty; modifierKeyPressed = false; modifierFlags = ModifierKeyFlags.None; int modifierPressedCount = 0; // the number of modifier keys being pressed this cycle ControllerPollingInfo nonModifierKeyInfo = new ControllerPollingInfo(); ControllerPollingInfo firstModifierKeyInfo = new ControllerPollingInfo(); ModifierKeyFlags curModifiers = ModifierKeyFlags.None; // Check all keys being pressed at present so we can handle modifier keys foreach(ControllerPollingInfo info in ReInput.controllers.Keyboard.PollForAllKeys()) { KeyCode key = info.keyboardKey; if(key == KeyCode.AltGr) continue; // skip AltGr key because it gets fired when alt and control are held on some keyboards // determine if a modifier key is being pressed if(Keyboard.IsModifierKey(info.keyboardKey)) { // a modifier key is pressed if(modifierPressedCount == 0) firstModifierKeyInfo = info; // store the polling info for the first modifier key pressed in case its the only key pressed curModifiers |= Keyboard.KeyCodeToModifierKeyFlags(key); // add the key to the current modifier flags modifierPressedCount += 1; // count how many modifier keys are pressed } else { // this is not a modifier key if(nonModifierKeyInfo.keyboardKey != KeyCode.None) continue; // skip after the first one detected, we only need one non-modifier key press nonModifierKeyInfo = info; // store the polling info } } // Commit immediately if a non-modifier key was pressed if(nonModifierKeyInfo.keyboardKey != KeyCode.None) { // a regular key was pressed // Make sure the primary button is in a down state or else we'll get immediate confirmation if using the keyboard to navigate if(!ReInput.controllers.Keyboard.GetKeyDown(nonModifierKeyInfo.keyboardKey)) return; // fail because key is not in a down state if(modifierPressedCount == 0) { // only the regular key was pressed pollingInfo = nonModifierKeyInfo; // copy polling info into entry return; } else { // one more more modifier keys was pressed too pollingInfo = nonModifierKeyInfo; // copy polling info into entry modifierFlags = curModifiers; // set the modifier key flags return; } } else if(modifierPressedCount > 0) { // one or more modifier keys were pressed, but no regular keys modifierKeyPressed = true; if(modifierPressedCount == 1) { // only one modifier is pressed, allow assigning just the modifier key // Assign the modifier key as the main key if the user holds it for 1 second if(ReInput.controllers.Keyboard.GetKeyTimePressed(firstModifierKeyInfo.keyboardKey) > 1.0f) { // key was pressed for one second pollingInfo = firstModifierKeyInfo; // copy polling info into entry return; } // Show the key that is being pressed label = Keyboard.GetKeyName(firstModifierKeyInfo.keyboardKey); } else { // more than one modifier key is pressed // do nothing because we don't want to assign modified modifier key presses such as Control + Alt, but you could if you wanted to. // Show the modifier keys being held label = Keyboard.ModifierKeyFlagsToString(curModifiers); } } } #endregion #region Controller Calibration private void StartAxisCalibration(int axisIndex) { if(currentPlayer == null) return; if(currentPlayer.controllers.joystickCount == 0) return; Joystick joystick = currentJoystick; if(axisIndex < 0 || axisIndex >= joystick.axisCount) return; pendingAxisCalibration = new AxisCalibrator(joystick, axisIndex); ShowCalibrateAxisStep1Window(); } private void EndAxisCalibration() { if(pendingAxisCalibration == null) return; pendingAxisCalibration.Commit(); pendingAxisCalibration = null; } #endregion #region UI Selection private void SetUISelection(GameObject selection) { if(EventSystem.current == null) return; EventSystem.current.SetSelectedGameObject(selection); } private void RestoreLastUISelection() { if(lastUISelection == null || !lastUISelection.activeInHierarchy) { SetDefaultUISelection(); return; } SetUISelection(lastUISelection); } private void SetDefaultUISelection() { if(!isOpen) return; if(references.defaultSelection == null) SetUISelection(null); else SetUISelection(references.defaultSelection.gameObject); } private void SelectDefaultMapCategory(bool redraw) { currentMapCategoryId = GetDefaultMapCategoryId(); OnMapCategorySelected(currentMapCategoryId, redraw); if(!showMapCategories) return; for(int i = 0; i < _mappingSets.Length; i++) { InputMapCategory cat = ReInput.mapping.GetMapCategory(_mappingSets[i].mapCategoryId); if(cat == null) continue; currentMapCategoryId = _mappingSets[i].mapCategoryId; break; } if(currentMapCategoryId < 0) return; // invalid, no map cats found // Disable the selected button and enable others for(int i = 0; i < _mappingSets.Length; i++) { bool state = _mappingSets[i].mapCategoryId == currentMapCategoryId ? false : true; mapCategoryButtons[i].SetInteractible(state, false); } } private void CheckUISelection() { if(!isFocused) return; if(currentUISelection == null) RestoreLastUISelection(); } private void OnUIElementSelected(GameObject selectedObject) { lastUISelection = selectedObject; // record the last selected UI element } #endregion #region Main Page private void SetIsFocused(bool state) { // Set interactible state on main page controls references.mainCanvasGroup.interactable = state; if(state) { // just received focus Redraw(false, false); RestoreLastUISelection(); // Block input for a short time to prevent immediate button-down events when making a new assignment or assigning a controller blockInputOnFocusEndTime = Time.unscaledTime + blockInputOnFocusTimeout; } } public void Toggle() { if(isOpen) Close(true); else Open(); } public void Open() { Open(false); } private void Open(bool force) { if(!initialized) Initialize(); if(!initialized) return; // failed to init if(!force && isOpen) return; Clear(); canvas.SetActive(true); OnPlayerSelected(0, false); SelectDefaultMapCategory(false); SetDefaultUISelection(); Redraw(true, false); if(_ScreenOpenedEvent != null) _ScreenOpenedEvent(); if(_onScreenOpened != null) _onScreenOpened.Invoke(); } public void Close(bool save) { if(!initialized) return; if(!isOpen) return; if(save) { // save data before closing if(ReInput.userDataStore != null) ReInput.userDataStore.Save(); } Clear(); canvas.SetActive(false); SetUISelection(null); // deselect if(_ScreenClosedEvent != null) _ScreenClosedEvent(); if(_onScreenClosed != null) _onScreenClosed.Invoke(); } #endregion #region Clear / Reset private void Clear() { windowManager.CancelAll(); lastUISelection = null; pendingInputMapping = null; pendingAxisCalibration = null; InputPollingStopped(); } private void ClearCompletely() { ClearSpawnedObjects(); ClearAllVars(); } private void ClearSpawnedObjects() { windowManager.ClearCompletely(); inputGrid.ClearAll(); foreach(GUIButton item in playerButtons) { Object.Destroy(item.gameObject); } playerButtons.Clear(); foreach(GUIButton item in mapCategoryButtons) { Object.Destroy(item.gameObject); } mapCategoryButtons.Clear(); foreach(GUIButton item in assignedControllerButtons) { Object.Destroy(item.gameObject); } assignedControllerButtons.Clear(); if(assignedControllerButtonsPlaceholder != null) { Object.Destroy(assignedControllerButtonsPlaceholder.gameObject); assignedControllerButtonsPlaceholder = null; } foreach(GameObject item in miscInstantiatedObjects) { Object.Destroy(item); } miscInstantiatedObjects.Clear(); } private void ClearVarsOnPlayerChange() { currentJoystickId = -1; } private void ClearVarsOnJoystickChange() { currentJoystickId = -1; } private void ClearAllVars() { initialized = false; Instance = null; // clear singleton instance playerCount = 0; inputGrid = null; windowManager = null; currentPlayerId = -1; currentMapCategoryId = -1; playerButtons = null; mapCategoryButtons = null; miscInstantiatedObjects = null; canvas = null; lastUISelection = null; currentJoystickId = -1; pendingInputMapping = null; pendingAxisCalibration = null; inputFieldActivatedDelegate = null; inputFieldInvertToggleStateChangedDelegate = null; isPollingForInput = false; } public void Reset() { if(!initialized) return; ClearCompletely(); Initialize(); if(isOpen) Open(true); } #endregion #region Misc private void SetActionAxisInverted(bool state, ControllerType controllerType, int actionElementMapId) { if(currentPlayer == null) return; ControllerMapWithAxes map = GetControllerMap(controllerType) as ControllerMapWithAxes; if(map == null) return; ActionElementMap aem = map.GetElementMap(actionElementMapId); if(aem == null) return; aem.invert = state; } private ControllerMap GetControllerMap(ControllerType type) { if(currentPlayer == null) return null; int controllerId = 0; switch(type) { case ControllerType.Keyboard: break; case ControllerType.Mouse: break; case ControllerType.Joystick: if(currentPlayer.controllers.joystickCount > 0) controllerId = currentJoystick.id; else return null; break; default: throw new System.NotImplementedException(); } // We cannot support multiple layouts in the same category in this system, so just get the first map in the category we find return currentPlayer.controllers.maps.GetFirstMapInCategory(type, controllerId, currentMapCategoryId); } private ControllerMap GetControllerMapOrCreateNew(ControllerType controllerType, int controllerId, int layoutId) { ControllerMap map = GetControllerMap(controllerType); if(map == null) { currentPlayer.controllers.maps.AddEmptyMap(controllerType, controllerId, currentMapCategoryId, layoutId); map = currentPlayer.controllers.maps.GetMap(controllerType, controllerId, currentMapCategoryId, layoutId); } return map; } private int CountIEnumerable(IEnumerable enumerable) { if(enumerable == null) return 0; IEnumerator enumerator = enumerable.GetEnumerator(); if(enumerator == null) return 0; int count = 0; while(enumerator.MoveNext()) { count++; } return count; } private int GetDefaultMapCategoryId() { if(_mappingSets.Length == 0) return 0; // use Default // Get the first valid map category id we find for(int i = 0; i < _mappingSets.Length; i++) { if(ReInput.mapping.GetMapCategory(_mappingSets[i].mapCategoryId) == null) continue; return _mappingSets[i].mapCategoryId; } return 0; } private void SubscribeFixedUISelectionEvents() { if(references.fixedSelectableUIElements == null) return; foreach(GameObject go in references.fixedSelectableUIElements) { UIElementInfo elementInfo = UnityTools.GetComponent(go); if(elementInfo == null) continue; elementInfo.OnSelectedEvent += OnUIElementSelected; } } private void SubscribeMenuControlInputEvents() { // Set up menu toggle event SubscribeRewiredInputEventAllPlayers(_screenToggleAction, OnScreenToggleActionPressed); // Set up player menu open event SubscribeRewiredInputEventAllPlayers(_screenOpenAction, OnScreenOpenActionPressed); // Set up player menu close event SubscribeRewiredInputEventAllPlayers(_screenCloseAction, OnScreenCloseActionPressed); // Set up universal cancel event SubscribeRewiredInputEventAllPlayers(_universalCancelAction, OnUniversalCancelActionPressed); } private void UnsubscribeMenuControlInputEvents() { // Remove menu toggle event UnsubscribeRewiredInputEventAllPlayers(_screenToggleAction, OnScreenToggleActionPressed); // Remove player menu open event UnsubscribeRewiredInputEventAllPlayers(_screenOpenAction, OnScreenOpenActionPressed); // Remove player menu close event UnsubscribeRewiredInputEventAllPlayers(_screenCloseAction, OnScreenCloseActionPressed); // Remove universal cancel event UnsubscribeRewiredInputEventAllPlayers(_universalCancelAction, OnUniversalCancelActionPressed); } private void SubscribeRewiredInputEventAllPlayers(int actionId, System.Action callback) { if(actionId < 0 || callback == null) return; if(ReInput.mapping.GetAction(actionId) == null) { Debug.LogWarning("Rewired Control Mapper: " + actionId + " is not a valid Action id!"); return; } // Allow control by all players including system foreach(Player p in ReInput.players.AllPlayers) { p.AddInputEventDelegate(callback, UpdateLoopType.Update, InputActionEventType.ButtonJustPressed, actionId); } } private void UnsubscribeRewiredInputEventAllPlayers(int actionId, System.Action callback) { if(actionId < 0 || callback == null) return; if(!Rewired.ReInput.isReady) return; // Rewired may have been destroyed already if(ReInput.mapping.GetAction(actionId) == null) { Debug.LogWarning("Rewired Control Mapper: " + actionId + " is not a valid Action id!"); return; } // Allow control by all players including system foreach(Player p in ReInput.players.AllPlayers) { p.RemoveInputEventDelegate(callback, UpdateLoopType.Update, InputActionEventType.ButtonJustPressed, actionId); } } private int GetMaxControllersPerPlayer() { if(_rewiredInputManager.userData.ConfigVars.autoAssignJoysticks) return _rewiredInputManager.userData.ConfigVars.maxJoysticksPerPlayer; return _maxControllersPerPlayer; } private bool ShowAssignedControllers() { if(!_showControllers) return false; if(_showAssignedControllers) return true; if(GetMaxControllersPerPlayer() != 1) return true; // force display if Player can have more than 1 controller return false; } private void InspectorPropertyChanged(bool reset = false) { if(reset) Reset(); } private void AssignController(Player player, int controllerId) { if(player == null) return; if(player.controllers.ContainsController(ControllerType.Joystick, controllerId)) return; // already assigned to this player // Remove any controllers first if player only allowed 1 at a time if(GetMaxControllersPerPlayer() == 1) { RemoveAllControllers(player); ClearVarsOnJoystickChange(); // redraw } // Manually remove the controller from all other Players first so the configs can be saved foreach(Player p in ReInput.players.Players) { if(p == player) continue; // skip self RemoveController(p, controllerId); } // Assign the controller to the Player player.controllers.AddController(ControllerType.Joystick, controllerId, false); // Load the controller data in case the user customized this controller before if(ReInput.userDataStore != null) ReInput.userDataStore.LoadControllerData(player.id, ControllerType.Joystick, controllerId); } private void RemoveAllControllers(Player player) { if(player == null) return; // Clear controllers from the Player manually so we can save them first IList joysticks = player.controllers.Joysticks; for(int i = joysticks.Count - 1; i >= 0; i--) { RemoveController(player, joysticks[i].id); } } private void RemoveController(Player player, int controllerId) { if(player == null) return; if(!player.controllers.ContainsController(ControllerType.Joystick, controllerId)) return; // Save the controller data before unassigning it if(ReInput.userDataStore != null) ReInput.userDataStore.SaveControllerData(player.id, ControllerType.Joystick, controllerId); // Unassign the controller player.controllers.RemoveController(ControllerType.Joystick, controllerId); } private bool IsAllowedAssignment(InputMapping pendingInputMapping, ControllerPollingInfo pollingInfo) { if(pendingInputMapping == null) return false; // Determine if user settings prevent this potential assignment // Handle user disabling of split-axis assignment fields if(pendingInputMapping.axisRange == AxisRange.Full) { // this is a full-axis assignment // If the user has disabled the split-axis fields, do not allow button or key assignments if(!_showSplitAxisInputFields && pollingInfo.elementType == ControllerElementType.Button) { return false; } } return true; } private void InputPollingStarted() { bool prev = isPollingForInput; isPollingForInput = true; if(!prev) { if(_InputPollingStartedEvent != null) _InputPollingStartedEvent(); if(_onInputPollingStarted != null) _onInputPollingStarted.Invoke(); } } private void InputPollingStopped() { bool prev = isPollingForInput; isPollingForInput = false; if(prev) { if(_InputPollingEndedEvent != null) _InputPollingEndedEvent(); if(_onInputPollingEnded != null) _onInputPollingEnded.Invoke(); } } #endregion #region Editor Recompile #if UNITY_EDITOR private bool recompiling; private void CheckEditorRecompile() { if(!recompiling) return; if(!ReInput.isReady) return; recompiling = false; PreInitialize(); Initialize(); } private void OnEditorRecompile() { recompiling = true; if(!initialized) return; Close(false); ClearCompletely(); } #endif #endregion #region Static Methods public static void ApplyTheme(ThemedElement.ElementInfo[] elementInfo) { if(Instance == null) return; if(Instance._themeSettings == null) return; if(!Instance._useThemeSettings) return; // themeing not allowed Instance._themeSettings.Apply(elementInfo); } public static UI.ControlMapper.LanguageData GetLanguage() { if(Instance == null) return null; return Instance._language; } #endregion } }