From efc42f44b500e83e3f6ec8d8400e1497fb9804c3 Mon Sep 17 00:00:00 2001 From: Shaun Lawrence <17139988+bijington@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:26:17 +0000 Subject: [PATCH 1/8] Add initial controller support --- Doom.sln | 7 + ManagedDoom.Maui/Game/AggregatedUserInput.cs | 50 +++++ .../Game/GameControllerUserInput.cs | 70 +++++++ ManagedDoom.Maui/Game/MauiDoom.cs | 13 +- ManagedDoom.Maui/Game/MauiUserInput.cs | 2 - ManagedDoom.Maui/ManagedDoom.Maui.csproj | 1 + Orbit.Input/ButtonElement.cs | 33 ++++ Orbit.Input/GameController.cs | 124 +++++++++++++ Orbit.Input/GameControllerManager.cs | 31 ++++ Orbit.Input/Orbit.Input.csproj | 53 ++++++ .../Platforms/Android/GameController.cs | 175 ++++++++++++++++++ .../Android/GameControllerManager.cs | 81 ++++++++ .../Platforms/MacCatalyst/GameController.cs | 78 ++++++++ .../MacCatalyst/GameControllerManager.cs | 28 +++ Orbit.Input/Platforms/Tizen/PlatformClass1.cs | 9 + .../Platforms/Windows/GameController.cs | 45 +++++ .../Windows/GameControllerManager.cs | 22 +++ Orbit.Input/Platforms/iOS/GameController.cs | 78 ++++++++ .../Platforms/iOS/GameControllerManager.cs | 26 +++ Orbit.Input/Shoulder.cs | 24 +++ Orbit.Input/Stick.cs | 24 +++ 21 files changed, 970 insertions(+), 4 deletions(-) create mode 100644 ManagedDoom.Maui/Game/AggregatedUserInput.cs create mode 100644 ManagedDoom.Maui/Game/GameControllerUserInput.cs create mode 100644 Orbit.Input/ButtonElement.cs create mode 100644 Orbit.Input/GameController.cs create mode 100644 Orbit.Input/GameControllerManager.cs create mode 100644 Orbit.Input/Orbit.Input.csproj create mode 100644 Orbit.Input/Platforms/Android/GameController.cs create mode 100644 Orbit.Input/Platforms/Android/GameControllerManager.cs create mode 100644 Orbit.Input/Platforms/MacCatalyst/GameController.cs create mode 100644 Orbit.Input/Platforms/MacCatalyst/GameControllerManager.cs create mode 100644 Orbit.Input/Platforms/Tizen/PlatformClass1.cs create mode 100644 Orbit.Input/Platforms/Windows/GameController.cs create mode 100644 Orbit.Input/Platforms/Windows/GameControllerManager.cs create mode 100644 Orbit.Input/Platforms/iOS/GameController.cs create mode 100644 Orbit.Input/Platforms/iOS/GameControllerManager.cs create mode 100644 Orbit.Input/Shoulder.cs create mode 100644 Orbit.Input/Stick.cs diff --git a/Doom.sln b/Doom.sln index 11c1516..7c8092f 100644 --- a/Doom.sln +++ b/Doom.sln @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orbit.Input", "Orbit.Input\Orbit.Input.csproj", "{2A39507B-6073-402A-B2D4-CE466C27819F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,6 +32,10 @@ Global {EA9720A8-3081-47A2-BC5A-8106CD7D608C}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA9720A8-3081-47A2-BC5A-8106CD7D608C}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA9720A8-3081-47A2-BC5A-8106CD7D608C}.Release|Any CPU.Build.0 = Release|Any CPU + {2A39507B-6073-402A-B2D4-CE466C27819F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A39507B-6073-402A-B2D4-CE466C27819F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A39507B-6073-402A-B2D4-CE466C27819F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A39507B-6073-402A-B2D4-CE466C27819F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -37,6 +43,7 @@ Global GlobalSection(NestedProjects) = preSolution {298BCEE7-9882-4C67-90A8-E0B997312FD4} = {594DD4E1-3404-4383-B554-7474931FDE71} {EA9720A8-3081-47A2-BC5A-8106CD7D608C} = {594DD4E1-3404-4383-B554-7474931FDE71} + {2A39507B-6073-402A-B2D4-CE466C27819F} = {594DD4E1-3404-4383-B554-7474931FDE71} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2EA5D015-FB83-4E41-A368-1D8DC542CA03} diff --git a/ManagedDoom.Maui/Game/AggregatedUserInput.cs b/ManagedDoom.Maui/Game/AggregatedUserInput.cs new file mode 100644 index 0000000..0794ac6 --- /dev/null +++ b/ManagedDoom.Maui/Game/AggregatedUserInput.cs @@ -0,0 +1,50 @@ +using ManagedDoom.UserInput; + +namespace ManagedDoom.Maui.Game; + +public class AggregatedUserInput : IUserInput +{ + private readonly IReadOnlyList _userInputs; + + public AggregatedUserInput(IReadOnlyList userInputs) + { + _userInputs = userInputs; + } + + public void BuildTicCmd(TicCmd cmd) + { + cmd.Clear(); + + foreach (var userInput in _userInputs) + { + userInput.BuildTicCmd(cmd); + } + } + + public void Reset() + { + foreach (var userInput in _userInputs) + { + userInput.Reset(); + } + } + + public void GrabMouse() + { + foreach (var userInput in _userInputs) + { + userInput.GrabMouse(); + } + } + + public void ReleaseMouse() + { + foreach (var userInput in _userInputs) + { + userInput.ReleaseMouse(); + } + } + + public int MaxMouseSensitivity { get; } + public int MouseSensitivity { get; set; } +} \ No newline at end of file diff --git a/ManagedDoom.Maui/Game/GameControllerUserInput.cs b/ManagedDoom.Maui/Game/GameControllerUserInput.cs new file mode 100644 index 0000000..c4cdd15 --- /dev/null +++ b/ManagedDoom.Maui/Game/GameControllerUserInput.cs @@ -0,0 +1,70 @@ +using ManagedDoom.UserInput; +using Orbit.Input; + +namespace ManagedDoom.Maui.Game; + +public class GameControllerUserInput : IUserInput +{ + private readonly Orbit.Input.GameController _gameController; + private int _currentIndex; + + public GameControllerUserInput() + { + _gameController = GameControllerManager.Current.GameControllers.First(); + + _gameController + .When( + button: "ButtonNorth", + isPressed: _ => + { + _currentIndex = (_currentIndex * TicCmdButtons.WeaponShift) % (7 * TicCmdButtons.WeaponShift); + }) + .When( + button: "RightTrigger", + changesValue: f => + { + + }); + } + + public void BuildTicCmd(TicCmd cmd) + { + if (_gameController.RightShoulder.Trigger > 0) + { + cmd.Buttons |= TicCmdButtons.Attack; + } + + if (_gameController.ButtonSouth) + { + cmd.Buttons |= TicCmdButtons.Use; + } + + if (_gameController.ButtonNorth) + { + cmd.Buttons |= TicCmdButtons.Change; + cmd.Buttons |= (byte)_currentIndex; + } + + cmd.AngleTurn -= (short)(_gameController.RightStick.XAxis * 0x150); + cmd.ForwardMove += (sbyte)(_gameController.LeftStick.YAxis * 0x8); + cmd.SideMove += (sbyte)(_gameController.LeftStick.XAxis * 0x8); + } + + public void Reset() + { + + } + + public void GrabMouse() + { + + } + + public void ReleaseMouse() + { + + } + + public int MaxMouseSensitivity { get; } + public int MouseSensitivity { get; set; } +} \ No newline at end of file diff --git a/ManagedDoom.Maui/Game/MauiDoom.cs b/ManagedDoom.Maui/Game/MauiDoom.cs index c453ad8..5b7530d 100644 --- a/ManagedDoom.Maui/Game/MauiDoom.cs +++ b/ManagedDoom.Maui/Game/MauiDoom.cs @@ -11,6 +11,7 @@ public class MauiDoom : MauiGame private MauiSound _sound; private MauiVideo _video; private MauiUserInput _input; + private GameControllerUserInput _gameController; private CommandLineArgs args; private Config _config; @@ -158,8 +159,16 @@ void Init(SkiaDrawingContext context, SKRect destination, float scale) } _input = new MauiUserInput(_config, !args.nomouse.Present, OnUiCommand); - - _doom = new Doom(args, _config, _content, _video, _sound, _music, _input); + _gameController = new GameControllerUserInput(); + + _doom = new Doom( + args, + _config, + _content, + _video, + _sound, + _music, + new AggregatedUserInput([_input, _gameController])); _input.Attach(_doom); diff --git a/ManagedDoom.Maui/Game/MauiUserInput.cs b/ManagedDoom.Maui/Game/MauiUserInput.cs index 834a883..31f520b 100644 --- a/ManagedDoom.Maui/Game/MauiUserInput.cs +++ b/ManagedDoom.Maui/Game/MauiUserInput.cs @@ -265,8 +265,6 @@ public void BuildTicCmd(TicCmd cmd) weaponKeys[5] = IsPressed(DoomKey.Num6); weaponKeys[6] = IsPressed(DoomKey.Num7); - cmd.Clear(); - var strafe = keyStrafe; var speed = keyRun ? 1 : 0; var forward = 0; diff --git a/ManagedDoom.Maui/ManagedDoom.Maui.csproj b/ManagedDoom.Maui/ManagedDoom.Maui.csproj index 31170c8..53baa3f 100644 --- a/ManagedDoom.Maui/ManagedDoom.Maui.csproj +++ b/ManagedDoom.Maui/ManagedDoom.Maui.csproj @@ -84,6 +84,7 @@ + diff --git a/Orbit.Input/ButtonElement.cs b/Orbit.Input/ButtonElement.cs new file mode 100644 index 0000000..cb65b31 --- /dev/null +++ b/Orbit.Input/ButtonElement.cs @@ -0,0 +1,33 @@ +using System.Runtime.CompilerServices; + +namespace Orbit.Input; + +public abstract class ButtonElement +{ + private readonly GameController controller; + private readonly string elementName; + + protected ButtonElement(GameController controller, string elementName) + { + this.controller = controller; + this.elementName = elementName; + } + + protected void SetState(ref bool field, bool newValue, [CallerMemberName] string? buttonName = null) + { + ArgumentNullException.ThrowIfNull(buttonName); + + field = newValue; + + this.controller.RaiseButtonPressed(elementName + buttonName, field); + } + + protected void SetValue(ref float field, float newValue, [CallerMemberName] string? buttonName = null) + { + ArgumentNullException.ThrowIfNull(buttonName); + + field = newValue; + + this.controller.RaiseButtonValueChanged(elementName + buttonName, field); + } +} diff --git a/Orbit.Input/GameController.cs b/Orbit.Input/GameController.cs new file mode 100644 index 0000000..d9e6e64 --- /dev/null +++ b/Orbit.Input/GameController.cs @@ -0,0 +1,124 @@ +using System.Runtime.CompilerServices; + +namespace Orbit.Input; + +public partial class GameController +{ + public Stick Dpad { get; } + + public Stick LeftStick { get; } + + public Stick RightStick { get; } + + private bool buttonNorth; + + public bool ButtonNorth + { + get => buttonNorth; + set => SetState(ref this.buttonNorth, value); + } + + private bool buttonSouth; + + public bool ButtonSouth + { + get => buttonSouth; + set => SetState(ref this.buttonSouth, value); + } + + private bool buttonEast; + + public bool ButtonEast + { + get => buttonEast; + set => SetState(ref this.buttonEast, value); + } + + private bool buttonWest; + + public bool ButtonWest + { + get => buttonWest; + set => SetState(ref this.buttonWest, value); + } + + private bool pause; + + public bool Pause + { + get => pause; + set => SetState(ref this.pause, value); + } + + public Shoulder LeftShoulder { get; } + + public Shoulder RightShoulder { get; } + + public GameController When(string button, Action isPressed) + { + if (buttonPressedCallbacks.TryGetValue(button, out var callbacks) is false) + { + callbacks = [isPressed]; + } + else + { + callbacks.Add(isPressed); + } + + buttonPressedCallbacks[button] = callbacks; + return this; + } + + public GameController When(string button, Action changesValue) + { + if (buttonValueChangeCallbacks.TryGetValue(button, out var callbacks) is false) + { + callbacks = [changesValue]; + } + else + { + callbacks.Add(changesValue); + } + + buttonValueChangeCallbacks[button] = callbacks; + return this; + } + + private readonly IDictionary>> buttonPressedCallbacks = new Dictionary>>(); + private readonly IDictionary>> buttonValueChangeCallbacks = new Dictionary>>(); + + internal void RaiseButtonPressed(string button, bool isPressed) + { + if (buttonPressedCallbacks.TryGetValue(button, out var callbacks) is false) + { + return; + } + + foreach (var callback in callbacks) + { + callback.Invoke(isPressed); + } + } + + internal void RaiseButtonValueChanged(string button, float value) + { + if (buttonValueChangeCallbacks.TryGetValue(button, out var callbacks) is false) + { + return; + } + + foreach (var callback in callbacks) + { + callback.Invoke(value); + } + } + + private void SetState(ref bool field, bool newValue, [CallerMemberName] string? buttonName = null) + { + ArgumentNullException.ThrowIfNull(buttonName); + + field = newValue; + + this.RaiseButtonPressed(buttonName, field); + } +} diff --git a/Orbit.Input/GameControllerManager.cs b/Orbit.Input/GameControllerManager.cs new file mode 100644 index 0000000..9b9a29a --- /dev/null +++ b/Orbit.Input/GameControllerManager.cs @@ -0,0 +1,31 @@ +namespace Orbit.Input; + +public partial class GameControllerManager +{ + private List gameControllers = []; + + private static GameControllerManager? current; + + public static GameControllerManager Current => current ?? (current = new GameControllerManager()); + + public partial Task Initialize(); + + public IReadOnlyCollection GameControllers => gameControllers; + + public event EventHandler? GameControllerConnected; + + private void OnGameControllerConnected(GameController controller) + { + this.GameControllerConnected?.Invoke(this, new GameControllerConnectedEventArgs(controller)); + } +} + +public class GameControllerConnectedEventArgs : EventArgs +{ + public GameControllerConnectedEventArgs(GameController gameController) + { + GameController = gameController; + } + + public GameController GameController { get; } +} \ No newline at end of file diff --git a/Orbit.Input/Orbit.Input.csproj b/Orbit.Input/Orbit.Input.csproj new file mode 100644 index 0000000..851f9a4 --- /dev/null +++ b/Orbit.Input/Orbit.Input.csproj @@ -0,0 +1,53 @@ + + + + net9.0-android;net9.0-ios;net9.0-maccatalyst + $(TargetFrameworks);net9.0-windows10.0.19041.0 + + + true + true + enable + enable + + 15.0 + 15.0 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + Orbit.Input + + + + Shaun Lawrence + The Orbit input library provides support for game controller input. + + .NET MAUI, Game Engine, 2D Game + + Bijington.Orbit.Input + + https://github.com/bijington/orbit + https://github.com/bijington/orbit + Orbit Input + Copyright(c) 2025 Shaun Lawrence + + MIT + true + true + true + snupkg + true + true + Release;Debug + + + + 1701;1702;CA1416 + + + + + + + diff --git a/Orbit.Input/Platforms/Android/GameController.cs b/Orbit.Input/Platforms/Android/GameController.cs new file mode 100644 index 0000000..90506d6 --- /dev/null +++ b/Orbit.Input/Platforms/Android/GameController.cs @@ -0,0 +1,175 @@ +using Android.Views; + +namespace Orbit.Input; + +// All the code in this file is only included on Android. +public partial class GameController +{ + public GameController(int deviceId) + { + Dpad = new Stick(this, nameof(Dpad)); + LeftStick = new Stick(this, nameof(LeftStick)); + RightStick = new Stick(this, nameof(RightStick)); + + LeftShoulder = new Shoulder(this, nameof(LeftShoulder)); + RightShoulder = new Shoulder(this, nameof(RightShoulder)); + } + + public void OnGenericMotionEvent(MotionEvent motionEvent) + { + // Check that the event came from a game controller + if (motionEvent.Source.HasFlag(InputSourceType.Joystick) && + motionEvent.Action == MotionEventActions.Move) + { + // Process all historical movement samples in the batch + var historySize = motionEvent.HistorySize; + + // Process the movements starting from the + // earliest historical position in the batch + for (var i = 0; i < historySize; i++) + { + // Process the event at historical position i + ProcessJoystickInput(motionEvent, i); + } + + // Process the current movement sample in the batch (position -1) + ProcessJoystickInput(motionEvent, -1); + } + } + + private static float GetCenteredAxis(MotionEvent motionEvent, InputDevice device, Axis axis, int historyPos) + { + var range = device.GetMotionRange(axis, motionEvent.Source); + + // A joystick at rest does not always report an absolute position of + // (0,0). Use the getFlat() method to determine the range of values + // bounding the joystick axis center. + if (range is not null) + { + var flat = range.Flat; + var value = historyPos < 0 ? motionEvent.GetAxisValue(axis): + motionEvent.GetHistoricalAxisValue(axis, historyPos); + + // Ignore axis values that are within the 'flat' region of the + // joystick axis center. + if (Math.Abs(value) > flat) + { + return value; + } + } + return 0; + } + + private void ProcessJoystickInput(MotionEvent motionEvent, int historyPos) + { + var inputDevice = motionEvent.Device; + + if (inputDevice is null) + { + return; + } + + LeftStick.XAxis = GetCenteredAxis(motionEvent, inputDevice, Axis.X, historyPos); + LeftStick.YAxis = GetCenteredAxis(motionEvent, inputDevice, Axis.Y, historyPos); + + RightStick.XAxis = GetCenteredAxis(motionEvent, inputDevice, Axis.Z, historyPos); + RightStick.YAxis = GetCenteredAxis(motionEvent, inputDevice, Axis.Rz, historyPos); + + LeftShoulder.Trigger = GetCenteredAxis(motionEvent, inputDevice, Axis.Ltrigger, historyPos); + RightShoulder.Trigger = GetCenteredAxis(motionEvent, inputDevice, Axis.Rtrigger, historyPos); + } + + public void OnKeyDown(InputEvent inputEvent) + { + if (!IsDpadDevice(inputEvent)) + { + return; + } + + switch (inputEvent) + { + // If the input event is a MotionEvent, check its hat axis values. + case MotionEvent motionEvent: + // Use the hat axis value to find the D-pad direction + Dpad.XAxis = motionEvent.GetAxisValue(Axis.HatX); + Dpad.YAxis = motionEvent.GetAxisValue(Axis.HatY); + break; + + // If the input event is a KeyEvent, check its key code. + // Use the key code to find the D-pad direction. + case KeyEvent { KeyCode: Keycode.DpadLeft }: + Dpad.XAxis = -1; + break; + + case KeyEvent { KeyCode: Keycode.DpadRight }: + Dpad.XAxis = 1; + break; + + case KeyEvent { KeyCode: Keycode.DpadDown }: + Dpad.YAxis = -1; + break; + + case KeyEvent { KeyCode: Keycode.DpadUp }: + Dpad.YAxis = 1; + break; + + case KeyEvent { KeyCode: Keycode.ButtonL1 }: + LeftShoulder.Button = true; + break; + + case KeyEvent { KeyCode: Keycode.ButtonR1 }: + RightShoulder.Button = true; + break; + } + } + + public void OnKeyUp(InputEvent inputEvent) + { + if (!IsDpadDevice(inputEvent)) + { + return; + } + + switch (inputEvent) + { + // If the input event is a MotionEvent, check its hat axis values. + case MotionEvent: + // Use the hat axis value to find the D-pad direction + Dpad.XAxis = 0; + Dpad.YAxis = 0; + break; + + // If the input event is a KeyEvent, check its key code. + // Use the key code to find the D-pad direction. + case KeyEvent { KeyCode: Keycode.DpadLeft }: + Dpad.XAxis = 0; + break; + + case KeyEvent { KeyCode: Keycode.DpadRight }: + Dpad.XAxis = 0; + break; + + case KeyEvent { KeyCode: Keycode.DpadDown }: + Dpad.YAxis = 0; + break; + + case KeyEvent { KeyCode: Keycode.DpadUp }: + Dpad.YAxis = 0; + break; + + case KeyEvent { KeyCode: Keycode.ButtonL1 }: + LeftShoulder.Button = false; + break; + + case KeyEvent { KeyCode: Keycode.ButtonR1 }: + RightShoulder.Button = false; + break; + } + } + + private static bool IsDpadDevice(InputEvent inputEvent) + { + // Check that input comes from a device with directional pads. + return inputEvent.Source.HasFlag(InputSourceType.Dpad); + } +} diff --git a/Orbit.Input/Platforms/Android/GameControllerManager.cs b/Orbit.Input/Platforms/Android/GameControllerManager.cs new file mode 100644 index 0000000..79b8c2b --- /dev/null +++ b/Orbit.Input/Platforms/Android/GameControllerManager.cs @@ -0,0 +1,81 @@ +using Android.Views; + +namespace Orbit.Input; + +public partial class GameControllerManager +{ + private GameControllerManager() + { + } + + public partial Task Initialize() + { + var deviceIds = InputDevice.GetDeviceIds(); + + if (deviceIds is null) + { + return Task.CompletedTask; + } + + foreach (var deviceId in deviceIds) + { + var device = InputDevice.GetDevice(deviceId); + + if (device is null) + { + continue; + } + + var sources = device.Sources; + + if (sources.HasFlag(InputSourceType.Gamepad) || sources.HasFlag(InputSourceType.Joystick)) + { + gameControllers.Add(new GameController(deviceId)); + } + } + + return Task.CompletedTask; + } + + public void OnGenericMotionEvent(MotionEvent? e) + { + if (e is null) + { + return; + } + + // TODO: thread safety? + foreach (var controller in gameControllers) + { + controller.OnGenericMotionEvent(e); + } + } + + public void OnKeyDown(Keycode keyCode, KeyEvent? e) + { + if (e is null) + { + return; + } + + // TODO: thread safety? + foreach (var controller in gameControllers) + { + controller.OnKeyDown(e); + } + } + + public void OnKeyUp(Keycode keyCode, KeyEvent? e) + { + if (e is null) + { + return; + } + + // TODO: thread safety? + foreach (var controller in gameControllers) + { + controller.OnKeyUp(e); + } + } +} \ No newline at end of file diff --git a/Orbit.Input/Platforms/MacCatalyst/GameController.cs b/Orbit.Input/Platforms/MacCatalyst/GameController.cs new file mode 100644 index 0000000..97de44b --- /dev/null +++ b/Orbit.Input/Platforms/MacCatalyst/GameController.cs @@ -0,0 +1,78 @@ +using System.Diagnostics; +using Foundation; +using GameController; + +namespace Orbit.Input; + +public partial class GameController +{ + private readonly GCController controller; + + public GameController(GCController controller) + { + this.controller = controller; + + Dpad = new Stick(this, nameof(Dpad)); + LeftStick = new Stick(this, nameof(LeftStick)); + RightStick = new Stick(this, nameof(RightStick)); + + LeftShoulder = new Shoulder(this, nameof(LeftShoulder)); + RightShoulder = new Shoulder(this, nameof(RightShoulder)); + + controller.PhysicalInputProfile.ValueDidChangeHandler += Changed; + } + + private void Changed(GCPhysicalInputProfile gamepad, GCControllerElement element) + { + Debug.WriteLine($"{element}"); + + switch (element) + { + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button A")): + ButtonSouth = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button B")): + ButtonEast = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button X")): + ButtonWest = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button Y")): + ButtonNorth = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Right Shoulder")): + RightShoulder.Button = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Right Trigger")): + RightShoulder.Trigger = buttonInput.Value; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Left Shoulder")): + LeftShoulder.Button = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Left Trigger")): + LeftShoulder.Trigger = buttonInput.Value; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button Menu")): + Pause = buttonInput.IsPressed; + break; + + case GCControllerDirectionPad directionPad when directionPad.Aliases.Contains(new NSString("Left Thumbstick")): + LeftStick.XAxis = directionPad.XAxis.Value; + LeftStick.YAxis = directionPad.YAxis.Value; + break; + + case GCControllerDirectionPad directionPad when directionPad.Aliases.Contains(new NSString("Right Thumbstick")): + RightStick.XAxis = directionPad.XAxis.Value; + RightStick.YAxis = directionPad.YAxis.Value; + break; + } + } +} \ No newline at end of file diff --git a/Orbit.Input/Platforms/MacCatalyst/GameControllerManager.cs b/Orbit.Input/Platforms/MacCatalyst/GameControllerManager.cs new file mode 100644 index 0000000..0bc8fdf --- /dev/null +++ b/Orbit.Input/Platforms/MacCatalyst/GameControllerManager.cs @@ -0,0 +1,28 @@ +using Foundation; + +using GameController; + +namespace Orbit.Input; + +public partial class GameControllerManager +{ + private GameControllerManager() + { + GCController.Notifications.ObserveDidConnect(ConnectToController); + } + + public partial async Task Initialize() + { + await GCController.StartWirelessControllerDiscoveryAsync(); + } + + private void ConnectToController(object? sender, NSNotificationEventArgs e) + { + if (e.Notification.Object is GCController controller) + { + var gameController = new GameController(controller); + gameControllers.Add(gameController); + OnGameControllerConnected(gameController); + } + } +} \ No newline at end of file diff --git a/Orbit.Input/Platforms/Tizen/PlatformClass1.cs b/Orbit.Input/Platforms/Tizen/PlatformClass1.cs new file mode 100644 index 0000000..3397f80 --- /dev/null +++ b/Orbit.Input/Platforms/Tizen/PlatformClass1.cs @@ -0,0 +1,9 @@ +using System; + +namespace Orbit.Controller +{ + // All the code in this file is only included on Tizen. + public class PlatformClass1 + { + } +} \ No newline at end of file diff --git a/Orbit.Input/Platforms/Windows/GameController.cs b/Orbit.Input/Platforms/Windows/GameController.cs new file mode 100644 index 0000000..4686f8e --- /dev/null +++ b/Orbit.Input/Platforms/Windows/GameController.cs @@ -0,0 +1,45 @@ +using Windows.Gaming.Input; + +namespace Orbit.Input; + +public partial class GameController +{ + private readonly Gamepad gamepad; + + public GameController(Gamepad gamepad) + { + this.gamepad = gamepad; + + Dpad = new Stick(this, nameof(Dpad)); + LeftStick = new Stick(this, nameof(LeftStick)); + RightStick = new Stick(this, nameof(RightStick)); + + LeftShoulder = new Shoulder(this, nameof(LeftShoulder)); + RightShoulder = new Shoulder(this, nameof(RightShoulder)); + } + + private void Update() + { + var reading = gamepad.GetCurrentReading(); + + LeftStick.XAxis = (float)reading.LeftThumbstickX; + LeftStick.YAxis = (float)reading.LeftThumbstickY; + + RightStick.XAxis = (float)reading.RightThumbstickX; + RightStick.YAxis = (float)reading.RightThumbstickY; + + LeftShoulder.Button = reading.Buttons.HasFlag(GamepadButtons.LeftShoulder); + LeftShoulder.Trigger = (float)reading.LeftTrigger; + + RightShoulder.Button = reading.Buttons.HasFlag(GamepadButtons.RightShoulder); + RightShoulder.Trigger = (float)reading.RightTrigger; + + ButtonNorth = reading.Buttons.HasFlag(GamepadButtons.Y); + ButtonEast = reading.Buttons.HasFlag(GamepadButtons.B); + ButtonSouth = reading.Buttons.HasFlag(GamepadButtons.A); + ButtonWest = reading.Buttons.HasFlag(GamepadButtons.X); + + Dpad.XAxis = reading.Buttons.HasFlag(GamepadButtons.DPadRight) ? 1f : reading.Buttons.HasFlag(GamepadButtons.DPadLeft) ? -1f : 0f; + Dpad.YAxis = reading.Buttons.HasFlag(GamepadButtons.DPadDown) ? 1f : reading.Buttons.HasFlag(GamepadButtons.DPadUp) ? -1f : 0f; + } +} \ No newline at end of file diff --git a/Orbit.Input/Platforms/Windows/GameControllerManager.cs b/Orbit.Input/Platforms/Windows/GameControllerManager.cs new file mode 100644 index 0000000..7be8907 --- /dev/null +++ b/Orbit.Input/Platforms/Windows/GameControllerManager.cs @@ -0,0 +1,22 @@ +using Windows.Gaming.Input; + +namespace Orbit.Input; + +public partial class GameControllerManager +{ + private GameControllerManager() + { + } + + public partial Task Initialize() + { + var gamePads = Gamepad.Gamepads; + + foreach (var gamePad in gamePads) + { + gameControllers.Add(new GameController(gamePad)); + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Orbit.Input/Platforms/iOS/GameController.cs b/Orbit.Input/Platforms/iOS/GameController.cs new file mode 100644 index 0000000..97de44b --- /dev/null +++ b/Orbit.Input/Platforms/iOS/GameController.cs @@ -0,0 +1,78 @@ +using System.Diagnostics; +using Foundation; +using GameController; + +namespace Orbit.Input; + +public partial class GameController +{ + private readonly GCController controller; + + public GameController(GCController controller) + { + this.controller = controller; + + Dpad = new Stick(this, nameof(Dpad)); + LeftStick = new Stick(this, nameof(LeftStick)); + RightStick = new Stick(this, nameof(RightStick)); + + LeftShoulder = new Shoulder(this, nameof(LeftShoulder)); + RightShoulder = new Shoulder(this, nameof(RightShoulder)); + + controller.PhysicalInputProfile.ValueDidChangeHandler += Changed; + } + + private void Changed(GCPhysicalInputProfile gamepad, GCControllerElement element) + { + Debug.WriteLine($"{element}"); + + switch (element) + { + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button A")): + ButtonSouth = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button B")): + ButtonEast = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button X")): + ButtonWest = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button Y")): + ButtonNorth = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Right Shoulder")): + RightShoulder.Button = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Right Trigger")): + RightShoulder.Trigger = buttonInput.Value; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Left Shoulder")): + LeftShoulder.Button = buttonInput.IsPressed; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Left Trigger")): + LeftShoulder.Trigger = buttonInput.Value; + break; + + case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button Menu")): + Pause = buttonInput.IsPressed; + break; + + case GCControllerDirectionPad directionPad when directionPad.Aliases.Contains(new NSString("Left Thumbstick")): + LeftStick.XAxis = directionPad.XAxis.Value; + LeftStick.YAxis = directionPad.YAxis.Value; + break; + + case GCControllerDirectionPad directionPad when directionPad.Aliases.Contains(new NSString("Right Thumbstick")): + RightStick.XAxis = directionPad.XAxis.Value; + RightStick.YAxis = directionPad.YAxis.Value; + break; + } + } +} \ No newline at end of file diff --git a/Orbit.Input/Platforms/iOS/GameControllerManager.cs b/Orbit.Input/Platforms/iOS/GameControllerManager.cs new file mode 100644 index 0000000..645437f --- /dev/null +++ b/Orbit.Input/Platforms/iOS/GameControllerManager.cs @@ -0,0 +1,26 @@ +using Foundation; + +using GameController; + +namespace Orbit.Input; + +public partial class GameControllerManager +{ + private GameControllerManager() + { + GCController.Notifications.ObserveDidConnect(ConnectToController); + } + + public partial async Task Initialize() + { + await GCController.StartWirelessControllerDiscoveryAsync(); + } + + private void ConnectToController(object? sender, NSNotificationEventArgs e) + { + if (e.Notification.Object is GCController controller) + { + gameControllers.Add(new GameController(controller)); + } + } +} \ No newline at end of file diff --git a/Orbit.Input/Shoulder.cs b/Orbit.Input/Shoulder.cs new file mode 100644 index 0000000..726468b --- /dev/null +++ b/Orbit.Input/Shoulder.cs @@ -0,0 +1,24 @@ +namespace Orbit.Input; + +public class Shoulder : ButtonElement +{ + public Shoulder(GameController controller, string name) : base(controller, name) + { + } + + private bool button; + + public bool Button + { + get => button; + set => SetState(ref this.button, value); + } + + private float trigger; + + public float Trigger + { + get => trigger; + set => SetValue(ref this.trigger, value); + } +} diff --git a/Orbit.Input/Stick.cs b/Orbit.Input/Stick.cs new file mode 100644 index 0000000..e8343b9 --- /dev/null +++ b/Orbit.Input/Stick.cs @@ -0,0 +1,24 @@ +namespace Orbit.Input; + +public class Stick : ButtonElement +{ + public Stick(GameController controller, string name) : base(controller, name) + { + } + + private float xAxis; + + public float XAxis + { + get => xAxis; + set => SetValue(ref this.xAxis, value); + } + + private float yAxis; + + public float YAxis + { + get => yAxis; + set => SetValue(ref this.yAxis, value); + } +} From 4a7d21ba762798ed27eacb2c3c05948e666d8446 Mon Sep 17 00:00:00 2001 From: Shaun Lawrence <17139988+bijington@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:38:39 +0000 Subject: [PATCH 2/8] Don't expect a controller to be connected --- .../Game/GameControllerUserInput.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ManagedDoom.Maui/Game/GameControllerUserInput.cs b/ManagedDoom.Maui/Game/GameControllerUserInput.cs index c4cdd15..73cd3e6 100644 --- a/ManagedDoom.Maui/Game/GameControllerUserInput.cs +++ b/ManagedDoom.Maui/Game/GameControllerUserInput.cs @@ -5,19 +5,25 @@ namespace ManagedDoom.Maui.Game; public class GameControllerUserInput : IUserInput { - private readonly Orbit.Input.GameController _gameController; + private Orbit.Input.GameController? _gameController; private int _currentIndex; public GameControllerUserInput() { - _gameController = GameControllerManager.Current.GameControllers.First(); + GameControllerManager.Current.GameControllerConnected += OnGameControllerConnected; + _ = GameControllerManager.Current.Initialize(); + } + + private void OnGameControllerConnected(object? sender, GameControllerConnectedEventArgs args) + { + _gameController = args.GameController; _gameController .When( button: "ButtonNorth", isPressed: _ => { - _currentIndex = (_currentIndex * TicCmdButtons.WeaponShift) % (7 * TicCmdButtons.WeaponShift); + _currentIndex = (_currentIndex + TicCmdButtons.WeaponShift) % (7 * TicCmdButtons.WeaponShift); }) .When( button: "RightTrigger", @@ -29,6 +35,11 @@ public GameControllerUserInput() public void BuildTicCmd(TicCmd cmd) { + if (_gameController is null) + { + return; + } + if (_gameController.RightShoulder.Trigger > 0) { cmd.Buttons |= TicCmdButtons.Attack; From 1348a82e2b6b0fe02f1bf181d157dff5f2819c95 Mon Sep 17 00:00:00 2001 From: Shaun Lawrence <17139988+bijington@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:18:52 +0000 Subject: [PATCH 3/8] Current changes --- .vscode/launch.json | 15 +++++++++++++++ ManagedDoom.Maui/Game/GameControllerUserInput.cs | 4 ++-- ManagedDoom.Maui/MainPage.xaml.cs | 9 ++++++++- ManagedDoom.Shared/ManagedDoom.Shared.csproj | 2 +- 4 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..91b6c66 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": ".NET Meteor Debugger", + "type": "dotnet-meteor.debugger", + "request": "launch", + "preLaunchTask": "dotnet-meteor: Build" + } + ] +} \ No newline at end of file diff --git a/ManagedDoom.Maui/Game/GameControllerUserInput.cs b/ManagedDoom.Maui/Game/GameControllerUserInput.cs index 73cd3e6..63e70ee 100644 --- a/ManagedDoom.Maui/Game/GameControllerUserInput.cs +++ b/ManagedDoom.Maui/Game/GameControllerUserInput.cs @@ -10,8 +10,8 @@ public class GameControllerUserInput : IUserInput public GameControllerUserInput() { - GameControllerManager.Current.GameControllerConnected += OnGameControllerConnected; - _ = GameControllerManager.Current.Initialize(); + // GameControllerManager.Current.GameControllerConnected += OnGameControllerConnected; + // _ = GameControllerManager.Current.Initialize(); } private void OnGameControllerConnected(object? sender, GameControllerConnectedEventArgs args) diff --git a/ManagedDoom.Maui/MainPage.xaml.cs b/ManagedDoom.Maui/MainPage.xaml.cs index 4f33981..d3d2e82 100644 --- a/ManagedDoom.Maui/MainPage.xaml.cs +++ b/ManagedDoom.Maui/MainPage.xaml.cs @@ -1,4 +1,5 @@ using DrawnUi.Maui.Draw; +using Orbit.Input; namespace ManagedDoom.Maui { @@ -13,6 +14,9 @@ public MainPage() #if IOS Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.SetPrefersHomeIndicatorAutoHidden(this, true); #endif + + Console.WriteLine("HELLO FROM DOOM"); + GameControllerManager.Current.GameControllerConnected += OnGameControllerConnected; } catch (Exception e) { @@ -20,7 +24,10 @@ public MainPage() } } - + private async void OnGameControllerConnected(object? sender, GameControllerConnectedEventArgs args) + { + await DisplayAlert(@"Game controller connected!", "Game controller connected!", "OK"); + } } } diff --git a/ManagedDoom.Shared/ManagedDoom.Shared.csproj b/ManagedDoom.Shared/ManagedDoom.Shared.csproj index 0b5648c..1b93b47 100644 --- a/ManagedDoom.Shared/ManagedDoom.Shared.csproj +++ b/ManagedDoom.Shared/ManagedDoom.Shared.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable True From 08d8746d2840fdddc94c5a24337f035158b276fb Mon Sep 17 00:00:00 2001 From: Shaun Lawrence <17139988+bijington@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:59:50 +0000 Subject: [PATCH 4/8] Fix ios controllers not connecting --- ManagedDoom.Maui/Game/GameControllerUserInput.cs | 4 ++-- ManagedDoom.Maui/Game/MauiVideo.cs | 2 +- ManagedDoom.Maui/MainPage.xaml.cs | 7 ------- Orbit.Input/Platforms/iOS/GameControllerManager.cs | 4 +++- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/ManagedDoom.Maui/Game/GameControllerUserInput.cs b/ManagedDoom.Maui/Game/GameControllerUserInput.cs index 63e70ee..73cd3e6 100644 --- a/ManagedDoom.Maui/Game/GameControllerUserInput.cs +++ b/ManagedDoom.Maui/Game/GameControllerUserInput.cs @@ -10,8 +10,8 @@ public class GameControllerUserInput : IUserInput public GameControllerUserInput() { - // GameControllerManager.Current.GameControllerConnected += OnGameControllerConnected; - // _ = GameControllerManager.Current.Initialize(); + GameControllerManager.Current.GameControllerConnected += OnGameControllerConnected; + _ = GameControllerManager.Current.Initialize(); } private void OnGameControllerConnected(object? sender, GameControllerConnectedEventArgs args) diff --git a/ManagedDoom.Maui/Game/MauiVideo.cs b/ManagedDoom.Maui/Game/MauiVideo.cs index ea3eb16..54bc5a3 100644 --- a/ManagedDoom.Maui/Game/MauiVideo.cs +++ b/ManagedDoom.Maui/Game/MauiVideo.cs @@ -71,7 +71,7 @@ public MauiVideo( public void Render(SKCanvas canvas, SKRect destination, Doom doom, Fixed frameFrac) { //if you have problems with System.Threadig.Tasks.Parellel - if (false) //fixed disabling interpreter for DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.DeviceType == DeviceType.Physical) + if (true) //fixed disabling interpreter for DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.DeviceType == DeviceType.Physical) { DrawScreen.Optimize = false; Render(doom, frameFrac); diff --git a/ManagedDoom.Maui/MainPage.xaml.cs b/ManagedDoom.Maui/MainPage.xaml.cs index d3d2e82..f70ce57 100644 --- a/ManagedDoom.Maui/MainPage.xaml.cs +++ b/ManagedDoom.Maui/MainPage.xaml.cs @@ -15,19 +15,12 @@ public MainPage() Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.SetPrefersHomeIndicatorAutoHidden(this, true); #endif - Console.WriteLine("HELLO FROM DOOM"); - GameControllerManager.Current.GameControllerConnected += OnGameControllerConnected; } catch (Exception e) { Super.DisplayException(this, e); } } - - private async void OnGameControllerConnected(object? sender, GameControllerConnectedEventArgs args) - { - await DisplayAlert(@"Game controller connected!", "Game controller connected!", "OK"); - } } } diff --git a/Orbit.Input/Platforms/iOS/GameControllerManager.cs b/Orbit.Input/Platforms/iOS/GameControllerManager.cs index 645437f..0bc8fdf 100644 --- a/Orbit.Input/Platforms/iOS/GameControllerManager.cs +++ b/Orbit.Input/Platforms/iOS/GameControllerManager.cs @@ -20,7 +20,9 @@ private void ConnectToController(object? sender, NSNotificationEventArgs e) { if (e.Notification.Object is GCController controller) { - gameControllers.Add(new GameController(controller)); + var gameController = new GameController(controller); + gameControllers.Add(gameController); + OnGameControllerConnected(gameController); } } } \ No newline at end of file From 9eb9eabb8fd3e4e7d0f6af14ee1df9c6bfa2d8a5 Mon Sep 17 00:00:00 2001 From: Shaun Lawrence <17139988+bijington@users.noreply.github.com> Date: Thu, 6 Mar 2025 08:58:09 +0000 Subject: [PATCH 5/8] Bring in the preview package for Orbit.Input --- Doom.sln | 7 - .../Game/GameControllerUserInput.cs | 37 ++-- ManagedDoom.Maui/Game/MauiGame.cs | 27 ++- ManagedDoom.Maui/MainPage.xaml.cs | 1 - ManagedDoom.Maui/ManagedDoom.Maui.csproj | 4 +- ManagedDoom.Maui/MauiProgram.cs | 3 +- .../Platforms/MacCatalyst/AppDelegate.cs | 30 ++- Orbit.Input/ButtonElement.cs | 33 ---- Orbit.Input/GameController.cs | 124 ------------- Orbit.Input/GameControllerManager.cs | 31 ---- Orbit.Input/Orbit.Input.csproj | 53 ------ .../Platforms/Android/GameController.cs | 175 ------------------ .../Android/GameControllerManager.cs | 81 -------- .../Platforms/MacCatalyst/GameController.cs | 78 -------- .../MacCatalyst/GameControllerManager.cs | 28 --- Orbit.Input/Platforms/Tizen/PlatformClass1.cs | 9 - .../Platforms/Windows/GameController.cs | 45 ----- .../Windows/GameControllerManager.cs | 22 --- Orbit.Input/Platforms/iOS/GameController.cs | 78 -------- .../Platforms/iOS/GameControllerManager.cs | 28 --- Orbit.Input/Shoulder.cs | 24 --- Orbit.Input/Stick.cs | 24 --- 22 files changed, 61 insertions(+), 881 deletions(-) delete mode 100644 Orbit.Input/ButtonElement.cs delete mode 100644 Orbit.Input/GameController.cs delete mode 100644 Orbit.Input/GameControllerManager.cs delete mode 100644 Orbit.Input/Orbit.Input.csproj delete mode 100644 Orbit.Input/Platforms/Android/GameController.cs delete mode 100644 Orbit.Input/Platforms/Android/GameControllerManager.cs delete mode 100644 Orbit.Input/Platforms/MacCatalyst/GameController.cs delete mode 100644 Orbit.Input/Platforms/MacCatalyst/GameControllerManager.cs delete mode 100644 Orbit.Input/Platforms/Tizen/PlatformClass1.cs delete mode 100644 Orbit.Input/Platforms/Windows/GameController.cs delete mode 100644 Orbit.Input/Platforms/Windows/GameControllerManager.cs delete mode 100644 Orbit.Input/Platforms/iOS/GameController.cs delete mode 100644 Orbit.Input/Platforms/iOS/GameControllerManager.cs delete mode 100644 Orbit.Input/Shoulder.cs delete mode 100644 Orbit.Input/Stick.cs diff --git a/Doom.sln b/Doom.sln index 7c8092f..11c1516 100644 --- a/Doom.sln +++ b/Doom.sln @@ -14,8 +14,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orbit.Input", "Orbit.Input\Orbit.Input.csproj", "{2A39507B-6073-402A-B2D4-CE466C27819F}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,10 +30,6 @@ Global {EA9720A8-3081-47A2-BC5A-8106CD7D608C}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA9720A8-3081-47A2-BC5A-8106CD7D608C}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA9720A8-3081-47A2-BC5A-8106CD7D608C}.Release|Any CPU.Build.0 = Release|Any CPU - {2A39507B-6073-402A-B2D4-CE466C27819F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A39507B-6073-402A-B2D4-CE466C27819F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A39507B-6073-402A-B2D4-CE466C27819F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A39507B-6073-402A-B2D4-CE466C27819F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -43,7 +37,6 @@ Global GlobalSection(NestedProjects) = preSolution {298BCEE7-9882-4C67-90A8-E0B997312FD4} = {594DD4E1-3404-4383-B554-7474931FDE71} {EA9720A8-3081-47A2-BC5A-8106CD7D608C} = {594DD4E1-3404-4383-B554-7474931FDE71} - {2A39507B-6073-402A-B2D4-CE466C27819F} = {594DD4E1-3404-4383-B554-7474931FDE71} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2EA5D015-FB83-4E41-A368-1D8DC542CA03} diff --git a/ManagedDoom.Maui/Game/GameControllerUserInput.cs b/ManagedDoom.Maui/Game/GameControllerUserInput.cs index 73cd3e6..913751c 100644 --- a/ManagedDoom.Maui/Game/GameControllerUserInput.cs +++ b/ManagedDoom.Maui/Game/GameControllerUserInput.cs @@ -11,26 +11,21 @@ public class GameControllerUserInput : IUserInput public GameControllerUserInput() { GameControllerManager.Current.GameControllerConnected += OnGameControllerConnected; - _ = GameControllerManager.Current.Initialize(); + _ = GameControllerManager.Current.StartDiscovery(); } private void OnGameControllerConnected(object? sender, GameControllerConnectedEventArgs args) { _gameController = args.GameController; - - _gameController - .When( - button: "ButtonNorth", - isPressed: _ => - { - _currentIndex = (_currentIndex + TicCmdButtons.WeaponShift) % (7 * TicCmdButtons.WeaponShift); - }) - .When( - button: "RightTrigger", - changesValue: f => - { - - }); + + _gameController.ButtonChanged += (o, eventArgs) => + { + if (eventArgs.ButtonName == _gameController.North.Name && + eventArgs.IsPressed) + { + _currentIndex = (_currentIndex + TicCmdButtons.WeaponShift) % (7 * TicCmdButtons.WeaponShift); + } + }; } public void BuildTicCmd(TicCmd cmd) @@ -40,25 +35,25 @@ public void BuildTicCmd(TicCmd cmd) return; } - if (_gameController.RightShoulder.Trigger > 0) + if (_gameController.RightShoulder.Trigger.Value > 0) { cmd.Buttons |= TicCmdButtons.Attack; } - if (_gameController.ButtonSouth) + if (_gameController.South.Value) { cmd.Buttons |= TicCmdButtons.Use; } - if (_gameController.ButtonNorth) + if (_gameController.North.Value) { cmd.Buttons |= TicCmdButtons.Change; cmd.Buttons |= (byte)_currentIndex; } - cmd.AngleTurn -= (short)(_gameController.RightStick.XAxis * 0x150); - cmd.ForwardMove += (sbyte)(_gameController.LeftStick.YAxis * 0x8); - cmd.SideMove += (sbyte)(_gameController.LeftStick.XAxis * 0x8); + cmd.AngleTurn -= (short)(_gameController.RightStick.XAxis.Value * 0x150); + cmd.ForwardMove += (sbyte)(_gameController.LeftStick.YAxis.Value * 0x8); + cmd.SideMove += (sbyte)(_gameController.LeftStick.XAxis.Value * 0x8); } public void Reset() diff --git a/ManagedDoom.Maui/Game/MauiGame.cs b/ManagedDoom.Maui/Game/MauiGame.cs index cc5c7d5..3c7e060 100644 --- a/ManagedDoom.Maui/Game/MauiGame.cs +++ b/ManagedDoom.Maui/Game/MauiGame.cs @@ -1,9 +1,6 @@ using DrawnUi.Maui.Draw; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Orbit.Input; +using KeyboardManager = Orbit.Input.KeyboardManager; namespace DrawnUi.Maui.Game { @@ -18,14 +15,14 @@ public class MauiGame : SkiaLayout public MauiGame() { - KeyboardManager.KeyDown += OnKeyboardDownEvent; - KeyboardManager.KeyUp += OnKeyboardUpEvent; + KeyboardManager.Current.KeyDown += OnKeyboardDownEvent; + KeyboardManager.Current.KeyUp += OnKeyboardUpEvent; } ~MauiGame() { - KeyboardManager.KeyUp -= OnKeyboardUpEvent; - KeyboardManager.KeyDown -= OnKeyboardDownEvent; + KeyboardManager.Current.KeyUp -= OnKeyboardUpEvent; + KeyboardManager.Current.KeyDown -= OnKeyboardDownEvent; } protected virtual void OnResumed() @@ -135,20 +132,20 @@ public virtual void OnKeyUp(MauiKey key) /// Do not use directly. It's public to be able to send keys to game manually if needed. /// /// - /// - public void OnKeyboardDownEvent(object sender, MauiKey key) + /// + public void OnKeyboardDownEvent(object sender, KeyboardKeyChangeEventArgs eventArgs) { - OnKeyDown(key); + OnKeyDown((MauiKey)eventArgs.Key); // TODO: We don't need both? } /// /// Do not use directly. It's public to be able to send keys to game manually if needed. /// /// - /// - public void OnKeyboardUpEvent(object sender, MauiKey key) + /// + public void OnKeyboardUpEvent(object sender, KeyboardKeyChangeEventArgs eventArgs) { - OnKeyUp(key); + OnKeyUp((MauiKey)eventArgs.Key);// TODO: We don't need both? } diff --git a/ManagedDoom.Maui/MainPage.xaml.cs b/ManagedDoom.Maui/MainPage.xaml.cs index f70ce57..2ce9196 100644 --- a/ManagedDoom.Maui/MainPage.xaml.cs +++ b/ManagedDoom.Maui/MainPage.xaml.cs @@ -1,5 +1,4 @@ using DrawnUi.Maui.Draw; -using Orbit.Input; namespace ManagedDoom.Maui { diff --git a/ManagedDoom.Maui/ManagedDoom.Maui.csproj b/ManagedDoom.Maui/ManagedDoom.Maui.csproj index 53baa3f..bac32fa 100644 --- a/ManagedDoom.Maui/ManagedDoom.Maui.csproj +++ b/ManagedDoom.Maui/ManagedDoom.Maui.csproj @@ -24,7 +24,7 @@ DOOM - com.mauigame.doom + com.tinysoft.doom 1.0 @@ -69,6 +69,7 @@ + @@ -84,7 +85,6 @@ - diff --git a/ManagedDoom.Maui/MauiProgram.cs b/ManagedDoom.Maui/MauiProgram.cs index bfe3d31..2a98c76 100644 --- a/ManagedDoom.Maui/MauiProgram.cs +++ b/ManagedDoom.Maui/MauiProgram.cs @@ -1,6 +1,7 @@ //#define DEBUG_MOBILE using DrawnUi.Maui.Draw; using Microsoft.Extensions.Logging; +using Orbit.Input; using Plugin.Maui.Audio; namespace ManagedDoom.Maui @@ -23,6 +24,7 @@ public static MauiApp CreateMauiApp() builder .UseMauiApp() .AddAudio() + .UseOrbitInput() #if DEBUG_MOBILE //don't need this to compile, it's for development to simulate mobile screen on desktop .UseDrawnUi(new() { @@ -38,7 +40,6 @@ public static MauiApp CreateMauiApp() #else .UseDrawnUi(new() { - UseDesktopKeyboard = true, MobileIsFullscreen = true, DesktopWindow = new() { diff --git a/ManagedDoom.Maui/Platforms/MacCatalyst/AppDelegate.cs b/ManagedDoom.Maui/Platforms/MacCatalyst/AppDelegate.cs index 3ccd39b..6da59e9 100644 --- a/ManagedDoom.Maui/Platforms/MacCatalyst/AppDelegate.cs +++ b/ManagedDoom.Maui/Platforms/MacCatalyst/AppDelegate.cs @@ -1,4 +1,7 @@ -using Foundation; +using System.Diagnostics; +using Foundation; +using Orbit.Input; +using UIKit; namespace ManagedDoom.Maui { @@ -6,5 +9,30 @@ namespace ManagedDoom.Maui public class AppDelegate : MauiUIApplicationDelegate { protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + + public override void PressesBegan(NSSet presses, UIPressesEvent evt) + { + KeyboardManager.Current.PressesBegan(presses, evt); + //base.PressesBegan(presses, evt); + } + + public override void PressesCancelled(NSSet presses, UIPressesEvent evt) + { + Trace.WriteLine(@"PressesCancelled"); + //KeyboardManager.Current.PressesCancelled(presses, evt); + //base.PressesCancelled(presses, evt); + } + + public override void PressesChanged(NSSet presses, UIPressesEvent evt) + { + Trace.WriteLine(@"PressesChanged"); + //base.PressesChanged(presses, evt); + } + + public override void PressesEnded(NSSet presses, UIPressesEvent evt) + { + KeyboardManager.Current.PressesEnded(presses, evt); + //base.PressesEnded(presses, evt); + } } } diff --git a/Orbit.Input/ButtonElement.cs b/Orbit.Input/ButtonElement.cs deleted file mode 100644 index cb65b31..0000000 --- a/Orbit.Input/ButtonElement.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace Orbit.Input; - -public abstract class ButtonElement -{ - private readonly GameController controller; - private readonly string elementName; - - protected ButtonElement(GameController controller, string elementName) - { - this.controller = controller; - this.elementName = elementName; - } - - protected void SetState(ref bool field, bool newValue, [CallerMemberName] string? buttonName = null) - { - ArgumentNullException.ThrowIfNull(buttonName); - - field = newValue; - - this.controller.RaiseButtonPressed(elementName + buttonName, field); - } - - protected void SetValue(ref float field, float newValue, [CallerMemberName] string? buttonName = null) - { - ArgumentNullException.ThrowIfNull(buttonName); - - field = newValue; - - this.controller.RaiseButtonValueChanged(elementName + buttonName, field); - } -} diff --git a/Orbit.Input/GameController.cs b/Orbit.Input/GameController.cs deleted file mode 100644 index d9e6e64..0000000 --- a/Orbit.Input/GameController.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace Orbit.Input; - -public partial class GameController -{ - public Stick Dpad { get; } - - public Stick LeftStick { get; } - - public Stick RightStick { get; } - - private bool buttonNorth; - - public bool ButtonNorth - { - get => buttonNorth; - set => SetState(ref this.buttonNorth, value); - } - - private bool buttonSouth; - - public bool ButtonSouth - { - get => buttonSouth; - set => SetState(ref this.buttonSouth, value); - } - - private bool buttonEast; - - public bool ButtonEast - { - get => buttonEast; - set => SetState(ref this.buttonEast, value); - } - - private bool buttonWest; - - public bool ButtonWest - { - get => buttonWest; - set => SetState(ref this.buttonWest, value); - } - - private bool pause; - - public bool Pause - { - get => pause; - set => SetState(ref this.pause, value); - } - - public Shoulder LeftShoulder { get; } - - public Shoulder RightShoulder { get; } - - public GameController When(string button, Action isPressed) - { - if (buttonPressedCallbacks.TryGetValue(button, out var callbacks) is false) - { - callbacks = [isPressed]; - } - else - { - callbacks.Add(isPressed); - } - - buttonPressedCallbacks[button] = callbacks; - return this; - } - - public GameController When(string button, Action changesValue) - { - if (buttonValueChangeCallbacks.TryGetValue(button, out var callbacks) is false) - { - callbacks = [changesValue]; - } - else - { - callbacks.Add(changesValue); - } - - buttonValueChangeCallbacks[button] = callbacks; - return this; - } - - private readonly IDictionary>> buttonPressedCallbacks = new Dictionary>>(); - private readonly IDictionary>> buttonValueChangeCallbacks = new Dictionary>>(); - - internal void RaiseButtonPressed(string button, bool isPressed) - { - if (buttonPressedCallbacks.TryGetValue(button, out var callbacks) is false) - { - return; - } - - foreach (var callback in callbacks) - { - callback.Invoke(isPressed); - } - } - - internal void RaiseButtonValueChanged(string button, float value) - { - if (buttonValueChangeCallbacks.TryGetValue(button, out var callbacks) is false) - { - return; - } - - foreach (var callback in callbacks) - { - callback.Invoke(value); - } - } - - private void SetState(ref bool field, bool newValue, [CallerMemberName] string? buttonName = null) - { - ArgumentNullException.ThrowIfNull(buttonName); - - field = newValue; - - this.RaiseButtonPressed(buttonName, field); - } -} diff --git a/Orbit.Input/GameControllerManager.cs b/Orbit.Input/GameControllerManager.cs deleted file mode 100644 index 9b9a29a..0000000 --- a/Orbit.Input/GameControllerManager.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Orbit.Input; - -public partial class GameControllerManager -{ - private List gameControllers = []; - - private static GameControllerManager? current; - - public static GameControllerManager Current => current ?? (current = new GameControllerManager()); - - public partial Task Initialize(); - - public IReadOnlyCollection GameControllers => gameControllers; - - public event EventHandler? GameControllerConnected; - - private void OnGameControllerConnected(GameController controller) - { - this.GameControllerConnected?.Invoke(this, new GameControllerConnectedEventArgs(controller)); - } -} - -public class GameControllerConnectedEventArgs : EventArgs -{ - public GameControllerConnectedEventArgs(GameController gameController) - { - GameController = gameController; - } - - public GameController GameController { get; } -} \ No newline at end of file diff --git a/Orbit.Input/Orbit.Input.csproj b/Orbit.Input/Orbit.Input.csproj deleted file mode 100644 index 851f9a4..0000000 --- a/Orbit.Input/Orbit.Input.csproj +++ /dev/null @@ -1,53 +0,0 @@ - - - - net9.0-android;net9.0-ios;net9.0-maccatalyst - $(TargetFrameworks);net9.0-windows10.0.19041.0 - - - true - true - enable - enable - - 15.0 - 15.0 - 21.0 - 10.0.17763.0 - 10.0.17763.0 - 6.5 - Orbit.Input - - - - Shaun Lawrence - The Orbit input library provides support for game controller input. - - .NET MAUI, Game Engine, 2D Game - - Bijington.Orbit.Input - - https://github.com/bijington/orbit - https://github.com/bijington/orbit - Orbit Input - Copyright(c) 2025 Shaun Lawrence - - MIT - true - true - true - snupkg - true - true - Release;Debug - - - - 1701;1702;CA1416 - - - - - - - diff --git a/Orbit.Input/Platforms/Android/GameController.cs b/Orbit.Input/Platforms/Android/GameController.cs deleted file mode 100644 index 90506d6..0000000 --- a/Orbit.Input/Platforms/Android/GameController.cs +++ /dev/null @@ -1,175 +0,0 @@ -using Android.Views; - -namespace Orbit.Input; - -// All the code in this file is only included on Android. -public partial class GameController -{ - public GameController(int deviceId) - { - Dpad = new Stick(this, nameof(Dpad)); - LeftStick = new Stick(this, nameof(LeftStick)); - RightStick = new Stick(this, nameof(RightStick)); - - LeftShoulder = new Shoulder(this, nameof(LeftShoulder)); - RightShoulder = new Shoulder(this, nameof(RightShoulder)); - } - - public void OnGenericMotionEvent(MotionEvent motionEvent) - { - // Check that the event came from a game controller - if (motionEvent.Source.HasFlag(InputSourceType.Joystick) && - motionEvent.Action == MotionEventActions.Move) - { - // Process all historical movement samples in the batch - var historySize = motionEvent.HistorySize; - - // Process the movements starting from the - // earliest historical position in the batch - for (var i = 0; i < historySize; i++) - { - // Process the event at historical position i - ProcessJoystickInput(motionEvent, i); - } - - // Process the current movement sample in the batch (position -1) - ProcessJoystickInput(motionEvent, -1); - } - } - - private static float GetCenteredAxis(MotionEvent motionEvent, InputDevice device, Axis axis, int historyPos) - { - var range = device.GetMotionRange(axis, motionEvent.Source); - - // A joystick at rest does not always report an absolute position of - // (0,0). Use the getFlat() method to determine the range of values - // bounding the joystick axis center. - if (range is not null) - { - var flat = range.Flat; - var value = historyPos < 0 ? motionEvent.GetAxisValue(axis): - motionEvent.GetHistoricalAxisValue(axis, historyPos); - - // Ignore axis values that are within the 'flat' region of the - // joystick axis center. - if (Math.Abs(value) > flat) - { - return value; - } - } - return 0; - } - - private void ProcessJoystickInput(MotionEvent motionEvent, int historyPos) - { - var inputDevice = motionEvent.Device; - - if (inputDevice is null) - { - return; - } - - LeftStick.XAxis = GetCenteredAxis(motionEvent, inputDevice, Axis.X, historyPos); - LeftStick.YAxis = GetCenteredAxis(motionEvent, inputDevice, Axis.Y, historyPos); - - RightStick.XAxis = GetCenteredAxis(motionEvent, inputDevice, Axis.Z, historyPos); - RightStick.YAxis = GetCenteredAxis(motionEvent, inputDevice, Axis.Rz, historyPos); - - LeftShoulder.Trigger = GetCenteredAxis(motionEvent, inputDevice, Axis.Ltrigger, historyPos); - RightShoulder.Trigger = GetCenteredAxis(motionEvent, inputDevice, Axis.Rtrigger, historyPos); - } - - public void OnKeyDown(InputEvent inputEvent) - { - if (!IsDpadDevice(inputEvent)) - { - return; - } - - switch (inputEvent) - { - // If the input event is a MotionEvent, check its hat axis values. - case MotionEvent motionEvent: - // Use the hat axis value to find the D-pad direction - Dpad.XAxis = motionEvent.GetAxisValue(Axis.HatX); - Dpad.YAxis = motionEvent.GetAxisValue(Axis.HatY); - break; - - // If the input event is a KeyEvent, check its key code. - // Use the key code to find the D-pad direction. - case KeyEvent { KeyCode: Keycode.DpadLeft }: - Dpad.XAxis = -1; - break; - - case KeyEvent { KeyCode: Keycode.DpadRight }: - Dpad.XAxis = 1; - break; - - case KeyEvent { KeyCode: Keycode.DpadDown }: - Dpad.YAxis = -1; - break; - - case KeyEvent { KeyCode: Keycode.DpadUp }: - Dpad.YAxis = 1; - break; - - case KeyEvent { KeyCode: Keycode.ButtonL1 }: - LeftShoulder.Button = true; - break; - - case KeyEvent { KeyCode: Keycode.ButtonR1 }: - RightShoulder.Button = true; - break; - } - } - - public void OnKeyUp(InputEvent inputEvent) - { - if (!IsDpadDevice(inputEvent)) - { - return; - } - - switch (inputEvent) - { - // If the input event is a MotionEvent, check its hat axis values. - case MotionEvent: - // Use the hat axis value to find the D-pad direction - Dpad.XAxis = 0; - Dpad.YAxis = 0; - break; - - // If the input event is a KeyEvent, check its key code. - // Use the key code to find the D-pad direction. - case KeyEvent { KeyCode: Keycode.DpadLeft }: - Dpad.XAxis = 0; - break; - - case KeyEvent { KeyCode: Keycode.DpadRight }: - Dpad.XAxis = 0; - break; - - case KeyEvent { KeyCode: Keycode.DpadDown }: - Dpad.YAxis = 0; - break; - - case KeyEvent { KeyCode: Keycode.DpadUp }: - Dpad.YAxis = 0; - break; - - case KeyEvent { KeyCode: Keycode.ButtonL1 }: - LeftShoulder.Button = false; - break; - - case KeyEvent { KeyCode: Keycode.ButtonR1 }: - RightShoulder.Button = false; - break; - } - } - - private static bool IsDpadDevice(InputEvent inputEvent) - { - // Check that input comes from a device with directional pads. - return inputEvent.Source.HasFlag(InputSourceType.Dpad); - } -} diff --git a/Orbit.Input/Platforms/Android/GameControllerManager.cs b/Orbit.Input/Platforms/Android/GameControllerManager.cs deleted file mode 100644 index 79b8c2b..0000000 --- a/Orbit.Input/Platforms/Android/GameControllerManager.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Android.Views; - -namespace Orbit.Input; - -public partial class GameControllerManager -{ - private GameControllerManager() - { - } - - public partial Task Initialize() - { - var deviceIds = InputDevice.GetDeviceIds(); - - if (deviceIds is null) - { - return Task.CompletedTask; - } - - foreach (var deviceId in deviceIds) - { - var device = InputDevice.GetDevice(deviceId); - - if (device is null) - { - continue; - } - - var sources = device.Sources; - - if (sources.HasFlag(InputSourceType.Gamepad) || sources.HasFlag(InputSourceType.Joystick)) - { - gameControllers.Add(new GameController(deviceId)); - } - } - - return Task.CompletedTask; - } - - public void OnGenericMotionEvent(MotionEvent? e) - { - if (e is null) - { - return; - } - - // TODO: thread safety? - foreach (var controller in gameControllers) - { - controller.OnGenericMotionEvent(e); - } - } - - public void OnKeyDown(Keycode keyCode, KeyEvent? e) - { - if (e is null) - { - return; - } - - // TODO: thread safety? - foreach (var controller in gameControllers) - { - controller.OnKeyDown(e); - } - } - - public void OnKeyUp(Keycode keyCode, KeyEvent? e) - { - if (e is null) - { - return; - } - - // TODO: thread safety? - foreach (var controller in gameControllers) - { - controller.OnKeyUp(e); - } - } -} \ No newline at end of file diff --git a/Orbit.Input/Platforms/MacCatalyst/GameController.cs b/Orbit.Input/Platforms/MacCatalyst/GameController.cs deleted file mode 100644 index 97de44b..0000000 --- a/Orbit.Input/Platforms/MacCatalyst/GameController.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Diagnostics; -using Foundation; -using GameController; - -namespace Orbit.Input; - -public partial class GameController -{ - private readonly GCController controller; - - public GameController(GCController controller) - { - this.controller = controller; - - Dpad = new Stick(this, nameof(Dpad)); - LeftStick = new Stick(this, nameof(LeftStick)); - RightStick = new Stick(this, nameof(RightStick)); - - LeftShoulder = new Shoulder(this, nameof(LeftShoulder)); - RightShoulder = new Shoulder(this, nameof(RightShoulder)); - - controller.PhysicalInputProfile.ValueDidChangeHandler += Changed; - } - - private void Changed(GCPhysicalInputProfile gamepad, GCControllerElement element) - { - Debug.WriteLine($"{element}"); - - switch (element) - { - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button A")): - ButtonSouth = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button B")): - ButtonEast = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button X")): - ButtonWest = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button Y")): - ButtonNorth = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Right Shoulder")): - RightShoulder.Button = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Right Trigger")): - RightShoulder.Trigger = buttonInput.Value; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Left Shoulder")): - LeftShoulder.Button = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Left Trigger")): - LeftShoulder.Trigger = buttonInput.Value; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button Menu")): - Pause = buttonInput.IsPressed; - break; - - case GCControllerDirectionPad directionPad when directionPad.Aliases.Contains(new NSString("Left Thumbstick")): - LeftStick.XAxis = directionPad.XAxis.Value; - LeftStick.YAxis = directionPad.YAxis.Value; - break; - - case GCControllerDirectionPad directionPad when directionPad.Aliases.Contains(new NSString("Right Thumbstick")): - RightStick.XAxis = directionPad.XAxis.Value; - RightStick.YAxis = directionPad.YAxis.Value; - break; - } - } -} \ No newline at end of file diff --git a/Orbit.Input/Platforms/MacCatalyst/GameControllerManager.cs b/Orbit.Input/Platforms/MacCatalyst/GameControllerManager.cs deleted file mode 100644 index 0bc8fdf..0000000 --- a/Orbit.Input/Platforms/MacCatalyst/GameControllerManager.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Foundation; - -using GameController; - -namespace Orbit.Input; - -public partial class GameControllerManager -{ - private GameControllerManager() - { - GCController.Notifications.ObserveDidConnect(ConnectToController); - } - - public partial async Task Initialize() - { - await GCController.StartWirelessControllerDiscoveryAsync(); - } - - private void ConnectToController(object? sender, NSNotificationEventArgs e) - { - if (e.Notification.Object is GCController controller) - { - var gameController = new GameController(controller); - gameControllers.Add(gameController); - OnGameControllerConnected(gameController); - } - } -} \ No newline at end of file diff --git a/Orbit.Input/Platforms/Tizen/PlatformClass1.cs b/Orbit.Input/Platforms/Tizen/PlatformClass1.cs deleted file mode 100644 index 3397f80..0000000 --- a/Orbit.Input/Platforms/Tizen/PlatformClass1.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Orbit.Controller -{ - // All the code in this file is only included on Tizen. - public class PlatformClass1 - { - } -} \ No newline at end of file diff --git a/Orbit.Input/Platforms/Windows/GameController.cs b/Orbit.Input/Platforms/Windows/GameController.cs deleted file mode 100644 index 4686f8e..0000000 --- a/Orbit.Input/Platforms/Windows/GameController.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Windows.Gaming.Input; - -namespace Orbit.Input; - -public partial class GameController -{ - private readonly Gamepad gamepad; - - public GameController(Gamepad gamepad) - { - this.gamepad = gamepad; - - Dpad = new Stick(this, nameof(Dpad)); - LeftStick = new Stick(this, nameof(LeftStick)); - RightStick = new Stick(this, nameof(RightStick)); - - LeftShoulder = new Shoulder(this, nameof(LeftShoulder)); - RightShoulder = new Shoulder(this, nameof(RightShoulder)); - } - - private void Update() - { - var reading = gamepad.GetCurrentReading(); - - LeftStick.XAxis = (float)reading.LeftThumbstickX; - LeftStick.YAxis = (float)reading.LeftThumbstickY; - - RightStick.XAxis = (float)reading.RightThumbstickX; - RightStick.YAxis = (float)reading.RightThumbstickY; - - LeftShoulder.Button = reading.Buttons.HasFlag(GamepadButtons.LeftShoulder); - LeftShoulder.Trigger = (float)reading.LeftTrigger; - - RightShoulder.Button = reading.Buttons.HasFlag(GamepadButtons.RightShoulder); - RightShoulder.Trigger = (float)reading.RightTrigger; - - ButtonNorth = reading.Buttons.HasFlag(GamepadButtons.Y); - ButtonEast = reading.Buttons.HasFlag(GamepadButtons.B); - ButtonSouth = reading.Buttons.HasFlag(GamepadButtons.A); - ButtonWest = reading.Buttons.HasFlag(GamepadButtons.X); - - Dpad.XAxis = reading.Buttons.HasFlag(GamepadButtons.DPadRight) ? 1f : reading.Buttons.HasFlag(GamepadButtons.DPadLeft) ? -1f : 0f; - Dpad.YAxis = reading.Buttons.HasFlag(GamepadButtons.DPadDown) ? 1f : reading.Buttons.HasFlag(GamepadButtons.DPadUp) ? -1f : 0f; - } -} \ No newline at end of file diff --git a/Orbit.Input/Platforms/Windows/GameControllerManager.cs b/Orbit.Input/Platforms/Windows/GameControllerManager.cs deleted file mode 100644 index 7be8907..0000000 --- a/Orbit.Input/Platforms/Windows/GameControllerManager.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Windows.Gaming.Input; - -namespace Orbit.Input; - -public partial class GameControllerManager -{ - private GameControllerManager() - { - } - - public partial Task Initialize() - { - var gamePads = Gamepad.Gamepads; - - foreach (var gamePad in gamePads) - { - gameControllers.Add(new GameController(gamePad)); - } - - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/Orbit.Input/Platforms/iOS/GameController.cs b/Orbit.Input/Platforms/iOS/GameController.cs deleted file mode 100644 index 97de44b..0000000 --- a/Orbit.Input/Platforms/iOS/GameController.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Diagnostics; -using Foundation; -using GameController; - -namespace Orbit.Input; - -public partial class GameController -{ - private readonly GCController controller; - - public GameController(GCController controller) - { - this.controller = controller; - - Dpad = new Stick(this, nameof(Dpad)); - LeftStick = new Stick(this, nameof(LeftStick)); - RightStick = new Stick(this, nameof(RightStick)); - - LeftShoulder = new Shoulder(this, nameof(LeftShoulder)); - RightShoulder = new Shoulder(this, nameof(RightShoulder)); - - controller.PhysicalInputProfile.ValueDidChangeHandler += Changed; - } - - private void Changed(GCPhysicalInputProfile gamepad, GCControllerElement element) - { - Debug.WriteLine($"{element}"); - - switch (element) - { - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button A")): - ButtonSouth = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button B")): - ButtonEast = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button X")): - ButtonWest = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button Y")): - ButtonNorth = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Right Shoulder")): - RightShoulder.Button = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Right Trigger")): - RightShoulder.Trigger = buttonInput.Value; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Left Shoulder")): - LeftShoulder.Button = buttonInput.IsPressed; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Left Trigger")): - LeftShoulder.Trigger = buttonInput.Value; - break; - - case GCControllerButtonInput buttonInput when buttonInput.Aliases.Contains(new NSString("Button Menu")): - Pause = buttonInput.IsPressed; - break; - - case GCControllerDirectionPad directionPad when directionPad.Aliases.Contains(new NSString("Left Thumbstick")): - LeftStick.XAxis = directionPad.XAxis.Value; - LeftStick.YAxis = directionPad.YAxis.Value; - break; - - case GCControllerDirectionPad directionPad when directionPad.Aliases.Contains(new NSString("Right Thumbstick")): - RightStick.XAxis = directionPad.XAxis.Value; - RightStick.YAxis = directionPad.YAxis.Value; - break; - } - } -} \ No newline at end of file diff --git a/Orbit.Input/Platforms/iOS/GameControllerManager.cs b/Orbit.Input/Platforms/iOS/GameControllerManager.cs deleted file mode 100644 index 0bc8fdf..0000000 --- a/Orbit.Input/Platforms/iOS/GameControllerManager.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Foundation; - -using GameController; - -namespace Orbit.Input; - -public partial class GameControllerManager -{ - private GameControllerManager() - { - GCController.Notifications.ObserveDidConnect(ConnectToController); - } - - public partial async Task Initialize() - { - await GCController.StartWirelessControllerDiscoveryAsync(); - } - - private void ConnectToController(object? sender, NSNotificationEventArgs e) - { - if (e.Notification.Object is GCController controller) - { - var gameController = new GameController(controller); - gameControllers.Add(gameController); - OnGameControllerConnected(gameController); - } - } -} \ No newline at end of file diff --git a/Orbit.Input/Shoulder.cs b/Orbit.Input/Shoulder.cs deleted file mode 100644 index 726468b..0000000 --- a/Orbit.Input/Shoulder.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Orbit.Input; - -public class Shoulder : ButtonElement -{ - public Shoulder(GameController controller, string name) : base(controller, name) - { - } - - private bool button; - - public bool Button - { - get => button; - set => SetState(ref this.button, value); - } - - private float trigger; - - public float Trigger - { - get => trigger; - set => SetValue(ref this.trigger, value); - } -} diff --git a/Orbit.Input/Stick.cs b/Orbit.Input/Stick.cs deleted file mode 100644 index e8343b9..0000000 --- a/Orbit.Input/Stick.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Orbit.Input; - -public class Stick : ButtonElement -{ - public Stick(GameController controller, string name) : base(controller, name) - { - } - - private float xAxis; - - public float XAxis - { - get => xAxis; - set => SetValue(ref this.xAxis, value); - } - - private float yAxis; - - public float YAxis - { - get => yAxis; - set => SetValue(ref this.yAxis, value); - } -} From 0b562dcc7f62ec3d828c4b439fb1e8262b7d34d3 Mon Sep 17 00:00:00 2001 From: Nick Kovalsky Date: Sat, 8 Mar 2025 07:26:59 +0300 Subject: [PATCH 6/8] latest nugets --- ManagedDoom.Maui/Game/MauiDoom.cs | 27 ++++++++---------------- ManagedDoom.Maui/Game/MauiSound.cs | 4 ++-- ManagedDoom.Maui/ManagedDoom.Maui.csproj | 4 ++-- ManagedDoom.Maui/MauiProgram.cs | 3 +-- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/ManagedDoom.Maui/Game/MauiDoom.cs b/ManagedDoom.Maui/Game/MauiDoom.cs index 5b7530d..a3e2c5d 100644 --- a/ManagedDoom.Maui/Game/MauiDoom.cs +++ b/ManagedDoom.Maui/Game/MauiDoom.cs @@ -85,14 +85,9 @@ bool OnUiCommand(UiCommand command) /// /// Paint the game, invoked every frame /// - /// - /// - /// - /// - protected override void Paint(SkiaDrawingContext context, SKRect destination, float scale, object arguments) + protected override void Paint(DrawingContext ctx) { - //base.Paint(ctx, destination, scale, arguments); - will not draw background color/gradient in this case - + frameCount++; //we draw the game every frame but sending the frameFrac 0 or 1 to interpolate updates @@ -115,21 +110,20 @@ protected override void Paint(SkiaDrawingContext context, SKRect destination, fl } //render doom - _video.Render(context.Canvas, destination, _doom, frameFrac); + _video.Render(ctx.Context.Canvas, ctx.Destination, _doom, frameFrac); //render custom ui - var drawnChildrenCount = DrawViews(context, DrawingRect, scale);//GetDoomScale(DrawingRect, scale)); + var drawnChildrenCount = DrawViews(ctx);//GetDoomScale(DrawingRect, scale)); } - /// /// Setup everything /// /// /// /// - void Init(SkiaDrawingContext context, SKRect destination, float scale) + void Init(DrawingContext context) { frameCount = -1; @@ -143,7 +137,7 @@ void Init(SkiaDrawingContext context, SKRect destination, float scale) var targetFps = 35 * _config.video_fpsscale; //todo apply to gameloop? - _video = new MauiVideo(_config, _content, context); + _video = new MauiVideo(_config, _content, context.Context); if (!args.nosound.Present && MauiProgram.UseSound) { @@ -189,19 +183,16 @@ void Reload() /// /// Initialise the game when ready to draw, one time /// - /// - /// - /// - protected override void Draw(SkiaDrawingContext context, SKRect destination, float scale) + protected override void Draw(DrawingContext context) { if (!_initialized) { _initialized = true; - Init(context, destination, scale); + Init(context); StartLoop(); } - base.Draw(context, destination, scale); + base.Draw(context); } public override void OnDisposing() diff --git a/ManagedDoom.Maui/Game/MauiSound.cs b/ManagedDoom.Maui/Game/MauiSound.cs index 607b0d8..e32d809 100644 --- a/ManagedDoom.Maui/Game/MauiSound.cs +++ b/ManagedDoom.Maui/Game/MauiSound.cs @@ -264,7 +264,7 @@ public void Update() var channel = soundMixer.Channels[i]; SetParam(channel, info); channel.Speed = GetPitch(info.Type, info.Reserved); - soundMixer.Play(i, buffers[(int)info.Reserved], loop: false); + soundMixer.Play(i, loop: false); info.Playing = info.Reserved; info.Reserved = Sfx.NONE; } @@ -280,7 +280,7 @@ public void Update() uiChannel.Volume = masterVolumeDecay; uiChannel.SetSource(buffers[(int)uiReserved].GetAudioStream()); - soundMixer.Play(channelCount - 1, buffers[(int)uiReserved], loop: false); + soundMixer.Play(channelCount - 1, loop: false); uiReserved = Sfx.NONE; } diff --git a/ManagedDoom.Maui/ManagedDoom.Maui.csproj b/ManagedDoom.Maui/ManagedDoom.Maui.csproj index bac32fa..7c3c238 100644 --- a/ManagedDoom.Maui/ManagedDoom.Maui.csproj +++ b/ManagedDoom.Maui/ManagedDoom.Maui.csproj @@ -68,7 +68,7 @@ - + @@ -76,7 +76,7 @@ - + + + + - - - - - From 3a947293e4656d74879dd6d0049a2bd1e5f8b602 Mon Sep 17 00:00:00 2001 From: Nick Kovalsky Date: Sun, 9 Mar 2025 08:03:24 +0300 Subject: [PATCH 8/8] Update MauiGame.cs --- ManagedDoom.Maui/Game/MauiGame.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ManagedDoom.Maui/Game/MauiGame.cs b/ManagedDoom.Maui/Game/MauiGame.cs index 3c7e060..10e85cb 100644 --- a/ManagedDoom.Maui/Game/MauiGame.cs +++ b/ManagedDoom.Maui/Game/MauiGame.cs @@ -19,10 +19,12 @@ public MauiGame() KeyboardManager.Current.KeyUp += OnKeyboardUpEvent; } - ~MauiGame() + public override void OnDisposing() { - KeyboardManager.Current.KeyUp -= OnKeyboardUpEvent; - KeyboardManager.Current.KeyDown -= OnKeyboardDownEvent; + KeyboardManager.Current.KeyDown -= OnKeyboardUpEvent; + KeyboardManager.Current.KeyUp -= OnKeyboardDownEvent; + + base.OnDisposing(); } protected virtual void OnResumed()