diff --git a/Source/MonoGame.Extended/InputEvents/Input.cs b/Source/MonoGame.Extended/InputEvents/Input.cs new file mode 100644 index 00000000..00686a35 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/Input.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace MonoGame.Extended.InputEvents +{ + public abstract class Input + { + protected Game Game { get; set; } + + protected Input(Game game) + { + Game = game; + } + + /*####################################################################*/ + /* Input Events */ + /*####################################################################*/ + + public abstract void Update(GameTime gameTime); + + /*####################################################################*/ + /* Keyboard Events */ + /*####################################################################*/ + + public abstract event EventHandler CharacterTyped; + + public abstract event EventHandler KeyDown; + public abstract event EventHandler KeyUp; + + /*####################################################################*/ + /* Mouse Events */ + /*####################################################################*/ + + public abstract event EventHandler MouseMoved; + public abstract event EventHandler MouseDoubleClick; + + public abstract event EventHandler MouseDown; + public abstract event EventHandler MouseUp; + + public abstract event EventHandler MouseWheel; + + /*####################################################################*/ + /* Touch Events */ + /*####################################################################*/ + + public abstract event EventHandler TouchBegan; + public abstract event EventHandler TouchMoved; + public abstract event EventHandler TouchEnded; + public abstract event EventHandler TouchCancelled; + } +} diff --git a/Source/MonoGame.Extended/InputEvents/MonoGame.Input/EventArgs/MonoGameMouseEventArgs.cs b/Source/MonoGame.Extended/InputEvents/MonoGame.Input/EventArgs/MonoGameMouseEventArgs.cs new file mode 100644 index 00000000..1927d528 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/MonoGame.Input/EventArgs/MonoGameMouseEventArgs.cs @@ -0,0 +1,23 @@ +using System; + +namespace Microsoft.Xna.Framework.Input +{ + internal class MonoGameMouseEventArgs : MouseEventArgs + { + internal TimeSpan Time { get; set; } + + internal MouseState Previous { get; private set; } + internal MouseState Current { get; private set; } + + internal MonoGameMouseEventArgs(int x, int y, TimeSpan time, MouseState previous, MouseState current, + MouseButton button = MouseButton.None, int? value = null, int? delta = null) + : base(x, y, button, value, delta) + { + X = x; + Y = y; + Time = time; + Previous = previous; + Current = current; + } + } +} diff --git a/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Keyboard/MonoGameMouseEvents.cs b/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Keyboard/MonoGameMouseEvents.cs new file mode 100644 index 00000000..e59f0b77 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Keyboard/MonoGameMouseEvents.cs @@ -0,0 +1,101 @@ +using System; +using System.Linq; + +namespace Microsoft.Xna.Framework.Input +{ + internal class MonoGameKeyboardEvents + { + internal event EventHandler CharacterTyped; + internal event EventHandler KeyPressed; + internal event EventHandler KeyReleased; + + + private static int InitialDelay { get; set; } + private static int RepeatDelay { get; set; } + + private Keys _lastKey; + private TimeSpan _lastPress; + private bool _isInitial; + + private KeyboardState _previous; + + public MonoGameKeyboardEvents() + { + InitialDelay = 800; + RepeatDelay = 50; + } + + public void Update(GameTime gameTime) + { + var current = Keyboard.GetState(); + + if (!current.IsKeyDown(Keys.LeftAlt) + && !current.IsKeyDown(Keys.RightAlt)) + { + foreach (var key in Enum.GetValues(typeof (Keys)) + .Cast() + .Where(key => current.IsKeyDown(key) && _previous.IsKeyUp(key))) + { + var args = new KeyboardKeyEventArgs(key); + + OnKeyPressed(this, args); + + _lastKey = key; + _lastPress = gameTime.TotalGameTime; + _isInitial = true; + } + } + + foreach (var key in + Enum.GetValues(typeof(Keys)) + .Cast() + .Where(key => current.IsKeyUp(key) && _previous.IsKeyDown(key))) + { + OnKeyReleased(this, new KeyboardKeyEventArgs(key)); + } + + var elapsedTime = (gameTime.TotalGameTime - _lastPress).TotalMilliseconds; + + if (current.IsKeyDown(_lastKey) && + ((_isInitial && elapsedTime > InitialDelay) || + (!_isInitial && elapsedTime > RepeatDelay))) + { + OnCharacterPressed(this, new KeyboardKeyEventArgs(_lastKey)); + + _lastPress = gameTime.TotalGameTime; + _isInitial = false; + } + + _previous = current; + } + + private void OnKeyPressed(object sender, KeyboardKeyEventArgs args) + { + if (KeyPressed != null) + { + KeyPressed(sender, args); + } + + OnCharacterPressed(sender, args); + } + + private void OnCharacterPressed(object sender, KeyboardKeyEventArgs args) + { + if (CharacterTyped == null) { return; } + + var character = KeyboardUtil.ToChar(args.Key, args.Modifiers); + if (character.HasValue) + { + CharacterTyped(this, new KeyboardCharacterEventArgs(character.Value)); + } + } + + private void OnKeyReleased(object sender, KeyboardKeyEventArgs args) + { + if (KeyReleased != null) + { + KeyReleased(sender, args); + } + } + } +} \ No newline at end of file diff --git a/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Mouse/MonoGameMouseEvents.cs b/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Mouse/MonoGameMouseEvents.cs new file mode 100644 index 00000000..48a6d6c3 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Mouse/MonoGameMouseEvents.cs @@ -0,0 +1,238 @@ +using System; + +namespace Microsoft.Xna.Framework.Input +{ + internal class MonoGameMouseEvents + { + internal event EventHandler ButtonReleased; + internal event EventHandler ButtonPressed; + internal event EventHandler ButtonDoubleClicked; + internal event EventHandler MouseMoved; + internal event EventHandler MouseWheelMoved; + + private MouseState _previous; + private MonoGameMouseEventArgs _lastClick; + + private readonly int _doubleClickMaxTimeDelta; + private readonly int _doubleClickMaxMovementDelta; + + internal MonoGameMouseEvents(int doubleClickMaxTimeDelta, int doubleClickMaxMovementDelta) + { + _doubleClickMaxTimeDelta = doubleClickMaxTimeDelta; + _doubleClickMaxMovementDelta = doubleClickMaxMovementDelta; + + _lastClick = new MonoGameMouseEventArgs( + -1, + -1, + new TimeSpan(), + Mouse.GetState(), + Mouse.GetState()); + } + + internal void Update(GameTime gameTime) + { + var current = Mouse.GetState(); + + // Check button press events. + if (current.LeftButton == ButtonState.Pressed + && _previous.LeftButton == ButtonState.Released) + { + OnButtonPressed(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.Left)); + } + + if (current.MiddleButton == ButtonState.Pressed + && _previous.MiddleButton == ButtonState.Released) + { + OnButtonPressed(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.Middle)); + } + + if (current.RightButton == ButtonState.Pressed + && _previous.RightButton == ButtonState.Released) + { + OnButtonPressed(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.Right)); + } + + if (current.XButton1 == ButtonState.Pressed + && _previous.XButton1 == ButtonState.Released) + { + OnButtonPressed(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.XButton1)); + } + + if (current.XButton2 == ButtonState.Pressed + && _previous.XButton2 == ButtonState.Released) + { + OnButtonPressed(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.XButton2)); + } + + // Check button releases. + if (current.LeftButton == ButtonState.Released + && _previous.LeftButton == ButtonState.Pressed) + { + OnButtonReleased(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.Left)); + } + + if (current.MiddleButton == ButtonState.Released + && _previous.MiddleButton == ButtonState.Pressed) + { + OnButtonReleased(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.Middle)); + } + + if (current.RightButton == ButtonState.Released + && _previous.RightButton == ButtonState.Pressed) + { + OnButtonReleased(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.Right)); + } + + if (current.XButton1 == ButtonState.Released + && _previous.XButton1 == ButtonState.Pressed) + { + OnButtonReleased(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.XButton1)); + } + + if (current.XButton2 == ButtonState.Released + && _previous.XButton2 == ButtonState.Pressed) + { + OnButtonReleased(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.XButton2)); + } + + // Check for any sort of mouse movement. If a button is down, it's a drag, + // otherwise it's a move. + if (_previous.X != current.X || _previous.Y != current.Y) + { + OnMouseMoved(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current)); + } + + // Handle mouse wheel events. + if (_previous.ScrollWheelValue != current.ScrollWheelValue) + { + var value = current.ScrollWheelValue / 120; + var delta = (current.ScrollWheelValue - _previous.ScrollWheelValue) / 120; + OnMouseWheelMoved(this, new MonoGameMouseEventArgs( + current.X, + current.Y, + gameTime.TotalGameTime, + _previous, + current, + MouseButton.None, + value, + delta)); + } + + _previous = current; + } + + private void OnButtonReleased(object sender, MonoGameMouseEventArgs args) + { + if (ButtonReleased != null) + { + ButtonReleased(sender, args); + } + } + + private void OnButtonPressed(object sender, MonoGameMouseEventArgs args) + { + // If this click is within the right time and position of the last click, + // raise a double-click event as well. + if (ButtonDoubleClicked != null && + _lastClick.Button == args.Button && + (args.Time - _lastClick.Time).TotalMilliseconds < _doubleClickMaxTimeDelta && + DistanceBetween(args.Current, _lastClick.Current) < _doubleClickMaxMovementDelta) + { + ButtonDoubleClicked(sender, args); + //args.Time = new TimeSpan(0); + } + else if (ButtonPressed != null) + { + ButtonPressed(sender, args); + } + + _lastClick = args; + } + + private static int DistanceBetween(MouseState a, MouseState b) + { + return Math.Abs(a.X - b.X) + Math.Abs(a.Y - b.Y); + } + + private void OnMouseMoved(object sender, MouseEventArgs args) + { + if (MouseMoved != null) + { + MouseMoved(sender, args); + } + } + + private void OnMouseWheelMoved(object sender, MouseEventArgs args) + { + if (MouseWheelMoved != null) + { + MouseWheelMoved(sender, args); + } + } + } +} diff --git a/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Touch/MonoGameTouchEvents.cs b/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Touch/MonoGameTouchEvents.cs new file mode 100644 index 00000000..5265f7fb --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/MonoGame.Input/Touch/MonoGameTouchEvents.cs @@ -0,0 +1,70 @@ +using System; +using Microsoft.Xna.Framework.Input.Touch; + +namespace Microsoft.Xna.Framework.Input +{ + public class MonoGameTouchEvents + { + internal event EventHandler TouchBegan; + internal event EventHandler TouchMoved; + internal event EventHandler TouchEnded; + internal event EventHandler TouchCancelled; + + + public void Update(GameTime gameTime) + { + TouchCollection touchCollection = TouchPanel.GetState(); + foreach (TouchLocation tl in touchCollection) + { + switch (tl.State) + { + case TouchLocationState.Pressed: + OnTouchBegan(this, new TouchEventArgs(tl)); + break; + case TouchLocationState.Moved: + OnTouchMoved(this, new TouchEventArgs(tl)); + break; + case TouchLocationState.Released: + OnTouchEnded(this, new TouchEventArgs(tl)); + break; + case TouchLocationState.Invalid: + OnTouchCancelled(this, new TouchEventArgs(tl)); + break; + } + } + } + + private void OnTouchBegan(object sender, TouchEventArgs args) + { + if (TouchBegan != null) + { + TouchBegan(sender, args); + } + } + + private void OnTouchMoved(object sender, TouchEventArgs args) + { + if (TouchMoved != null) + { + TouchMoved(sender, args); + } + } + + private void OnTouchEnded(object sender, TouchEventArgs args) + { + if (TouchEnded != null) + { + TouchEnded(sender, args); + } + } + + private void OnTouchCancelled(object sender, TouchEventArgs args) + { + if (TouchCancelled != null) + { + TouchCancelled(sender, args); + } + } + } +} + diff --git a/Source/MonoGame.Extended/InputEvents/MonoGameInput.cs b/Source/MonoGame.Extended/InputEvents/MonoGameInput.cs new file mode 100644 index 00000000..a94d59fb --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/MonoGameInput.cs @@ -0,0 +1,131 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace MonoGame.Extended.InputEvents +{ + public sealed class MonoGameInput : Input + { + private readonly MonoGameMouseEvents _mouseEvents; + private readonly MonoGameKeyboardEvents _monoGameKeyboardEvents; + private readonly MonoGameTouchEvents _monoGameTouchEvents; + + public MonoGameInput(Game game, int? doubleClickMaxTimeDelta = null, + int? doubleClickMaxMovementDelta = null) : base(game) + { + if (doubleClickMaxTimeDelta == null) + { + doubleClickMaxTimeDelta = 500; //Windows default + } + + if (doubleClickMaxMovementDelta == null) + { + doubleClickMaxMovementDelta = 2; //Windows default + } + + _mouseEvents = new MonoGameMouseEvents( + doubleClickMaxTimeDelta.Value, + doubleClickMaxTimeDelta.Value); + + _monoGameKeyboardEvents = new MonoGameKeyboardEvents(); + + _monoGameTouchEvents = new MonoGameTouchEvents(); + } + + override public void Update(GameTime gameTime) + { + _mouseEvents.Update(gameTime); + _monoGameKeyboardEvents.Update(gameTime); + _monoGameTouchEvents.Update(gameTime); + } + + + /*####################################################################*/ + /* Keyboard Events */ + /*####################################################################*/ + + public override event EventHandler CharacterTyped + { + add { _monoGameKeyboardEvents.CharacterTyped += value; } + remove { _monoGameKeyboardEvents.CharacterTyped -= value; } + } + + public override event EventHandler KeyDown + { + add { _monoGameKeyboardEvents.KeyPressed += value; } + remove { _monoGameKeyboardEvents.KeyPressed -= value; } + } + + public override event EventHandler KeyUp + { + add { _monoGameKeyboardEvents.KeyReleased += value; } + remove { _monoGameKeyboardEvents.KeyReleased -= value; } + } + + + /*####################################################################*/ + /* Mouse Events */ + /*####################################################################*/ + + //Movement + public override event EventHandler MouseMoved + { + add { _mouseEvents.MouseMoved += value; } + remove { _mouseEvents.MouseMoved -= value; } + } + + //Buttons + public override event EventHandler MouseDoubleClick + { + add { _mouseEvents.ButtonDoubleClicked += value; } + remove { _mouseEvents.ButtonDoubleClicked -= value; } + } + + public override event EventHandler MouseDown + { + add { _mouseEvents.ButtonPressed += value; } + remove { _mouseEvents.ButtonPressed -= value; } + } + + public override event EventHandler MouseUp + { + add { _mouseEvents.ButtonReleased += value; } + remove { _mouseEvents.ButtonReleased -= value; } + } + + //Wheel + public override event EventHandler MouseWheel + { + add { _mouseEvents.MouseWheelMoved += value; } + remove { _mouseEvents.MouseWheelMoved -= value; } + } + + /*####################################################################*/ + /* Touch Events */ + /*####################################################################*/ + + public override event EventHandler TouchBegan + { + add { _monoGameTouchEvents.TouchBegan += value; } + remove { _monoGameTouchEvents.TouchBegan -= value; } + } + + public override event EventHandler TouchMoved + { + add { _monoGameTouchEvents.TouchMoved += value; } + remove { _monoGameTouchEvents.TouchMoved -= value; } + } + + public override event EventHandler TouchEnded + { + add { _monoGameTouchEvents.TouchEnded += value; } + remove { _monoGameTouchEvents.TouchEnded -= value; } + } + + public override event EventHandler TouchCancelled + { + add { _monoGameTouchEvents.TouchCancelled += value; } + remove { _monoGameTouchEvents.TouchCancelled -= value; } + } + } +} diff --git a/Source/MonoGame.Extended/InputEvents/README.md b/Source/MonoGame.Extended/InputEvents/README.md new file mode 100644 index 00000000..f15834d1 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/README.md @@ -0,0 +1,45 @@ +# MonoGame-EventDriven-Input + +This project provides event driven input for desktop MonoGame projects. The input has been designed to mimic the behavior of the Windows Message Queue as closely as possible creating as familiar as possible behavior. + +## Getting Started + +Using the library is very simple. Simple create an instance of the MonoGameInput object, call Update in the Game.Update function, and then subscribe to the events. You can even create multiple MonoGameInput instances as an easy way or organize different input schemes (for example one for your menu and one for gameplay input). To disable a MonoGameInput instance simply stop updating it. + +```C# +Input _eventDrivenInput; +_eventDrivenInput = new MonoGameInput(this); + +_eventDrivenInput.Update(gameTime); + +_eventDrivenInput.KeyDown += (sender, keyDown) => +{ + //Subscribe to the events +}; +``` + +Below are the currently available events. + +```C# +public abstract event EventHandler CharacterTyped; +public abstract event EventHandler KeyDown; +public abstract event EventHandler KeyUp; + +public abstract event EventHandler MouseMoved; +public abstract event EventHandler MouseDoubleClick; +public abstract event EventHandler MouseDown; +public abstract event EventHandler MouseUp; +public abstract event EventHandler MouseWheel; + +public abstract event EventHandler TouchBegan; +public abstract event EventHandler TouchMoved; +public abstract event EventHandler TouchEnded; +public abstract event EventHandler TouchCancelled; +``` + +## Roadmap + + - Move the project into [MonoGame.Extended](https://github.com/craftworkgames/MonoGame.Extended). + - Add support for subscribing event classes to provide an abstraction over the event subscription. + - Test against FNA and XNA + diff --git a/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/KeyboardCharacterEventArgs.cs b/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/KeyboardCharacterEventArgs.cs new file mode 100644 index 00000000..65e131e6 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/KeyboardCharacterEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace Microsoft.Xna.Framework.Input +{ + public class KeyboardCharacterEventArgs : EventArgs + { + public char Character { get; private set; } + + internal KeyboardCharacterEventArgs(char character) + { + Character = character; + } + } +} diff --git a/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/KeyboardKeyEventArgs.cs b/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/KeyboardKeyEventArgs.cs new file mode 100644 index 00000000..89dfc559 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/KeyboardKeyEventArgs.cs @@ -0,0 +1,30 @@ +using System; + +namespace Microsoft.Xna.Framework.Input +{ + public class KeyboardKeyEventArgs : EventArgs + { + public Keys Key { get; private set; } + public Modifiers Modifiers { get; private set; } + + protected internal KeyboardKeyEventArgs(Keys key) + { + Key = key; + + var current = Keyboard.GetState(); + Modifiers = Modifiers.None; + if (current.IsKeyDown(Keys.LeftControl) || current.IsKeyDown(Keys.RightControl)) + { + Modifiers |= Modifiers.Control; + } + if (current.IsKeyDown(Keys.LeftShift) || current.IsKeyDown(Keys.RightShift)) + { + Modifiers |= Modifiers.Shift; + } + if (current.IsKeyDown(Keys.LeftAlt) || current.IsKeyDown(Keys.RightAlt)) + { + Modifiers |= Modifiers.Alt; + } + } + } +} diff --git a/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/MouseEventArgs.cs b/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/MouseEventArgs.cs new file mode 100644 index 00000000..d0cb17b3 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/MouseEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace Microsoft.Xna.Framework.Input +{ + public abstract class MouseEventArgs : EventArgs + { + public int X { get; set; } + public int Y { get; set; } + + public MouseButton Button { get; protected set; } + + public int? Value { get; protected set; } + public int? Delta { get; protected set; } + + protected internal MouseEventArgs(int x, int y, MouseButton button, int? value, int? delta) + { + X = x; + Y = y; + Button = button; + Value = value; + Delta = delta; + } + } +} diff --git a/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/TouchEventArgs.cs b/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/TouchEventArgs.cs new file mode 100644 index 00000000..e40ce796 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/Shared/EventArgs/TouchEventArgs.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.Xna.Framework.Input.Touch; + +namespace Microsoft.Xna.Framework.Input +{ + public class TouchEventArgs : EventArgs + { + public TouchLocation TouchLocation { get; private set; } + + internal TouchEventArgs (TouchLocation touchLocation) + { + TouchLocation = touchLocation; + } + + public override bool Equals(System.Object obj) + { + if (obj == null) { return false; } + + // If parameter cannot be cast to Point return false. + TouchEventArgs t = obj as TouchEventArgs; + if ((System.Object)t == null) + { + return false; + } + + return TouchLocation.GetHashCode () == t.GetHashCode(); + } + + public override int GetHashCode() + { + return TouchLocation.Id; + } + } +} + diff --git a/Source/MonoGame.Extended/InputEvents/Shared/Keyboard/KeyboardUtil.cs b/Source/MonoGame.Extended/InputEvents/Shared/Keyboard/KeyboardUtil.cs new file mode 100644 index 00000000..611f0024 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/Shared/Keyboard/KeyboardUtil.cs @@ -0,0 +1,98 @@ +namespace Microsoft.Xna.Framework.Input +{ + internal static class KeyboardUtil + { + internal static char? ToChar(Keys key, Modifiers modifiers = Modifiers.None) + { + if (key == Keys.A) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'A' : 'a'; } + if (key == Keys.B) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'B' : 'b'; } + if (key == Keys.C) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'C' : 'c'; } + if (key == Keys.D) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'D' : 'd'; } + if (key == Keys.E) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'E' : 'e'; } + if (key == Keys.F) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'F' : 'f'; } + if (key == Keys.G) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'G' : 'g'; } + if (key == Keys.H) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'H' : 'h'; } + if (key == Keys.I) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'I' : 'i'; } + if (key == Keys.J) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'J' : 'j'; } + if (key == Keys.K) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'K' : 'k'; } + if (key == Keys.L) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'L' : 'l'; } + if (key == Keys.M) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'M' : 'm'; } + if (key == Keys.N) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'N' : 'n'; } + if (key == Keys.O) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'O' : 'o'; } + if (key == Keys.P) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'P' : 'p'; } + if (key == Keys.Q) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'Q' : 'q'; } + if (key == Keys.R) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'R' : 'r'; } + if (key == Keys.S) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'S' : 's'; } + if (key == Keys.T) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'T' : 't'; } + if (key == Keys.U) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'U' : 'u'; } + if (key == Keys.V) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'V' : 'v'; } + if (key == Keys.W) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'W' : 'w'; } + if (key == Keys.X) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'X' : 'x'; } + if (key == Keys.Y) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'Y' : 'y'; } + if (key == Keys.Z) { return ((modifiers & Modifiers.Shift) == Modifiers.Shift) ? 'Z' : 'z'; } + + if ((key == Keys.D0 && !ShiftDown(modifiers)) || key == Keys.NumPad0) { return '0'; } + if ((key == Keys.D1 && !ShiftDown(modifiers)) || key == Keys.NumPad1) { return '1'; } + if ((key == Keys.D2 && !ShiftDown(modifiers)) || key == Keys.NumPad2) { return '2'; } + if ((key == Keys.D3 && !ShiftDown(modifiers)) || key == Keys.NumPad3) { return '3'; } + if ((key == Keys.D4 && !ShiftDown(modifiers)) || key == Keys.NumPad4) { return '4'; } + if ((key == Keys.D5 && !ShiftDown(modifiers)) || key == Keys.NumPad5) { return '5'; } + if ((key == Keys.D6 && !ShiftDown(modifiers)) || key == Keys.NumPad6) { return '6'; } + if ((key == Keys.D7 && !ShiftDown(modifiers)) || key == Keys.NumPad7) { return '7'; } + if ((key == Keys.D8 && !ShiftDown(modifiers)) || key == Keys.NumPad8) { return '8'; } + if ((key == Keys.D9 && !ShiftDown(modifiers)) || key == Keys.NumPad9) { return '9'; } + + if (key == Keys.D0 && ShiftDown(modifiers)) { return ')'; } + if (key == Keys.D1 && ShiftDown(modifiers)) { return '!'; } + if (key == Keys.D2 && ShiftDown(modifiers)) { return '@'; } + if (key == Keys.D3 && ShiftDown(modifiers)) { return '#'; } + if (key == Keys.D4 && ShiftDown(modifiers)) { return '$'; } + if (key == Keys.D5 && ShiftDown(modifiers)) { return '%'; } + if (key == Keys.D6 && ShiftDown(modifiers)) { return '^'; } + if (key == Keys.D7 && ShiftDown(modifiers)) { return '&'; } + if (key == Keys.D8 && ShiftDown(modifiers)) { return '*'; } + if (key == Keys.D9 && ShiftDown(modifiers)) { return '('; } + + if (key == Keys.Space) { return ' '; } + if (key == Keys.Tab) { return '\t'; } + if (key == Keys.Enter) { return (char)13; } + if (key == Keys.Back) { return (char)8; } + + if (key == Keys.Add) { return '+'; } + if (key == Keys.Decimal) { return '.'; } + if (key == Keys.Divide) { return '/'; } + if (key == Keys.Multiply) { return '*'; } + if (key == Keys.OemBackslash) { return '\\'; } + if (key == Keys.OemComma && !ShiftDown(modifiers)) { return ','; } + if (key == Keys.OemComma && ShiftDown(modifiers)) { return '<'; } + if (key == Keys.OemOpenBrackets && !ShiftDown(modifiers)) { return '['; } + if (key == Keys.OemOpenBrackets && ShiftDown(modifiers)) { return '{'; } + if (key == Keys.OemCloseBrackets && !ShiftDown(modifiers)) { return ']'; } + if (key == Keys.OemCloseBrackets && ShiftDown(modifiers)) { return '}'; } + if (key == Keys.OemPeriod && !ShiftDown(modifiers)) { return '.'; } + if (key == Keys.OemPeriod && ShiftDown(modifiers)) { return '>'; } + if (key == Keys.OemPipe && !ShiftDown(modifiers)) { return '\\'; } + if (key == Keys.OemPipe && ShiftDown(modifiers)) { return '|'; } + if (key == Keys.OemPlus && !ShiftDown(modifiers)) { return '='; } + if (key == Keys.OemPlus && ShiftDown(modifiers)) { return '+'; } + if (key == Keys.OemMinus && !ShiftDown(modifiers)) { return '-'; } + if (key == Keys.OemMinus && ShiftDown(modifiers)) { return '_'; } + if (key == Keys.OemQuestion && !ShiftDown(modifiers)) { return '/'; } + if (key == Keys.OemQuestion && ShiftDown(modifiers)) { return '?'; } + if (key == Keys.OemQuotes && !ShiftDown(modifiers)) { return '\''; } + if (key == Keys.OemQuotes && ShiftDown(modifiers)) { return '"'; } + if (key == Keys.OemSemicolon && !ShiftDown(modifiers)) { return ';'; } + if (key == Keys.OemSemicolon && ShiftDown(modifiers)) { return ':'; } + if (key == Keys.OemTilde && !ShiftDown(modifiers)) { return '`'; } + if (key == Keys.OemTilde && ShiftDown(modifiers)) { return '~'; } + if (key == Keys.Subtract) { return '-'; } + + return null; + } + + private static bool ShiftDown(Modifiers modifiers) + { + return ((modifiers & Modifiers.Shift) == Modifiers.Shift); + } + } +} diff --git a/Source/MonoGame.Extended/InputEvents/Shared/Keyboard/Modifiers.cs b/Source/MonoGame.Extended/InputEvents/Shared/Keyboard/Modifiers.cs new file mode 100644 index 00000000..44d389cb --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/Shared/Keyboard/Modifiers.cs @@ -0,0 +1,13 @@ +using System; + +namespace Microsoft.Xna.Framework.Input +{ + [Flags] + public enum Modifiers + { + Control = 1, + Shift = 2, + Alt = 4, + None = 0 + }; +} diff --git a/Source/MonoGame.Extended/InputEvents/Shared/Mouse/MouseButton.cs b/Source/MonoGame.Extended/InputEvents/Shared/Mouse/MouseButton.cs new file mode 100644 index 00000000..07ecd6d4 --- /dev/null +++ b/Source/MonoGame.Extended/InputEvents/Shared/Mouse/MouseButton.cs @@ -0,0 +1,12 @@ +namespace Microsoft.Xna.Framework.Input +{ + public enum MouseButton + { + None, + Left, + Middle, + Right, + XButton1, + XButton2 + }; +} diff --git a/Source/MonoGame.Extended/MonoGame.Extended.csproj b/Source/MonoGame.Extended/MonoGame.Extended.csproj index cc5d4e3b..c543b8e5 100644 --- a/Source/MonoGame.Extended/MonoGame.Extended.csproj +++ b/Source/MonoGame.Extended/MonoGame.Extended.csproj @@ -47,6 +47,19 @@ + + + + + + + + + + + + + @@ -77,6 +90,7 @@ + diff --git a/Source/Sandbox/SandboxGame.cs b/Source/Sandbox/SandboxGame.cs index 1ca1f171..69913101 100644 --- a/Source/Sandbox/SandboxGame.cs +++ b/Source/Sandbox/SandboxGame.cs @@ -9,6 +9,7 @@ using MonoGame.Extended.Maps.Tiled; using MonoGame.Extended.Sprites; using MonoGame.Extended.TextureAtlases; using MonoGame.Extended.ViewportAdapters; +using MonoGame.Extended.InputEvents; namespace Sandbox { @@ -25,11 +26,15 @@ namespace Sandbox private Camera2D _camera; private Texture2D _backgroundTexture; private Sprite _sprite; - private MouseState _previousMouseState; private ViewportAdapter _viewportAdapter; private BitmapFont _bitmapFont; private TiledMap _tiledMap; + private Input _eventDrivenInput; + + private Vector2 _cameraDirection = Vector2.Zero; + private float _cameraRotation = 0f; + public SandboxGame() { _graphicsDeviceManager = new GraphicsDeviceManager(this); @@ -55,6 +60,8 @@ namespace Sandbox Window.AllowUserResizing = true; Window.ClientSizeChanged += (s, e) => _viewportAdapter.OnClientSizeChanged(); + + SetUpInput(); base.Initialize(); } @@ -86,59 +93,18 @@ namespace Sandbox protected override void Update(GameTime gameTime) { - var deltaTime = (float) gameTime.ElapsedGameTime.TotalSeconds; + _eventDrivenInput.Update(gameTime); + + var deltaTime = (float) gameTime.ElapsedGameTime.TotalMilliseconds * .001f; var keyboardState = Keyboard.GetState(); var mouseState = Mouse.GetState(); //var gamePadState = GamePad.GetState(PlayerIndex.One); if (keyboardState.IsKeyDown(Keys.Escape)) - Exit(); - - var up = new Vector2(0, -250); - var right = new Vector2(250, 0); - - // rotation - if (keyboardState.IsKeyDown(Keys.Q)) - _camera.Rotation -= deltaTime; - - if (keyboardState.IsKeyDown(Keys.W)) - _camera.Rotation += deltaTime; - - // movement - var direction = Vector2.Zero; - - if (keyboardState.IsKeyDown(Keys.Up)) - direction += up * deltaTime; - - if (keyboardState.IsKeyDown(Keys.Down)) - direction += -up * deltaTime; + Exit(); - if (keyboardState.IsKeyDown(Keys.Left)) - direction += -right * deltaTime; - - if (keyboardState.IsKeyDown(Keys.Right)) - direction += right * deltaTime; - - _camera.Move(direction); - - // zoom - var scrollWheelDelta = mouseState.ScrollWheelValue - _previousScrollWheelValue; - - if (scrollWheelDelta != 0) - _camera.Zoom += scrollWheelDelta * 0.0001f; - - _previousScrollWheelValue = mouseState.ScrollWheelValue; - - // look at - if (mouseState.LeftButton == ButtonState.Pressed && _previousMouseState.LeftButton == ButtonState.Released) - { - var p = _viewportAdapter.PointToScreen(mouseState.X, mouseState.Y); - Trace.WriteLine(string.Format("{0},{1} => {2},{3}", mouseState.X, mouseState.Y, p.X, p.Y)); - } - - _previousMouseState = mouseState; - - _sprite.Rotation += deltaTime; + _camera.Move(_cameraDirection * deltaTime); + _camera.Rotation += _cameraRotation * deltaTime; base.Update(gameTime); } @@ -153,16 +119,17 @@ namespace Sandbox _tiledMap.Draw(_camera); - //_spriteBatch.Begin(); + _spriteBatch.Begin(); //_spriteBatch.DrawString(_bitmapFont, "This is MonoGame.Extended", new Vector2(50, 50), new Color(Color.Black, 0.5f)); //_spriteBatch.DrawString(_bitmapFont, string.Format("Camera: {0}", _camera.Position), new Vector2(50, 100), Color.Black); - ////_spriteBatch.DrawString(_bitmapFont, - //// "Contrary to popular belief, Lorem Ipsum is not simply random text.\n\n" + - //// "It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard " + - //// "McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin " + - //// "words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, " + - //// "discovered the undoubtable source.", new Vector2(50, 100), new Color(Color.Black, 0.5f), _viewportAdapter.VirtualWidth - 50); - //_spriteBatch.End(); + //_spriteBatch.DrawString(_bitmapFont, string.Format("Camera Direction: {0}", _cameraDirection), new Vector2(50, 150), Color.Black); + //_spriteBatch.DrawString(_bitmapFont, + // "Contrary to popular belief, Lorem Ipsum is not simply random text.\n\n" + + // "It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard " + + // "McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin " + + // "words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, " + + // "discovered the undoubtable source.", new Vector2(50, 100), new Color(Color.Black, 0.5f), _viewportAdapter.VirtualWidth - 50); + _spriteBatch.End(); _spriteBatch.Begin(transformMatrix: _camera.GetViewMatrix()); _spriteBatch.Draw(_textureRegion, _sprite.GetBoundingRectangle(), Color.White); @@ -171,5 +138,86 @@ namespace Sandbox base.Draw(gameTime); } + + private void SetUpInput() + { + _eventDrivenInput = new MonoGameInput(this); + + var up = new Vector2(0, -250); + var right = new Vector2(250, 0); + + //camera movment + _eventDrivenInput.KeyDown += (sender, keyDown) => + { + switch (keyDown.Key) + { + case Keys.Escape: + Exit(); + break; + + case Keys.Q: + _cameraRotation += 1; + break; + case Keys.W: + _cameraRotation -= 1; + break; + + case Keys.Up: + _cameraDirection += up; + break; + case Keys.Down: + _cameraDirection += -up; + break; + case Keys.Left: + _cameraDirection += -right; + break; + case Keys.Right: + _cameraDirection += right; + break; + } + }; + + _eventDrivenInput.KeyUp += (sender, keyUp) => + { + switch (keyUp.Key) + { + case Keys.Q: + _cameraRotation -= 1; + break; + case Keys.W: + _cameraRotation += 1; + break; + + case Keys.Up: + _cameraDirection -= up; + break; + case Keys.Down: + _cameraDirection -= -up; + break; + case Keys.Left: + _cameraDirection -= -right; + break; + case Keys.Right: + _cameraDirection -= right; + break; + } + }; + + // zoom + _eventDrivenInput.MouseWheel += (sender, mouseEvent) => + { + _camera.Zoom += mouseEvent.Delta.Value * 0.0001f; + }; + + // look at + _eventDrivenInput.MouseUp += (sender, mouseUp) => + { + if (mouseUp.Button == MouseButton.Left) + { + var p = _viewportAdapter.PointToScreen(mouseUp.X, mouseUp.Y); + Trace.WriteLine(string.Format("{0},{1} => {2},{3}", mouseUp.X, mouseUp.Y, p.X, p.Y)); + } + }; + } } }