// Copyright (c) 2017 Augie R. Maddox, Guavaman Enterprises. All rights reserved. #pragma warning disable 0649 // disable warnings about unused variables namespace Rewired.Demos { using UnityEngine; using System; using System.Collections.Generic; using Rewired; [AddComponentMenu("")] public class ControlRemappingDemo1 : MonoBehaviour { private const float defaultModalWidth = 250.0f; private const float defaultModalHeight = 200.0f; private const float assignmentTimeout = 5.0f; // Helper objects private DialogHelper dialog; // Listener private InputMapper inputMapper = new InputMapper(); private InputMapper.ConflictFoundEventData conflictFoundEventData; // GUI state management private bool guiState; private bool busy; private bool pageGUIState; // Selections private Player selectedPlayer; private int selectedMapCategoryId; private ControllerSelection selectedController; ControllerMap selectedMap; // Other flags private bool showMenu; private bool startListening; // Scroll view positions private Vector2 actionScrollPos; private Vector2 calibrateScrollPos; // Queues private Queue actionQueue; // Setup vars private bool setupFinished; // Editor state management [System.NonSerialized] private bool initialized; private bool isCompiling; // Styles GUIStyle style_wordWrap; GUIStyle style_centeredBox; #region Initialization private void Awake() { inputMapper.options.timeout = assignmentTimeout; inputMapper.options.ignoreMouseXAxis = true; inputMapper.options.ignoreMouseYAxis = true; Initialize(); } private void OnEnable() { Subscribe(); } private void OnDisable() { Unsubscribe(); } private void Initialize() { dialog = new DialogHelper(); actionQueue = new Queue(); selectedController = new ControllerSelection(); ReInput.ControllerConnectedEvent += JoystickConnected; ReInput.ControllerPreDisconnectEvent += JoystickPreDisconnect; // runs before joystick is completely disconnected so we can save maps ReInput.ControllerDisconnectedEvent += JoystickDisconnected; // final disconnect that runs after joystick has been fully removed ResetAll(); initialized = true; ReInput.userDataStore.Load(); // load saved user maps on start if there are any to load if(ReInput.unityJoystickIdentificationRequired) { IdentifyAllJoysticks(); } } private void Setup() { if(setupFinished) return; // Create styles style_wordWrap = new GUIStyle(GUI.skin.label); style_wordWrap.wordWrap = true; style_centeredBox = new GUIStyle(GUI.skin.box); style_centeredBox.alignment = TextAnchor.MiddleCenter; setupFinished = true; } private void Subscribe() { Unsubscribe(); inputMapper.ConflictFoundEvent += OnConflictFound; inputMapper.StoppedEvent += OnStopped; } private void Unsubscribe() { inputMapper.RemoveAllEventListeners(); } #endregion #region Main Update public void OnGUI() { #if UNITY_EDITOR // Check for script recompile in the editor CheckRecompile(); #endif if(!initialized) return; Setup(); HandleMenuControl(); if(!showMenu) { DrawInitialScreen(); return; } SetGUIStateStart(); // Process queue ProcessQueue(); // Draw contents DrawPage(); ShowDialog(); SetGUIStateEnd(); // Clear momentary vars busy = false; } #endregion #region Menu Control private void HandleMenuControl() { if(dialog.enabled) return; // don't allow closing the menu while dialog is open so there won't be issues remapping the Menu button if(Event.current.type == EventType.Layout) { if(ReInput.players.GetSystemPlayer().GetButtonDown("Menu")) { if(showMenu) { // menu is open and will be closed ReInput.userDataStore.Save(); // save all maps when menu is closed Close(); } else { Open(); } } } } private void Close() { ClearWorkingVars(); showMenu = false; } private void Open() { showMenu = true; } #endregion #region Draw private void DrawInitialScreen() { GUIContent content; ActionElementMap map = ReInput.players.GetSystemPlayer().controllers.maps.GetFirstElementMapWithAction("Menu", true); if(map != null) { content = new GUIContent("Press " + map.elementIdentifierName + " to open the menu."); } else { content = new GUIContent("There is no element assigned to open the menu!"); } // Draw the box GUILayout.BeginArea(GetScreenCenteredRect(300, 50)); GUILayout.Box(content, style_centeredBox, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true)); GUILayout.EndArea(); } private void DrawPage() { if(GUI.enabled != pageGUIState) GUI.enabled = pageGUIState; Rect screenRect = new Rect((Screen.width - (Screen.width * 0.9f)) * 0.5f, (Screen.height - (Screen.height * 0.9f)) * 0.5f, Screen.width * 0.9f, Screen.height * 0.9f); GUILayout.BeginArea(screenRect); // Player Selector DrawPlayerSelector(); // Joystick Selector DrawJoystickSelector(); // Mouse DrawMouseAssignment(); // Controllers DrawControllerSelector(); // Controller Calibration DrawCalibrateButton(); // Categories DrawMapCategories(); // Create scroll view actionScrollPos = GUILayout.BeginScrollView(actionScrollPos); // Actions DrawCategoryActions(); GUILayout.EndScrollView(); GUILayout.EndArea(); } private void DrawPlayerSelector() { if(ReInput.players.allPlayerCount == 0) { GUILayout.Label("There are no players."); return; } GUILayout.Space(15); GUILayout.Label("Players:"); GUILayout.BeginHorizontal(); foreach(Player player in ReInput.players.GetPlayers(true)) { if(selectedPlayer == null) selectedPlayer = player; // if no player selected, select first bool prevValue = player == selectedPlayer ? true : false; bool value = GUILayout.Toggle(prevValue, player.descriptiveName != string.Empty ? player.descriptiveName : player.name, "Button", GUILayout.ExpandWidth(false)); if(value != prevValue) { // value changed if(value) { // selected selectedPlayer = player; selectedController.Clear(); // reset the device selection selectedMapCategoryId = -1; // clear category selection } // do not allow deselection } } GUILayout.EndHorizontal(); } private void DrawMouseAssignment() { bool origGuiEnabled = GUI.enabled; // save GUI state if(selectedPlayer == null) GUI.enabled = false; GUILayout.Space(15); GUILayout.Label("Assign Mouse:"); GUILayout.BeginHorizontal(); bool prevValue = selectedPlayer != null && selectedPlayer.controllers.hasMouse ? true : false; bool value = GUILayout.Toggle(prevValue, "Assign Mouse", "Button", GUILayout.ExpandWidth(false)); if(value != prevValue) { // user clicked if(value) { selectedPlayer.controllers.hasMouse = true; foreach(Player player in ReInput.players.Players) { // de-assign mouse from all players except System if(player == selectedPlayer) continue; // skip self player.controllers.hasMouse = false; } } else { selectedPlayer.controllers.hasMouse = false; } } GUILayout.EndHorizontal(); if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state } private void DrawJoystickSelector() { bool origGuiEnabled = GUI.enabled; // save GUI state if(selectedPlayer == null) GUI.enabled = false; GUILayout.Space(15); GUILayout.Label("Assign Joysticks:"); GUILayout.BeginHorizontal(); bool prevValue = selectedPlayer == null || selectedPlayer.controllers.joystickCount == 0 ? true : false; bool value = GUILayout.Toggle(prevValue, "None", "Button", GUILayout.ExpandWidth(false)); if(value != prevValue) { // user clicked selectedPlayer.controllers.ClearControllersOfType(ControllerType.Joystick); ControllerSelectionChanged(); // do not allow deselection } if(selectedPlayer != null) { foreach(Joystick joystick in ReInput.controllers.Joysticks) { prevValue = selectedPlayer.controllers.ContainsController(joystick); value = GUILayout.Toggle(prevValue, joystick.name, "Button", GUILayout.ExpandWidth(false)); if(value != prevValue) { // user clicked EnqueueAction(new JoystickAssignmentChange(selectedPlayer.id, joystick.id, value)); } } } GUILayout.EndHorizontal(); if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state } private void DrawControllerSelector() { if(selectedPlayer == null) return; bool origGuiEnabled = GUI.enabled; // save GUI state GUILayout.Space(15); GUILayout.Label("Controller to Map:"); GUILayout.BeginHorizontal(); bool value, prevValue; if(!selectedController.hasSelection) { selectedController.Set(0, ControllerType.Keyboard); // select keyboard if nothing selected ControllerSelectionChanged(); } // Keyboard prevValue = selectedController.type == ControllerType.Keyboard; value = GUILayout.Toggle(prevValue, ReInput.controllers.Keyboard.name, "Button", GUILayout.ExpandWidth(false)); if(value != prevValue) { selectedController.Set(0, ControllerType.Keyboard); // set current selected device to this ControllerSelectionChanged(); } // Mouse if(!selectedPlayer.controllers.hasMouse) GUI.enabled = false; // disable mouse if player doesn't have mouse assigned prevValue = selectedController.type == ControllerType.Mouse; value = GUILayout.Toggle(prevValue, ReInput.controllers.Mouse.name, "Button", GUILayout.ExpandWidth(false)); if(value != prevValue) { selectedController.Set(0, ControllerType.Mouse); // set current selected device to this ControllerSelectionChanged(); } if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // re-enable gui // Joystick List foreach(Joystick j in selectedPlayer.controllers.Joysticks) { prevValue = selectedController.type == ControllerType.Joystick && selectedController.id == j.id; value = GUILayout.Toggle(prevValue, j.name, "Button", GUILayout.ExpandWidth(false)); if(value != prevValue) { selectedController.Set(j.id, ControllerType.Joystick); // set current selected device to this ControllerSelectionChanged(); } } GUILayout.EndHorizontal(); if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state } private void DrawCalibrateButton() { if(selectedPlayer == null) return; bool origGuiEnabled = GUI.enabled; // save GUI state GUILayout.Space(10); Controller controller = selectedController.hasSelection ? selectedPlayer.controllers.GetController(selectedController.type, selectedController.id) : null; if(controller == null || selectedController.type != ControllerType.Joystick) { GUI.enabled = false; GUILayout.Button("Select a controller to calibrate", GUILayout.ExpandWidth(false)); if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; } else { // Calibrate joystick if(GUILayout.Button("Calibrate " + controller.name, GUILayout.ExpandWidth(false))) { Joystick joystick = controller as Joystick; if(joystick != null) { CalibrationMap calibrationMap = joystick.calibrationMap; if(calibrationMap != null) { EnqueueAction(new Calibration(selectedPlayer, joystick, calibrationMap)); } } } } if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state } private void DrawMapCategories() { if(selectedPlayer == null) return; if(!selectedController.hasSelection) return; bool origGuiEnabled = GUI.enabled; // save GUI state GUILayout.Space(15); GUILayout.Label("Categories:"); GUILayout.BeginHorizontal(); foreach(InputMapCategory category in ReInput.mapping.UserAssignableMapCategories) { if(!selectedPlayer.controllers.maps.ContainsMapInCategory(selectedController.type, category.id)) { // if player has no maps in this category for controller don't allow them to select it GUI.enabled = false; } else { // Select first available category if none selected if(selectedMapCategoryId < 0) { selectedMapCategoryId = category.id; // if no category selected, select first selectedMap = selectedPlayer.controllers.maps.GetFirstMapInCategory(selectedController.type, selectedController.id, category.id); } } bool prevValue = category.id == selectedMapCategoryId ? true : false; bool value = GUILayout.Toggle(prevValue, category.descriptiveName != string.Empty ? category.descriptiveName : category.name, "Button", GUILayout.ExpandWidth(false)); if(value != prevValue) { // category changed selectedMapCategoryId = category.id; selectedMap = selectedPlayer.controllers.maps.GetFirstMapInCategory(selectedController.type, selectedController.id, category.id); } if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; } GUILayout.EndHorizontal(); if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state } private void DrawCategoryActions() { if(selectedPlayer == null) return; if(selectedMapCategoryId < 0) return; bool origGuiEnabled = GUI.enabled; // save GUI state if(selectedMap == null) return; // controller map does not exist for this category in this controller GUILayout.Space(15); GUILayout.Label("Actions:"); InputMapCategory mapCategory = ReInput.mapping.GetMapCategory(selectedMapCategoryId); // get the selected map category if(mapCategory == null) return; InputCategory actionCategory = ReInput.mapping.GetActionCategory(mapCategory.name); // get the action category with the same name if(actionCategory == null) return; // no action category exists with the same name float labelWidth = 150.0f; // Draw the list of actions for the selected action category foreach(InputAction action in ReInput.mapping.ActionsInCategory(actionCategory.id)) { string name = action.descriptiveName != string.Empty ? action.descriptiveName : action.name; if(action.type == InputActionType.Button) { GUILayout.BeginHorizontal(); GUILayout.Label(name, GUILayout.Width(labelWidth)); DrawAddActionMapButton(selectedPlayer.id, action, AxisRange.Positive, selectedController, selectedMap); // Add assignment button // Write out assigned elements foreach(ActionElementMap elementMap in selectedMap.AllMaps) { if(elementMap.actionId != action.id) continue; DrawActionAssignmentButton(selectedPlayer.id, action, AxisRange.Positive, selectedController, selectedMap, elementMap); } GUILayout.EndHorizontal(); } else if(action.type == InputActionType.Axis) { // Axis // Draw main axis label and actions assigned to the full axis if(selectedController.type != ControllerType.Keyboard) { // don't draw this for keyboards since keys can only be assigned to the +/- axes anyway GUILayout.BeginHorizontal(); GUILayout.Label(name, GUILayout.Width(labelWidth)); DrawAddActionMapButton(selectedPlayer.id, action, AxisRange.Full, selectedController, selectedMap); // Add assignment button // Write out assigned elements foreach(ActionElementMap elementMap in selectedMap.AllMaps) { if(elementMap.actionId != action.id) continue; if(elementMap.elementType == ControllerElementType.Button) continue; // skip buttons, will handle below if(elementMap.axisType == AxisType.Split) continue; // skip split axes, will handle below DrawActionAssignmentButton(selectedPlayer.id, action, AxisRange.Full, selectedController, selectedMap, elementMap); DrawInvertButton(selectedPlayer.id, action, Pole.Positive, selectedController, selectedMap, elementMap); } GUILayout.EndHorizontal(); } // Positive action string positiveName = action.positiveDescriptiveName != string.Empty ? action.positiveDescriptiveName : action.descriptiveName + " +"; GUILayout.BeginHorizontal(); GUILayout.Label(positiveName, GUILayout.Width(labelWidth)); DrawAddActionMapButton(selectedPlayer.id, action, AxisRange.Positive, selectedController, selectedMap); // Add assignment button // Write out assigned elements foreach(ActionElementMap elementMap in selectedMap.AllMaps) { if(elementMap.actionId != action.id) continue; if(elementMap.axisContribution != Pole.Positive) continue; // axis contribution is incorrect, skip if(elementMap.axisType == AxisType.Normal) continue; // normal axes handled above DrawActionAssignmentButton(selectedPlayer.id, action, AxisRange.Positive, selectedController, selectedMap, elementMap); } GUILayout.EndHorizontal(); // Negative action string negativeName = action.negativeDescriptiveName != string.Empty ? action.negativeDescriptiveName : action.descriptiveName + " -"; GUILayout.BeginHorizontal(); GUILayout.Label(negativeName, GUILayout.Width(labelWidth)); DrawAddActionMapButton(selectedPlayer.id, action, AxisRange.Negative, selectedController, selectedMap); // Add assignment button // Write out assigned elements foreach(ActionElementMap elementMap in selectedMap.AllMaps) { if(elementMap.actionId != action.id) continue; if(elementMap.axisContribution != Pole.Negative) continue; // axis contribution is incorrect, skip if(elementMap.axisType == AxisType.Normal) continue; // normal axes handled above DrawActionAssignmentButton(selectedPlayer.id, action, AxisRange.Negative, selectedController, selectedMap, elementMap); } GUILayout.EndHorizontal(); } } if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state } #endregion #region Buttons private void DrawActionAssignmentButton(int playerId, InputAction action, AxisRange actionRange, ControllerSelection controller, ControllerMap controllerMap, ActionElementMap elementMap) { if(GUILayout.Button(elementMap.elementIdentifierName, GUILayout.ExpandWidth(false), GUILayout.MinWidth(30.0f))) { InputMapper.Context context = new InputMapper.Context() { actionId = action.id, actionRange = actionRange, controllerMap = controllerMap, actionElementMapToReplace = elementMap }; EnqueueAction(new ElementAssignmentChange(ElementAssignmentChangeType.ReassignOrRemove, context)); startListening = true; } GUILayout.Space(4); } private void DrawInvertButton(int playerId, InputAction action, Pole actionAxisContribution, ControllerSelection controller, ControllerMap controllerMap, ActionElementMap elementMap) { bool value = elementMap.invert; bool newValue = GUILayout.Toggle(value, "Invert", GUILayout.ExpandWidth(false)); if(newValue != value) { elementMap.invert = newValue; } GUILayout.Space(10); } private void DrawAddActionMapButton(int playerId, InputAction action, AxisRange actionRange, ControllerSelection controller, ControllerMap controllerMap) { if(GUILayout.Button("Add...", GUILayout.ExpandWidth(false))) { InputMapper.Context context = new InputMapper.Context() { actionId = action.id, actionRange = actionRange, controllerMap = controllerMap }; EnqueueAction(new ElementAssignmentChange(ElementAssignmentChangeType.Add, context)); startListening = true; } GUILayout.Space(10); } #endregion #region Dialog Boxes private void ShowDialog() { dialog.Update(); } #region Draw Window Functions private void DrawModalWindow(string title, string message) { if(!dialog.enabled) return; // prevent this from running after dialog is closed GUILayout.Space(5); // Message GUILayout.Label(message, style_wordWrap); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(); // Buttons dialog.DrawConfirmButton("Okay"); GUILayout.FlexibleSpace(); dialog.DrawCancelButton(); GUILayout.EndHorizontal(); } private void DrawModalWindow_OkayOnly(string title, string message) { if(!dialog.enabled) return; // prevent this from running after dialog is closed GUILayout.Space(5); // Message GUILayout.Label(message, style_wordWrap); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(); // Buttons dialog.DrawConfirmButton("Okay"); GUILayout.EndHorizontal(); } private void DrawElementAssignmentWindow(string title, string message) { if(!dialog.enabled) return; // prevent this from running after dialog is closed GUILayout.Space(5); // Message GUILayout.Label(message, style_wordWrap); GUILayout.FlexibleSpace(); ElementAssignmentChange entry = actionQueue.Peek() as ElementAssignmentChange; // get item from queue if(entry == null) { dialog.Cancel(); return; } float time; // Do not start until dialog is ready if(!dialog.busy) { // Start the listener if(startListening && inputMapper.status == InputMapper.Status.Idle) { inputMapper.Start(entry.context); startListening = false; } // Check for conflicts if(conflictFoundEventData != null) { // a conflict is pending dialog.Confirm(); return; } time = inputMapper.timeRemaining; // Check for timeout if(time == 0f) { dialog.Cancel(); return; } } else { time = inputMapper.options.timeout; } // Show the cancel timer GUILayout.Label("Assignment will be canceled in " + ((int)Mathf.Ceil(time)).ToString() + "...", style_wordWrap); } private void DrawElementAssignmentProtectedConflictWindow(string title, string message) { if(!dialog.enabled) return; // prevent this from running after dialog is closed GUILayout.Space(5); // Message GUILayout.Label(message, style_wordWrap); GUILayout.FlexibleSpace(); ElementAssignmentChange entry = actionQueue.Peek() as ElementAssignmentChange; // get item from queue if(entry == null) { dialog.Cancel(); return; } // Draw Buttons GUILayout.BeginHorizontal(); dialog.DrawConfirmButton(UserResponse.Custom1, "Add"); GUILayout.FlexibleSpace(); dialog.DrawCancelButton(); GUILayout.EndHorizontal(); } private void DrawElementAssignmentNormalConflictWindow(string title, string message) { if(!dialog.enabled) return; // prevent this from running after dialog is closed GUILayout.Space(5); // Message GUILayout.Label(message, style_wordWrap); GUILayout.FlexibleSpace(); ElementAssignmentChange entry = actionQueue.Peek() as ElementAssignmentChange; // get item from queue if(entry == null) { dialog.Cancel(); return; } // Draw Buttons GUILayout.BeginHorizontal(); dialog.DrawConfirmButton(UserResponse.Confirm, "Replace"); GUILayout.FlexibleSpace(); dialog.DrawConfirmButton(UserResponse.Custom1, "Add"); GUILayout.FlexibleSpace(); dialog.DrawCancelButton(); GUILayout.EndHorizontal(); } private void DrawReassignOrRemoveElementAssignmentWindow(string title, string message) { if(!dialog.enabled) return; // prevent this from running after dialog is closed GUILayout.Space(5); // Message GUILayout.Label(message, style_wordWrap); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(); // Buttons dialog.DrawConfirmButton("Reassign"); GUILayout.FlexibleSpace(); dialog.DrawCancelButton("Remove"); GUILayout.EndHorizontal(); } private void DrawFallbackJoystickIdentificationWindow(string title, string message) { if(!dialog.enabled) return; // prevent this from running after dialog is closed FallbackJoystickIdentification entry = actionQueue.Peek() as FallbackJoystickIdentification; if(entry == null) { dialog.Cancel(); return; } GUILayout.Space(5); // Message GUILayout.Label(message, style_wordWrap); GUILayout.Label("Press any button or axis on \"" + entry.joystickName + "\" now.", style_wordWrap); GUILayout.FlexibleSpace(); if(GUILayout.Button("Skip")) { dialog.Cancel(); return; } // Do not allow assignment until dialog is ready if(dialog.busy) return; // Remap the joystick input source bool success = ReInput.controllers.SetUnityJoystickIdFromAnyButtonOrAxisPress(entry.joystickId, 0.8f, false); if(!success) return; // Finish dialog.Confirm(); } private void DrawCalibrationWindow(string title, string message) { if(!dialog.enabled) return; // prevent this from running after dialog is closed Calibration entry = actionQueue.Peek() as Calibration; if(entry == null) { dialog.Cancel(); return; } GUILayout.Space(5); // Message GUILayout.Label(message, style_wordWrap); GUILayout.Space(20); GUILayout.BeginHorizontal(); bool origGUIEnabled = GUI.enabled; // Controller element selection GUILayout.BeginVertical(GUILayout.Width(200)); // Create a scroll view for the axis list in case using the default controller map which has a lot of axes calibrateScrollPos = GUILayout.BeginScrollView(calibrateScrollPos); if(entry.recording) GUI.enabled = false; // don't allow switching while recording min/max IList axisIdentifiers = entry.joystick.AxisElementIdentifiers; for(int i = 0; i < axisIdentifiers.Count; i++) { ControllerElementIdentifier identifier = axisIdentifiers[i]; bool isSelected = entry.selectedElementIdentifierId == identifier.id; bool newValue = GUILayout.Toggle(isSelected, identifier.name, "Button", GUILayout.ExpandWidth(false)); if(isSelected != newValue) { entry.selectedElementIdentifierId = identifier.id; // store the selection index } } if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; // restore gui GUILayout.EndScrollView(); GUILayout.EndVertical(); // Selected object information and controls GUILayout.BeginVertical(GUILayout.Width(200)); if(entry.selectedElementIdentifierId >= 0) { float axisValue = entry.joystick.GetAxisRawById(entry.selectedElementIdentifierId); GUILayout.Label("Raw Value: " + axisValue.ToString()); // Get the axis index from the element identifier id int axisIndex = entry.joystick.GetAxisIndexById(entry.selectedElementIdentifierId); AxisCalibration axis = entry.calibrationMap.GetAxis(axisIndex); // get the axis calibration // Show current axis information GUILayout.Label("Calibrated Value: " + entry.joystick.GetAxisById(entry.selectedElementIdentifierId)); GUILayout.Label("Zero: " + axis.calibratedZero); GUILayout.Label("Min: " + axis.calibratedMin); GUILayout.Label("Max: " + axis.calibratedMax); GUILayout.Label("Dead Zone: " + axis.deadZone); GUILayout.Space(15); // Enabled -- allows user to disable an axis entirely if its giving them problems bool newEnabled = GUILayout.Toggle(axis.enabled, "Enabled", "Button", GUILayout.ExpandWidth(false)); if(axis.enabled != newEnabled) { axis.enabled = newEnabled; } GUILayout.Space(10); // Records Min/Max bool newRecording = GUILayout.Toggle(entry.recording, "Record Min/Max", "Button", GUILayout.ExpandWidth(false)); if(newRecording != entry.recording) { if(newRecording) { // just started recording // Clear previous calibration so we can record min max from this session only axis.calibratedMax = 0.0f; axis.calibratedMin = 0.0f; } entry.recording = newRecording; } if(entry.recording) { axis.calibratedMin = Mathf.Min(axis.calibratedMin, axisValue, axis.calibratedMin); axis.calibratedMax = Mathf.Max(axis.calibratedMax, axisValue, axis.calibratedMax); GUI.enabled = false; } // Set Zero state if(GUILayout.Button("Set Zero", GUILayout.ExpandWidth(false))) { axis.calibratedZero = axisValue; } // Set Dead Zone if(GUILayout.Button("Set Dead Zone", GUILayout.ExpandWidth(false))) { axis.deadZone = axisValue; } // Invert bool newInvert = GUILayout.Toggle(axis.invert, "Invert", "Button", GUILayout.ExpandWidth(false)); if(axis.invert != newInvert) { axis.invert = newInvert; } GUILayout.Space(10); if(GUILayout.Button("Reset", GUILayout.ExpandWidth(false))) { axis.Reset(); } if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; } else { GUILayout.Label("Select an axis to begin."); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); // Close Button GUILayout.FlexibleSpace(); if(entry.recording) GUI.enabled = false; if(GUILayout.Button("Close")) { calibrateScrollPos = new Vector2(); // clear the scroll view position dialog.Confirm(); } if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; } #endregion #region Result Callbacks private void DialogResultCallback(int queueActionId, UserResponse response) { foreach(QueueEntry entry in actionQueue) { // find the right one and cancel or confirm it if(entry.id != queueActionId) continue; if(response != UserResponse.Cancel) entry.Confirm(response); // mark the entry as confirmed and record user response else entry.Cancel(); // mark the entry as canceled break; } } #endregion #region Misc private Rect GetScreenCenteredRect(float width, float height) { return new Rect( (float)(Screen.width * 0.5f - width * 0.5f), (float)(Screen.height * 0.5 - height * 0.5f), width, height ); } #endregion #endregion #region Action Queue private void EnqueueAction(QueueEntry entry) { if(entry == null) return; busy = true; GUI.enabled = false; // disable control on everything until next cycle actionQueue.Enqueue(entry); } private void ProcessQueue() { if(dialog.enabled) return; // dialog is open, do not process queue if(busy || actionQueue.Count == 0) return; while(actionQueue.Count > 0) { QueueEntry queueEntry = actionQueue.Peek(); // get next item from queue bool goNext = false; // Process different types of actions switch(queueEntry.queueActionType) { case QueueActionType.JoystickAssignment: goNext = ProcessJoystickAssignmentChange((JoystickAssignmentChange)queueEntry); break; case QueueActionType.ElementAssignment: goNext = ProcessElementAssignmentChange((ElementAssignmentChange)queueEntry); break; case QueueActionType.FallbackJoystickIdentification: goNext = ProcessFallbackJoystickIdentification((FallbackJoystickIdentification)queueEntry); break; case QueueActionType.Calibrate: goNext = ProcessCalibration((Calibration)queueEntry); break; } // Quit processing the queue if we opened a modal if(!goNext) break; // Remove item from queue since we're done with it actionQueue.Dequeue(); } } private bool ProcessJoystickAssignmentChange(JoystickAssignmentChange entry) { // Handle user cancelation if(entry.state == QueueEntry.State.Canceled) { // action was canceled return true; } Player player = ReInput.players.GetPlayer(entry.playerId); if(player == null) return true; if(!entry.assign) { // deassign joystick player.controllers.RemoveController(ControllerType.Joystick, entry.joystickId); ControllerSelectionChanged(); return true; } // Assign joystick if(player.controllers.ContainsController(ControllerType.Joystick, entry.joystickId)) return true; // same player, nothing to change bool alreadyAssigned = ReInput.controllers.IsJoystickAssigned(entry.joystickId); if(!alreadyAssigned || entry.state == QueueEntry.State.Confirmed) { // not assigned or user confirmed the action already, do it player.controllers.AddController(ControllerType.Joystick, entry.joystickId, true); ControllerSelectionChanged(); return true; } // Create dialog and start waiting for user confirmation dialog.StartModal(entry.id, DialogHelper.DialogType.JoystickConflict, new WindowProperties { title = "Joystick Reassignment", message = "This joystick is already assigned to another player. Do you want to reassign this joystick to " + player.descriptiveName + "?", rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight), windowDrawDelegate = DrawModalWindow }, DialogResultCallback); return false; // don't process anything more in this queue } private bool ProcessElementAssignmentChange(ElementAssignmentChange entry) { switch(entry.changeType) { case ElementAssignmentChangeType.ReassignOrRemove: return ProcessRemoveOrReassignElementAssignment(entry); case ElementAssignmentChangeType.Remove: return ProcessRemoveElementAssignment(entry); case ElementAssignmentChangeType.Add: case ElementAssignmentChangeType.Replace: return ProcessAddOrReplaceElementAssignment(entry); case ElementAssignmentChangeType.ConflictCheck: return ProcessElementAssignmentConflictCheck(entry); default: throw new System.NotImplementedException(); } } private bool ProcessRemoveOrReassignElementAssignment(ElementAssignmentChange entry) { if(entry.context.controllerMap == null) return true; if(entry.state == QueueEntry.State.Canceled) { // delete entry // Enqueue a new action to delete the entry ElementAssignmentChange newEntry = new ElementAssignmentChange(entry); // copy the entry newEntry.changeType = ElementAssignmentChangeType.Remove; // change the type to Remove actionQueue.Enqueue(newEntry); // enqueue the new entry return true; } // Check for user confirmation if(entry.state == QueueEntry.State.Confirmed) { // reassign entry // Enqueue a new action to reassign the entry ElementAssignmentChange newEntry = new ElementAssignmentChange(entry); // copy the entry newEntry.changeType = ElementAssignmentChangeType.Replace; // change the type to Replace actionQueue.Enqueue(newEntry); // enqueue the new entry return true; } // Create dialog and start waiting for user assignment dialog.StartModal(entry.id, DialogHelper.DialogType.AssignElement, new WindowProperties { title = "Reassign or Remove", message = "Do you want to reassign or remove this assignment?", rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight), windowDrawDelegate = DrawReassignOrRemoveElementAssignmentWindow }, DialogResultCallback); return false; } private bool ProcessRemoveElementAssignment(ElementAssignmentChange entry) { if(entry.context.controllerMap == null) return true; if(entry.state == QueueEntry.State.Canceled) return true; // user canceled // Delete element if(entry.state == QueueEntry.State.Confirmed) { // user confirmed, delete it entry.context.controllerMap.DeleteElementMap(entry.context.actionElementMapToReplace.id); return true; } // Create dialog and start waiting for user confirmation dialog.StartModal(entry.id, DialogHelper.DialogType.DeleteAssignmentConfirmation, new WindowProperties { title = "Remove Assignment", message = "Are you sure you want to remove this assignment?", rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight), windowDrawDelegate = DrawModalWindow }, DialogResultCallback); return false; // don't process anything more in this queue } private bool ProcessAddOrReplaceElementAssignment(ElementAssignmentChange entry) { if(entry.state == QueueEntry.State.Canceled) { inputMapper.Stop(); return true; // user canceled } // Check for user confirmation if(entry.state == QueueEntry.State.Confirmed) { // the action assignment has been confirmed if(Event.current.type != EventType.Layout) return false; // only make changes in layout to avoid GUI errors when new controls appear // Handle conflicts if(conflictFoundEventData != null) { // we had conflicts // Enqueue a conflict check ElementAssignmentChange newEntry = new ElementAssignmentChange(entry); // clone the entry newEntry.changeType = ElementAssignmentChangeType.ConflictCheck; // set the new type to check for conflicts actionQueue.Enqueue(newEntry); // enqueue the new entry } return true; // finished } // Customize the message for different controller types and different platforms string message; if(entry.context.controllerMap.controllerType == ControllerType.Keyboard) { #if UNITY_5_4_OR_NEWER bool isOSX = Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer; #else bool isOSX = Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXWebPlayer; #endif if(isOSX) { message = "Press any key to assign it to this action. You may also use the modifier keys Command, Control, Alt, and Shift. If you wish to assign a modifier key itself to this action, press and hold the key for 1 second."; } else { message = "Press any key to assign it to this action. You may also use the modifier keys Control, Alt, and Shift. If you wish to assign a modifier key itself to this action, press and hold the key for 1 second."; } // Editor modifier key disclaimer if(Application.isEditor) { message += "\n\nNOTE: Some modifier key combinations will not work in the Unity Editor, but they will work in a game build."; } } else if(entry.context.controllerMap.controllerType == ControllerType.Mouse) { message = "Press any mouse button or axis to assign it to this action.\n\nTo assign mouse movement axes, move the mouse quickly in the direction you want mapped to the action. Slow movements will be ignored."; } else { message = "Press any button or axis to assign it to this action."; } // Create dialog and start waiting for user assignment dialog.StartModal(entry.id, DialogHelper.DialogType.AssignElement, new WindowProperties { title = "Assign", message = message, rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight), windowDrawDelegate = DrawElementAssignmentWindow }, DialogResultCallback); return false; } private bool ProcessElementAssignmentConflictCheck(ElementAssignmentChange entry) { if(entry.context.controllerMap == null) return true; if(entry.state == QueueEntry.State.Canceled) { inputMapper.Stop(); return true; // user canceled } if(conflictFoundEventData == null) return true; // error // Check for user confirmation if(entry.state == QueueEntry.State.Confirmed) { if(entry.response == UserResponse.Confirm) { // remove and add conflictFoundEventData.responseCallback(InputMapper.ConflictResponse.Replace); } else if(entry.response == UserResponse.Custom1) { // add without removing conflictFoundEventData.responseCallback(InputMapper.ConflictResponse.Add); } else throw new System.NotImplementedException(); return true; // finished } // Open a different dialog depending on if a protected conflict was found if(conflictFoundEventData.isProtected) { string message = conflictFoundEventData.assignment.elementDisplayName + " is already in use and is protected from reassignment. You cannot remove the protected assignment, but you can still assign the action to this element. If you do so, the element will trigger multiple actions when activated."; // Create dialog and start waiting for user assignment dialog.StartModal(entry.id, DialogHelper.DialogType.AssignElement, new WindowProperties { title = "Assignment Conflict", message = message, rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight), windowDrawDelegate = DrawElementAssignmentProtectedConflictWindow }, DialogResultCallback); } else { string message = conflictFoundEventData.assignment.elementDisplayName + " is already in use. You may replace the other conflicting assignments, add this assignment anyway which will leave multiple actions assigned to this element, or cancel this assignment."; // Create dialog and start waiting for user assignment dialog.StartModal(entry.id, DialogHelper.DialogType.AssignElement, new WindowProperties { title = "Assignment Conflict", message = message, rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight), windowDrawDelegate = DrawElementAssignmentNormalConflictWindow }, DialogResultCallback); } return false; } private bool ProcessFallbackJoystickIdentification(FallbackJoystickIdentification entry) { // Handle user cancelation if(entry.state == QueueEntry.State.Canceled) { // action was canceled return true; } // Identify joystick if(entry.state == QueueEntry.State.Confirmed) { // nothing to do, done return true; } // Create dialog and start waiting for user confirmation dialog.StartModal(entry.id, DialogHelper.DialogType.JoystickConflict, new WindowProperties { title = "Joystick Identification Required", message = "A joystick has been attached or removed. You will need to identify each joystick by pressing a button on the controller listed below:", rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight), windowDrawDelegate = DrawFallbackJoystickIdentificationWindow }, DialogResultCallback, 1.0f); // add a longer delay after the dialog opens to prevent one joystick press from being used for subsequent joysticks if held for a short time return false; // don't process anything more in this queue } private bool ProcessCalibration(Calibration entry) { // Handle user cancelation if(entry.state == QueueEntry.State.Canceled) { // action was canceled return true; } if(entry.state == QueueEntry.State.Confirmed) { return true; } // Create dialog and start waiting for user confirmation dialog.StartModal(entry.id, DialogHelper.DialogType.JoystickConflict, new WindowProperties { title = "Calibrate Controller", message = "Select an axis to calibrate on the " + entry.joystick.name + ".", rect = GetScreenCenteredRect(450, 480), windowDrawDelegate = DrawCalibrationWindow }, DialogResultCallback); return false; // don't process anything more in this queue } #endregion #region Selection Chaging private void PlayerSelectionChanged() { ClearControllerSelection(); } private void ControllerSelectionChanged() { ClearMapSelection(); } private void ClearControllerSelection() { selectedController.Clear(); // reset the device selection because joystick list will have changed ClearMapSelection(); } #endregion #region Clear private void ClearMapSelection() { selectedMapCategoryId = -1; // clear map cat selection selectedMap = null; } private void ResetAll() { ClearWorkingVars(); initialized = false; showMenu = false; } private void ClearWorkingVars() { selectedPlayer = null; ClearMapSelection(); selectedController.Clear(); actionScrollPos = new Vector2(); dialog.FullReset(); actionQueue.Clear(); busy = false; startListening = false; conflictFoundEventData = null; inputMapper.Stop(); } #endregion #region GUI State private void SetGUIStateStart() { guiState = true; if(busy) guiState = false; pageGUIState = guiState && !busy && !dialog.enabled && !dialog.busy; // enable page gui only if not busy and not in dialog mode if(GUI.enabled != guiState) GUI.enabled = guiState; } private void SetGUIStateEnd() { // always enable GUI again before exiting guiState = true; if(!GUI.enabled) GUI.enabled = guiState; } #endregion #region Joystick Connection Callbacks private void JoystickConnected(ControllerStatusChangedEventArgs args) { // Reload maps if a joystick is connected if(ReInput.controllers.IsControllerAssigned(args.controllerType, args.controllerId)) { // Load the maps for the player(s) that are assigned this joystick foreach(Player player in ReInput.players.AllPlayers) { if(player.controllers.ContainsController(args.controllerType, args.controllerId)) { ReInput.userDataStore.LoadControllerData(player.id, args.controllerType, args.controllerId); } } } else { // Just load the general joystick save data ReInput.userDataStore.LoadControllerData(args.controllerType, args.controllerId); } // Always force reidentification of all joysticks when a joystick is added or removed when using Unity input on a platform that requires manual identification if(ReInput.unityJoystickIdentificationRequired) IdentifyAllJoysticks(); } private void JoystickPreDisconnect(ControllerStatusChangedEventArgs args) { // Check if the current editing controller was just disconnected and deselect it if(selectedController.hasSelection && args.controllerType == selectedController.type && args.controllerId == selectedController.id) { ClearControllerSelection(); // joystick was disconnected } // Save the user maps before the joystick is disconnected if in the menu since user may have changed something if(showMenu) { if(ReInput.controllers.IsControllerAssigned(args.controllerType, args.controllerId)) { foreach(Player player in ReInput.players.AllPlayers) { if(!player.controllers.ContainsController(args.controllerType, args.controllerId)) continue; ReInput.userDataStore.SaveControllerData(player.id, args.controllerType, args.controllerId); } } else { ReInput.userDataStore.SaveControllerData(args.controllerType, args.controllerId); } } } private void JoystickDisconnected(ControllerStatusChangedEventArgs args) { // Close dialogs and clear queue if a joystick is disconnected if(showMenu) ClearWorkingVars(); // Always force reidentification of all joysticks when a joystick is added or removed when using Unity input if(ReInput.unityJoystickIdentificationRequired) IdentifyAllJoysticks(); } #endregion #region Mapping Listener Event Handlers private void OnConflictFound(InputMapper.ConflictFoundEventData data) { this.conflictFoundEventData = data; } private void OnStopped(InputMapper.StoppedEventData data) { this.conflictFoundEventData = null; } #endregion #region Fallback Methods public void IdentifyAllJoysticks() { // Check if there are any joysticks if(ReInput.controllers.joystickCount == 0) return; // no joysticks, nothing to do // Clear all vars first which will clear dialogs and queues ClearWorkingVars(); // Open the menu if its closed Open(); // Enqueue the joysticks up for identification foreach(Joystick joystick in ReInput.controllers.Joysticks) { actionQueue.Enqueue(new FallbackJoystickIdentification(joystick.id, joystick.name)); // enqueue each joystick for identification } } #endregion #region Editor Methods protected void CheckRecompile() { #if UNITY_EDITOR // Destroy system if recompiling if(UnityEditor.EditorApplication.isCompiling) { // editor is recompiling if(!isCompiling) { // this is the first cycle of recompile ResetAll(); isCompiling = true; } GUILayout.Window(0, GetScreenCenteredRect(300, 100), RecompileWindow, new GUIContent("Scripts are Compiling")); return; } // Check for end of compile if(isCompiling) { // compiling is done isCompiling = false; Initialize(); } #endif } private void RecompileWindow(int windowId) { #if UNITY_EDITOR GUILayout.FlexibleSpace(); GUILayout.Label("Please wait while script compilation finishes..."); GUILayout.FlexibleSpace(); #endif } #endregion #region Classes private class ControllerSelection { private int _id; private int _idPrev; private ControllerType _type; private ControllerType _typePrev; public ControllerSelection() { Clear(); } public int id { get { return _id; } set { _idPrev = _id; _id = value; } } public ControllerType type { get { return _type; } set { _typePrev = _type; _type = value; } } public int idPrev { get { return _idPrev; } } public ControllerType typePrev { get { return _typePrev; } } public bool hasSelection { get { return _id >= 0; } } public void Set(int id, ControllerType type) { this.id = id; this.type = type; } public void Clear() { _id = -1; _idPrev = -1; _type = ControllerType.Joystick; _typePrev = ControllerType.Joystick; } } private class DialogHelper { private const float openBusyDelay = 0.25f; // a small delay after opening the window that prevents assignment input for a short time after window opens private const float closeBusyDelay = 0.1f; // a small after closing the window that the GUI will still be busy to prevent button clickthrough private DialogType _type; private bool _enabled; private float _busyTime; private bool _busyTimerRunning; private float busyTimer { get { if(!_busyTimerRunning) return 0.0f; return _busyTime - Time.realtimeSinceStartup; } } public bool enabled { get { return _enabled; } set { if(value) { if(_type == DialogType.None) return; // cannot enable, no type set StateChanged(openBusyDelay); } else { _enabled = value; _type = DialogType.None; StateChanged(closeBusyDelay); } } } public DialogType type { get { if(!_enabled) return DialogType.None; return _type; } set { if(value == DialogType.None) { _enabled = false; StateChanged(closeBusyDelay); } else { _enabled = true; StateChanged(openBusyDelay); } _type = value; } } public bool busy { get { return _busyTimerRunning; } } private Action drawWindowDelegate; private GUI.WindowFunction drawWindowFunction; private WindowProperties windowProperties; private int currentActionId; private Action resultCallback; public DialogHelper() { drawWindowDelegate = DrawWindow; drawWindowFunction = new GUI.WindowFunction(drawWindowDelegate); } public void StartModal(int queueActionId, DialogType type, WindowProperties windowProperties, Action resultCallback) { StartModal(queueActionId, type, windowProperties, resultCallback, -1.0f); } public void StartModal(int queueActionId, DialogType type, WindowProperties windowProperties, Action resultCallback, float openBusyDelay) { currentActionId = queueActionId; this.windowProperties = windowProperties; this.type = type; this.resultCallback = resultCallback; if(openBusyDelay >= 0.0f) StateChanged(openBusyDelay); // override with user defined open busy delay } public void Update() { Draw(); UpdateTimers(); } public void Draw() { if(!_enabled) return; bool origGuiEnabled = GUI.enabled; GUI.enabled = true; GUILayout.Window(windowProperties.windowId, windowProperties.rect, drawWindowFunction, windowProperties.title); GUI.FocusWindow(windowProperties.windowId); if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; } public void DrawConfirmButton() { DrawConfirmButton("Confirm"); } public void DrawConfirmButton(string title) { bool origGUIEnabled = GUI.enabled; // store original gui state if(busy) GUI.enabled = false; // disable GUI if dialog is busy to prevent click though if(GUILayout.Button(title)) { Confirm(UserResponse.Confirm); } if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; // restore GUI } public void DrawConfirmButton(UserResponse response) { DrawConfirmButton(response, "Confirm"); } public void DrawConfirmButton(UserResponse response, string title) { bool origGUIEnabled = GUI.enabled; // store original gui state if(busy) GUI.enabled = false; // disable GUI if dialog is busy to prevent click though if(GUILayout.Button(title)) { Confirm(response); } if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; // restore GUI } public void DrawCancelButton() { DrawCancelButton("Cancel"); } public void DrawCancelButton(string title) { bool origGUIEnabled = GUI.enabled; // store original gui state if(busy) GUI.enabled = false; // disable GUI if dialog is busy to prevent click though if(GUILayout.Button(title)) { Cancel(); } if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; // restore GUI } public void Confirm() { Confirm(UserResponse.Confirm); } public void Confirm(UserResponse response) { resultCallback(currentActionId, response); Close(); } public void Cancel() { resultCallback(currentActionId, UserResponse.Cancel); Close(); } private void DrawWindow(int windowId) { windowProperties.windowDrawDelegate(windowProperties.title, windowProperties.message); } private void UpdateTimers() { if(_busyTimerRunning) { if(busyTimer <= 0.0f) _busyTimerRunning = false; } } private void StartBusyTimer(float time) { _busyTime = time + Time.realtimeSinceStartup; _busyTimerRunning = true; } private void Close() { Reset(); StateChanged(closeBusyDelay); } private void StateChanged(float delay) { StartBusyTimer(delay); } private void Reset() { _enabled = false; _type = DialogType.None; currentActionId = -1; resultCallback = null; } private void ResetTimers() { _busyTimerRunning = false; } public void FullReset() { Reset(); ResetTimers(); } // Enums public enum DialogType { None = 0, JoystickConflict = 1, ElementConflict = 2, KeyConflict = 3, DeleteAssignmentConfirmation = 10, AssignElement = 11 } } private abstract class QueueEntry { public int id { get; protected set; } public QueueActionType queueActionType { get; protected set; } public State state { get; protected set; } public UserResponse response { get; protected set; } private static int uidCounter; protected static int nextId { get { int id = uidCounter; uidCounter += 1; return id; } } public QueueEntry(QueueActionType queueActionType) { id = nextId; this.queueActionType = queueActionType; } public void Confirm(UserResponse response) { state = State.Confirmed; this.response = response; } public void Cancel() { state = State.Canceled; } public enum State { Waiting = 0, Confirmed = 1, Canceled = 2 } } private class JoystickAssignmentChange : QueueEntry { public int playerId { get; private set; } public int joystickId { get; private set; } public bool assign { get; private set; } public JoystickAssignmentChange( int newPlayerId, int joystickId, bool assign ) : base(QueueActionType.JoystickAssignment) { this.playerId = newPlayerId; this.joystickId = joystickId; this.assign = assign; } } private class ElementAssignmentChange : QueueEntry { public ElementAssignmentChangeType changeType { get; set; } public InputMapper.Context context { get; private set; } public ElementAssignmentChange(ElementAssignmentChangeType changeType, InputMapper.Context context) : base(QueueActionType.ElementAssignment) { this.changeType = changeType; this.context = context; } public ElementAssignmentChange(ElementAssignmentChange other) : this(other.changeType, other.context.Clone()) { } } private class FallbackJoystickIdentification : QueueEntry { public int joystickId { get; private set; } public string joystickName { get; private set; } public FallbackJoystickIdentification( int joystickId, string joystickName ) : base(QueueActionType.FallbackJoystickIdentification) { this.joystickId = joystickId; this.joystickName = joystickName; } } private class Calibration : QueueEntry { public Player player { get; private set; } public ControllerType controllerType { get; private set; } public Joystick joystick { get; private set; } public CalibrationMap calibrationMap { get; private set; } public int selectedElementIdentifierId; public bool recording; public Calibration( Player player, Joystick joystick, CalibrationMap calibrationMap ) : base(QueueActionType.Calibrate) { this.player = player; this.joystick = joystick; this.calibrationMap = calibrationMap; selectedElementIdentifierId = -1; } } private struct WindowProperties { public int windowId; public Rect rect; public Action windowDrawDelegate; public string title; public string message; } #endregion #region Enums private enum QueueActionType { None = 0, JoystickAssignment = 1, ElementAssignment = 2, FallbackJoystickIdentification = 3, Calibrate = 4 } private enum ElementAssignmentChangeType { Add = 0, Replace = 1, Remove = 2, ReassignOrRemove = 3, ConflictCheck = 4 } public enum UserResponse { Confirm = 0, Cancel = 1, Custom1 = 2, Custom2 = 3 } #endregion } }