Added input management, added lables to input handler entries, added filtering by label

This commit is contained in:
Perry 2025-11-03 09:37:46 +01:00
parent 7366d5cb22
commit a2b524ee04
5 changed files with 180 additions and 0 deletions

View file

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKeys_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa69b121f85054691b2a8d28643003c23136600_003F08_003F1a47fbcb_003FKeys_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View file

@ -2,6 +2,7 @@ using System;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using MonoGameLibrary.Input;
namespace MonoGameLibrary; namespace MonoGameLibrary;
@ -88,4 +89,9 @@ public class Core : Game
// Create the sprite batch instance. // Create the sprite batch instance.
spriteBatch = new SpriteBatch(graphicsDevice); spriteBatch = new SpriteBatch(graphicsDevice);
} }
protected override void Update(GameTime gameTime) {
InputManager.NextInputCycle();
base.Update(gameTime);
}
} }

View file

@ -0,0 +1,6 @@
namespace MonoGameLibrary.Input;
public class InputListenerHook(bool enabled, bool oneTimeOnly = false) {
public bool Enabled { get; set; } = enabled;
public bool RemoveOnNextTrigger { get; set; } = oneTimeOnly;
}

View file

@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;
namespace MonoGameLibrary.Input;
public static class InputManager {
private static Dictionary<Keys, List<InputEntry>> keyPressListeners = new();
private static Dictionary<Keys, List<InputEntry>> keyHoldListeners = new(); // The bool is for enabling/disabling that method
private static Dictionary<Keys, List<InputEntry>> keyReleaseListeners = new();
private static Dictionary<string, InputEntry> labelDict = new();
private static KeyboardState oldState = Keyboard.GetState();
private static KeyboardState newState;
// public static ConcurrentQueue<Action> inputEventQueue = new();
public delegate void KeypressHandler(KeyboardState state);
private static int nextUnnamedId = 0;
// public static void StartListening() {
// processInputThread.Start();
// Console.WriteLine("Started listening for input");
// }
public static void NextInputCycle() {
Queue<KeypressHandler> inputEventQueue = new();
// Read
newState = Keyboard.GetState();
Keys[] pressed = newState.GetPressedKeys();
Keys[] pressedLastFrame = oldState.GetPressedKeys();
Array.ForEach(pressed, key => {
if (oldState.IsKeyUp(key)){
if (keyPressListeners.TryGetValue(key, out var pressHandlers)){
pressHandlers.ForEach(t => {
if (t.Enabled){
inputEventQueue.Enqueue(t.Action);
}
if (t.Hook.RemoveOnNextTrigger){
pressHandlers.Remove(t);
}
});
}
}
if (keyHoldListeners.TryGetValue(key, out var holdHandlers)){
holdHandlers.ForEach(t => {
if (t.Enabled){
inputEventQueue.Enqueue(t.Action);
}
if (t.Hook.RemoveOnNextTrigger){
holdHandlers.Remove(t);
}
});
}
});
Array.ForEach(pressedLastFrame, key => {
if (!keyReleaseListeners.ContainsKey(key)){
return;
}
if (newState.IsKeyUp(key)){
if (keyReleaseListeners.TryGetValue(key, out var releaseHandlers))
releaseHandlers.ForEach(t => {
if (t.Enabled){
inputEventQueue.Enqueue(t.Action);
}
if (t.Hook.RemoveOnNextTrigger){
releaseHandlers.Remove(t);
}
});
}
});
oldState = newState;
// Execute
foreach (KeypressHandler handler in inputEventQueue){
handler(newState);
}
}
// private static Thread processInputThread = new(() => {
//
// });
public static void AddListener(string label, Keys key, KeypressHandler action, InputTiming timing, InputListenerHook hook) {
if (label.StartsWith("!")){
throw new ArgumentException("Label cannot start with !");
}
InputEntry entry = new (label, key, action, timing, hook);
_addListener(entry);
}
public static void AddListener(Keys key, KeypressHandler action, InputTiming timing, InputListenerHook hook) {
InputEntry entry = new ($"!{Enum.GetName(typeof(Keys), key)}Handler{nextUnnamedId++}", key, action, timing, hook);
_addListener(entry);
}
private static void _addListener(InputEntry entry) {
Dictionary<Keys, List<InputEntry>> workingDict;
switch (entry.Timing){
case InputTiming.PRESS:
workingDict = keyPressListeners;
break;
case InputTiming.HOLD:
workingDict = keyHoldListeners;
break;
case InputTiming.RELEASE:
workingDict = keyReleaseListeners;
break;
default:
throw new ArgumentOutOfRangeException(nameof(entry.Timing), entry.Timing, null);
}
if (!workingDict.ContainsKey(entry.Key)){
workingDict.Add(entry.Key, new());
}
workingDict[entry.Key].Add(entry);
labelDict.Add(entry.Label, entry);
}
public static InputEntry GetEntry(string label) {
return labelDict[label];
}
public class InputEntry {
public string Label;
public Keys Key;
public KeypressHandler Action;
public InputTiming Timing;
public InputListenerHook Hook;
public bool Enabled => Hook.Enabled;
public InputEntry(string label, Keys key, KeypressHandler action, InputTiming timing, InputListenerHook hook) {
Key = key;
Action = action;
Timing = timing;
Hook = hook;
Label = label;
}
}
// private static List<InputEntry> inputQueue = new();
}

View file

@ -0,0 +1,7 @@
namespace MonoGameLibrary.Input;
public enum InputTiming {
PRESS,
HOLD,
RELEASE
}