摄像机区域的架构改动
This commit is contained in:
17
Assets/_Game/Scripts/Input/BaseGames.Input.asmdef
Normal file
17
Assets/_Game/Scripts/Input/BaseGames.Input.asmdef
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "BaseGames.Input",
|
||||
"rootNamespace": "BaseGames.Input",
|
||||
"references": [
|
||||
"BaseGames.Core.Events",
|
||||
"Unity.InputSystem"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
7
Assets/_Game/Scripts/Input/BaseGames.Input.asmdef.meta
Normal file
7
Assets/_Game/Scripts/Input/BaseGames.Input.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6434706d2660f5438b88ed83d273edb
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Assets/_Game/Scripts/Input/ConflictDetector.cs
Normal file
46
Assets/_Game/Scripts/Input/ConflictDetector.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace BaseGames.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// 输入冲突检测器(架构 04_InputModule §6)。
|
||||
/// 扫描给定 Action 集合,返回所有绑定了相同按键路径的 Action 名称。
|
||||
/// 挂载在与 RebindPanel 同一 GameObject 上(或作为子组件)。
|
||||
/// </summary>
|
||||
public class ConflictDetector : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回存在冲突的 Action 名称集合。
|
||||
/// 两个 Action 绑定了同一 effectivePath → 互为冲突。
|
||||
/// </summary>
|
||||
/// <param name="actions">待扫描的 Action 序列(通常来自 Gameplay Map)。</param>
|
||||
public HashSet<string> FindConflicts(IEnumerable<InputAction> actions)
|
||||
{
|
||||
var pathToActions = new Dictionary<string, List<string>>();
|
||||
|
||||
foreach (var action in actions)
|
||||
{
|
||||
foreach (var binding in action.bindings)
|
||||
{
|
||||
// 跳过复合绑定父项(如 WASD 组合中的 "2DVector")
|
||||
if (binding.isComposite || string.IsNullOrEmpty(binding.effectivePath))
|
||||
continue;
|
||||
|
||||
if (!pathToActions.TryGetValue(binding.effectivePath, out var list))
|
||||
pathToActions[binding.effectivePath] = list = new List<string>();
|
||||
list.Add(action.name);
|
||||
}
|
||||
}
|
||||
|
||||
var conflicted = new HashSet<string>();
|
||||
foreach (var kv in pathToActions)
|
||||
if (kv.Value.Count > 1)
|
||||
foreach (var name in kv.Value)
|
||||
conflicted.Add(name);
|
||||
|
||||
return conflicted;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Input/ConflictDetector.cs.meta
Normal file
11
Assets/_Game/Scripts/Input/ConflictDetector.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9125920fa11ef0b4abdeeb7a623cfdb6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
80
Assets/_Game/Scripts/Input/InputBuffer.cs
Normal file
80
Assets/_Game/Scripts/Input/InputBuffer.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// 帧级输入缓冲。持续 _bufferDuration 秒,允许玩家提前输入跳跃/攻击/冲刺。
|
||||
/// 须与 PlayerController 在同一 GameObject 上。
|
||||
/// </summary>
|
||||
public class InputBuffer : MonoBehaviour
|
||||
{
|
||||
// _inputReader 由 PlayerController.Awake 注入,无需在 Inspector 单独配置。
|
||||
private InputReaderSO _inputReader;
|
||||
[SerializeField] private float _jumpBufferDuration = 0.15f;
|
||||
[SerializeField] private float _attackBufferDuration = 0.12f;
|
||||
[SerializeField] private float _dashBufferDuration = 0.10f;
|
||||
|
||||
private float _jumpBuffer;
|
||||
private float _attackBuffer;
|
||||
private float _dashBuffer;
|
||||
|
||||
// ── Named handlers to allow proper unsubscription ─────────────────────
|
||||
private void HandleJumpStarted() => _jumpBuffer = _jumpBufferDuration;
|
||||
private void HandleAttackStarted() => _attackBuffer = _attackBufferDuration;
|
||||
private void HandleDashStarted() => _dashBuffer = _dashBufferDuration;
|
||||
|
||||
/// <summary>由 PlayerController 在 Awake 中注入 InputReader,无需在 Inspector 单独指定。</summary>
|
||||
public void Init(InputReaderSO reader)
|
||||
{
|
||||
_inputReader = reader;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_inputReader == null) return;
|
||||
_inputReader.JumpStartedEvent += HandleJumpStarted;
|
||||
_inputReader.AttackEvent += HandleAttackStarted;
|
||||
_inputReader.DashEvent += HandleDashStarted;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_inputReader == null) return;
|
||||
_inputReader.JumpStartedEvent -= HandleJumpStarted;
|
||||
_inputReader.AttackEvent -= HandleAttackStarted;
|
||||
_inputReader.DashEvent -= HandleDashStarted;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
float dt = Time.deltaTime;
|
||||
_jumpBuffer = Mathf.Max(0f, _jumpBuffer - dt);
|
||||
_attackBuffer = Mathf.Max(0f, _attackBuffer - dt);
|
||||
_dashBuffer = Mathf.Max(0f, _dashBuffer - dt);
|
||||
}
|
||||
|
||||
/// <summary>消耗跳跃缓冲(读取并清空)。</summary>
|
||||
public bool ConsumeJump()
|
||||
{
|
||||
if (_jumpBuffer <= 0f) return false;
|
||||
_jumpBuffer = 0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>消耗攻击缓冲(读取并清空)。</summary>
|
||||
public bool ConsumeAttack()
|
||||
{
|
||||
if (_attackBuffer <= 0f) return false;
|
||||
_attackBuffer = 0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>消耗冲刺缓冲(读取并清空)。</summary>
|
||||
public bool ConsumeDash()
|
||||
{
|
||||
if (_dashBuffer <= 0f) return false;
|
||||
_dashBuffer = 0f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Input/InputBuffer.cs.meta
Normal file
11
Assets/_Game/Scripts/Input/InputBuffer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 323fa3d8339022e4bbd37c12332f151a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
33
Assets/_Game/Scripts/Input/InputReaderBootstrap.cs
Normal file
33
Assets/_Game/Scripts/Input/InputReaderBootstrap.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// 在运行时启用 InputReaderSO 的 ActionMap。
|
||||
/// 挂在 Persistent 场景的 InputReaderHolder 上。
|
||||
/// _inputReader 必须在 Inspector 中赋值,框架不提供运行时自动查找回退。
|
||||
/// </summary>
|
||||
public sealed class InputReaderBootstrap : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private InputReaderSO _inputReader;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Debug.Assert(_inputReader != null,
|
||||
"[InputReaderBootstrap] _inputReader 未在 Inspector 中赋值!请在 Persistent 场景的 InputReaderHolder 上手动指定 InputReaderSO 资产。",
|
||||
this);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_inputReader == null) return;
|
||||
_inputReader.LoadBindingOverrides(); // 从 PlayerPrefs 恢复用户自定义绑定
|
||||
_inputReader.EnableGameplayInput();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_inputReader?.DisableAllInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Input/InputReaderBootstrap.cs.meta
Normal file
11
Assets/_Game/Scripts/Input/InputReaderBootstrap.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec65fc21755ac2943ba5e0b526601b31
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
296
Assets/_Game/Scripts/Input/InputReaderSO.cs
Normal file
296
Assets/_Game/Scripts/Input/InputReaderSO.cs
Normal file
@@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Input
|
||||
{
|
||||
[CreateAssetMenu(menuName = "BaseGames/Input/InputReader")]
|
||||
public class InputReaderSO : ScriptableObject
|
||||
{
|
||||
[SerializeField] private InputActionAsset _inputActions;
|
||||
[SerializeField] private VoidEventChannelSO _onPauseRequested;
|
||||
|
||||
// ── Gameplay Events ───────────────────────────────────────────────────
|
||||
public event Action<Vector2> MoveEvent;
|
||||
public event Action JumpStartedEvent;
|
||||
public event Action JumpCancelledEvent;
|
||||
public event Action AttackEvent;
|
||||
public event Action DownAttackEvent;
|
||||
public event Action UpAttackEvent;
|
||||
public event Action ParryEvent;
|
||||
public event Action DashEvent;
|
||||
public event Action UseSpringEvent;
|
||||
public event Action SwitchSkyFormEvent;
|
||||
public event Action SwitchEarthFormEvent;
|
||||
public event Action SwitchDeathFormEvent;
|
||||
public event Action SoulSkillEvent;
|
||||
public event Action SpiritSkill1StartedEvent;
|
||||
public event Action SpiritSkill1CancelledEvent;
|
||||
public event Action SpiritSkill2StartedEvent;
|
||||
public event Action SpiritSkill2CancelledEvent;
|
||||
public event Action SpellCastEvent;
|
||||
public event Action InteractEvent;
|
||||
|
||||
// ── UI Events ─────────────────────────────────────────────────────────
|
||||
public event Action PauseEvent;
|
||||
public event Action<Vector2> NavigateEvent;
|
||||
public event Action SubmitEvent;
|
||||
public event Action CancelEvent;
|
||||
public event Action<Vector2> PointEvent;
|
||||
|
||||
// ── Polling ───────────────────────────────────────────────────────────
|
||||
public Vector2 MoveInput { get; private set; }
|
||||
|
||||
// ── Runtime state ─────────────────────────────────────────────────────
|
||||
private InputActionMap _gameplay;
|
||||
private InputActionMap _ui;
|
||||
private bool _isBound;
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
if (_inputActions == null)
|
||||
{
|
||||
Debug.LogError("[InputReaderSO] _inputActions is NULL! Asset not assigned in inspector?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Always disable first so the Input System tears down stale state,
|
||||
// then re-enable to create a fresh state for this Play session.
|
||||
_inputActions.Disable();
|
||||
_inputActions.Enable();
|
||||
|
||||
_gameplay = _inputActions.FindActionMap("Gameplay", throwIfNotFound: false);
|
||||
if (_gameplay == null)
|
||||
Debug.LogError("[InputReaderSO] Could not find 'Gameplay' action map in asset!");
|
||||
|
||||
_ui = _inputActions.FindActionMap("UI", throwIfNotFound: false);
|
||||
if (_ui == null)
|
||||
Debug.LogWarning("[InputReaderSO] Could not find 'UI' action map in asset!");
|
||||
|
||||
if (_gameplay != null && !_isBound)
|
||||
{
|
||||
|
||||
BindActions();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Debug.Assert(_onPauseRequested != null,
|
||||
"[InputReaderSO] _onPauseRequested 未赋值,请在 Inspector 中指定 EVT_PauseRequested。", this);
|
||||
// Reset private state on every OnEnable so stale ScriptableObject
|
||||
// references from a previous Play session don't cause
|
||||
// 'Map must be contained in state' errors.
|
||||
_gameplay = null;
|
||||
_ui = null;
|
||||
_isBound = false;
|
||||
EnsureInitialized();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_gameplay?.Disable();
|
||||
_ui?.Disable();
|
||||
_isBound = false;
|
||||
}
|
||||
|
||||
// ── Action Map Switching ──────────────────────────────────────────────
|
||||
public void EnableGameplayInput()
|
||||
{
|
||||
// Disable UI map if it's active
|
||||
if (_ui != null && _ui.enabled)
|
||||
{
|
||||
|
||||
_ui.Disable();
|
||||
}
|
||||
|
||||
// Ensure gameplay map is enabled
|
||||
if (_gameplay != null && !_gameplay.enabled)
|
||||
{
|
||||
|
||||
_gameplay.Enable();
|
||||
}
|
||||
else if (_gameplay == null)
|
||||
{
|
||||
Debug.LogError("[InputReaderSO.EnableGameplayInput] _gameplay is NULL!");
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableUIInput()
|
||||
{
|
||||
// Disable gameplay map if it's active
|
||||
if (_gameplay != null && _gameplay.enabled)
|
||||
{
|
||||
|
||||
_gameplay.Disable();
|
||||
}
|
||||
|
||||
// Ensure UI map is enabled
|
||||
if (_ui != null && !_ui.enabled)
|
||||
{
|
||||
|
||||
_ui.Enable();
|
||||
}
|
||||
else if (_ui == null)
|
||||
{
|
||||
Debug.LogWarning("[InputReaderSO.EnableUIInput] _ui is NULL!");
|
||||
}
|
||||
}
|
||||
|
||||
public void DisableAllInput() { _gameplay?.Disable(); _ui?.Disable(); }
|
||||
|
||||
// ── Binding ───────────────────────────────────────────────────────────
|
||||
private void BindActions()
|
||||
{
|
||||
if (_gameplay == null)
|
||||
{
|
||||
Debug.LogWarning("[InputReaderSO.BindActions] Skipped: _gameplay is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BindPerformed(_gameplay, "Move", ctx =>
|
||||
{
|
||||
MoveInput = ctx.ReadValue<Vector2>();
|
||||
MoveEvent?.Invoke(MoveInput);
|
||||
});
|
||||
BindCanceled(_gameplay, "Move", _ =>
|
||||
{
|
||||
MoveInput = Vector2.zero;
|
||||
MoveEvent?.Invoke(Vector2.zero);
|
||||
});
|
||||
|
||||
BindStarted(_gameplay, "Jump", () => JumpStartedEvent?.Invoke());
|
||||
BindCanceled(_gameplay, "Jump", () => JumpCancelledEvent?.Invoke());
|
||||
BindStarted(_gameplay, "Attack", () => AttackEvent?.Invoke());
|
||||
BindStarted(_gameplay, "DownAttack", () => DownAttackEvent?.Invoke());
|
||||
BindStarted(_gameplay, "UpAttack", () => UpAttackEvent?.Invoke());
|
||||
BindStarted(_gameplay, "Parry", () => ParryEvent?.Invoke());
|
||||
BindStarted(_gameplay, "Dash", () => DashEvent?.Invoke());
|
||||
BindStarted(_gameplay, "UseSpring", () => UseSpringEvent?.Invoke());
|
||||
BindStarted(_gameplay, "SwitchSkyForm", () => SwitchSkyFormEvent?.Invoke());
|
||||
BindStarted(_gameplay, "SwitchEarthForm", () => SwitchEarthFormEvent?.Invoke());
|
||||
BindStarted(_gameplay, "SwitchDeathForm", () => SwitchDeathFormEvent?.Invoke());
|
||||
BindStarted(_gameplay, "SoulSkill", () => SoulSkillEvent?.Invoke());
|
||||
BindStarted(_gameplay, "SpiritSkill1", () => SpiritSkill1StartedEvent?.Invoke());
|
||||
BindCanceled(_gameplay, "SpiritSkill1", () => SpiritSkill1CancelledEvent?.Invoke());
|
||||
BindStarted(_gameplay, "SpiritSkill2", () => SpiritSkill2StartedEvent?.Invoke());
|
||||
BindCanceled(_gameplay, "SpiritSkill2", () => SpiritSkill2CancelledEvent?.Invoke());
|
||||
BindStarted(_gameplay, "Spell", () => SpellCastEvent?.Invoke());
|
||||
BindStarted(_gameplay, "Interact", () => InteractEvent?.Invoke());
|
||||
|
||||
|
||||
BindStarted(_gameplay, "Pause", HandlePause);
|
||||
|
||||
if (_ui != null)
|
||||
{
|
||||
BindPerformed(_ui, "Navigate", ctx => NavigateEvent?.Invoke(ctx.ReadValue<Vector2>()));
|
||||
BindCanceled(_ui, "Navigate", _ => NavigateEvent?.Invoke(Vector2.zero));
|
||||
BindStarted(_ui, "Submit", () => SubmitEvent?.Invoke());
|
||||
BindStarted(_ui, "Cancel", () => CancelEvent?.Invoke());
|
||||
BindStarted(_ui, "Pause", HandlePause);
|
||||
BindPerformed(_ui, "Point", ctx => PointEvent?.Invoke(ctx.ReadValue<Vector2>()));
|
||||
}
|
||||
|
||||
_isBound = true;
|
||||
}
|
||||
|
||||
private void HandlePause()
|
||||
{
|
||||
PauseEvent?.Invoke();
|
||||
_onPauseRequested?.Raise();
|
||||
}
|
||||
|
||||
private static void BindStarted(InputActionMap map, string name, Action callback)
|
||||
{
|
||||
var action = map.FindAction(name, throwIfNotFound: false);
|
||||
if (action != null)
|
||||
{
|
||||
action.started += _ => callback();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[BindStarted] Action '{name}' not found in map");
|
||||
}
|
||||
}
|
||||
|
||||
private static void BindPerformed(InputActionMap map, string name,
|
||||
Action<InputAction.CallbackContext> callback)
|
||||
{
|
||||
var action = map.FindAction(name, throwIfNotFound: false);
|
||||
if (action != null) action.performed += callback;
|
||||
}
|
||||
|
||||
private static void BindCanceled(InputActionMap map, string name,
|
||||
Action<InputAction.CallbackContext> callback)
|
||||
{
|
||||
var action = map.FindAction(name, throwIfNotFound: false);
|
||||
if (action != null) action.canceled += callback;
|
||||
}
|
||||
|
||||
private static void BindCanceled(InputActionMap map, string name, Action callback)
|
||||
{
|
||||
var action = map.FindAction(name, throwIfNotFound: false);
|
||||
if (action != null) action.canceled += _ => callback();
|
||||
}
|
||||
|
||||
// ── Rebinding API (P4-3) ──────────────────────────────────────────────
|
||||
|
||||
private const string PrefKey = "InputBindings";
|
||||
|
||||
/// <summary>查找单个 Action(供 RebindActionRow 显示当前绑定路径)。</summary>
|
||||
public InputAction FindAction(string name)
|
||||
=> _inputActions?.FindAction(name);
|
||||
|
||||
/// <summary>返回 Gameplay Map 所有 Action(供 ConflictDetector 扫描冲突)。</summary>
|
||||
public IEnumerable<InputAction> GetAllActionMap()
|
||||
{
|
||||
var map = _inputActions?.FindActionMap("Gameplay");
|
||||
return map != null ? (IEnumerable<InputAction>)map.actions : Array.Empty<InputAction>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动交互式重绑定(Unity InputSystem RebindingOperation)。
|
||||
/// 调用方无需持有返回值;完成或取消后自动 Dispose。
|
||||
/// </summary>
|
||||
public void StartRebinding(
|
||||
string actionName, int bindingIndex, Action onComplete, Action onCancel)
|
||||
{
|
||||
var action = _inputActions?.FindAction(actionName);
|
||||
if (action == null) { onCancel?.Invoke(); return; }
|
||||
|
||||
action.Disable();
|
||||
action.PerformInteractiveRebinding(bindingIndex)
|
||||
.OnComplete(op => { op.Dispose(); action.Enable(); onComplete?.Invoke(); })
|
||||
.OnCancel(op => { op.Dispose(); action.Enable(); onCancel?.Invoke(); })
|
||||
.Start();
|
||||
}
|
||||
|
||||
/// <summary>将当前绑定覆盖序列化为 JSON,存入 PlayerPrefs(key = "InputBindings")。</summary>
|
||||
public void SaveBindingOverrides()
|
||||
{
|
||||
if (_inputActions == null) return;
|
||||
PlayerPrefs.SetString(PrefKey, _inputActions.SaveBindingOverridesAsJson());
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
/// <summary>从 PlayerPrefs 加载并应用绑定覆盖(首次启动时无操作)。</summary>
|
||||
public void LoadBindingOverrides()
|
||||
{
|
||||
if (_inputActions != null && PlayerPrefs.HasKey(PrefKey))
|
||||
_inputActions.LoadBindingOverridesFromJson(PlayerPrefs.GetString(PrefKey));
|
||||
}
|
||||
|
||||
/// <summary>重置所有绑定为默认值并清除 PlayerPrefs 记录。</summary>
|
||||
public void ResetBindings()
|
||||
{
|
||||
_inputActions?.RemoveAllBindingOverrides();
|
||||
PlayerPrefs.DeleteKey(PrefKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Input/InputReaderSO.cs.meta
Normal file
11
Assets/_Game/Scripts/Input/InputReaderSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3945955c08d2670458e14d41e1236946
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user