/// <summary> /// Apply the given binding override to all bindings in the map that are matched by the override. /// </summary> /// <param name="actionMap"></param> /// <param name="bindingOverride"></param> /// <returns>The number of bindings overridden in the given map.</returns> /// <exception cref="ArgumentNullException"><paramref name="actionMap"/> is <c>null</c>.</exception> /// <exception cref="InvalidOperationException"><paramref name="actionMap"/> is currently enabled.</exception> /// <remarks> /// </remarks> public static int ApplyBindingOverride(this InputActionMap actionMap, InputBinding bindingOverride) { if (actionMap == null) { throw new ArgumentNullException("actionMap"); } actionMap.ThrowIfModifyingBindingsIsNotAllowed(); var bindings = actionMap.m_Bindings; if (bindings == null) { return(0); } // Go through all bindings in the map and match them to the override. var bindingCount = bindings.Length; var matchCount = 0; for (var i = 0; i < bindingCount; ++i) { if (!bindingOverride.Matches(ref bindings[i])) { continue; } // Set overrides on binding. bindings[i].overridePath = bindingOverride.overridePath; bindings[i].overrideInteractions = bindingOverride.overrideInteractions; ++matchCount; } if (matchCount > 0) { actionMap.ClearPerActionCachedBindingData(); } return(matchCount); }
public static MapJson FromMap(InputActionMap map) { ActionJson[] jsonActions = null; BindingJson[] jsonBindings = null; var actions = map.m_Actions; if (actions != null) { var actionCount = actions.Length; jsonActions = new ActionJson[actionCount]; for (var i = 0; i < actionCount; ++i) { jsonActions[i] = ActionJson.FromAction(actions[i]); } } var bindings = map.m_Bindings; if (bindings != null) { var bindingCount = bindings.Length; jsonBindings = new BindingJson[bindingCount]; for (var i = 0; i < bindingCount; ++i) { jsonBindings[i] = BindingJson.FromBinding(bindings[i]); } } return(new MapJson { name = map.name, id = map.id.ToString(), actions = jsonActions, bindings = jsonBindings, }); }
public static InputAction AddAction(this InputActionMap map, string name, string binding = null, string interactions = null, string groups = null, string expectedControlLayout = null) { if (map == null) { throw new ArgumentNullException("map"); } if (string.IsNullOrEmpty(name)) { throw new ArgumentException("Action must have name", "name"); } if (map.enabled) { throw new InvalidOperationException( string.Format("Cannot add action '{0}' to map '{1}' while it the map is enabled", name, map)); } if (map.TryGetAction(name) != null) { throw new InvalidOperationException( string.Format("Cannot add action with duplicate name '{0}' to set '{1}'", name, map.name)); } // Append action to array. var action = new InputAction(name); action.expectedControlLayout = expectedControlLayout; ArrayHelpers.Append(ref map.m_Actions, action); action.m_ActionMap = map; ////TODO: make sure we blast out existing action map state // Add binding, if supplied. if (!string.IsNullOrEmpty(binding)) { action.AppendBinding(binding, interactions: interactions, groups: groups); } return(action); }
public static int ApplyBindingOverridesOnMatchingControls(this InputActionMap actionMap, InputControl control) { if (actionMap == null) { throw new ArgumentNullException("actionMap"); } if (control == null) { throw new ArgumentNullException("control"); } var actions = actionMap.actions; var actionCount = actions.Count; var numMatchingControls = 0; for (var i = 0; i < actionCount; ++i) { var action = actions[i]; numMatchingControls = action.ApplyBindingOverridesOnMatchingControls(control); } return(numMatchingControls); }
public static void Rename(this InputActionAsset asset, InputActionMap map) { throw new NotImplementedException(); }
public void Pop(InputActionMap actionMap) { throw new NotImplementedException(); }
/// <summary> /// Resolve and add all bindings and actions from the given map. /// </summary> /// <param name="map"></param> /// <exception cref="Exception"></exception> public void AddActionMap(InputActionMap map) { Debug.Assert(map != null); Debug.Assert(map.m_MapIndex == InputActionMapState.kInvalidIndex); // Keep track of indices for this map. var bindingStartIndex = totalBindingCount; var controlStartIndex = totalControlCount; var interactionStartIndex = totalInteractionCount; var processorStartIndex = totalProcessorCount; var compositeStartIndex = totalCompositeCount; var actionStartIndex = totalActionCount; // Allocate binding states. var bindingsInThisMap = map.m_Bindings; var bindingCountInThisMap = bindingsInThisMap != null ? bindingsInThisMap.Length : 0; totalBindingCount += bindingCountInThisMap; ArrayHelpers.GrowBy(ref bindingStates, totalBindingCount); ////TODO: make sure composite objects get all the bindings they need ////TODO: handle case where we have bindings resolving to the same control //// (not so clear cut what to do there; each binding may have a different interaction setup, for example) var currentCompositeIndex = InputActionMapState.kInvalidIndex; var actionsInThisMap = map.m_Actions; var actionCountInThisMap = actionsInThisMap != null ? actionsInThisMap.Length : 0; for (var n = 0; n < bindingCountInThisMap; ++n) { var unresolvedBinding = bindingsInThisMap[n]; // Skip binding if it is disabled (path is empty string). var path = unresolvedBinding.effectivePath; if (unresolvedBinding.path == "") { continue; } // Try to find action. var actionIndex = InputActionMapState.kInvalidIndex; var actionName = unresolvedBinding.action; if (!string.IsNullOrEmpty(actionName)) { actionIndex = map.TryGetActionIndex(actionName); } else if (map.m_SingletonAction != null) { // Special-case for singleton actions that don't have names. actionIndex = 0; } // Instantiate processors. var firstProcessorIndex = 0; var numProcessors = 0; var processors = unresolvedBinding.effectiveProcessors; if (!string.IsNullOrEmpty(processors)) { firstProcessorIndex = ResolveProcessors(processors); if (processors != null) { numProcessors = totalProcessorCount - firstProcessorIndex; } } ////TODO: allow specifying parameters for composite on its path (same way as parameters work for interactions) // If it's the start of a composite chain, create the composite. if (unresolvedBinding.isComposite) { ////REVIEW: what to do about interactions on composites? // Instantiate. For composites, the path is the name of the composite. var composite = InstantiateBindingComposite(unresolvedBinding.path); currentCompositeIndex = ArrayHelpers.AppendWithCapacity(ref composites, ref totalCompositeCount, composite); bindingStates[bindingStartIndex + n] = new InputActionMapState.BindingState { actionIndex = actionIndex, compositeIndex = currentCompositeIndex, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, mapIndex = totalMapCount, }; // The composite binding entry itself does not resolve to any controls. // It creates a composite binding object which is then populated from // subsequent bindings. continue; } // If we've reached the end of a composite chain, finish // off the current composite. if (!unresolvedBinding.isPartOfComposite && currentCompositeIndex != InputActionMapState.kInvalidIndex) { currentCompositeIndex = InputActionMapState.kInvalidIndex; } // Look up controls. var firstControlIndex = totalControlCount; if (controls == null) { controls = new InputControl[10]; } var resolvedControls = new ArrayOrListWrapper <InputControl>(controls, totalControlCount); var numControls = InputSystem.GetControls(path, ref resolvedControls); controls = resolvedControls.array; totalControlCount = resolvedControls.count; // Instantiate interactions. var firstInteractionIndex = 0; var numInteractions = 0; var interactions = unresolvedBinding.effectiveInteractions; if (!string.IsNullOrEmpty(interactions)) { firstInteractionIndex = ResolveInteractions(interactions); if (interactionStates != null) { numInteractions = totalInteractionCount - firstInteractionIndex; } } // Add entry for resolved binding. bindingStates[bindingStartIndex + n] = new InputActionMapState.BindingState { controlStartIndex = firstControlIndex, controlCount = numControls, interactionStartIndex = firstInteractionIndex, interactionCount = numInteractions, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, isPartOfComposite = unresolvedBinding.isPartOfComposite, actionIndex = actionIndex, compositeIndex = currentCompositeIndex, mapIndex = totalMapCount, }; // If the binding is part of a composite, pass the resolve controls // on to the composite. if (unresolvedBinding.isPartOfComposite && currentCompositeIndex != InputActionMapState.kInvalidIndex && numControls != 0) { ////REVIEW: what should we do when a single binding in a composite resolves to multiple controls? //// if the composite has more than one bindable control, it's not readily apparent how we would group them if (numControls > 1) { throw new NotImplementedException("Handling case where single binding in composite resolves to multiple controls"); } // Make sure the binding is named. The name determines what in the composite // to bind to. if (string.IsNullOrEmpty(unresolvedBinding.name)) { throw new Exception(string.Format( "Binding that is part of composite '{0}' is missing a name", composites[currentCompositeIndex])); } // Install the control on the binding. BindControlInComposite(composites[currentCompositeIndex], unresolvedBinding.name, controls[firstControlIndex]); } } // Set up control to binding index mapping. var controlCountInThisMap = totalControlCount - controlStartIndex; ArrayHelpers.GrowBy(ref controlIndexToBindingIndex, controlCountInThisMap); for (var i = 0; i < bindingCountInThisMap; ++i) { var numControls = bindingStates[bindingStartIndex + i].controlCount; var startIndex = bindingStates[bindingStartIndex + i].controlStartIndex; for (var n = 0; n < numControls; ++n) { controlIndexToBindingIndex[startIndex + n] = i; } } // Store indices for map. var numMaps = totalMapCount; var mapIndex = ArrayHelpers.AppendWithCapacity(ref maps, ref numMaps, map); ArrayHelpers.AppendWithCapacity(ref mapIndices, ref totalMapCount, new InputActionMapState.ActionMapIndices { actionStartIndex = actionStartIndex, actionCount = actionCountInThisMap, controlStartIndex = controlStartIndex, controlCount = controlCountInThisMap, bindingStartIndex = bindingStartIndex, bindingCount = bindingCountInThisMap, interactionStartIndex = interactionStartIndex, interactionCount = totalInteractionCount - interactionStartIndex, processorStartIndex = processorStartIndex, processorCount = totalProcessorCount - processorStartIndex, compositeStartIndex = compositeStartIndex, compositeCount = totalCompositeCount - compositeStartIndex, }); map.m_MapIndex = mapIndex; // Allocate action states. if (actionCountInThisMap > 0) { // Assign action indices. var actions = map.m_Actions; for (var i = 0; i < actionCountInThisMap; ++i) { actions[i].m_ActionIndex = totalActionCount + i; } ArrayHelpers.GrowBy(ref actionStates, actionCountInThisMap); totalActionCount += actionCountInThisMap; for (var i = 0; i < actionCountInThisMap; ++i) { actionStates[i].mapIndex = mapIndex; } } }
/// <summary> /// Resolve and add all bindings and actions from the given map. /// </summary> /// <param name="map"></param> /// <remarks> /// This is where all binding resolution happens for actions. The method walks through the binding array /// in <paramref name="map"/> and adds any controls, interactions, processors, and composites as it goes. /// </remarks> public unsafe void AddActionMap(InputActionMap map) { Debug.Assert(map != null); var actionsInThisMap = map.m_Actions; var bindingsInThisMap = map.m_Bindings; var bindingCountInThisMap = bindingsInThisMap?.Length ?? 0; var actionCountInThisMap = actionsInThisMap?.Length ?? 0; var mapIndex = totalMapCount; // Keep track of indices for this map. var actionStartIndex = totalActionCount; var bindingStartIndex = totalBindingCount; var controlStartIndex = totalControlCount; var interactionStartIndex = totalInteractionCount; var processorStartIndex = totalProcessorCount; var compositeStartIndex = totalCompositeCount; // Allocate an initial block of memory. We probably will have to re-allocate once // at the end to accommodate interactions and controls added from the map. var newMemory = new InputActionState.UnmanagedMemory(); newMemory.Allocate( mapCount: totalMapCount + 1, actionCount: totalActionCount + actionCountInThisMap, bindingCount: totalBindingCount + bindingCountInThisMap, // We reallocate for the following once we know the final count. interactionCount: totalInteractionCount, compositeCount: totalCompositeCount, controlCount: totalControlCount); if (memory.isAllocated) { newMemory.CopyDataFrom(memory); } ////TODO: make sure composite objects get all the bindings they need ////TODO: handle case where we have bindings resolving to the same control //// (not so clear cut what to do there; each binding may have a different interaction setup, for example) var currentCompositeBindingIndex = InputActionState.kInvalidIndex; var currentCompositeIndex = InputActionState.kInvalidIndex; var currentCompositePartCount = 0; var currentCompositeActionIndexInMap = InputActionState.kInvalidIndex; InputAction currentCompositeAction = null; var bindingMaskOnThisMap = map.m_BindingMask; var devicesForThisMap = map.devices; // Can't use `using` as we need to use it with `ref`. var resolvedControls = new InputControlList <InputControl>(Allocator.Temp); // We gather all controls in temporary memory and then move them over into newMemory once // we're done resolving. try { for (var n = 0; n < bindingCountInThisMap; ++n) { var bindingStatesPtr = newMemory.bindingStates; ref var unresolvedBinding = ref bindingsInThisMap[n]; var bindingIndex = bindingStartIndex + n; var isComposite = unresolvedBinding.isComposite; var isPartOfComposite = !isComposite && unresolvedBinding.isPartOfComposite; var bindingState = &bindingStatesPtr[bindingIndex]; try { ////TODO: if it's a composite, check if any of the children matches our binding masks (if any) and skip composite if none do // Set binding state to defaults. bindingState->mapIndex = totalMapCount; bindingState->compositeOrCompositeBindingIndex = InputActionState.kInvalidIndex; bindingState->actionIndex = InputActionState.kInvalidIndex; // Make sure that if it's part of a composite, we are actually part of a composite. if (isPartOfComposite && currentCompositeBindingIndex == InputActionState.kInvalidIndex) { throw new Exception( $"Binding '{unresolvedBinding}' is marked as being part of a composite but the preceding binding is not a composite"); } // Skip binding if it is disabled (path is empty string). var path = unresolvedBinding.effectivePath; if (unresolvedBinding.path == "") { continue; } // Skip binding if it doesn't match with our binding mask (might be empty). if (!isComposite && bindingMask != null && !bindingMask.Value.Matches(ref unresolvedBinding)) { continue; } // Skip binding if it doesn't match the binding mask on the map (might be empty). if (!isComposite && bindingMaskOnThisMap != null && !bindingMaskOnThisMap.Value.Matches(ref unresolvedBinding)) { continue; } // Try to find action. // // NOTE: We ignore actions on bindings that are part of composites. We only allow // actions to be triggered from the composite itself. var actionIndexInMap = InputActionState.kInvalidIndex; var actionName = unresolvedBinding.action; InputAction action = null; if (!isPartOfComposite) { if (!string.IsNullOrEmpty(actionName)) { ////REVIEW: should we fail here if we don't manage to find the action actionIndexInMap = map.TryGetActionIndex(actionName); } else if (map.m_SingletonAction != null) { // Special-case for singleton actions that don't have names. actionIndexInMap = 0; } if (actionIndexInMap != InputActionState.kInvalidIndex) { action = actionsInThisMap[actionIndexInMap]; } } else { actionIndexInMap = currentCompositeActionIndexInMap; action = currentCompositeAction; } // Skip binding if it doesn't match the binding mask on the action (might be empty). if (!isComposite && action?.m_BindingMask != null && !action.m_BindingMask.Value.Matches(ref unresolvedBinding)) { continue; } // Instantiate processors. var firstProcessorIndex = InputActionState.kInvalidIndex; var numProcessors = 0; var processorString = unresolvedBinding.effectiveProcessors; if (!string.IsNullOrEmpty(processorString)) { // Add processors from binding. firstProcessorIndex = ResolveProcessors(processorString); if (firstProcessorIndex != InputActionState.kInvalidIndex) { numProcessors = totalProcessorCount - firstProcessorIndex; } } if (action != null && !string.IsNullOrEmpty(action.m_Processors)) { // Add processors from action. var index = ResolveProcessors(action.m_Processors); if (index != InputActionState.kInvalidIndex) { if (firstProcessorIndex == InputActionState.kInvalidIndex) { firstProcessorIndex = index; } numProcessors += totalProcessorCount - index; } } // Instantiate interactions. var firstInteractionIndex = InputActionState.kInvalidIndex; var numInteractions = 0; var interactionString = unresolvedBinding.effectiveInteractions; if (!string.IsNullOrEmpty(interactionString)) { // Add interactions from binding. firstInteractionIndex = ResolveInteractions(interactionString); if (firstInteractionIndex != InputActionState.kInvalidIndex) { numInteractions = totalInteractionCount - firstInteractionIndex; } } if (action != null && !string.IsNullOrEmpty(action.m_Interactions)) { // Add interactions from action. var index = ResolveInteractions(action.m_Interactions); if (index != InputActionState.kInvalidIndex) { if (firstInteractionIndex == InputActionState.kInvalidIndex) { firstInteractionIndex = index; } numInteractions += totalInteractionCount - index; } } // If it's the start of a composite chain, create the composite. if (isComposite) { var actionIndexForComposite = actionIndexInMap != InputActionState.kInvalidIndex ? actionStartIndex + actionIndexInMap : InputActionState.kInvalidIndex; // Instantiate. For composites, the path is the name of the composite. var composite = InstantiateBindingComposite(unresolvedBinding.path); currentCompositeIndex = ArrayHelpers.AppendWithCapacity(ref composites, ref totalCompositeCount, composite); currentCompositeBindingIndex = bindingIndex; currentCompositeAction = action; currentCompositeActionIndexInMap = actionIndexInMap; *bindingState = new InputActionState.BindingState { actionIndex = actionIndexForComposite, compositeOrCompositeBindingIndex = currentCompositeIndex, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, interactionCount = numInteractions, interactionStartIndex = firstInteractionIndex, mapIndex = totalMapCount, isComposite = true, // Record where the controls for parts of the composite start. controlStartIndex = memory.controlCount + resolvedControls.Count, }; // The composite binding entry itself does not resolve to any controls. // It creates a composite binding object which is then populated from // subsequent bindings. continue; } // If we've reached the end of a composite chain, finish // off the current composite. if (!isPartOfComposite && currentCompositeBindingIndex != InputActionState.kInvalidIndex) { currentCompositePartCount = 0; currentCompositeBindingIndex = InputActionState.kInvalidIndex; currentCompositeIndex = InputActionState.kInvalidIndex; currentCompositeAction = null; currentCompositeActionIndexInMap = InputActionState.kInvalidIndex; } // Look up controls. // // NOTE: We continuously add controls here to `resolvedControls`. Once we've completed our // pass over the bindings in the map, `resolvedControls` will have all the controls for // the current map. var firstControlIndex = memory.controlCount + resolvedControls.Count; var numControls = 0; if (devicesForThisMap != null) { // Search in devices for only this map. var list = devicesForThisMap.Value; for (var i = 0; i < list.Count; ++i) { var device = list[i]; if (!device.added) { continue; // Skip devices that have been removed. } numControls += InputControlPath.TryFindControls(device, path, 0, ref resolvedControls); } } else { // Search globally. numControls = InputSystem.FindControls(path, ref resolvedControls); } // If the binding is part of a composite, pass the resolved controls // on to the composite. var partIndex = InputActionState.kInvalidIndex; var actionIndexForBinding = InputActionState.kInvalidIndex; if (isPartOfComposite && currentCompositeBindingIndex != InputActionState.kInvalidIndex && numControls > 0) { // Make sure the binding is named. The name determines what in the composite // to bind to. if (string.IsNullOrEmpty(unresolvedBinding.name)) { throw new Exception( $"Binding '{unresolvedBinding}' that is part of composite '{composites[currentCompositeIndex]}' is missing a name"); } // Give a part index for the partIndex = AssignCompositePartIndex(composites[currentCompositeIndex], unresolvedBinding.name, ref currentCompositePartCount); // Keep track of total number of controls bound in the composite. bindingStatesPtr[currentCompositeBindingIndex].controlCount += numControls; // Force action index on part binding to be same as that of composite. actionIndexForBinding = bindingStatesPtr[currentCompositeBindingIndex].actionIndex; } else if (actionIndexInMap != InputActionState.kInvalidIndex) { actionIndexForBinding = actionStartIndex + actionIndexInMap; } // Add entry for resolved binding. *bindingState = new InputActionState.BindingState { controlStartIndex = firstControlIndex, controlCount = numControls, interactionStartIndex = firstInteractionIndex, interactionCount = numInteractions, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, isPartOfComposite = unresolvedBinding.isPartOfComposite, partIndex = partIndex, actionIndex = actionIndexForBinding, compositeOrCompositeBindingIndex = currentCompositeBindingIndex, mapIndex = totalMapCount, }; } catch (Exception exception) { Debug.LogError( $"{exception.GetType().Name} while resolving binding '{unresolvedBinding}' in action map '{map}'"); Debug.LogException(exception); // Don't swallow exceptions that indicate something is wrong in the code rather than // in the data. if (exception.IsExceptionIndicatingBugInCode()) { throw exception; } } } // Re-allocate memory to accommodate controls and interaction states. The count for those // we only know once we've completed all resolution. var controlCountInThisMap = resolvedControls.Count; var newTotalControlCount = memory.controlCount + controlCountInThisMap; if (newMemory.interactionCount != totalInteractionCount || newMemory.compositeCount != totalCompositeCount || newMemory.controlCount != newTotalControlCount) { var finalMemory = new InputActionState.UnmanagedMemory(); finalMemory.Allocate( mapCount: newMemory.mapCount, actionCount: newMemory.actionCount, bindingCount: newMemory.bindingCount, controlCount: newTotalControlCount, interactionCount: totalInteractionCount, compositeCount: totalCompositeCount); finalMemory.CopyDataFrom(newMemory); newMemory.Dispose(); newMemory = finalMemory; } // Add controls to array. var controlCountInArray = memory.controlCount; ArrayHelpers.AppendListWithCapacity(ref controls, ref controlCountInArray, resolvedControls); Debug.Assert(controlCountInArray == newTotalControlCount, "Control array should have combined count of old and new controls"); // Set up control to binding index mapping. for (var i = 0; i < bindingCountInThisMap; ++i) { var bindingStatesPtr = newMemory.bindingStates; var bindingState = &bindingStatesPtr[bindingStartIndex + i]; var numControls = bindingState->controlCount; var startIndex = bindingState->controlStartIndex; for (var n = 0; n < numControls; ++n) { newMemory.controlIndexToBindingIndex[startIndex + n] = bindingStartIndex + i; } } // Initialize initial interaction states. for (var i = memory.interactionCount; i < newMemory.interactionCount; ++i) { newMemory.interactionStates[i].phase = InputActionPhase.Waiting; } // Initialize action data. var runningIndexInBindingIndices = memory.bindingCount; for (var i = 0; i < actionCountInThisMap; ++i) { var action = actionsInThisMap[i]; var actionIndex = actionStartIndex + i; // Correlate action with its trigger state. action.m_ActionIndex = actionIndex; // Collect bindings for action. var bindingStartIndexForAction = runningIndexInBindingIndices; var bindingCountForAction = 0; var numPossibleConcurrentActuations = 0; for (var n = 0; n < bindingCountInThisMap; ++n) { var bindingIndex = bindingStartIndex + n; var bindingState = &newMemory.bindingStates[bindingIndex]; if (bindingState->actionIndex != actionIndex) { continue; } if (bindingState->isPartOfComposite) { continue; } Debug.Assert(bindingIndex <= ushort.MaxValue, "Binding index exceeds limit"); newMemory.actionBindingIndices[runningIndexInBindingIndices] = (ushort)bindingIndex; ++runningIndexInBindingIndices; ++bindingCountForAction; // Keep track of how many concurrent actuations we may be seeing on the action so that // we know whether we need to enable conflict resolution or not. if (bindingState->isComposite) { // Composite binding. Actuates as a whole. Check if the composite has successfully // resolved any controls. If so, it adds one possible actuation. if (bindingState->controlCount > 0) { ++numPossibleConcurrentActuations; } } else { // Normal binding. Every successfully resolved control results in one possible actuation. numPossibleConcurrentActuations += bindingState->controlCount; } } Debug.Assert(bindingStartIndexForAction < ushort.MaxValue, "Binding start index on action exceeds limit"); Debug.Assert(bindingCountForAction < ushort.MaxValue, "Binding count on action exceeds limit"); newMemory.actionBindingIndicesAndCounts[i * 2] = (ushort)bindingStartIndexForAction; newMemory.actionBindingIndicesAndCounts[i * 2 + 1] = (ushort)bindingCountForAction; // See if we may need conflict resolution on this action. Never needed for pass-through actions. // Otherwise, if we have more than one bound control or have several bindings and one of them // is a composite, we enable it. var isPassThroughAction = action.passThrough; var mayNeedConflictResolution = !isPassThroughAction && numPossibleConcurrentActuations > 1; // Initialize initial trigger state. newMemory.actionStates[actionIndex] = new InputActionState.TriggerState { phase = InputActionPhase.Disabled, mapIndex = mapIndex, controlIndex = InputActionState.kInvalidIndex, interactionIndex = InputActionState.kInvalidIndex, continuous = action.continuous, passThrough = isPassThroughAction, mayNeedConflictResolution = mayNeedConflictResolution, }; } // Store indices for map. newMemory.mapIndices[mapIndex] = new InputActionState.ActionMapIndices { actionStartIndex = actionStartIndex, actionCount = actionCountInThisMap, controlStartIndex = controlStartIndex, controlCount = controlCountInThisMap, bindingStartIndex = bindingStartIndex, bindingCount = bindingCountInThisMap, interactionStartIndex = interactionStartIndex, interactionCount = totalInteractionCount - interactionStartIndex, processorStartIndex = processorStartIndex, processorCount = totalProcessorCount - processorStartIndex, compositeStartIndex = compositeStartIndex, compositeCount = totalCompositeCount - compositeStartIndex, }; map.m_MapIndexInState = mapIndex; var finalActionMapCount = memory.mapCount; ArrayHelpers.AppendWithCapacity(ref maps, ref finalActionMapCount, map, capacityIncrement: 4); Debug.Assert(finalActionMapCount == newMemory.mapCount, "Final action map count should match old action map count plus one"); // As a final act, swap the new memory in. memory.Dispose(); memory = newMemory; }
public InputActionMap[] ToMaps() { var mapList = new List <InputActionMap>(); var actionLists = new List <List <InputAction> >(); var bindingLists = new List <List <InputBinding> >(); // Process actions listed at toplevel. var actionCount = actions != null ? actions.Length : 0; for (var i = 0; i < actionCount; ++i) { var jsonAction = actions[i]; if (string.IsNullOrEmpty(jsonAction.name)) { throw new Exception(string.Format("Action number {0} has no name", i + 1)); } ////REVIEW: make sure all action names are unique? // Determine name of action map. string mapName = null; var actionName = jsonAction.name; var indexOfFirstSlash = actionName.IndexOf('/'); if (indexOfFirstSlash != -1) { mapName = actionName.Substring(0, indexOfFirstSlash); actionName = actionName.Substring(indexOfFirstSlash + 1); if (string.IsNullOrEmpty(actionName)) { throw new Exception(string.Format( "Invalid action name '{0}' (missing action name after '/')", jsonAction.name)); } } // Try to find existing map. InputActionMap map = null; var mapIndex = 0; for (; mapIndex < mapList.Count; ++mapIndex) { if (string.Compare(mapList[mapIndex].name, mapName, StringComparison.InvariantCultureIgnoreCase) == 0) { map = mapList[mapIndex]; break; } } // Create new map if it's the first action in the map. if (map == null) { map = new InputActionMap(mapName); mapIndex = mapList.Count; mapList.Add(map); actionLists.Add(new List <InputAction>()); bindingLists.Add(new List <InputBinding>()); } // Create action. var action = new InputAction(actionName); actionLists[mapIndex].Add(action); // Add bindings. if (jsonAction.bindings != null) { var bindingsForMap = bindingLists[mapIndex]; for (var n = 0; n < jsonAction.bindings.Length; ++n) { var jsonBinding = jsonAction.bindings[n]; var binding = jsonBinding.ToBinding(); binding.action = action.m_Name; bindingsForMap.Add(binding); } } } // Process maps. var mapCount = maps != null ? maps.Length : 0; for (var i = 0; i < mapCount; ++i) { var jsonMap = maps[i]; var mapName = jsonMap.name; if (string.IsNullOrEmpty(mapName)) { throw new Exception(string.Format("Map number {0} has no name", i + 1)); } // Try to find existing map. InputActionMap map = null; var mapIndex = 0; for (; mapIndex < mapList.Count; ++mapIndex) { if (string.Compare(mapList[mapIndex].name, mapName, StringComparison.InvariantCultureIgnoreCase) == 0) { map = mapList[mapIndex]; break; } } // Create new map if we haven't seen it before. if (map == null) { map = new InputActionMap(mapName); mapIndex = mapList.Count; mapList.Add(map); actionLists.Add(new List <InputAction>()); bindingLists.Add(new List <InputBinding>()); } // Process actions in map. var actionCountInMap = jsonMap.actions != null ? jsonMap.actions.Length : 0; for (var n = 0; n < actionCountInMap; ++n) { var jsonAction = jsonMap.actions[n]; if (string.IsNullOrEmpty(jsonAction.name)) { throw new Exception(string.Format("Action number {0} in map '{1}' has no name", i + 1, mapName)); } // Create action. var action = new InputAction(jsonAction.name); actionLists[mapIndex].Add(action); // Add bindings. if (jsonAction.bindings != null) { var bindingList = bindingLists[mapIndex]; for (var k = 0; k < jsonAction.bindings.Length; ++k) { var jsonBinding = jsonAction.bindings[k]; var binding = jsonBinding.ToBinding(); binding.action = action.m_Name; bindingList.Add(binding); } } } // Process bindings in map. var bindingCountInMap = jsonMap.bindings != null ? jsonMap.bindings.Length : 0; var bindingsForMap = bindingLists[mapIndex]; for (var n = 0; n < bindingCountInMap; ++n) { var jsonBinding = jsonMap.bindings[n]; var binding = jsonBinding.ToBinding(); bindingsForMap.Add(binding); } } // Finalize arrays. for (var i = 0; i < mapList.Count; ++i) { var map = mapList[i]; var actionArray = actionLists[i].ToArray(); var bindingArray = bindingLists[i].ToArray(); map.m_Actions = actionArray; map.m_Bindings = bindingArray; for (var n = 0; n < actionArray.Length; ++n) { var action = actionArray[n]; action.m_ActionMap = map; } } return(mapList.ToArray()); }
/// <summary> /// Enable all actions in the given map but only with bindings /// </summary> /// <param name="map"></param> /// <param name="scheme"></param> /// <remarks> /// </remarks> public static void Enable(this InputActionMap map, InputControlScheme scheme) { throw new NotImplementedException(); }
/// <summary> /// Get all overrides applied to bindings in the given map. /// </summary> /// <param name="actionMap"></param> /// <returns></returns> public static IEnumerable <InputBinding> GetBindingOverrides(this InputActionMap actionMap) { throw new NotImplementedException(); }
public void RemoveActionMap(InputActionMap actionMap) { //nuke event data throw new NotImplementedException(); }
/// <summary> /// Resolve and add all bindings and actions from the given map. /// </summary> /// <param name="map"></param> /// <exception cref="Exception"></exception> public void AddActionMap(InputActionMap map) { Debug.Assert(map != null); // Keep track of indices for this map. var bindingStartIndex = totalBindingCount; var controlStartIndex = totalControlCount; var interactionStartIndex = totalInteractionCount; var processorStartIndex = totalProcessorCount; var compositeStartIndex = totalCompositeCount; var actionStartIndex = totalActionCount; // Allocate binding states. var bindingsInThisMap = map.m_Bindings; var bindingCountInThisMap = bindingsInThisMap != null ? bindingsInThisMap.Length : 0; totalBindingCount += bindingCountInThisMap; ArrayHelpers.GrowBy(ref bindingStates, totalBindingCount); ////TODO: make sure composite objects get all the bindings they need ////TODO: handle case where we have bindings resolving to the same control //// (not so clear cut what to do there; each binding may have a different interaction setup, for example) var currentCompositeBindingIndex = InputActionMapState.kInvalidIndex; var currentCompositeIndex = InputActionMapState.kInvalidIndex; var currentCompositePartIndex = 0; var bindingMaskOnThisMap = map.m_BindingMask; var actionsInThisMap = map.m_Actions; var devicesForThisMap = map.devices; var actionCountInThisMap = actionsInThisMap != null ? actionsInThisMap.Length : 0; var resolvedControls = new InputControlList <InputControl>(Allocator.Temp); try { for (var n = 0; n < bindingCountInThisMap; ++n) { var unresolvedBinding = bindingsInThisMap[n]; var bindingIndex = bindingStartIndex + n; // Set binding state to defaults. bindingStates[bindingIndex].mapIndex = totalMapCount; bindingStates[bindingIndex].compositeOrCompositeBindingIndex = InputActionMapState.kInvalidIndex; bindingStates[bindingIndex].actionIndex = InputActionMapState.kInvalidIndex; // Skip binding if it is disabled (path is empty string). var path = unresolvedBinding.effectivePath; if (unresolvedBinding.path == "") { continue; } // Skip binding if it doesn't match with our binding mask (might be empty). if (bindingMask != null && !bindingMask.Value.Matches(ref unresolvedBinding)) { continue; } // Skip binding if it doesn't match the binding mask on the map (might be empty). if (bindingMaskOnThisMap != null && !bindingMaskOnThisMap.Value.Matches(ref unresolvedBinding)) { continue; } // Try to find action. // NOTE: Technically, we allow individual bindings of composites to trigger actions independent // of the action triggered by the composite. var actionIndexInMap = InputActionMapState.kInvalidIndex; var actionName = unresolvedBinding.action; if (!string.IsNullOrEmpty(actionName)) { actionIndexInMap = map.TryGetActionIndex(actionName); } else if (map.m_SingletonAction != null) { // Special-case for singleton actions that don't have names. actionIndexInMap = 0; } // Skip binding if it doesn't match the binding mask on the action (might be empty). if (actionIndexInMap != InputActionMapState.kInvalidIndex) { var action = actionsInThisMap[actionIndexInMap]; if (action.m_BindingMask != null && !action.m_BindingMask.Value.Matches(ref unresolvedBinding)) { continue; } } // Instantiate processors. var firstProcessorIndex = 0; var numProcessors = 0; var processors = unresolvedBinding.effectiveProcessors; if (!string.IsNullOrEmpty(processors)) { firstProcessorIndex = ResolveProcessors(processors); if (processors != null) { numProcessors = totalProcessorCount - firstProcessorIndex; } } // Instantiate interactions. var firstInteractionIndex = 0; var numInteractions = 0; var interactions = unresolvedBinding.effectiveInteractions; if (!string.IsNullOrEmpty(interactions)) { firstInteractionIndex = ResolveInteractions(interactions); if (interactionStates != null) { numInteractions = totalInteractionCount - firstInteractionIndex; } } ////TODO: allow specifying parameters for composite on its path (same way as parameters work for interactions) //// (Example: "Axis(min=-1,max=1)" creates an axis that goes from -1..1 instead of the default 0..1) // If it's the start of a composite chain, create the composite. if (unresolvedBinding.isComposite) { ////REVIEW: what to do about interactions on composites? // Instantiate. For composites, the path is the name of the composite. var composite = InstantiateBindingComposite(unresolvedBinding.path); currentCompositeIndex = ArrayHelpers.AppendWithCapacity(ref composites, ref totalCompositeCount, composite); currentCompositeBindingIndex = bindingIndex; bindingStates[bindingIndex] = new InputActionMapState.BindingState { actionIndex = actionStartIndex + actionIndexInMap, compositeOrCompositeBindingIndex = currentCompositeIndex, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, interactionCount = numInteractions, interactionStartIndex = firstInteractionIndex, mapIndex = totalMapCount, isComposite = true, }; // The composite binding entry itself does not resolve to any controls. // It creates a composite binding object which is then populated from // subsequent bindings. continue; } // If we've reached the end of a composite chain, finish // off the current composite. if (!unresolvedBinding.isPartOfComposite && currentCompositeBindingIndex != InputActionMapState.kInvalidIndex) { currentCompositePartIndex = 0; currentCompositeBindingIndex = InputActionMapState.kInvalidIndex; currentCompositeIndex = InputActionMapState.kInvalidIndex; } // Look up controls. var firstControlIndex = totalControlCount; int numControls = 0; if (devicesForThisMap != null) { // Search in devices for only this map. var list = devicesForThisMap.Value; for (var i = 0; i < list.Count; ++i) { var device = list[i]; if (!device.added) { continue; // Skip devices that have been removed. } numControls += InputControlPath.TryFindControls(device, path, 0, ref resolvedControls); } } else { // Search globally. numControls = InputSystem.FindControls(path, ref resolvedControls); } if (numControls > 0) { resolvedControls.AppendTo(ref controls, ref totalControlCount); resolvedControls.Clear(); } // If the binding is part of a composite, pass the resolved controls // on to the composite. if (unresolvedBinding.isPartOfComposite && currentCompositeBindingIndex != InputActionMapState.kInvalidIndex && numControls > 0) { // Make sure the binding is named. The name determines what in the composite // to bind to. if (string.IsNullOrEmpty(unresolvedBinding.name)) { throw new Exception(string.Format( "Binding with path '{0}' that is part of composite '{1}' is missing a name", path, composites[currentCompositeIndex])); } // Install the controls on the binding. BindControlInComposite(composites[currentCompositeIndex], unresolvedBinding.name, ref currentCompositePartIndex); } // Add entry for resolved binding. bindingStates[bindingIndex] = new InputActionMapState.BindingState { controlStartIndex = firstControlIndex, controlCount = numControls, interactionStartIndex = firstInteractionIndex, interactionCount = numInteractions, processorStartIndex = firstProcessorIndex, processorCount = numProcessors, isPartOfComposite = unresolvedBinding.isPartOfComposite, partIndex = currentCompositePartIndex, actionIndex = actionIndexInMap, compositeOrCompositeBindingIndex = currentCompositeBindingIndex, mapIndex = totalMapCount, }; } } finally { resolvedControls.Dispose(); } // Set up control to binding index mapping. var controlCountInThisMap = totalControlCount - controlStartIndex; ArrayHelpers.GrowBy(ref controlIndexToBindingIndex, controlCountInThisMap); for (var i = 0; i < bindingCountInThisMap; ++i) { var numControls = bindingStates[bindingStartIndex + i].controlCount; var startIndex = bindingStates[bindingStartIndex + i].controlStartIndex; for (var n = 0; n < numControls; ++n) { controlIndexToBindingIndex[startIndex + n] = i; } } // Store indices for map. var numMaps = totalMapCount; var mapIndex = ArrayHelpers.AppendWithCapacity(ref maps, ref numMaps, map); ArrayHelpers.AppendWithCapacity(ref mapIndices, ref totalMapCount, new InputActionMapState.ActionMapIndices { actionStartIndex = actionStartIndex, actionCount = actionCountInThisMap, controlStartIndex = controlStartIndex, controlCount = controlCountInThisMap, bindingStartIndex = bindingStartIndex, bindingCount = bindingCountInThisMap, interactionStartIndex = interactionStartIndex, interactionCount = totalInteractionCount - interactionStartIndex, processorStartIndex = processorStartIndex, processorCount = totalProcessorCount - processorStartIndex, compositeStartIndex = compositeStartIndex, compositeCount = totalCompositeCount - compositeStartIndex, }); map.m_MapIndexInState = mapIndex; // Allocate action states. if (actionCountInThisMap > 0) { // Assign action indices. var actions = map.m_Actions; for (var i = 0; i < actionCountInThisMap; ++i) { actions[i].m_ActionIndex = totalActionCount + i; } ArrayHelpers.GrowBy(ref actionStates, actionCountInThisMap); totalActionCount += actionCountInThisMap; for (var i = 0; i < actionCountInThisMap; ++i) { actionStates[i].mapIndex = mapIndex; } } }
// Replace the contents of the asset with the action sets in the // given JSON string. public void LoadFromJson(string json) { m_ActionMaps = InputActionMap.FromJson(json); }
// Return a JSON representation of the asset. public string ToJson() { return(InputActionMap.ToJson(m_ActionMaps)); }
internal BindingSyntax(InputActionMap map, InputAction action, int bindingIndex) { m_ActionMap = map; m_Action = action; m_BindingIndex = bindingIndex; }
internal CompositeSyntax(InputActionMap map, InputAction action, int compositeIndex) { m_Action = action; m_ActionMap = map; m_CompositeIndex = compositeIndex; }
public static int GetBindingOverrides(this InputActionMap actionMap, List <InputBinding> overrides) { throw new NotImplementedException(); }