diff --git a/GFramework.Core/events/ArchitectureEvents.cs b/GFramework.Core/events/ArchitectureEvents.cs index 53f0070..251e2a2 100644 --- a/GFramework.Core/events/ArchitectureEvents.cs +++ b/GFramework.Core/events/ArchitectureEvents.cs @@ -7,5 +7,5 @@ public static class ArchitectureEvents /// 架构初始化完成事件 /// 在所有 Model / System Init 执行完毕后派发 /// - public readonly struct ArchitectureInitializedEvent { } + public readonly struct ArchitectureInitializedEvent; } \ No newline at end of file diff --git a/GFramework.Game/input/IGameInputEvent.cs b/GFramework.Game/input/IGameInputEvent.cs new file mode 100644 index 0000000..2a4d79e --- /dev/null +++ b/GFramework.Game/input/IGameInputEvent.cs @@ -0,0 +1,6 @@ +namespace GFramework.Game.input; + +/// +/// 游戏输入事件接口 +/// +public interface IGameInputEvent; diff --git a/GFramework.Game/input/IInputContext.cs b/GFramework.Game/input/IInputContext.cs new file mode 100644 index 0000000..259a475 --- /dev/null +++ b/GFramework.Game/input/IInputContext.cs @@ -0,0 +1,14 @@ +namespace GFramework.Game.input; + +/// +/// 输入上下文接口,用于处理游戏中的输入事件 +/// +public interface IInputContext +{ + /// + /// 处理游戏输入事件 + /// + /// 要处理的游戏输入事件 + /// 返回 true 表示输入被吃掉,不再向下传播;返回 false 表示输入未被处理,可以继续传播给其他处理器 + bool Handle(IGameInputEvent input); +} diff --git a/GFramework.Game/input/InputContextStack.cs b/GFramework.Game/input/InputContextStack.cs new file mode 100644 index 0000000..358dde4 --- /dev/null +++ b/GFramework.Game/input/InputContextStack.cs @@ -0,0 +1,33 @@ +namespace GFramework.Game.input; + +/// +/// 输入上下文堆栈管理器,用于管理多个输入上下文的堆栈结构 +/// +public class InputContextStack +{ + private readonly Stack _stack = new(); + + /// + /// 将指定的输入上下文压入堆栈顶部 + /// + /// 要压入堆栈的输入上下文对象 + public void Push(IInputContext context) => _stack.Push(context); + + /// + /// 弹出堆栈顶部的输入上下文 + /// + public void Pop() => _stack.Pop(); + + /// + /// 处理游戏输入事件,遍历堆栈中的所有上下文直到找到能够处理该事件的上下文 + /// + /// 要处理的游戏输入事件 + /// 如果堆栈中任意一个上下文成功处理了输入事件则返回true,否则返回false + public bool Handle(IGameInputEvent input) + { + // 遍历堆栈中的所有上下文,调用其Handle方法处理输入事件 + // Any方法会在第一个返回true的上下文处停止遍历 + return _stack.Any(ctx => ctx.Handle(input)); + } +} + diff --git a/GFramework.Game/input/InputSystem.cs b/GFramework.Game/input/InputSystem.cs new file mode 100644 index 0000000..796dfc6 --- /dev/null +++ b/GFramework.Game/input/InputSystem.cs @@ -0,0 +1,46 @@ +using GFramework.Core.extensions; +using GFramework.Core.system; + +namespace GFramework.Game.input; + +/// +/// 输入系统类,负责管理输入上下文堆栈并处理游戏输入事件 +/// +public class InputSystem : AbstractSystem +{ + private readonly InputContextStack _contextStack = new(); + + /// + /// 将输入上下文推入上下文堆栈 + /// + /// 要推入的输入上下文对象 + public void PushContext(IInputContext ctx) + => _contextStack.Push(ctx); + + /// + /// 从上下文堆栈中弹出顶层输入上下文 + /// + public void PopContext() + => _contextStack.Pop(); + + /// + /// 处理游戏输入事件,首先尝试通过上下文堆栈处理,如果未被处理则发送事件 + /// + /// 要处理的游戏输入事件 + public void Handle(IGameInputEvent input) + { + // 尝试通过上下文堆栈处理输入事件 + if (_contextStack.Handle(input)) + return; + // 如果上下文堆栈未能处理,则发送该事件 + this.SendEvent(input); + } + + /// + /// 系统初始化方法,在系统启动时调用 + /// + protected override void OnInit() + { + + } +} diff --git a/GFramework.Godot/system/AbstractInputSystem.cs b/GFramework.Godot/system/AbstractInputSystem.cs deleted file mode 100644 index cd70213..0000000 --- a/GFramework.Godot/system/AbstractInputSystem.cs +++ /dev/null @@ -1,115 +0,0 @@ -using GFramework.Game.assets; - -namespace GFramework.Godot.system; - -/// -/// 抽象输入系统基类,实现了IInputSystem接口的基本功能 -/// -public abstract class AbstractInputSystem : AbstractAssetCatalogSystem, IInputSystem -{ - // 存储动作名称与按键绑定的映射关系 - private readonly Dictionary _actionBindings = new(); - - // 存储动作名称与当前状态的映射关系 - private readonly Dictionary _actionStates = new(); - - // 存储动作名称与上一帧状态的映射关系 - private readonly Dictionary _previousActionStates = new(); - - /// - public virtual void SetBinding(string actionName, string keyCode) - { - _actionBindings[actionName] = keyCode; - } - - /// - public virtual string GetBinding(string actionName) - { - return _actionBindings.TryGetValue(actionName, out var binding) ? binding : string.Empty; - } - - /// - public virtual bool IsActionPressed(string actionName) - { - return _actionStates.TryGetValue(actionName, out var state) && state; - } - - /// - public virtual bool IsActionJustPressed(string actionName) - { - var current = _actionStates.TryGetValue(actionName, out var currentState) && currentState; - var previous = _previousActionStates.TryGetValue(actionName, out var previousState) && previousState; - return current && !previous; - } - - /// - public virtual bool IsActionJustReleased(string actionName) - { - var current = _actionStates.TryGetValue(actionName, out var currentState) && currentState; - var previous = _previousActionStates.TryGetValue(actionName, out var previousState) && previousState; - return !current && previous; - } - - /// - public virtual void AddAction(string actionName, string defaultKeyCode) - { - if (!_actionBindings.ContainsKey(actionName)) - { - _actionBindings[actionName] = defaultKeyCode; - _actionStates[actionName] = false; - _previousActionStates[actionName] = false; - } - } - - /// - public virtual void RemoveAction(string actionName) - { - _actionBindings.Remove(actionName); - _actionStates.Remove(actionName); - _previousActionStates.Remove(actionName); - } - - /// - public abstract void SaveConfiguration(); - - /// - public abstract void LoadConfiguration(); - - /// - public abstract void Update(double delta); - - /// - /// 更新输入状态,在每一帧调用 - /// - protected virtual void UpdateInputStates() - { - // 保存当前状态作为下一帧的前一状态 - foreach (var kvp in _actionStates) - { - _previousActionStates[kvp.Key] = kvp.Value; - } - - // 更新当前状态 - foreach (var kvp in _actionBindings) - { - var actionName = kvp.Key; - var keyCode = kvp.Value; - - // 这里需要根据具体平台和引擎实现具体的按键检测逻辑 - // 当前只是一个占位实现 - _actionStates[actionName] = CheckKeyPressed(keyCode); - } - } - - /// - /// 检查特定按键是否被按下 - /// - /// 按键码 - /// 按键是否被按下 - protected virtual bool CheckKeyPressed(string keyCode) - { - // 这里需要根据Godot引擎的具体实现来检查按键状态 - // 目前只是示例实现 - return false; - } -} \ No newline at end of file diff --git a/GFramework.Godot/system/GodotInputSystem.cs b/GFramework.Godot/system/GodotInputSystem.cs deleted file mode 100644 index 4e2cfb6..0000000 --- a/GFramework.Godot/system/GodotInputSystem.cs +++ /dev/null @@ -1,195 +0,0 @@ -// using Godot; -// -// namespace GFramework.Godot.system; -// -// /// -// /// Godot引擎专用的输入系统实现 -// /// -// public class GodotInputSystem : AbstractInputSystem -// { -// private InputMap _inputMap; -// private string _configPath; -// -// public override void Init() -// { -// base.Init(); -// -// _inputMap = new InputMap(); -// _configPath = "user://input_config.json"; -// -// // 添加一些默认的输入动作 -// AddDefaultActions(); -// -// // 尝试加载用户配置 -// LoadConfiguration(); -// } -// -// public override void Destroy() -// { -// // 保存配置 -// SaveConfiguration(); -// base.Destroy(); -// } -// -// /// -// /// 添加默认输入动作 -// /// -// private void AddDefaultActions() -// { -// _inputMap.AddAction(new InputAction("move_left", InputActionType.Axis, "Left")); -// _inputMap.AddAction(new InputAction("move_right", InputActionType.Axis, "Right")); -// _inputMap.AddAction(new InputAction("move_up", InputActionType.Axis, "Up")); -// _inputMap.AddAction(new InputAction("move_down", InputActionType.Axis, "Down")); -// _inputMap.AddAction(new InputAction("jump", InputActionType.Button, "Space")); -// _inputMap.AddAction(new InputAction("attack", InputActionType.Button, "LeftMouse")); -// _inputMap.AddAction(new InputAction("interact", InputActionType.Button, "E")); -// } -// -// /// -// public override void SaveConfiguration() -// { -// try -// { -// var configData = new Dictionary(); -// foreach (var action in _inputMap.GetAllActions()) -// { -// configData[action.Name] = action.CurrentBindings; -// } -// -// var json = Json.Stringify(configData); -// File.WriteAllText(ProjectSettings.GlobalizePath(_configPath), json); -// } -// catch (Exception ex) -// { -// GD.PrintErr($"Failed to save input configuration: {ex.Message}"); -// } -// } -// -// /// -// public override void LoadConfiguration() -// { -// try -// { -// if (!File.Exists(ProjectSettings.GlobalizePath(_configPath))) -// { -// // 配置文件不存在,使用默认配置 -// return; -// } -// -// var json = File.ReadAllText(ProjectSettings.GlobalizePath(_configPath)); -// var parsed = Json.ParseString(json); -// if (parsed is not Core.Godot.Collections.Dictionary dict) -// { -// GD.PrintErr("Invalid input configuration file"); -// return; -// } -// -// foreach (var key in dict.Keys) -// { -// var action = _inputMap.GetAction(key.AsString()); -// if (action != null && dict[key] is Core.Godot.Collections.Array array) -// { -// var bindings = new string[array.Count]; -// for (int i = 0; i < array.Count; i++) -// { -// bindings[i] = array[i].AsString(); -// } -// action.SetBindings(bindings); -// } -// } -// } -// catch (Exception ex) -// { -// GD.PrintErr($"Failed to load input configuration: {ex.Message}"); -// } -// } -// -// /// -// protected override bool CheckKeyPressed(string keyCode) -// { -// // 根据Godot的输入系统检查按键状态 -// return Input.IsPhysicalKeyPressed(GodotKeyMapper.GetKeyFromString(keyCode)) || -// Input.IsMouseButtonPressed(GodotKeyMapper.GetMouseButtonFromString(keyCode)); -// } -// -// /// -// public override void Update(double delta) -// { -// UpdateInputStates(); -// } -// -// protected override void RegisterAssets() -// { -// throw new NotImplementedException(); -// } -// } -// -// /// -// /// Godot按键映射辅助类 -// /// -// public static class GodotKeyMapper -// { -// private static readonly Dictionary KeyMap = new() -// { -// { "Left", Key.Left }, -// { "Right", Key.Right }, -// { "Up", Key.Up }, -// { "Down", Key.Down }, -// { "Space", Key.Space }, -// { "Enter", Key.Enter }, -// { "Escape", Key.Escape }, -// { "A", Key.A }, -// { "B", Key.B }, -// { "C", Key.C }, -// { "D", Key.D }, -// { "E", Key.E }, -// { "F", Key.F }, -// { "G", Key.G }, -// { "H", Key.H }, -// { "I", Key.I }, -// { "J", Key.J }, -// { "K", Key.K }, -// { "L", Key.L }, -// { "M", Key.M }, -// { "N", Key.N }, -// { "O", Key.O }, -// { "P", Key.P }, -// { "Q", Key.Q }, -// { "R", Key.R }, -// { "S", Key.S }, -// { "T", Key.T }, -// { "U", Key.U }, -// { "V", Key.V }, -// { "W", Key.W }, -// { "X", Key.X }, -// { "Y", Key.Y }, -// { "Z", Key.Z }, -// { "0", Key.Key0 }, -// { "1", Key.Key1 }, -// { "2", Key.Key2 }, -// { "3", Key.Key3 }, -// { "4", Key.Key4 }, -// { "5", Key.Key5 }, -// { "6", Key.Key6 }, -// { "7", Key.Key7 }, -// { "8", Key.Key8 }, -// { "9", Key.Key9 } -// }; -// -// private static readonly Dictionary MouseButtonMap = new() -// { -// { "LeftMouse", MouseButton.Left }, -// { "RightMouse", MouseButton.Right }, -// { "MiddleMouse", MouseButton.Middle } -// }; -// -// public static Key GetKeyFromString(string keyString) -// { -// return KeyMap.GetValueOrDefault(keyString, Key.None); -// } -// -// public static MouseButton GetMouseButtonFromString(string mouseButtonString) -// { -// return MouseButtonMap.GetValueOrDefault(mouseButtonString, MouseButton.None); -// } -// } \ No newline at end of file diff --git a/GFramework.Godot/system/IInputSystem.cs b/GFramework.Godot/system/IInputSystem.cs deleted file mode 100644 index 4e44727..0000000 --- a/GFramework.Godot/system/IInputSystem.cs +++ /dev/null @@ -1,73 +0,0 @@ -using GFramework.Core.system; - -namespace GFramework.Godot.system; - -/// -/// 输入系统接口,用于统一管理游戏中的输入操作和键位绑定 -/// -public interface IInputSystem : ISystem -{ - /// - /// 设置指定动作的按键绑定 - /// - /// 动作名称 - /// 按键码 - void SetBinding(string actionName, string keyCode); - - /// - /// 获取指定动作的按键绑定 - /// - /// 动作名称 - /// 绑定的按键码 - string GetBinding(string actionName); - - /// - /// 检查指定动作是否正在被执行 - /// - /// 动作名称 - /// 如果动作正在执行则返回true,否则返回false - bool IsActionPressed(string actionName); - - /// - /// 检查指定动作是否刚刚开始执行 - /// - /// 动作名称 - /// 如果动作刚刚开始执行则返回true,否则返回false - bool IsActionJustPressed(string actionName); - - /// - /// 检查指定动作是否刚刚停止执行 - /// - /// 动作名称 - /// 如果动作刚刚停止执行则返回true,否则返回false - bool IsActionJustReleased(string actionName); - - /// - /// 添加输入动作 - /// - /// 动作名称 - /// 默认按键绑定 - void AddAction(string actionName, string defaultKeyCode); - - /// - /// 移除输入动作 - /// - /// 动作名称 - void RemoveAction(string actionName); - - /// - /// 保存输入配置到文件 - /// - void SaveConfiguration(); - - /// - /// 从文件加载输入配置 - /// - void LoadConfiguration(); - - /// - /// 更新输入系统状态,应在每帧调用 - /// - /// 帧间隔时间 - void Update(double delta); -} \ No newline at end of file diff --git a/GFramework.Godot/system/InputAction.cs b/GFramework.Godot/system/InputAction.cs deleted file mode 100644 index f1cf934..0000000 --- a/GFramework.Godot/system/InputAction.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace GFramework.Godot.system; - -/// -/// 输入动作类,表示游戏中的一种输入行为 -/// -public class InputAction -{ - /// - /// 动作名称 - /// - public string Name { get; } - - /// - /// 动作类型 - /// - public InputActionType ActionType { get; } - - /// - /// 默认按键绑定 - /// - public string[] DefaultBindings { get; } - - /// - /// 当前按键绑定 - /// - public string[] CurrentBindings { get; private set; } - - /// - /// 构造函数 - /// - /// 动作名称 - /// 动作类型 - /// 默认按键绑定 - public InputAction(string name, InputActionType actionType, params string[] defaultBindings) - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - ActionType = actionType; - DefaultBindings = defaultBindings ?? throw new ArgumentNullException(nameof(defaultBindings)); - CurrentBindings = (string[])defaultBindings.Clone(); - } - - /// - /// 重置为默认按键绑定 - /// - public void ResetToDefault() - { - CurrentBindings = (string[])DefaultBindings.Clone(); - } - - /// - /// 设置新的按键绑定 - /// - /// 新的按键绑定 - public void SetBindings(params string[] bindings) - { - CurrentBindings = bindings ?? throw new ArgumentNullException(nameof(bindings)); - } -} - -/// -/// 输入动作类型枚举 -/// -public enum InputActionType -{ - /// - /// 按钮类型,如跳跃、射击等 - /// - Button, - - /// - /// 轴类型,如移动、视角控制等 - /// - Axis, - - /// - /// 向量类型,如二维移动控制 - /// - Vector -} \ No newline at end of file diff --git a/GFramework.Godot/system/InputMap.cs b/GFramework.Godot/system/InputMap.cs deleted file mode 100644 index 416656f..0000000 --- a/GFramework.Godot/system/InputMap.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace GFramework.Godot.system; - -/// -/// 输入映射类,管理所有的输入动作及其绑定 -/// -public class InputMap -{ - private readonly Dictionary _actions = new(); - - /// - /// 添加输入动作 - /// - /// 输入动作 - public void AddAction(InputAction action) - { - _actions[action.Name] = action; - } - - /// - /// 移除输入动作 - /// - /// 动作名称 - public void RemoveAction(string actionName) - { - _actions.Remove(actionName); - } - - /// - /// 获取输入动作 - /// - /// 动作名称 - /// 输入动作,如果不存在则返回null - public InputAction GetAction(string actionName) - { - return _actions.GetValueOrDefault(actionName); - } - - /// - /// 获取所有输入动作 - /// - /// 输入动作列表 - public IEnumerable GetAllActions() - { - return _actions.Values; - } - - /// - /// 检查是否存在指定名称的动作 - /// - /// 动作名称 - /// 存在返回true,否则返回false - public bool ContainsAction(string actionName) - { - return _actions.ContainsKey(actionName); - } - - /// - /// 根据按键查找绑定的动作 - /// - /// 按键码 - /// 绑定到该按键的所有动作 - public IEnumerable FindActionsByBinding(string keyCode) - { - return _actions.Values.Where(action => - action.CurrentBindings.Contains(keyCode)); - } -} \ No newline at end of file