清理项目目录,引入 InputModule 做多平台输入适配

This commit is contained in:
SepComet
2026-06-16 17:09:19 +08:00
committed by basil
parent d28ccaa7f9
commit 29c4468d54
227 changed files with 16702 additions and 2059 deletions
@@ -0,0 +1,74 @@
using System;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace SepCore.InputModule.Runtime
{
public static class BindingOverridePersistence
{
public const int CurrentVersion = 2;
[Serializable]
private class Container
{
public int version;
public string payload;
}
public static string Serialize(string overridesJson)
{
Container container = new Container { version = CurrentVersion, payload = overridesJson };
return JsonUtility.ToJson(container);
}
public static bool TryDeserialize(string storedData, out string overridesJson)
{
overridesJson = null;
if (string.IsNullOrWhiteSpace(storedData))
{
return false;
}
string trimmed = storedData.TrimStart();
if (trimmed.StartsWith("["))
{
overridesJson = storedData;
return true;
}
if (trimmed.StartsWith("{"))
{
Container container;
try
{
container = JsonUtility.FromJson<Container>(storedData);
}
catch (ArgumentException)
{
Log.Warning("[InputModule] Binding overrides data is corrupted. Skipping load.");
return false;
}
if (container == null)
{
Log.Warning("[InputModule] Binding overrides data is corrupted. Skipping load.");
return false;
}
if (container.version > CurrentVersion)
{
Log.Warning("[InputModule] Binding overrides data version {0} is newer than supported version {1}. Skipping load.",
container.version, CurrentVersion);
return false;
}
overridesJson = container.payload;
return true;
}
Log.Warning("[InputModule] Unrecognized binding overrides data format. Skipping load.");
return false;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a41e1727c0974741972bf59521aae50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,33 @@
using UnityEngine.InputSystem;
namespace SepCore.InputModule.Runtime
{
internal static class InputDeviceKindUtility
{
public static InputDeviceKind FromDevice(InputDevice device)
{
if (device == null)
{
return InputDeviceKind.Unknown;
}
if (device is Gamepad)
{
return InputDeviceKind.Gamepad;
}
if (device is Touchscreen)
{
return InputDeviceKind.Touch;
}
if (device is Keyboard || device is Mouse)
{
return InputDeviceKind.KeyboardMouse;
}
return InputDeviceKind.Unknown;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dfceff161ff25aa4fa504b3334f23233
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,839 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityGameFramework.Runtime;
namespace SepCore.InputModule.Runtime
{
[DisallowMultipleComponent]
[AddComponentMenu("Input Module")]
public sealed class InputModuleComponent : GameFrameworkComponent
{
private const string DefaultBindingOverrideSettingKey = "SepCore.InputModule.BindingOverrides";
[SerializeField] private InputActionAsset _inputActionsAsset = null;
[SerializeField] private InputContextId _startupContext = InputContextId.None;
[SerializeField] private bool _loadBindingOverridesOnInit = true;
[SerializeField] private bool _saveBindingOverridesOnDestroy = false;
[SerializeField] private string _bindingOverrideSettingKey = DefaultBindingOverrideSettingKey;
private readonly Dictionary<InputActionId, Action<InputCommand>> _listeners =
new Dictionary<InputActionId, Action<InputCommand>>();
private readonly Dictionary<InputContextId, InputActionMap> _actionMaps =
new Dictionary<InputContextId, InputActionMap>();
private readonly InputContextStack _contextStack = new InputContextStack();
private InputActionAsset _runtimeActions = null;
private SettingComponent _settingComponent = null;
private bool _isInitialized = false;
private bool _onInitCalled = false;
private bool _ownsRuntimeActions = false;
private InputActionRebindingExtensions.RebindingOperation _activeRebindOperation;
private bool _rebindCanceledByUser;
private IInputPromptMap _promptMap;
public event Action<InputCommand> CommandTriggered;
public event Action<InputDeviceKind> DeviceKindChanged;
public event Action<InputContextId> ContextChanged;
public event Action<RebindResult> RebindCompleted;
public InputDeviceKind CurrentDeviceKind { get; private set; } = InputDeviceKind.Unknown;
public InputContextId CurrentContext => _contextStack.Current;
public bool IsInitialized => _isInitialized;
public bool IsRebinding => _activeRebindOperation != null;
public IInputPromptMap PromptMap
{
get
{
if (_promptMap == null)
{
_promptMap = new InputModuleDefaultPromptMap(_runtimeActions);
}
return _promptMap;
}
set => _promptMap = value;
}
public InputActionAsset RuntimeActions
{
get
{
EnsureInitialized();
return _runtimeActions;
}
}
protected override void Awake()
{
base.Awake();
}
private void OnEnable()
{
if (_isInitialized)
{
ApplyContextState();
SubscribeToGlobalDeviceTracking();
}
}
private void OnDisable()
{
UnsubscribeFromGlobalDeviceTracking();
_runtimeActions?.Disable();
}
private void OnDestroy()
{
if (!_isInitialized)
{
return;
}
CancelRebinding();
if (_saveBindingOverridesOnDestroy)
{
SaveBindingOverrides();
}
UnsubscribeFromGlobalDeviceTracking();
UnsubscribeFromActions();
if (_ownsRuntimeActions && _runtimeActions != null)
{
if (Application.isPlaying)
{
Destroy(_runtimeActions);
}
else
{
DestroyImmediate(_runtimeActions);
}
}
_runtimeActions = null;
_actionMaps.Clear();
_listeners.Clear();
_isInitialized = false;
_onInitCalled = false;
}
public void RegisterListener(InputActionId actionId, Action<InputCommand> listener)
{
if (listener == null)
{
return;
}
EnsureInitialized();
_listeners[actionId] = _listeners.TryGetValue(actionId, out Action<InputCommand> existing)
? existing + listener
: listener;
}
public bool TryGetPrompt(InputActionId actionId, out InputPrompt prompt)
{
return PromptMap.TryGetPrompt(actionId, CurrentDeviceKind, out prompt);
}
public void InjectCommand(InputCommand command)
{
if (!_isInitialized)
{
return;
}
CommandTriggered?.Invoke(command);
if (_listeners.TryGetValue(command.ActionId, out Action<InputCommand> listener))
{
listener?.Invoke(command);
}
}
public void ForceDeviceKind(InputDeviceKind deviceKind)
{
if (deviceKind == InputDeviceKind.Unknown || deviceKind == CurrentDeviceKind)
{
return;
}
InputDeviceKind previous = CurrentDeviceKind;
CurrentDeviceKind = deviceKind;
Log.Debug("[InputModule] Device changed (forced): {0} -> {1}", previous, deviceKind);
DeviceKindChanged?.Invoke(CurrentDeviceKind);
}
public void UnregisterListener(InputActionId actionId, Action<InputCommand> listener)
{
if (listener == null || !_listeners.TryGetValue(actionId, out Action<InputCommand> existing))
{
return;
}
existing -= listener;
if (existing == null)
{
_listeners.Remove(actionId);
return;
}
_listeners[actionId] = existing;
}
public void OnInit()
{
if (_isInitialized)
{
Log.Debug("[InputModule] OnInit skipped - already initialized.");
return;
}
_onInitCalled = true;
EnsureInitialized();
ApplyContextState();
}
public void SetContext(InputContextId context)
{
EnsureInitialized();
if (_contextStack.Set(context))
{
ApplyContextState();
NotifyContextChanged();
Log.Debug("[InputModule] SetContext -> {0}", context);
}
}
public void SetUIContext()
{
SetContext(InputContextId.UI);
}
public void SetGameplayExploreContext()
{
SetContext(InputContextId.GameplayExplore);
}
public void PushContext(InputContextId context)
{
EnsureInitialized();
if (_contextStack.Push(context))
{
ApplyContextState();
NotifyContextChanged();
Log.Debug("[InputModule] PushContext -> {0} (stack depth: {1})", context, _contextStack.Count);
}
}
public bool PopContext()
{
EnsureInitialized();
if (!_contextStack.Pop(out _))
{
return false;
}
ApplyContextState();
NotifyContextChanged();
Log.Debug("[InputModule] PopContext -> {0} (stack depth: {1})", _contextStack.Current, _contextStack.Count);
return true;
}
public void ClearContexts()
{
EnsureInitialized();
if (_contextStack.Clear())
{
ApplyContextState();
NotifyContextChanged();
Log.Debug("[InputModule] ClearContexts");
}
}
public bool StartRebinding(
InputContextId contextId,
InputActionId actionId,
int bindingIndex,
float timeoutSeconds = 5f,
InputDeviceKind expectedDeviceKind = InputDeviceKind.Unknown)
{
EnsureInitialized();
if (_activeRebindOperation != null)
{
return false;
}
if (!TryGetAction(contextId, actionId, out InputAction action))
{
return false;
}
if (bindingIndex < 0 || bindingIndex >= action.bindings.Count)
{
return false;
}
InputBinding binding = action.bindings[bindingIndex];
string previousEffectivePath = binding.effectivePath;
string previousOverridePath = binding.overridePath;
_rebindCanceledByUser = false;
PushContext(InputContextId.Rebind);
try
{
DisableActionForRebind(action);
_activeRebindOperation = CreateRebindOperation(
action,
contextId,
actionId,
bindingIndex,
timeoutSeconds,
expectedDeviceKind,
previousEffectivePath,
previousOverridePath);
_activeRebindOperation.Start();
}
catch
{
_activeRebindOperation?.Dispose();
_activeRebindOperation = null;
if (_contextStack.Current == InputContextId.Rebind)
{
PopContext();
}
throw;
}
RebindCompleted?.Invoke(new RebindResult(RebindStatus.Started, contextId, actionId, bindingIndex, previousEffectivePath, previousEffectivePath));
return true;
}
public void CancelRebinding()
{
if (_activeRebindOperation == null)
{
return;
}
_rebindCanceledByUser = true;
_activeRebindOperation.Cancel();
}
public void ResetBindingToDefault(InputContextId contextId, InputActionId actionId, int bindingIndex, bool saveAfterReset = false)
{
EnsureInitialized();
if (!TryGetAction(contextId, actionId, out InputAction action))
{
return;
}
if (bindingIndex < 0 || bindingIndex >= action.bindings.Count)
{
return;
}
action.RemoveBindingOverride(bindingIndex);
if (saveAfterReset)
{
SaveBindingOverrides();
}
}
public void ResetActionToDefaults(InputContextId contextId, InputActionId actionId, bool saveAfterReset = false)
{
EnsureInitialized();
if (!TryGetAction(contextId, actionId, out InputAction action))
{
return;
}
action.RemoveAllBindingOverrides();
if (saveAfterReset)
{
SaveBindingOverrides();
}
}
public void ResetAllBindingsToDefaults(bool saveAfterReset = false)
{
EnsureInitialized();
_runtimeActions.RemoveAllBindingOverrides();
if (saveAfterReset)
{
SaveBindingOverrides();
}
}
public InputBindingSnapshot SaveBindingOverrides()
{
EnsureInitialized();
string overridesJson = _runtimeActions.SaveBindingOverridesAsJson();
string storedData = BindingOverridePersistence.Serialize(overridesJson);
SettingComponent settingComponent = GetSettingComponent();
if (settingComponent != null)
{
settingComponent.SetString(_bindingOverrideSettingKey, storedData);
}
return new InputBindingSnapshot(overridesJson);
}
public void LoadBindingOverrides()
{
EnsureInitialized();
LoadBindingOverridesCore();
}
private void LoadBindingOverridesCore()
{
if (_runtimeActions == null)
{
return;
}
SettingComponent settingComponent = GetSettingComponent();
if (settingComponent == null || !settingComponent.HasSetting(_bindingOverrideSettingKey))
{
return;
}
string storedData = settingComponent.GetString(_bindingOverrideSettingKey);
if (!BindingOverridePersistence.TryDeserialize(storedData, out string overridesJson))
{
return;
}
if (string.IsNullOrEmpty(overridesJson))
{
return;
}
_runtimeActions.LoadBindingOverridesFromJson(overridesJson);
}
public void ApplyBindingSnapshot(InputBindingSnapshot snapshot, bool saveAfterApply = false)
{
EnsureInitialized();
_runtimeActions.LoadBindingOverridesFromJson(snapshot.OverridesJson);
if (saveAfterApply)
{
SaveBindingOverrides();
}
}
private void EnsureInitialized()
{
if (_isInitialized)
{
return;
}
_runtimeActions = _inputActionsAsset != null
? Instantiate(_inputActionsAsset)
: InputModuleDefaultActions.Create();
_ownsRuntimeActions = true;
CacheActionMaps();
SubscribeToActions();
_isInitialized = true;
SubscribeToGlobalDeviceTracking();
if (_promptMap is InputModuleDefaultPromptMap)
{
_promptMap = null;
}
int mapCount = _actionMaps.Count;
if (mapCount == 0)
{
Log.Warning("[InputModule] Initialized but no action maps were cached. Check that action map names match InputContextId enum values.");
}
else
{
Log.Info("[InputModule] Initialized. Source: {0}, Cached maps: {1}, Startup context: {2}",
_inputActionsAsset != null ? "Serialized asset" : "Default actions",
mapCount,
_startupContext);
}
if (_loadBindingOverridesOnInit)
{
LoadBindingOverridesCore();
}
_contextStack.Set(_startupContext);
if (!_onInitCalled)
{
Log.Warning("[InputModule] Auto-initialized before OnInit() was called. Action maps will not be enabled until OnInit() is invoked.");
}
}
private void CacheActionMaps()
{
_actionMaps.Clear();
foreach (InputActionMap map in _runtimeActions.actionMaps)
{
if (TryParseContextId(map.name, out InputContextId contextId))
{
_actionMaps[contextId] = map;
}
else
{
Log.Warning("[InputModule] ActionMap '{0}' does not match any InputContextId and will be ignored.", map.name);
}
}
}
private void SubscribeToActions()
{
foreach (InputAction action in _runtimeActions)
{
action.started += OnActionTriggered;
action.performed += OnActionTriggered;
action.canceled += OnActionTriggered;
}
}
private void UnsubscribeFromActions()
{
if (_runtimeActions == null)
{
return;
}
foreach (InputAction action in _runtimeActions)
{
action.started -= OnActionTriggered;
action.performed -= OnActionTriggered;
action.canceled -= OnActionTriggered;
}
}
private void SubscribeToGlobalDeviceTracking()
{
InputSystem.onActionChange -= OnGlobalActionChange;
InputSystem.onActionChange += OnGlobalActionChange;
}
private void UnsubscribeFromGlobalDeviceTracking()
{
InputSystem.onActionChange -= OnGlobalActionChange;
}
private void OnGlobalActionChange(object obj, InputActionChange change)
{
if (change == InputActionChange.ActionPerformed && obj is InputAction.CallbackContext context)
{
UpdateDeviceKind(context);
}
}
// Disables all action maps, then enables Global + the topmost context in the stack.
// Only called from OnInit() (explicit activation) and OnEnable() (re-enable after disable).
// The stack remembers context history for Pop restoration; only the current (topmost)
// context is active at any time, preventing overlapping bindings from stacked maps.
private void ApplyContextState()
{
if (_runtimeActions == null)
{
return;
}
_runtimeActions.Disable();
if (_actionMaps.TryGetValue(InputContextId.Global, out InputActionMap globalMap))
{
globalMap.Enable();
}
InputContextId current = _contextStack.Current;
if (current != InputContextId.None && _actionMaps.TryGetValue(current, out InputActionMap currentMap))
{
currentMap.Enable();
}
}
private void NotifyContextChanged()
{
ContextChanged?.Invoke(CurrentContext);
}
private void OnActionTriggered(InputAction.CallbackContext context)
{
if (_activeRebindOperation != null)
{
return;
}
InputActionId actionId = ParseActionId(context);
InputContextId contextId = ParseContextId(context);
InputDeviceKind deviceKind = UpdateDeviceKind(context);
InputCommand command = CreateCommand(actionId, contextId, deviceKind, context);
CommandTriggered?.Invoke(command);
if (_listeners.TryGetValue(actionId, out Action<InputCommand> listener))
{
listener?.Invoke(command);
}
}
private InputDeviceKind UpdateDeviceKind(InputAction.CallbackContext context)
{
InputDeviceKind deviceKind = InputDeviceKindUtility.FromDevice(context.control?.device);
if (deviceKind != InputDeviceKind.Unknown && deviceKind != CurrentDeviceKind)
{
InputDeviceKind previous = CurrentDeviceKind;
CurrentDeviceKind = deviceKind;
Log.Debug("[InputModule] Device changed: {0} -> {1}", previous, deviceKind);
DeviceKindChanged?.Invoke(CurrentDeviceKind);
}
return CurrentDeviceKind;
}
private static InputCommand CreateCommand(
InputActionId actionId,
InputContextId contextId,
InputDeviceKind deviceKind,
InputAction.CallbackContext context)
{
Vector2 vector2Value = Vector2.zero;
float scalarValue = 0f;
if (context.action.expectedControlType == "Vector2")
{
vector2Value = context.ReadValue<Vector2>();
scalarValue = vector2Value.magnitude;
}
else
{
scalarValue = context.ReadValue<float>();
}
return new InputCommand(
actionId,
contextId,
ConvertPhase(context.phase),
deviceKind,
vector2Value,
scalarValue,
context.time);
}
private SettingComponent GetSettingComponent()
{
if (_settingComponent == null)
{
_settingComponent = UnityGameFramework.Runtime.GameEntry.GetComponent<SettingComponent>();
}
return _settingComponent;
}
private static InputActionId ParseActionId(InputAction.CallbackContext context)
{
return Enum.TryParse(context.action.name, out InputActionId actionId)
? actionId
: InputActionId.Unknown;
}
private static InputContextId ParseContextId(InputAction.CallbackContext context)
{
return TryParseContextId(context.action.actionMap.name, out InputContextId contextId)
? contextId
: InputContextId.None;
}
private static bool TryParseContextId(string rawContextId, out InputContextId contextId)
{
return Enum.TryParse(rawContextId, out contextId);
}
private static InputCommandPhase ConvertPhase(InputActionPhase phase)
{
switch (phase)
{
case InputActionPhase.Started:
return InputCommandPhase.Started;
case InputActionPhase.Canceled:
return InputCommandPhase.Canceled;
default:
return InputCommandPhase.Performed;
}
}
private bool TryGetAction(InputContextId contextId, InputActionId actionId, out InputAction action)
{
action = null;
if (!_actionMaps.TryGetValue(contextId, out InputActionMap map))
{
return false;
}
action = map.FindAction(actionId.ToString());
return action != null;
}
private bool DetectConflict(InputAction action, int bindingIndex, string newPath)
{
if (string.IsNullOrEmpty(newPath))
{
return false;
}
InputActionMap targetMap = action.actionMap;
InputBinding targetBinding = action.bindings[bindingIndex];
foreach (InputActionMap map in _runtimeActions.actionMaps)
{
if (map != targetMap && map.name != nameof(InputContextId.Global))
{
continue;
}
foreach (InputBinding binding in map.bindings)
{
if (map == targetMap && binding.id == targetBinding.id)
{
continue;
}
if (string.Equals(binding.effectivePath, newPath, System.StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
private InputActionRebindingExtensions.RebindingOperation CreateRebindOperation(
InputAction action,
InputContextId contextId,
InputActionId actionId,
int bindingIndex,
float timeoutSeconds,
InputDeviceKind expectedDeviceKind,
string previousEffectivePath,
string previousOverridePath)
{
InputActionRebindingExtensions.RebindingOperation operation = new InputActionRebindingExtensions.RebindingOperation()
.WithAction(action)
.WithTargetBinding(bindingIndex)
.WithTimeout(timeoutSeconds)
.WithControlsExcluding("<Pointer>/delta")
.WithControlsExcluding("<Pointer>/position")
.WithControlsExcluding("<Touchscreen>/touch*/position")
.WithControlsExcluding("<Touchscreen>/touch*/delta")
.WithControlsExcluding("<Mouse>/clickCount")
.WithMatchingEventsBeingSuppressed()
.OnCancel(_ =>
{
RebindStatus status = _rebindCanceledByUser ? RebindStatus.Canceled : RebindStatus.Timeout;
CompleteRebind(new RebindResult(status, contextId, actionId, bindingIndex, action.bindings[bindingIndex].effectivePath, previousEffectivePath));
})
.OnComplete(rebindOperation =>
{
InputDeviceKind actualDeviceKind = InputDeviceKindUtility.FromDevice(rebindOperation.selectedControl?.device);
if (expectedDeviceKind != InputDeviceKind.Unknown && actualDeviceKind != expectedDeviceKind)
{
RevertBinding(action, bindingIndex, previousOverridePath);
CompleteRebind(new RebindResult(RebindStatus.Canceled, contextId, actionId, bindingIndex, action.bindings[bindingIndex].effectivePath, previousEffectivePath));
return;
}
string newPath = action.bindings[bindingIndex].effectivePath;
if (DetectConflict(action, bindingIndex, newPath))
{
RevertBinding(action, bindingIndex, previousOverridePath);
CompleteRebind(new RebindResult(RebindStatus.ConflictDetected, contextId, actionId, bindingIndex, newPath, previousEffectivePath));
return;
}
CompleteRebind(new RebindResult(RebindStatus.Succeeded, contextId, actionId, bindingIndex, newPath, previousEffectivePath));
});
if (operation.expectedControlType != "Button")
{
operation.WithCancelingThrough("<Keyboard>/escape");
}
return operation;
}
private void CompleteRebind(RebindResult result)
{
_activeRebindOperation?.Dispose();
_activeRebindOperation = null;
if (_contextStack.Current == InputContextId.Rebind)
{
PopContext();
}
RebindCompleted?.Invoke(result);
}
private static void RevertBinding(InputAction action, int bindingIndex, string previousOverridePath)
{
if (string.IsNullOrEmpty(previousOverridePath))
{
action.RemoveBindingOverride(bindingIndex);
}
else
{
action.ApplyBindingOverride(bindingIndex, previousOverridePath);
}
}
private static void DisableActionForRebind(InputAction action)
{
InputActionMap map = action.actionMap;
if (map == null)
return;
if (map.enabled)
{
map.Disable();
return;
}
// Map is already disabled but action.enabled may still report true
// because map.Disable() bails early when m_Enabled is already false.
// Force-sync by briefly enabling and re-disabling the map.
if (action.enabled)
{
map.Enable();
map.Disable();
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 895365716cd1c724194968e895e434bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,72 @@
using UnityEngine;
using UnityEngine.InputSystem;
namespace SepCore.InputModule.Runtime
{
public static class InputModuleDefaultActions
{
public static InputActionAsset Create()
{
InputActionAsset asset = ScriptableObject.CreateInstance<InputActionAsset>();
InputActionMap globalMap = new InputActionMap(nameof(InputContextId.Global));
AddPauseAction(globalMap);
asset.AddActionMap(globalMap);
InputActionMap uiMap = new InputActionMap(nameof(InputContextId.UI));
AddNavigateAction(uiMap, nameof(InputActionId.Navigate));
AddButtonAction(uiMap, nameof(InputActionId.Confirm), "<Keyboard>/enter", "<Keyboard>/numpadEnter", "<Gamepad>/buttonSouth");
AddButtonAction(uiMap, nameof(InputActionId.Cancel), "<Keyboard>/escape", "<Keyboard>/backspace", "<Gamepad>/buttonEast");
asset.AddActionMap(uiMap);
InputActionMap gameplayExploreMap = new InputActionMap(nameof(InputContextId.GameplayExplore));
AddNavigateAction(gameplayExploreMap, nameof(InputActionId.Move));
AddButtonAction(gameplayExploreMap, nameof(InputActionId.Sprint), "<Keyboard>/leftShift", "<Gamepad>/leftStickPress");
AddButtonAction(gameplayExploreMap, nameof(InputActionId.Interact), "<Keyboard>/e", "<Gamepad>/buttonSouth");
asset.AddActionMap(gameplayExploreMap);
InputActionMap dialogMap = new InputActionMap(nameof(InputContextId.Dialog));
AddNavigateAction(dialogMap, nameof(InputActionId.Navigate));
AddButtonAction(dialogMap, nameof(InputActionId.Confirm), "<Keyboard>/enter", "<Keyboard>/numpadEnter", "<Gamepad>/buttonSouth");
AddButtonAction(dialogMap, nameof(InputActionId.Cancel), "<Keyboard>/escape", "<Keyboard>/backspace", "<Gamepad>/buttonEast");
asset.AddActionMap(dialogMap);
InputActionMap rebindMap = new InputActionMap(nameof(InputContextId.Rebind));
asset.AddActionMap(rebindMap);
return asset;
}
private static void AddPauseAction(InputActionMap map)
{
AddButtonAction(map, nameof(InputActionId.Pause), "<Keyboard>/escape", "<Gamepad>/start");
}
private static void AddButtonAction(InputActionMap map, string actionName, params string[] bindings)
{
InputAction action = map.AddAction(actionName, InputActionType.Button);
foreach (string binding in bindings)
{
action.AddBinding(binding);
}
}
private static void AddNavigateAction(InputActionMap map, string actionName)
{
InputAction action = map.AddAction(actionName, InputActionType.Value);
action.expectedControlType = "Vector2";
action.AddCompositeBinding("2DVector")
.With("Up", "<Keyboard>/w")
.With("Down", "<Keyboard>/s")
.With("Left", "<Keyboard>/a")
.With("Right", "<Keyboard>/d");
action.AddCompositeBinding("2DVector")
.With("Up", "<Keyboard>/upArrow")
.With("Down", "<Keyboard>/downArrow")
.With("Left", "<Keyboard>/leftArrow")
.With("Right", "<Keyboard>/rightArrow");
action.AddBinding("<Gamepad>/leftStick");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f1bc975c14c639b4faecf539e338b313
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using UnityEngine.InputSystem;
namespace SepCore.InputModule.Runtime
{
public sealed class InputModuleDefaultPromptMap : IInputPromptMap
{
private readonly InputActionAsset _asset;
private readonly Dictionary<(InputActionId, InputDeviceKind), InputPrompt> _entries;
public InputModuleDefaultPromptMap(InputActionAsset asset = null)
{
_asset = asset;
_entries = new Dictionary<(InputActionId, InputDeviceKind), InputPrompt>();
PopulateKeyboardMouse();
PopulateGamepad();
PopulateTouch();
}
public bool TryGetPrompt(InputActionId actionId, InputDeviceKind deviceKind, out InputPrompt prompt)
{
if (_asset != null && TryDeriveFromOverride(actionId, deviceKind, out prompt))
{
return true;
}
return _entries.TryGetValue((actionId, deviceKind), out prompt);
}
private bool TryDeriveFromOverride(InputActionId actionId, InputDeviceKind deviceKind, out InputPrompt prompt)
{
prompt = InputPrompt.None;
InputAction action = _asset.FindAction(actionId.ToString());
if (action == null)
{
return false;
}
string devicePrefix = GetDevicePrefix(deviceKind);
if (devicePrefix == null)
{
return false;
}
for (int i = 0; i < action.bindings.Count; i++)
{
InputBinding binding = action.bindings[i];
if (binding.isComposite || binding.isPartOfComposite)
{
continue;
}
if (binding.overridePath == null)
{
continue;
}
string effectivePath = binding.effectivePath;
if (effectivePath != null && effectivePath.StartsWith(devicePrefix, StringComparison.OrdinalIgnoreCase))
{
string label = InputControlPath.ToHumanReadableString(effectivePath);
prompt = new InputPrompt(label);
return true;
}
}
return false;
}
private static string GetDevicePrefix(InputDeviceKind deviceKind)
{
switch (deviceKind)
{
case InputDeviceKind.KeyboardMouse: return "<Keyboard>";
case InputDeviceKind.Gamepad: return "<Gamepad>";
case InputDeviceKind.Touch: return "<Touchscreen>";
default: return null;
}
}
private void PopulateKeyboardMouse()
{
Add(InputActionId.Pause, InputDeviceKind.KeyboardMouse, "Esc", "keyboard_esc");
Add(InputActionId.Navigate, InputDeviceKind.KeyboardMouse, "WASD");
Add(InputActionId.Confirm, InputDeviceKind.KeyboardMouse, "Enter", "keyboard_enter");
Add(InputActionId.Cancel, InputDeviceKind.KeyboardMouse, "Esc", "keyboard_esc");
Add(InputActionId.Move, InputDeviceKind.KeyboardMouse, "WASD");
Add(InputActionId.Sprint, InputDeviceKind.KeyboardMouse, "L Shift", "keyboard_shift");
Add(InputActionId.Interact, InputDeviceKind.KeyboardMouse, "E", "keyboard_e");
}
private void PopulateGamepad()
{
Add(InputActionId.Pause, InputDeviceKind.Gamepad, "Menu", "gamepad_menu");
Add(InputActionId.Navigate, InputDeviceKind.Gamepad, "L Stick", "gamepad_full_ls");
Add(InputActionId.Confirm, InputDeviceKind.Gamepad, "A", "gamepad_a");
Add(InputActionId.Cancel, InputDeviceKind.Gamepad, "B", "gamepad_b");
Add(InputActionId.Move, InputDeviceKind.Gamepad, "L Stick", "gamepad_full_ls");
Add(InputActionId.Sprint, InputDeviceKind.Gamepad, "L3", "gamepad_press_ls");
Add(InputActionId.Interact, InputDeviceKind.Gamepad, "A", "gamepad_a");
}
private void PopulateTouch()
{
Add(InputActionId.Pause, InputDeviceKind.Touch, "Pause");
Add(InputActionId.Navigate, InputDeviceKind.Touch, "Swipe");
Add(InputActionId.Confirm, InputDeviceKind.Touch, "Tap");
Add(InputActionId.Cancel, InputDeviceKind.Touch, "Back");
Add(InputActionId.Move, InputDeviceKind.Touch, "Joystick");
Add(InputActionId.Interact, InputDeviceKind.Touch, "Tap");
}
private void Add(InputActionId actionId, InputDeviceKind deviceKind, string textLabel, string spriteName = null)
{
_entries[(actionId, deviceKind)] = new InputPrompt(textLabel, spriteName);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
{
"reference": "GUID:436e23dbdc31e7d4fb5c3f804548b2df"
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4b8c810f3fb3fd948afcf17809e9217c
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: