chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

View 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
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e6434706d2660f5438b88ed83d273edb
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using UnityEngine;
namespace BaseGames.Input
{
/// <summary>
/// 帧级输入缓冲。持续 _bufferDuration 秒,允许玩家提前输入跳跃/攻击/冲刺。
/// 须与 PlayerController 在同一 GameObject 上。
/// </summary>
public class InputBuffer : MonoBehaviour
{
[SerializeField] 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;
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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 323fa3d8339022e4bbd37c12332f151a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,149 @@
using System;
using UnityEngine;
using UnityEngine.InputSystem;
using BaseGames.Core.Events;
namespace BaseGames.Input
{
[CreateAssetMenu(menuName = "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 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 OnEnable()
{
if (_inputActions == null) return;
_gameplay = _inputActions.FindActionMap("Gameplay", throwIfNotFound: false);
_ui = _inputActions.FindActionMap("UI", throwIfNotFound: false);
BindActions();
}
private void OnDisable()
{
_gameplay?.Disable();
_ui?.Disable();
_isBound = false;
}
// ── Action Map Switching ──────────────────────────────────────────────
public void EnableGameplayInput() { _ui?.Disable(); _gameplay?.Enable(); }
public void EnableUIInput() { _gameplay?.Disable(); _ui?.Enable(); }
public void DisableAllInput() { _gameplay?.Disable(); _ui?.Disable(); }
// ── Binding ───────────────────────────────────────────────────────────
private void BindActions()
{
if (_gameplay == null || _isBound) 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, "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();
}
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();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3945955c08d2670458e14d41e1236946
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
// Placeholder to prevent asmdef-no-scripts warning.
namespace BaseGames.Input { }

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ead9c6110166b424ab45c55ce99801c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: