mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-25 14:08:34 +01:00
finished xml docs for the MLEM core package
This commit is contained in:
parent
e9cc9b7d99
commit
dc48c4caa1
21 changed files with 888 additions and 5 deletions
|
@ -138,6 +138,10 @@ namespace MLEM.Animations {
|
||||||
this.IsPaused = false;
|
this.IsPaused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback for when a sprite animation is completed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="animation">The animation that has completed</param>
|
||||||
public delegate void Completed(SpriteAnimation animation);
|
public delegate void Completed(SpriteAnimation animation);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,11 @@ namespace MLEM.Animations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback delegate for when a <see cref="SpriteAnimationGroup"/>'s current animation changed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="oldAnim">The previous animation</param>
|
||||||
|
/// <param name="newAnim">The new animation</param>
|
||||||
public delegate void AnimationChanged(SpriteAnimation oldAnim, SpriteAnimation newAnim);
|
public delegate void AnimationChanged(SpriteAnimation oldAnim, SpriteAnimation newAnim);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,11 @@ namespace MLEM.Extensions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies the alpha value from <see cref="other"/> into this color.
|
/// Copies the alpha value from another color into this color.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="color">The color</param>
|
/// <param name="color">The color</param>
|
||||||
/// <param name="other">The color to copy the alpha from</param>
|
/// <param name="other">The color to copy the alpha from</param>
|
||||||
/// <returns>The <see cref="color"/> with <see cref="other"/>'s alpha value</returns>
|
/// <returns>The first color with the second color's alpha value</returns>
|
||||||
public static Color CopyAlpha(this Color color, Color other) {
|
public static Color CopyAlpha(this Color color, Color other) {
|
||||||
return color * (other.A / 255F);
|
return color * (other.A / 255F);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,17 +75,29 @@ namespace MLEM.Extensions {
|
||||||
return new TargetContext(device, target);
|
return new TargetContext(device, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a context in which a <see cref="RenderTarget2D"/> is applied.
|
||||||
|
/// This class should be used with <see cref="GraphicsExtensions.WithRenderTarget"/>.
|
||||||
|
/// </summary>
|
||||||
public struct TargetContext : IDisposable {
|
public struct TargetContext : IDisposable {
|
||||||
|
|
||||||
private readonly GraphicsDevice device;
|
private readonly GraphicsDevice device;
|
||||||
private readonly RenderTargetBinding[] lastTargets;
|
private readonly RenderTargetBinding[] lastTargets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new target context with the given settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The graphics device to apply the target on</param>
|
||||||
|
/// <param name="target">The target to apply</param>
|
||||||
public TargetContext(GraphicsDevice device, RenderTarget2D target) {
|
public TargetContext(GraphicsDevice device, RenderTarget2D target) {
|
||||||
this.device = device;
|
this.device = device;
|
||||||
this.lastTargets = device.RenderTargetCount <= 0 ? null : device.GetRenderTargets();
|
this.lastTargets = device.RenderTargetCount <= 0 ? null : device.GetRenderTargets();
|
||||||
device.SetRenderTarget(target);
|
device.SetRenderTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes this target context, which causes the graphics device's previous render targets to be re-applied.
|
||||||
|
/// </summary>
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
this.device.SetRenderTargets(this.lastTargets);
|
this.device.SetRenderTargets(this.lastTargets);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace MLEM.Extensions {
|
||||||
/// <param name="first">The first number to equate</param>
|
/// <param name="first">The first number to equate</param>
|
||||||
/// <param name="second">The second number to equate</param>
|
/// <param name="second">The second number to equate</param>
|
||||||
/// <param name="tolerance">The equality tolerance</param>
|
/// <param name="tolerance">The equality tolerance</param>
|
||||||
/// <returns>Whether or not <see cref="first"/> and <see cref="second"/> are different by at most <see cref="tolerance"/></returns>
|
/// <returns>Whether or not the two values are different by at most <c>tolerance</c></returns>
|
||||||
public static bool Equals(this float first, float second, float tolerance) {
|
public static bool Equals(this float first, float second, float tolerance) {
|
||||||
return Math.Abs(first - second) <= tolerance;
|
return Math.Abs(first - second) <= tolerance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,47 +9,125 @@ using MLEM.Extensions;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
|
|
||||||
namespace MLEM.Input {
|
namespace MLEM.Input {
|
||||||
|
/// <summary>
|
||||||
|
/// An input handler is a more advanced wrapper around MonoGame's default input system.
|
||||||
|
/// It includes keyboard, mouse, gamepad and touch states, as well as a new "pressed" state for keys and the ability for keyboard and gamepad repeat events.
|
||||||
|
/// </summary>
|
||||||
public class InputHandler : GameComponent {
|
public class InputHandler : GameComponent {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the keyboard state from the last update call
|
||||||
|
/// </summary>
|
||||||
public KeyboardState LastKeyboardState { get; private set; }
|
public KeyboardState LastKeyboardState { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the current keyboard state
|
||||||
|
/// </summary>
|
||||||
public KeyboardState KeyboardState { get; private set; }
|
public KeyboardState KeyboardState { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the keyboard keys that are currently being pressed
|
||||||
|
/// </summary>
|
||||||
public Keys[] PressedKeys { get; private set; }
|
public Keys[] PressedKeys { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set this property to false to disable keyboard handling for this input handler.
|
||||||
|
/// </summary>
|
||||||
public bool HandleKeyboard;
|
public bool HandleKeyboard;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the mouse state from the last update call
|
||||||
|
/// </summary>
|
||||||
public MouseState LastMouseState { get; private set; }
|
public MouseState LastMouseState { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the current mouse state
|
||||||
|
/// </summary>
|
||||||
public MouseState MouseState { get; private set; }
|
public MouseState MouseState { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the current position of the mouse, extracted from <see cref="MouseState"/>
|
||||||
|
/// </summary>
|
||||||
public Point MousePosition => this.MouseState.Position;
|
public Point MousePosition => this.MouseState.Position;
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the position of the mouse from the last update call, extracted from <see cref="LastMouseState"/>
|
||||||
|
/// </summary>
|
||||||
public Point LastMousePosition => this.LastMouseState.Position;
|
public Point LastMousePosition => this.LastMouseState.Position;
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the current scroll wheel value, in increments of 120
|
||||||
|
/// </summary>
|
||||||
public int ScrollWheel => this.MouseState.ScrollWheelValue;
|
public int ScrollWheel => this.MouseState.ScrollWheelValue;
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the scroll wheel value from the last update call, in increments of 120
|
||||||
|
/// </summary>
|
||||||
public int LastScrollWheel => this.LastMouseState.ScrollWheelValue;
|
public int LastScrollWheel => this.LastMouseState.ScrollWheelValue;
|
||||||
|
/// <summary>
|
||||||
|
/// Set this property to false to disable mouse handling for this input handler.
|
||||||
|
/// </summary>
|
||||||
public bool HandleMouse;
|
public bool HandleMouse;
|
||||||
|
|
||||||
private readonly GamePadState[] lastGamepads = new GamePadState[GamePad.MaximumGamePadCount];
|
private readonly GamePadState[] lastGamepads = new GamePadState[GamePad.MaximumGamePadCount];
|
||||||
private readonly GamePadState[] gamepads = new GamePadState[GamePad.MaximumGamePadCount];
|
private readonly GamePadState[] gamepads = new GamePadState[GamePad.MaximumGamePadCount];
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the amount of gamepads that are currently connected.
|
||||||
|
/// This property is automatically updated in <see cref="Update()"/>
|
||||||
|
/// </summary>
|
||||||
public int ConnectedGamepads { get; private set; }
|
public int ConnectedGamepads { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set this property to false to disable keyboard handling for this input handler.
|
||||||
|
/// </summary>
|
||||||
public bool HandleGamepads;
|
public bool HandleGamepads;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the touch state from the last update call
|
||||||
|
/// </summary>
|
||||||
public TouchCollection LastTouchState { get; private set; }
|
public TouchCollection LastTouchState { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the current touch state
|
||||||
|
/// </summary>
|
||||||
public TouchCollection TouchState { get; private set; }
|
public TouchCollection TouchState { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Contains all of the gestures that have finished during the last update call.
|
||||||
|
/// To easily query these gestures, use <see cref="GetGesture"/>
|
||||||
|
/// </summary>
|
||||||
public readonly ReadOnlyCollection<GestureSample> Gestures;
|
public readonly ReadOnlyCollection<GestureSample> Gestures;
|
||||||
private readonly List<GestureSample> gestures = new List<GestureSample>();
|
private readonly List<GestureSample> gestures = new List<GestureSample>();
|
||||||
|
/// <summary>
|
||||||
|
/// Set this property to false to disable touch handling for this input handler.
|
||||||
|
/// </summary>
|
||||||
public bool HandleTouch;
|
public bool HandleTouch;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is the amount of time that has to pass before the first keyboard repeat event is triggered.
|
||||||
|
/// <seealso cref="KeyRepeatRate"/>
|
||||||
|
/// </summary>
|
||||||
public TimeSpan KeyRepeatDelay = TimeSpan.FromSeconds(0.65);
|
public TimeSpan KeyRepeatDelay = TimeSpan.FromSeconds(0.65);
|
||||||
|
/// <summary>
|
||||||
|
/// This is the amount of time that has to pass between keyboard repeat events.
|
||||||
|
/// <seealso cref="KeyRepeatDelay"/>
|
||||||
|
/// </summary>
|
||||||
public TimeSpan KeyRepeatRate = TimeSpan.FromSeconds(0.05);
|
public TimeSpan KeyRepeatRate = TimeSpan.FromSeconds(0.05);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set this property to false to disable keyboard repeat event handling.
|
||||||
|
/// </summary>
|
||||||
public bool HandleKeyboardRepeats = true;
|
public bool HandleKeyboardRepeats = true;
|
||||||
private DateTime heldKeyStart;
|
private DateTime heldKeyStart;
|
||||||
private DateTime lastKeyRepeat;
|
private DateTime lastKeyRepeat;
|
||||||
private bool triggerKeyRepeat;
|
private bool triggerKeyRepeat;
|
||||||
private Keys heldKey;
|
private Keys heldKey;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set this property to false to disable gamepad repeat event handling.
|
||||||
|
/// </summary>
|
||||||
public bool HandleGamepadRepeats = true;
|
public bool HandleGamepadRepeats = true;
|
||||||
private readonly DateTime[] heldGamepadButtonStarts = new DateTime[GamePad.MaximumGamePadCount];
|
private readonly DateTime[] heldGamepadButtonStarts = new DateTime[GamePad.MaximumGamePadCount];
|
||||||
private readonly DateTime[] lastGamepadButtonRepeats = new DateTime[GamePad.MaximumGamePadCount];
|
private readonly DateTime[] lastGamepadButtonRepeats = new DateTime[GamePad.MaximumGamePadCount];
|
||||||
private readonly bool[] triggerGamepadButtonRepeat = new bool[GamePad.MaximumGamePadCount];
|
private readonly bool[] triggerGamepadButtonRepeat = new bool[GamePad.MaximumGamePadCount];
|
||||||
private readonly Buttons?[] heldGamepadButtons = new Buttons?[GamePad.MaximumGamePadCount];
|
private readonly Buttons?[] heldGamepadButtons = new Buttons?[GamePad.MaximumGamePadCount];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new input handler with optional initial values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handleKeyboard">If keyboard input should be handled</param>
|
||||||
|
/// <param name="handleMouse">If mouse input should be handled</param>
|
||||||
|
/// <param name="handleGamepads">If gamepad input should be handled</param>
|
||||||
|
/// <param name="handleTouch">If touch input should be handled</param>
|
||||||
public InputHandler(bool handleKeyboard = true, bool handleMouse = true, bool handleGamepads = true, bool handleTouch = true) : base(null) {
|
public InputHandler(bool handleKeyboard = true, bool handleMouse = true, bool handleGamepads = true, bool handleTouch = true) : base(null) {
|
||||||
this.HandleKeyboard = handleKeyboard;
|
this.HandleKeyboard = handleKeyboard;
|
||||||
this.HandleMouse = handleMouse;
|
this.HandleMouse = handleMouse;
|
||||||
|
@ -58,6 +136,10 @@ namespace MLEM.Input {
|
||||||
this.Gestures = this.gestures.AsReadOnly();
|
this.Gestures = this.gestures.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates this input handler, querying pressed and released keys and calculating repeat events.
|
||||||
|
/// Call this in your <see cref="Game.Update"/> method.
|
||||||
|
/// </summary>
|
||||||
public void Update() {
|
public void Update() {
|
||||||
if (this.HandleKeyboard) {
|
if (this.HandleKeyboard) {
|
||||||
this.LastKeyboardState = this.KeyboardState;
|
this.LastKeyboardState = this.KeyboardState;
|
||||||
|
@ -151,34 +233,55 @@ namespace MLEM.Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Update()"/>
|
||||||
public override void Update(GameTime gameTime) {
|
public override void Update(GameTime gameTime) {
|
||||||
this.Update();
|
this.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the state of the <c>index</c>th gamepad from the last update call
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The zero-based gamepad index</param>
|
||||||
|
/// <returns>The state of the gamepad last update</returns>
|
||||||
public GamePadState GetLastGamepadState(int index) {
|
public GamePadState GetLastGamepadState(int index) {
|
||||||
return this.lastGamepads[index];
|
return this.lastGamepads[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the current state of the <c>index</c>th gamepad
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The zero-based gamepad index</param>
|
||||||
|
/// <returns>The current state of the gamepad</returns>
|
||||||
public GamePadState GetGamepadState(int index) {
|
public GamePadState GetGamepadState(int index) {
|
||||||
return this.gamepads[index];
|
return this.gamepads[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Microsoft.Xna.Framework.Input.KeyboardState.IsKeyDown"/>
|
||||||
public bool IsKeyDown(Keys key) {
|
public bool IsKeyDown(Keys key) {
|
||||||
return this.KeyboardState.IsKeyDown(key);
|
return this.KeyboardState.IsKeyDown(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Microsoft.Xna.Framework.Input.KeyboardState.IsKeyUp"/>
|
||||||
public bool IsKeyUp(Keys key) {
|
public bool IsKeyUp(Keys key) {
|
||||||
return this.KeyboardState.IsKeyUp(key);
|
return this.KeyboardState.IsKeyUp(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Microsoft.Xna.Framework.Input.KeyboardState.IsKeyDown"/>
|
||||||
public bool WasKeyDown(Keys key) {
|
public bool WasKeyDown(Keys key) {
|
||||||
return this.LastKeyboardState.IsKeyDown(key);
|
return this.LastKeyboardState.IsKeyDown(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Microsoft.Xna.Framework.Input.KeyboardState.IsKeyUp"/>
|
||||||
public bool WasKeyUp(Keys key) {
|
public bool WasKeyUp(Keys key) {
|
||||||
return this.LastKeyboardState.IsKeyUp(key);
|
return this.LastKeyboardState.IsKeyUp(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given key is considered pressed.
|
||||||
|
/// A key is considered pressed if it was not down the last update call, but is down the current update call.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to query</param>
|
||||||
|
/// <returns>If the key is pressed</returns>
|
||||||
public bool IsKeyPressed(Keys key) {
|
public bool IsKeyPressed(Keys key) {
|
||||||
// if the queried key is the held key and a repeat should be triggered, return true
|
// if the queried key is the held key and a repeat should be triggered, return true
|
||||||
if (this.HandleKeyboardRepeats && key == this.heldKey && this.triggerKeyRepeat)
|
if (this.HandleKeyboardRepeats && key == this.heldKey && this.triggerKeyRepeat)
|
||||||
|
@ -186,30 +289,62 @@ namespace MLEM.Input {
|
||||||
return this.WasKeyUp(key) && this.IsKeyDown(key);
|
return this.WasKeyUp(key) && this.IsKeyDown(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given modifier key is down.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modifier">The modifier key</param>
|
||||||
|
/// <returns>If the modifier key is down</returns>
|
||||||
public bool IsModifierKeyDown(ModifierKey modifier) {
|
public bool IsModifierKeyDown(ModifierKey modifier) {
|
||||||
return modifier.GetKeys().Any(this.IsKeyDown);
|
return modifier.GetKeys().Any(this.IsKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given mouse button is currently down.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="button">The button to query</param>
|
||||||
|
/// <returns>Whether or not the queried button is down</returns>
|
||||||
public bool IsMouseButtonDown(MouseButton button) {
|
public bool IsMouseButtonDown(MouseButton button) {
|
||||||
return this.MouseState.GetState(button) == ButtonState.Pressed;
|
return this.MouseState.GetState(button) == ButtonState.Pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given mouse button is currently up.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="button">The button to query</param>
|
||||||
|
/// <returns>Whether or not the queried button is up</returns>
|
||||||
public bool IsMouseButtonUp(MouseButton button) {
|
public bool IsMouseButtonUp(MouseButton button) {
|
||||||
return this.MouseState.GetState(button) == ButtonState.Released;
|
return this.MouseState.GetState(button) == ButtonState.Released;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given mouse button was down the last update call.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="button">The button to query</param>
|
||||||
|
/// <returns>Whether or not the queried button was down</returns>
|
||||||
public bool WasMouseButtonDown(MouseButton button) {
|
public bool WasMouseButtonDown(MouseButton button) {
|
||||||
return this.LastMouseState.GetState(button) == ButtonState.Pressed;
|
return this.LastMouseState.GetState(button) == ButtonState.Pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given mouse button was up the last update call.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="button">The button to query</param>
|
||||||
|
/// <returns>Whether or not the queried button was up</returns>
|
||||||
public bool WasMouseButtonUp(MouseButton button) {
|
public bool WasMouseButtonUp(MouseButton button) {
|
||||||
return this.LastMouseState.GetState(button) == ButtonState.Released;
|
return this.LastMouseState.GetState(button) == ButtonState.Released;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given mouse button is considered pressed.
|
||||||
|
/// A mouse button is considered pressed if it was up the last update call, and is down the current update call.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="button">The button to query</param>
|
||||||
|
/// <returns>Whether the button is pressed</returns>
|
||||||
public bool IsMouseButtonPressed(MouseButton button) {
|
public bool IsMouseButtonPressed(MouseButton button) {
|
||||||
return this.WasMouseButtonUp(button) && this.IsMouseButtonDown(button);
|
return this.WasMouseButtonUp(button) && this.IsMouseButtonDown(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="GamePadState.IsButtonDown"/>
|
||||||
public bool IsGamepadButtonDown(Buttons button, int index = -1) {
|
public bool IsGamepadButtonDown(Buttons button, int index = -1) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
for (var i = 0; i < this.ConnectedGamepads; i++)
|
for (var i = 0; i < this.ConnectedGamepads; i++)
|
||||||
|
@ -220,6 +355,7 @@ namespace MLEM.Input {
|
||||||
return this.GetGamepadState(index).IsButtonDown(button);
|
return this.GetGamepadState(index).IsButtonDown(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="GamePadState.IsButtonUp"/>
|
||||||
public bool IsGamepadButtonUp(Buttons button, int index = -1) {
|
public bool IsGamepadButtonUp(Buttons button, int index = -1) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
for (var i = 0; i < this.ConnectedGamepads; i++)
|
for (var i = 0; i < this.ConnectedGamepads; i++)
|
||||||
|
@ -230,6 +366,7 @@ namespace MLEM.Input {
|
||||||
return this.GetGamepadState(index).IsButtonUp(button);
|
return this.GetGamepadState(index).IsButtonUp(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="GamePadState.IsButtonDown"/>
|
||||||
public bool WasGamepadButtonDown(Buttons button, int index = -1) {
|
public bool WasGamepadButtonDown(Buttons button, int index = -1) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
for (var i = 0; i < this.ConnectedGamepads; i++)
|
for (var i = 0; i < this.ConnectedGamepads; i++)
|
||||||
|
@ -240,6 +377,7 @@ namespace MLEM.Input {
|
||||||
return this.GetLastGamepadState(index).IsButtonDown(button);
|
return this.GetLastGamepadState(index).IsButtonDown(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="GamePadState.IsButtonUp"/>
|
||||||
public bool WasGamepadButtonUp(Buttons button, int index = -1) {
|
public bool WasGamepadButtonUp(Buttons button, int index = -1) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
for (var i = 0; i < this.ConnectedGamepads; i++)
|
for (var i = 0; i < this.ConnectedGamepads; i++)
|
||||||
|
@ -250,6 +388,13 @@ namespace MLEM.Input {
|
||||||
return this.GetLastGamepadState(index).IsButtonUp(button);
|
return this.GetLastGamepadState(index).IsButtonUp(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given gamepad button on the given index is considered pressed.
|
||||||
|
/// A gamepad button is considered pressed if it was down the last update call, and is up the current update call.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="button">The button to query</param>
|
||||||
|
/// <param name="index">The zero-based index of the gamepad, or -1 for any gamepad</param>
|
||||||
|
/// <returns>Whether the given button is pressed</returns>
|
||||||
public bool IsGamepadButtonPressed(Buttons button, int index = -1) {
|
public bool IsGamepadButtonPressed(Buttons button, int index = -1) {
|
||||||
if (this.HandleGamepadRepeats) {
|
if (this.HandleGamepadRepeats) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
|
@ -263,6 +408,12 @@ namespace MLEM.Input {
|
||||||
return this.WasGamepadButtonUp(button, index) && this.IsGamepadButtonDown(button, index);
|
return this.WasGamepadButtonUp(button, index) && this.IsGamepadButtonDown(button, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries for a gesture of a given type that finished during the current update call.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type of gesture to query for</param>
|
||||||
|
/// <param name="sample">The resulting gesture sample, or default if there isn't one</param>
|
||||||
|
/// <returns>True if a gesture of the type was found, otherwise false</returns>
|
||||||
public bool GetGesture(GestureType type, out GestureSample sample) {
|
public bool GetGesture(GestureType type, out GestureSample sample) {
|
||||||
foreach (var gesture in this.Gestures) {
|
foreach (var gesture in this.Gestures) {
|
||||||
if (gesture.GestureType == type) {
|
if (gesture.GestureType == type) {
|
||||||
|
@ -273,6 +424,14 @@ namespace MLEM.Input {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns if a given control of any kind is down.
|
||||||
|
/// This is a helper function that can be passed a <see cref="Keys"/>, <see cref="Buttons"/> or <see cref="MouseButton"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="control">The control whose down state to query</param>
|
||||||
|
/// <param name="index">The index of the gamepad to query (if applicable), or -1 for any gamepad</param>
|
||||||
|
/// <returns>Whether the given control is down</returns>
|
||||||
|
/// <exception cref="ArgumentException">If the passed control isn't of a supported type</exception>
|
||||||
public bool IsDown(object control, int index = -1) {
|
public bool IsDown(object control, int index = -1) {
|
||||||
if (control is Keys key)
|
if (control is Keys key)
|
||||||
return this.IsKeyDown(key);
|
return this.IsKeyDown(key);
|
||||||
|
@ -283,6 +442,14 @@ namespace MLEM.Input {
|
||||||
throw new ArgumentException(nameof(control));
|
throw new ArgumentException(nameof(control));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns if a given control of any kind is up.
|
||||||
|
/// This is a helper function that can be passed a <see cref="Keys"/>, <see cref="Buttons"/> or <see cref="MouseButton"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="control">The control whose up state to query</param>
|
||||||
|
/// <param name="index">The index of the gamepad to query (if applicable), or -1 for any gamepad</param>
|
||||||
|
/// <returns>Whether the given control is down</returns>
|
||||||
|
/// <exception cref="ArgumentException">If the passed control isn't of a supported type</exception>
|
||||||
public bool IsUp(object control, int index = -1) {
|
public bool IsUp(object control, int index = -1) {
|
||||||
if (control is Keys key)
|
if (control is Keys key)
|
||||||
return this.IsKeyUp(key);
|
return this.IsKeyUp(key);
|
||||||
|
@ -293,6 +460,14 @@ namespace MLEM.Input {
|
||||||
throw new ArgumentException(nameof(control));
|
throw new ArgumentException(nameof(control));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns if a given control of any kind is pressed.
|
||||||
|
/// This is a helper function that can be passed a <see cref="Keys"/>, <see cref="Buttons"/> or <see cref="MouseButton"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="control">The control whose pressed state to query</param>
|
||||||
|
/// <param name="index">The index of the gamepad to query (if applicable), or -1 for any gamepad</param>
|
||||||
|
/// <returns>Whether the given control is down</returns>
|
||||||
|
/// <exception cref="ArgumentException">If the passed control isn't of a supported type</exception>
|
||||||
public bool IsPressed(object control, int index = -1) {
|
public bool IsPressed(object control, int index = -1) {
|
||||||
if (control is Keys key)
|
if (control is Keys key)
|
||||||
return this.IsKeyPressed(key);
|
return this.IsKeyPressed(key);
|
||||||
|
@ -303,18 +478,26 @@ namespace MLEM.Input {
|
||||||
throw new ArgumentException(nameof(control));
|
throw new ArgumentException(nameof(control));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IsDown"/>
|
||||||
public bool IsAnyDown(params object[] control) {
|
public bool IsAnyDown(params object[] control) {
|
||||||
return control.Any(c => this.IsDown(c));
|
return control.Any(c => this.IsDown(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IsUp"/>
|
||||||
public bool IsAnyUp(params object[] control) {
|
public bool IsAnyUp(params object[] control) {
|
||||||
return control.Any(c => this.IsUp(c));
|
return control.Any(c => this.IsUp(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IsPressed"/>
|
||||||
public bool IsAnyPressed(params object[] control) {
|
public bool IsAnyPressed(params object[] control) {
|
||||||
return control.Any(c => this.IsPressed(c));
|
return control.Any(c => this.IsPressed(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function to enable gestures for a <see cref="TouchPanel"/> easily.
|
||||||
|
/// Note that, if other gestures were previously enabled, they will not get overridden.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gestures">The gestures to enable</param>
|
||||||
public static void EnableGestures(params GestureType[] gestures) {
|
public static void EnableGestures(params GestureType[] gestures) {
|
||||||
foreach (var gesture in gestures)
|
foreach (var gesture in gestures)
|
||||||
TouchPanel.EnabledGestures |= gesture;
|
TouchPanel.EnabledGestures |= gesture;
|
||||||
|
|
|
@ -4,10 +4,21 @@ using Microsoft.Xna.Framework.Input;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
|
|
||||||
namespace MLEM.Input {
|
namespace MLEM.Input {
|
||||||
|
/// <summary>
|
||||||
|
/// A set of extension methods for dealing with <see cref="Keys"/> and <see cref="Keyboard"/>
|
||||||
|
/// </summary>
|
||||||
public static class KeysExtensions {
|
public static class KeysExtensions {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All enum values of <see cref="ModifierKey"/>
|
||||||
|
/// </summary>
|
||||||
public static readonly ModifierKey[] ModifierKeys = EnumHelper.GetValues<ModifierKey>().ToArray();
|
public static readonly ModifierKey[] ModifierKeys = EnumHelper.GetValues<ModifierKey>().ToArray();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all of the keys that the given modifier key represents
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modifier">The modifier key</param>
|
||||||
|
/// <returns>All of the keys the modifier key represents</returns>
|
||||||
public static IEnumerable<Keys> GetKeys(this ModifierKey modifier) {
|
public static IEnumerable<Keys> GetKeys(this ModifierKey modifier) {
|
||||||
switch (modifier) {
|
switch (modifier) {
|
||||||
case ModifierKey.Shift:
|
case ModifierKey.Shift:
|
||||||
|
@ -25,6 +36,12 @@ namespace MLEM.Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the modifier key that the given key represents.
|
||||||
|
/// If there is no matching modifier key, <see cref="ModifierKey.None"/> is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to convert to a modifier key</param>
|
||||||
|
/// <returns>The modifier key, or <see cref="ModifierKey.None"/></returns>
|
||||||
public static ModifierKey GetModifier(this Keys key) {
|
public static ModifierKey GetModifier(this Keys key) {
|
||||||
foreach (var mod in ModifierKeys) {
|
foreach (var mod in ModifierKeys) {
|
||||||
if (GetKeys(mod).Contains(key))
|
if (GetKeys(mod).Contains(key))
|
||||||
|
@ -33,17 +50,38 @@ namespace MLEM.Input {
|
||||||
return ModifierKey.None;
|
return ModifierKey.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given key is a modifier key or not.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key</param>
|
||||||
|
/// <returns>If the key is a modifier key</returns>
|
||||||
public static bool IsModifier(this Keys key) {
|
public static bool IsModifier(this Keys key) {
|
||||||
return GetModifier(key) != ModifierKey.None;
|
return GetModifier(key) != ModifierKey.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An enum representing modifier keys.
|
||||||
|
/// A modifier key is a key that is usually pressed as part of key combination to change the function of a regular key.
|
||||||
|
/// </summary>
|
||||||
public enum ModifierKey {
|
public enum ModifierKey {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No modifier key. Only used for <see cref="KeysExtensions.GetModifier"/>
|
||||||
|
/// </summary>
|
||||||
None,
|
None,
|
||||||
|
/// <summary>
|
||||||
|
/// The shift modifier key. This represents Left Shift and Right Shift keys.
|
||||||
|
/// </summary>
|
||||||
Shift,
|
Shift,
|
||||||
|
/// <summary>
|
||||||
|
/// The control modifier key. This represents Left Control and Right Control.
|
||||||
|
/// </summary>
|
||||||
Control,
|
Control,
|
||||||
|
/// <summary>
|
||||||
|
/// The alt modifier key. This represents Alt and Alt Graph.
|
||||||
|
/// </summary>
|
||||||
Alt
|
Alt
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,23 @@ using Microsoft.Xna.Framework.Input;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
|
|
||||||
namespace MLEM.Input {
|
namespace MLEM.Input {
|
||||||
|
/// <summary>
|
||||||
|
/// A set of extension methods for dealing with <see cref="MouseButton"/> and <see cref="Mouse"/>
|
||||||
|
/// </summary>
|
||||||
public static class MouseExtensions {
|
public static class MouseExtensions {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All enum values of <see cref="MouseButton"/>
|
||||||
|
/// </summary>
|
||||||
public static readonly MouseButton[] MouseButtons = EnumHelper.GetValues<MouseButton>().ToArray();
|
public static readonly MouseButton[] MouseButtons = EnumHelper.GetValues<MouseButton>().ToArray();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the <see cref="ButtonState"/> of the given mouse button.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">The mouse's current state</param>
|
||||||
|
/// <param name="button">The button whose state to query</param>
|
||||||
|
/// <returns>The state of the button</returns>
|
||||||
|
/// <exception cref="ArgumentException">If a mouse button out of range is passed</exception>
|
||||||
public static ButtonState GetState(this MouseState state, MouseButton button) {
|
public static ButtonState GetState(this MouseState state, MouseButton button) {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case MouseButton.Left:
|
case MouseButton.Left:
|
||||||
|
@ -27,12 +40,31 @@ namespace MLEM.Input {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This enum is a list of possible mouse buttons.
|
||||||
|
/// It serves as a wrapper around <see cref="MouseState"/>'s button properties.
|
||||||
|
/// </summary>
|
||||||
public enum MouseButton {
|
public enum MouseButton {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The left mouse button, or <see cref="MouseState.LeftButton"/>
|
||||||
|
/// </summary>
|
||||||
Left,
|
Left,
|
||||||
|
/// <summary>
|
||||||
|
/// The middle mouse button, or <see cref="MouseState.MiddleButton"/>
|
||||||
|
/// </summary>
|
||||||
Middle,
|
Middle,
|
||||||
|
/// <summary>
|
||||||
|
/// The right mouse button, or <see cref="MouseState.RightButton"/>
|
||||||
|
/// </summary>
|
||||||
Right,
|
Right,
|
||||||
|
/// <summary>
|
||||||
|
/// The first extra mouse button, or <see cref="MouseState.XButton1"/>
|
||||||
|
/// </summary>
|
||||||
Extra1,
|
Extra1,
|
||||||
|
/// <summary>
|
||||||
|
/// The second extra mouse button, or <see cref="MouseState.XButton2"/>
|
||||||
|
/// </summary>
|
||||||
Extra2
|
Extra2
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,29 @@ using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
namespace MLEM.Misc {
|
namespace MLEM.Misc {
|
||||||
|
/// <summary>
|
||||||
|
/// This class contains a <see cref="DrawAutoTile"/> method that allows users to easily draw a tile with automatic connections.
|
||||||
|
/// For auto-tiling in this manner to work, auto-tiled textures have to be laid out in a format described in <see cref="DrawAutoTile"/>.
|
||||||
|
/// </summary>
|
||||||
public class AutoTiling {
|
public class AutoTiling {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method allows for a tiled texture to be drawn in an auto-tiling mode.
|
||||||
|
/// This allows, for example, a grass patch on a tilemap to have nice looking edges that transfer over into a path without any hard edges between tiles.
|
||||||
|
///
|
||||||
|
/// For auto-tiling in this way to work, the tiles have to be laid out in a specific order. This order is shown in the auto-tiling demo's textures.
|
||||||
|
/// For more information and an example, see <see href="https://github.com/Ellpeck/MLEM/blob/master/Demos/AutoTilingDemo.cs#L20-L28"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="batch"></param>
|
||||||
|
/// <param name="pos"></param>
|
||||||
|
/// <param name="texture"></param>
|
||||||
|
/// <param name="textureRegion"></param>
|
||||||
|
/// <param name="connectsTo"></param>
|
||||||
|
/// <param name="color"></param>
|
||||||
|
/// <param name="rotation"></param>
|
||||||
|
/// <param name="origin"></param>
|
||||||
|
/// <param name="scale"></param>
|
||||||
|
/// <param name="layerDepth"></param>
|
||||||
public static void DrawAutoTile(SpriteBatch batch, Vector2 pos, Texture2D texture, Rectangle textureRegion, ConnectsTo connectsTo, Color color, float rotation = 0, Vector2? origin = null, Vector2? scale = null, float layerDepth = 0) {
|
public static void DrawAutoTile(SpriteBatch batch, Vector2 pos, Texture2D texture, Rectangle textureRegion, ConnectsTo connectsTo, Color color, float rotation = 0, Vector2? origin = null, Vector2? scale = null, float layerDepth = 0) {
|
||||||
var org = origin ?? Vector2.Zero;
|
var org = origin ?? Vector2.Zero;
|
||||||
var sc = scale ?? Vector2.One;
|
var sc = scale ?? Vector2.One;
|
||||||
|
@ -26,6 +47,11 @@ namespace MLEM.Misc {
|
||||||
batch.Draw(texture, new Vector2(pos.X + 0.5F * size.X * sc.X, pos.Y + 0.5F * size.Y * sc.Y), new Rectangle(textureRegion.X + halfSize.X + xDr * size.X, textureRegion.Y + halfSize.Y, halfSize.X, halfSize.Y), color, rotation, org, sc, SpriteEffects.None, layerDepth);
|
batch.Draw(texture, new Vector2(pos.X + 0.5F * size.X * sc.X, pos.Y + 0.5F * size.Y * sc.Y), new Rectangle(textureRegion.X + halfSize.X + xDr * size.X, textureRegion.Y + halfSize.Y, halfSize.X, halfSize.Y), color, rotation, org, sc, SpriteEffects.None, layerDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate function that determines if a given offset position connects to an auto-tile location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xOff">The x offset</param>
|
||||||
|
/// <param name="yOff">The y offset</param>
|
||||||
public delegate bool ConnectsTo(int xOff, int yOff);
|
public delegate bool ConnectsTo(int xOff, int yOff);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,37 +5,100 @@ using System.Linq;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
namespace MLEM.Misc {
|
namespace MLEM.Misc {
|
||||||
|
/// <summary>
|
||||||
|
/// An enum that represents two-dimensional directions.
|
||||||
|
/// Both straight and diagonal directions are supported.
|
||||||
|
/// </summary>
|
||||||
public enum Direction2 {
|
public enum Direction2 {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The up direction, or -y.
|
||||||
|
/// </summary>
|
||||||
Up,
|
Up,
|
||||||
|
/// <summary>
|
||||||
|
/// The right direction, or +x.
|
||||||
|
/// </summary>
|
||||||
Right,
|
Right,
|
||||||
|
/// <summary>
|
||||||
|
/// The down direction, or +y.
|
||||||
|
/// </summary>
|
||||||
Down,
|
Down,
|
||||||
|
/// <summary>
|
||||||
|
/// The left direction, or -x.
|
||||||
|
/// </summary>
|
||||||
Left,
|
Left,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The up and right direction, or +x, -y.
|
||||||
|
/// </summary>
|
||||||
UpRight,
|
UpRight,
|
||||||
|
/// <summary>
|
||||||
|
/// The down and right direction, or +x, +y.
|
||||||
|
/// </summary>
|
||||||
DownRight,
|
DownRight,
|
||||||
|
/// <summary>
|
||||||
|
/// The down and left direction, or -x, +y.
|
||||||
|
/// </summary>
|
||||||
DownLeft,
|
DownLeft,
|
||||||
|
/// <summary>
|
||||||
|
/// The up and left direction, or -x, -y.
|
||||||
|
/// </summary>
|
||||||
UpLeft,
|
UpLeft,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No direction.
|
||||||
|
/// </summary>
|
||||||
None
|
None
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A set of helper and extension methods for dealing with <see cref="Direction2"/>
|
||||||
|
/// </summary>
|
||||||
public static class Direction2Helper {
|
public static class Direction2Helper {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All <see cref="Direction2"/> enum values
|
||||||
|
/// </summary>
|
||||||
public static readonly Direction2[] All = EnumHelper.GetValues<Direction2>().ToArray();
|
public static readonly Direction2[] All = EnumHelper.GetValues<Direction2>().ToArray();
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Direction2.Up"/> through <see cref="Direction2.Left"/> directions
|
||||||
|
/// </summary>
|
||||||
public static readonly Direction2[] Adjacent = All.Where(IsAdjacent).ToArray();
|
public static readonly Direction2[] Adjacent = All.Where(IsAdjacent).ToArray();
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Direction2.UpRight"/> through <see cref="Direction2.UpLeft"/> directions
|
||||||
|
/// </summary>
|
||||||
public static readonly Direction2[] Diagonals = All.Where(IsDiagonal).ToArray();
|
public static readonly Direction2[] Diagonals = All.Where(IsDiagonal).ToArray();
|
||||||
|
/// <summary>
|
||||||
|
/// All directions except <see cref="Direction2.None"/>
|
||||||
|
/// </summary>
|
||||||
public static readonly Direction2[] AllExceptNone = All.Where(dir => dir != Direction2.None).ToArray();
|
public static readonly Direction2[] AllExceptNone = All.Where(dir => dir != Direction2.None).ToArray();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns if the given direction is considered an "adjacent" direction.
|
||||||
|
/// An adjacent direction is one that is not a diagonal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dir">The direction to query</param>
|
||||||
|
/// <returns>Whether the direction is adjacent</returns>
|
||||||
public static bool IsAdjacent(this Direction2 dir) {
|
public static bool IsAdjacent(this Direction2 dir) {
|
||||||
return dir <= Direction2.Left;
|
return dir <= Direction2.Left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns if the given direction is considered a diagonal direction.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dir">The direction to query</param>
|
||||||
|
/// <returns>Whether the direction is diagonal</returns>
|
||||||
public static bool IsDiagonal(this Direction2 dir) {
|
public static bool IsDiagonal(this Direction2 dir) {
|
||||||
return dir >= Direction2.UpRight && dir <= Direction2.UpLeft;
|
return dir >= Direction2.UpRight && dir <= Direction2.UpLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the directional offset of a given direction.
|
||||||
|
/// The offset direction will be exactly one unit in each axis that the direction represents.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dir">The direction whose offset to query</param>
|
||||||
|
/// <returns>The direction's offset</returns>
|
||||||
public static Point Offset(this Direction2 dir) {
|
public static Point Offset(this Direction2 dir) {
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case Direction2.Up:
|
case Direction2.Up:
|
||||||
|
@ -59,11 +122,23 @@ namespace MLEM.Misc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps each direction in the given enumerable of directions to its <see cref="Offset"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directions">The direction enumerable</param>
|
||||||
|
/// <returns>The directions' offsets, in the same order as the input directions.</returns>
|
||||||
public static IEnumerable<Point> Offsets(this IEnumerable<Direction2> directions) {
|
public static IEnumerable<Point> Offsets(this IEnumerable<Direction2> directions) {
|
||||||
foreach (var dir in directions)
|
foreach (var dir in directions)
|
||||||
yield return dir.Offset();
|
yield return dir.Offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the opposite of the given direction.
|
||||||
|
/// For "adjacent" directions, this is the direction that points into the same axis, but the opposite sign.
|
||||||
|
/// For diagonal directions, this is the direction that points into the opposite of both the x and y axis.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dir">The direction whose opposite to get</param>
|
||||||
|
/// <returns>The opposite of the direction</returns>
|
||||||
public static Direction2 Opposite(this Direction2 dir) {
|
public static Direction2 Opposite(this Direction2 dir) {
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case Direction2.Up:
|
case Direction2.Up:
|
||||||
|
@ -87,11 +162,22 @@ namespace MLEM.Misc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the angle of the direction in radians, where <see cref="Direction2.Right"/> has an angle of 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dir">The direction whose angle to get</param>
|
||||||
|
/// <returns>The direction's angle</returns>
|
||||||
public static float Angle(this Direction2 dir) {
|
public static float Angle(this Direction2 dir) {
|
||||||
var offset = dir.Offset();
|
var offset = dir.Offset();
|
||||||
return (float) Math.Atan2(offset.Y, offset.X);
|
return (float) Math.Atan2(offset.Y, offset.X);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotates the given direction clockwise and returns the resulting direction.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dir">The direction to rotate</param>
|
||||||
|
/// <param name="fortyFiveDegrees">Whether to rotate by 45 degrees. If this is false, the rotation is 90 degrees instead.</param>
|
||||||
|
/// <returns>The rotated direction</returns>
|
||||||
public static Direction2 RotateCw(this Direction2 dir, bool fortyFiveDegrees = false) {
|
public static Direction2 RotateCw(this Direction2 dir, bool fortyFiveDegrees = false) {
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case Direction2.Up:
|
case Direction2.Up:
|
||||||
|
@ -115,6 +201,12 @@ namespace MLEM.Misc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotates the given direction counter-clockwise and returns the resulting direction.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dir">The direction to rotate counter-clockwise</param>
|
||||||
|
/// <param name="fortyFiveDegrees">Whether to rotate by 45 degrees. If this is false, the rotation is 90 degrees instead.</param>
|
||||||
|
/// <returns>The rotated direction</returns>
|
||||||
public static Direction2 RotateCcw(this Direction2 dir, bool fortyFiveDegrees = false) {
|
public static Direction2 RotateCcw(this Direction2 dir, bool fortyFiveDegrees = false) {
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case Direction2.Up:
|
case Direction2.Up:
|
||||||
|
|
|
@ -4,11 +4,25 @@ using System.Linq;
|
||||||
using Microsoft.Xna.Framework.Input;
|
using Microsoft.Xna.Framework.Input;
|
||||||
|
|
||||||
namespace MLEM.Misc {
|
namespace MLEM.Misc {
|
||||||
|
/// <summary>
|
||||||
|
/// A helper class that allows easier usage of <see cref="Enum"/> values.
|
||||||
|
/// </summary>
|
||||||
public static class EnumHelper {
|
public static class EnumHelper {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All values of the <see cref="Buttons"/> enum.
|
||||||
|
/// </summary>
|
||||||
public static readonly Buttons[] Buttons = GetValues<Buttons>().ToArray();
|
public static readonly Buttons[] Buttons = GetValues<Buttons>().ToArray();
|
||||||
|
/// <summary>
|
||||||
|
/// All values of the <see cref="Keys"/> enum.
|
||||||
|
/// </summary>
|
||||||
public static readonly Keys[] Keys = GetValues<Keys>().ToArray();
|
public static readonly Keys[] Keys = GetValues<Keys>().ToArray();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all of the values of the given enum type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type whose enum to get</typeparam>
|
||||||
|
/// <returns>An enumerable of the values of the enum, in declaration order.</returns>
|
||||||
public static IEnumerable<T> GetValues<T>() {
|
public static IEnumerable<T> GetValues<T>() {
|
||||||
return Enum.GetValues(typeof(T)).Cast<T>();
|
return Enum.GetValues(typeof(T)).Cast<T>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,19 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MLEM.Misc {
|
namespace MLEM.Misc {
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an object that can hold generic key-value based data.
|
||||||
|
/// A lot of MLEM components extend this class to allow for users to add additional data to them easily.
|
||||||
|
/// </summary>
|
||||||
public class GenericDataHolder {
|
public class GenericDataHolder {
|
||||||
|
|
||||||
private Dictionary<string, object> data;
|
private Dictionary<string, object> data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Store a piece of generic data on this object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to store the data by</param>
|
||||||
|
/// <param name="data">The data to store in the object</param>
|
||||||
public void SetData(string key, object data) {
|
public void SetData(string key, object data) {
|
||||||
if (data == default) {
|
if (data == default) {
|
||||||
if (this.data != null)
|
if (this.data != null)
|
||||||
|
@ -17,12 +26,22 @@ namespace MLEM.Misc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a piece of generic data of the given type on this object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key that the data is stored by</param>
|
||||||
|
/// <typeparam name="T">The type of the data stored</typeparam>
|
||||||
|
/// <returns>The data, or default if it doesn't exist</returns>
|
||||||
public T GetData<T>(string key) {
|
public T GetData<T>(string key) {
|
||||||
if (this.data != null && this.data.TryGetValue(key, out var val) && val is T t)
|
if (this.data != null && this.data.TryGetValue(key, out var val) && val is T t)
|
||||||
return t;
|
return t;
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all of the generic data that this object stores.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The generic data on this object</returns>
|
||||||
public IReadOnlyCollection<string> GetDataKeys() {
|
public IReadOnlyCollection<string> GetDataKeys() {
|
||||||
if (this.data == null)
|
if (this.data == null)
|
||||||
return Array.Empty<string>();
|
return Array.Empty<string>();
|
||||||
|
|
|
@ -1,15 +1,44 @@
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
namespace MLEM.Misc {
|
namespace MLEM.Misc {
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a generic padding.
|
||||||
|
/// A padding is an object of data that stores an offset from each side of a rectangle or square.
|
||||||
|
/// </summary>
|
||||||
public struct Padding {
|
public struct Padding {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of padding on the left side
|
||||||
|
/// </summary>
|
||||||
public float Left;
|
public float Left;
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of padding on the right side
|
||||||
|
/// </summary>
|
||||||
public float Right;
|
public float Right;
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of padding on the top side
|
||||||
|
/// </summary>
|
||||||
public float Top;
|
public float Top;
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of padding on the bottom side
|
||||||
|
/// </summary>
|
||||||
public float Bottom;
|
public float Bottom;
|
||||||
|
/// <summary>
|
||||||
|
/// The total width of this padding, a sum of the left and right padding.
|
||||||
|
/// </summary>
|
||||||
public float Width => this.Left + this.Right;
|
public float Width => this.Left + this.Right;
|
||||||
|
/// <summary>
|
||||||
|
/// The total height of this padding, a sum of the top and bottom padding.
|
||||||
|
/// </summary>
|
||||||
public float Height => this.Top + this.Bottom;
|
public float Height => this.Top + this.Bottom;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new padding with the specified borders.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The amount of padding on the left side</param>
|
||||||
|
/// <param name="right">The amount of padding on the right side</param>
|
||||||
|
/// <param name="top">The amount of padding on the top side</param>
|
||||||
|
/// <param name="bottom">The amount of padding on the bottom side</param>
|
||||||
public Padding(float left, float right, float top, float bottom) {
|
public Padding(float left, float right, float top, float bottom) {
|
||||||
this.Left = left;
|
this.Left = left;
|
||||||
this.Right = right;
|
this.Right = right;
|
||||||
|
@ -17,38 +46,58 @@ namespace MLEM.Misc {
|
||||||
this.Bottom = bottom;
|
this.Bottom = bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implicitly creates a padding from the given two-dimensional vector.
|
||||||
|
/// The left and right padding will both be the vector's x value, and the top and bottom padding will both be the vector's y value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vec">The vector to convert</param>
|
||||||
|
/// <returns>A padding based on the vector's values</returns>
|
||||||
public static implicit operator Padding(Vector2 vec) {
|
public static implicit operator Padding(Vector2 vec) {
|
||||||
return new Padding(vec.X, vec.X, vec.Y, vec.Y);
|
return new Padding(vec.X, vec.X, vec.Y, vec.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scales a padding by a scalar.
|
||||||
|
/// </summary>
|
||||||
public static Padding operator *(Padding p, float scale) {
|
public static Padding operator *(Padding p, float scale) {
|
||||||
return new Padding(p.Left * scale, p.Right * scale, p.Top * scale, p.Bottom * scale);
|
return new Padding(p.Left * scale, p.Right * scale, p.Top * scale, p.Bottom * scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two paddings together in a memberwise fashion.
|
||||||
|
/// </summary>
|
||||||
public static Padding operator +(Padding left, Padding right) {
|
public static Padding operator +(Padding left, Padding right) {
|
||||||
return new Padding(left.Left + right.Left, left.Right + right.Right, left.Top + right.Top, left.Bottom + right.Bottom);
|
return new Padding(left.Left + right.Left, left.Right + right.Right, left.Top + right.Top, left.Bottom + right.Bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subtracts two paddings in a memberwise fashion.
|
||||||
|
/// </summary>
|
||||||
public static Padding operator -(Padding left, Padding right) {
|
public static Padding operator -(Padding left, Padding right) {
|
||||||
return new Padding(left.Left - right.Left, left.Right - right.Right, left.Top - right.Top, left.Bottom - right.Bottom);
|
return new Padding(left.Left - right.Left, left.Right - right.Right, left.Top - right.Top, left.Bottom - right.Bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Equals(Padding)"/>
|
||||||
public static bool operator ==(Padding left, Padding right) {
|
public static bool operator ==(Padding left, Padding right) {
|
||||||
return left.Equals(right);
|
return left.Equals(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Equals(Padding)"/>
|
||||||
public static bool operator !=(Padding left, Padding right) {
|
public static bool operator !=(Padding left, Padding right) {
|
||||||
return !(left == right);
|
return !(left == right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Equals(object)"/>
|
||||||
public bool Equals(Padding other) {
|
public bool Equals(Padding other) {
|
||||||
return this.Left.Equals(other.Left) && this.Right.Equals(other.Right) && this.Top.Equals(other.Top) && this.Bottom.Equals(other.Bottom);
|
return this.Left.Equals(other.Left) && this.Right.Equals(other.Right) && this.Top.Equals(other.Top) && this.Bottom.Equals(other.Bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool Equals(object obj) {
|
public override bool Equals(object obj) {
|
||||||
return obj is Padding other && this.Equals(other);
|
return obj is Padding other && this.Equals(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override int GetHashCode() {
|
public override int GetHashCode() {
|
||||||
var hashCode = this.Left.GetHashCode();
|
var hashCode = this.Left.GetHashCode();
|
||||||
hashCode = (hashCode * 397) ^ this.Right.GetHashCode();
|
hashCode = (hashCode * 397) ^ this.Right.GetHashCode();
|
||||||
|
|
|
@ -4,26 +4,59 @@ using Microsoft.Xna.Framework;
|
||||||
using MLEM.Extensions;
|
using MLEM.Extensions;
|
||||||
|
|
||||||
namespace MLEM.Misc {
|
namespace MLEM.Misc {
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a float-based version of <see cref="Rectangle"/>
|
||||||
|
/// </summary>
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public struct RectangleF : IEquatable<RectangleF> {
|
public struct RectangleF : IEquatable<RectangleF> {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The empty rectangle, with an x, y, width and height of 0.
|
||||||
|
/// </summary>
|
||||||
public static RectangleF Empty => default;
|
public static RectangleF Empty => default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The x position of the top left corner of this rectangle.
|
||||||
|
/// </summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public float X;
|
public float X;
|
||||||
|
/// <summary>
|
||||||
|
/// The y position of the top left corner of this rectangle.
|
||||||
|
/// </summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public float Y;
|
public float Y;
|
||||||
|
/// <summary>
|
||||||
|
/// The width of this rectangle.
|
||||||
|
/// </summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public float Width;
|
public float Width;
|
||||||
|
/// <summary>
|
||||||
|
/// The height of this rectangle.
|
||||||
|
/// </summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public float Height;
|
public float Height;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="X"/>
|
||||||
public float Left => this.X;
|
public float Left => this.X;
|
||||||
|
/// <summary>
|
||||||
|
/// The x position of the bottom right corner of this rectangle.
|
||||||
|
/// </summary>
|
||||||
public float Right => this.X + this.Width;
|
public float Right => this.X + this.Width;
|
||||||
|
/// <inheritdoc cref="Y"/>
|
||||||
public float Top => this.Y;
|
public float Top => this.Y;
|
||||||
|
/// <summary>
|
||||||
|
/// The y position of the bottom right corner of this rectangle.
|
||||||
|
/// </summary>
|
||||||
public float Bottom => this.Y + this.Height;
|
public float Bottom => this.Y + this.Height;
|
||||||
|
/// <summary>
|
||||||
|
/// A boolean that is true if this rectangle is empty.
|
||||||
|
/// A rectangle is considered empty if the width or height is 0.
|
||||||
|
/// </summary>
|
||||||
public bool IsEmpty => this.Width <= 0 || this.Height <= 0;
|
public bool IsEmpty => this.Width <= 0 || this.Height <= 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The top left corner of this rectangle
|
||||||
|
/// </summary>
|
||||||
public Vector2 Location {
|
public Vector2 Location {
|
||||||
get => new Vector2(this.X, this.Y);
|
get => new Vector2(this.X, this.Y);
|
||||||
set {
|
set {
|
||||||
|
@ -31,6 +64,9 @@ namespace MLEM.Misc {
|
||||||
this.Y = value.Y;
|
this.Y = value.Y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The size, that is, the width and height of this rectangle.
|
||||||
|
/// </summary>
|
||||||
public Vector2 Size {
|
public Vector2 Size {
|
||||||
get => new Vector2(this.Width, this.Height);
|
get => new Vector2(this.Width, this.Height);
|
||||||
set {
|
set {
|
||||||
|
@ -38,8 +74,18 @@ namespace MLEM.Misc {
|
||||||
this.Height = value.Y;
|
this.Height = value.Y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The center of this rectangle, based on the top left corner and the size.
|
||||||
|
/// </summary>
|
||||||
public Vector2 Center => new Vector2(this.X + this.Width / 2, this.Y + this.Height / 2);
|
public Vector2 Center => new Vector2(this.X + this.Width / 2, this.Y + this.Height / 2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new rectangle with the specified location and size
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">The x coordinate of the top left corner of the rectangle</param>
|
||||||
|
/// <param name="y">The y coordinate of the top left corner of the rectangle</param>
|
||||||
|
/// <param name="width">The width of the rectangle</param>
|
||||||
|
/// <param name="height">The height of the rectangle</param>
|
||||||
public RectangleF(float x, float y, float width, float height) {
|
public RectangleF(float x, float y, float width, float height) {
|
||||||
this.X = x;
|
this.X = x;
|
||||||
this.Y = y;
|
this.Y = y;
|
||||||
|
@ -47,6 +93,11 @@ namespace MLEM.Misc {
|
||||||
this.Height = height;
|
this.Height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new rectangle with the specified location and size vectors
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">The top left corner of the rectangle</param>
|
||||||
|
/// <param name="size">The size of the rectangle, where x represents width and the y represents height</param>
|
||||||
public RectangleF(Vector2 location, Vector2 size) {
|
public RectangleF(Vector2 location, Vector2 size) {
|
||||||
this.X = location.X;
|
this.X = location.X;
|
||||||
this.Y = location.Y;
|
this.Y = location.Y;
|
||||||
|
@ -54,6 +105,13 @@ namespace MLEM.Misc {
|
||||||
this.Height = size.Y;
|
this.Height = size.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new rectangle based on two corners that form a bounding box.
|
||||||
|
/// The resulting rectangle will encompass both corners as well as all of the space between them.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="corner1">The first corner to use</param>
|
||||||
|
/// <param name="corner2">The second corner to use</param>
|
||||||
|
/// <returns></returns>
|
||||||
public static RectangleF FromCorners(Vector2 corner1, Vector2 corner2) {
|
public static RectangleF FromCorners(Vector2 corner1, Vector2 corner2) {
|
||||||
var minX = Math.Min(corner1.X, corner2.X);
|
var minX = Math.Min(corner1.X, corner2.X);
|
||||||
var minY = Math.Min(corner1.Y, corner2.Y);
|
var minY = Math.Min(corner1.Y, corner2.Y);
|
||||||
|
@ -62,46 +120,65 @@ namespace MLEM.Misc {
|
||||||
return new RectangleF(minX, minY, maxX - minX, maxY - minY);
|
return new RectangleF(minX, minY, maxX - minX, maxY - minY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a float-based rectangle to an int-based rectangle, flooring each value in the process.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rect">The rectangle to convert</param>
|
||||||
|
/// <returns>The resulting rectangle</returns>
|
||||||
public static explicit operator Rectangle(RectangleF rect) {
|
public static explicit operator Rectangle(RectangleF rect) {
|
||||||
return new Rectangle(rect.X.Floor(), rect.Y.Floor(), rect.Width.Floor(), rect.Height.Floor());
|
return new Rectangle(rect.X.Floor(), rect.Y.Floor(), rect.Width.Floor(), rect.Height.Floor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an int-based rectangle to a float-based rectangle.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rect">The rectangle to convert</param>
|
||||||
|
/// <returns>The resulting rectangle</returns>
|
||||||
public static explicit operator RectangleF(Rectangle rect) {
|
public static explicit operator RectangleF(Rectangle rect) {
|
||||||
return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height);
|
return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Equals(RectangleF)"/>
|
||||||
public static bool operator ==(RectangleF a, RectangleF b) {
|
public static bool operator ==(RectangleF a, RectangleF b) {
|
||||||
return a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height;
|
return a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Equals(RectangleF)"/>
|
||||||
public static bool operator !=(RectangleF a, RectangleF b) {
|
public static bool operator !=(RectangleF a, RectangleF b) {
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Contains(float, float)"/>
|
||||||
public bool Contains(float x, float y) {
|
public bool Contains(float x, float y) {
|
||||||
return this.X <= x && x < this.X + this.Width && this.Y <= y && y < this.Y + this.Height;
|
return this.X <= x && x < this.X + this.Width && this.Y <= y && y < this.Y + this.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Contains(Vector2)"/>
|
||||||
public bool Contains(Vector2 value) {
|
public bool Contains(Vector2 value) {
|
||||||
return this.Contains(value.X, value.Y);
|
return this.Contains(value.X, value.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Contains(Rectangle)"/>
|
||||||
public bool Contains(RectangleF value) {
|
public bool Contains(RectangleF value) {
|
||||||
return this.X <= value.X && value.X + value.Width <= this.X + this.Width && this.Y <= value.Y && value.Y + value.Height <= this.Y + this.Height;
|
return this.X <= value.X && value.X + value.Width <= this.X + this.Width && this.Y <= value.Y && value.Y + value.Height <= this.Y + this.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool Equals(object obj) {
|
public override bool Equals(object obj) {
|
||||||
return obj is RectangleF f && this == f;
|
return obj is RectangleF f && this == f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public bool Equals(RectangleF other) {
|
public bool Equals(RectangleF other) {
|
||||||
return this == other;
|
return this == other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override int GetHashCode() {
|
public override int GetHashCode() {
|
||||||
return (((17 * 23 + this.X.GetHashCode()) * 23 + this.Y.GetHashCode()) * 23 + this.Width.GetHashCode()) * 23 + this.Height.GetHashCode();
|
return (((17 * 23 + this.X.GetHashCode()) * 23 + this.Y.GetHashCode()) * 23 + this.Width.GetHashCode()) * 23 + this.Height.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Inflate(float,float)"/>
|
||||||
public void Inflate(float horizontalAmount, float verticalAmount) {
|
public void Inflate(float horizontalAmount, float verticalAmount) {
|
||||||
this.X -= horizontalAmount;
|
this.X -= horizontalAmount;
|
||||||
this.Y -= verticalAmount;
|
this.Y -= verticalAmount;
|
||||||
|
@ -109,10 +186,12 @@ namespace MLEM.Misc {
|
||||||
this.Height += verticalAmount * 2;
|
this.Height += verticalAmount * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Intersects(Rectangle)"/>
|
||||||
public bool Intersects(RectangleF value) {
|
public bool Intersects(RectangleF value) {
|
||||||
return value.Left < this.Right && this.Left < value.Right && value.Top < this.Bottom && this.Top < value.Bottom;
|
return value.Left < this.Right && this.Left < value.Right && value.Top < this.Bottom && this.Top < value.Bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Intersect(Rectangle, Rectangle)"/>
|
||||||
public static RectangleF Intersect(RectangleF value1, RectangleF value2) {
|
public static RectangleF Intersect(RectangleF value1, RectangleF value2) {
|
||||||
if (value1.Intersects(value2)) {
|
if (value1.Intersects(value2)) {
|
||||||
var num1 = Math.Min(value1.X + value1.Width, value2.X + value2.Width);
|
var num1 = Math.Min(value1.X + value1.Width, value2.X + value2.Width);
|
||||||
|
@ -125,26 +204,31 @@ namespace MLEM.Misc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Offset(float, float)"/>
|
||||||
public void Offset(float offsetX, float offsetY) {
|
public void Offset(float offsetX, float offsetY) {
|
||||||
this.X += offsetX;
|
this.X += offsetX;
|
||||||
this.Y += offsetY;
|
this.Y += offsetY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Offset(Vector2)"/>
|
||||||
public void Offset(Vector2 amount) {
|
public void Offset(Vector2 amount) {
|
||||||
this.X += amount.X;
|
this.X += amount.X;
|
||||||
this.Y += amount.Y;
|
this.Y += amount.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
return "{X:" + this.X + " Y:" + this.Y + " Width:" + this.Width + " Height:" + this.Height + "}";
|
return "{X:" + this.X + " Y:" + this.Y + " Width:" + this.Width + " Height:" + this.Height + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Union(Rectangle, Rectangle)"/>
|
||||||
public static RectangleF Union(RectangleF value1, RectangleF value2) {
|
public static RectangleF Union(RectangleF value1, RectangleF value2) {
|
||||||
var x = Math.Min(value1.X, value2.X);
|
var x = Math.Min(value1.X, value2.X);
|
||||||
var y = Math.Min(value1.Y, value2.Y);
|
var y = Math.Min(value1.Y, value2.Y);
|
||||||
return new RectangleF(x, y, Math.Max(value1.Right, value2.Right) - x, Math.Max(value1.Bottom, value2.Bottom) - y);
|
return new RectangleF(x, y, Math.Max(value1.Right, value2.Right) - x, Math.Max(value1.Bottom, value2.Bottom) - y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Deconstruct"/>
|
||||||
public void Deconstruct(out float x, out float y, out float width, out float height) {
|
public void Deconstruct(out float x, out float y, out float width, out float height) {
|
||||||
x = this.X;
|
x = this.X;
|
||||||
y = this.Y;
|
y = this.Y;
|
||||||
|
|
|
@ -6,9 +6,19 @@ using Microsoft.Xna.Framework.Input;
|
||||||
using MLEM.Input;
|
using MLEM.Input;
|
||||||
|
|
||||||
namespace MLEM.Misc {
|
namespace MLEM.Misc {
|
||||||
|
/// <summary>
|
||||||
|
/// A text input wrapper is a wrapper around MonoGame's built-in text input event.
|
||||||
|
/// Since said text input event does not exist on non-Desktop devices, we want to wrap it in a wrapper that is platform-independent for MLEM.
|
||||||
|
/// See subclasses of this wrapper or <see href="https://mlem.ellpeck.de/articles/ui.html#text-input"/> for more info.
|
||||||
|
/// </summary>
|
||||||
public abstract class TextInputWrapper {
|
public abstract class TextInputWrapper {
|
||||||
|
|
||||||
private static TextInputWrapper current;
|
private static TextInputWrapper current;
|
||||||
|
/// <summary>
|
||||||
|
/// The current text input wrapper.
|
||||||
|
/// Set this value before starting your game if you want to use text input wrapping.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
public static TextInputWrapper Current {
|
public static TextInputWrapper Current {
|
||||||
get {
|
get {
|
||||||
if (current == null)
|
if (current == null)
|
||||||
|
@ -18,26 +28,59 @@ namespace MLEM.Misc {
|
||||||
set => current = value;
|
set => current = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if this text input wrapper requires an on-screen keyboard.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>If this text input wrapper requires an on-screen keyboard</returns>
|
||||||
public abstract bool RequiresOnScreenKeyboard();
|
public abstract bool RequiresOnScreenKeyboard();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a text input listener to this text input wrapper.
|
||||||
|
/// The supplied listener will be called whenever a character is input.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">The game's window</param>
|
||||||
|
/// <param name="callback">The callback that should be called whenever a character is pressed</param>
|
||||||
public abstract void AddListener(GameWindow window, TextInputCallback callback);
|
public abstract void AddListener(GameWindow window, TextInputCallback callback);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate method that can be used for <see cref="TextInputWrapper.AddListener"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The object that sent the event. The <see cref="TextInputWrapper"/> used in most cases.</param>
|
||||||
|
/// <param name="key">The key that was pressed</param>
|
||||||
|
/// <param name="character">The character that corresponds to that key</param>
|
||||||
public delegate void TextInputCallback(object sender, Keys key, char character);
|
public delegate void TextInputCallback(object sender, Keys key, char character);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A text input wrapper for DesktopGL devices.
|
||||||
|
/// This wrapper uses the built-in MonoGame TextInput event, which makes this listener work with any keyboard localization natively.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// This listener is initialized as follows:
|
||||||
|
/// <code>
|
||||||
|
/// new TextInputWrapper.DesktopGl{TextInputEventArgs}((w, c) => w.TextInput += c);
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
public class DesktopGl<T> : TextInputWrapper {
|
public class DesktopGl<T> : TextInputWrapper {
|
||||||
|
|
||||||
private MemberInfo key;
|
private MemberInfo key;
|
||||||
private MemberInfo character;
|
private MemberInfo character;
|
||||||
private readonly Action<GameWindow, EventHandler<T>> addListener;
|
private readonly Action<GameWindow, EventHandler<T>> addListener;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new DesktopGL-based text input wrapper
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="addListener">The function that is used to add a text input listener</param>
|
||||||
public DesktopGl(Action<GameWindow, EventHandler<T>> addListener) {
|
public DesktopGl(Action<GameWindow, EventHandler<T>> addListener) {
|
||||||
this.addListener = addListener;
|
this.addListener = addListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool RequiresOnScreenKeyboard() {
|
public override bool RequiresOnScreenKeyboard() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void AddListener(GameWindow window, TextInputCallback callback) {
|
public override void AddListener(GameWindow window, TextInputCallback callback) {
|
||||||
this.addListener(window, (sender, args) => {
|
this.addListener(window, (sender, args) => {
|
||||||
// the old versions of DesktopGL use a property here, while the
|
// the old versions of DesktopGL use a property here, while the
|
||||||
|
@ -69,32 +112,54 @@ namespace MLEM.Misc {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A text input wrapper for mobile platforms as well as consoles.
|
||||||
|
/// This text input wrapper performs no actions itself, as it signals that an on-screen keyboard is required.
|
||||||
|
/// </summary>
|
||||||
public class Mobile : TextInputWrapper {
|
public class Mobile : TextInputWrapper {
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool RequiresOnScreenKeyboard() {
|
public override bool RequiresOnScreenKeyboard() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void AddListener(GameWindow window, TextInputCallback callback) {
|
public override void AddListener(GameWindow window, TextInputCallback callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A text input wrapper that does nothing.
|
||||||
|
/// This can be used if no text input is required for the game.
|
||||||
|
/// </summary>
|
||||||
public class None : TextInputWrapper {
|
public class None : TextInputWrapper {
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool RequiresOnScreenKeyboard() {
|
public override bool RequiresOnScreenKeyboard() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void AddListener(GameWindow window, TextInputCallback callback) {
|
public override void AddListener(GameWindow window, TextInputCallback callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A primitive text input wrapper that is locked to the American keyboard localization.
|
||||||
|
/// Only use this text input wrapper if <see cref="TextInputWrapper.DesktopGl{T}"/> is unavailable for some reason.
|
||||||
|
///
|
||||||
|
/// Note that, when using this text input wrapper, its <see cref="Update"/> method has to be called periodically.
|
||||||
|
/// </summary>
|
||||||
public class Primitive : TextInputWrapper {
|
public class Primitive : TextInputWrapper {
|
||||||
|
|
||||||
private TextInputCallback callback;
|
private TextInputCallback callback;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates this text input wrapper by querying pressed keys and sending corresponding input events.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">The input handler to use for text input querying</param>
|
||||||
public void Update(InputHandler handler) {
|
public void Update(InputHandler handler) {
|
||||||
var pressed = handler.KeyboardState.GetPressedKeys().Except(handler.LastKeyboardState.GetPressedKeys());
|
var pressed = handler.KeyboardState.GetPressedKeys().Except(handler.LastKeyboardState.GetPressedKeys());
|
||||||
var shift = handler.IsModifierKeyDown(ModifierKey.Shift);
|
var shift = handler.IsModifierKeyDown(ModifierKey.Shift);
|
||||||
|
@ -105,10 +170,12 @@ namespace MLEM.Misc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool RequiresOnScreenKeyboard() {
|
public override bool RequiresOnScreenKeyboard() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void AddListener(GameWindow window, TextInputCallback callback) {
|
public override void AddListener(GameWindow window, TextInputCallback callback) {
|
||||||
this.callback += callback;
|
this.callback += callback;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,18 +4,61 @@ using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MLEM.Pathfinding {
|
namespace MLEM.Pathfinding {
|
||||||
|
/// <summary>
|
||||||
|
/// This is an abstract implementation of the A* path finding algorithm.
|
||||||
|
/// This implementation is used by <see cref="AStar2"/>, a 2-dimensional A* path finding algorithm, and <see cref="AStar3"/>, a 3-dimensional A* path finding algorithm.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of points used for this path</typeparam>
|
||||||
public abstract class AStar<T> {
|
public abstract class AStar<T> {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A value that represents an infinite path cost, or a cost for a location that cannot possibly be reached.
|
||||||
|
/// </summary>
|
||||||
public static readonly float InfiniteCost = float.PositiveInfinity;
|
public static readonly float InfiniteCost = float.PositiveInfinity;
|
||||||
|
/// <summary>
|
||||||
|
/// The array of all directions that will be checked for path finding.
|
||||||
|
/// Note that this array is only used if <see cref="DefaultAllowDiagonals"/> is true.
|
||||||
|
/// </summary>
|
||||||
public readonly T[] AllDirections;
|
public readonly T[] AllDirections;
|
||||||
|
/// <summary>
|
||||||
|
/// The array of all adjacent directions that will be checked for path finding.
|
||||||
|
/// Note that this array is only used if <see cref="DefaultAllowDiagonals"/> is false.
|
||||||
|
/// </summary>
|
||||||
public readonly T[] AdjacentDirections;
|
public readonly T[] AdjacentDirections;
|
||||||
|
/// <summary>
|
||||||
|
/// The default cost function that determines the cost for each path finding position.
|
||||||
|
/// </summary>
|
||||||
public GetCost DefaultCostFunction;
|
public GetCost DefaultCostFunction;
|
||||||
|
/// <summary>
|
||||||
|
/// The default cost for a path point.
|
||||||
|
/// </summary>
|
||||||
public float DefaultCost;
|
public float DefaultCost;
|
||||||
|
/// <summary>
|
||||||
|
/// The default amount of maximum tries that will be used before path finding is aborted.
|
||||||
|
/// </summary>
|
||||||
public int DefaultMaxTries;
|
public int DefaultMaxTries;
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not diagonal directions are considered while finding a path.
|
||||||
|
/// </summary>
|
||||||
public bool DefaultAllowDiagonals;
|
public bool DefaultAllowDiagonals;
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of tries required for finding the last queried path
|
||||||
|
/// </summary>
|
||||||
public int LastTriesNeeded { get; private set; }
|
public int LastTriesNeeded { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time required for finding the last queried path
|
||||||
|
/// </summary>
|
||||||
public TimeSpan LastTimeNeeded { get; private set; }
|
public TimeSpan LastTimeNeeded { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new A* pathfinder with the supplied default settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="allDirections">All directions that should be checked</param>
|
||||||
|
/// <param name="adjacentDirections">All adjacent directions that should be checked</param>
|
||||||
|
/// <param name="defaultCostFunction">The default function for cost determination of a path point</param>
|
||||||
|
/// <param name="defaultAllowDiagonals">Whether or not diagonals should be allowed by default</param>
|
||||||
|
/// <param name="defaultCost">The default cost for a path point</param>
|
||||||
|
/// <param name="defaultMaxTries">The default amount of tries before path finding is aborted</param>
|
||||||
protected AStar(T[] allDirections, T[] adjacentDirections, GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) {
|
protected AStar(T[] allDirections, T[] adjacentDirections, GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) {
|
||||||
this.AllDirections = allDirections;
|
this.AllDirections = allDirections;
|
||||||
this.AdjacentDirections = adjacentDirections;
|
this.AdjacentDirections = adjacentDirections;
|
||||||
|
@ -25,10 +68,21 @@ namespace MLEM.Pathfinding {
|
||||||
this.DefaultAllowDiagonals = defaultAllowDiagonals;
|
this.DefaultAllowDiagonals = defaultAllowDiagonals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="FindPath"/>
|
||||||
public Task<Stack<T>> FindPathAsync(T start, T goal, GetCost costFunction = null, float? defaultCost = null, int? maxTries = null, bool? allowDiagonals = null) {
|
public Task<Stack<T>> FindPathAsync(T start, T goal, GetCost costFunction = null, float? defaultCost = null, int? maxTries = null, bool? allowDiagonals = null) {
|
||||||
return Task.Run(() => this.FindPath(start, goal, costFunction, defaultCost, maxTries, allowDiagonals));
|
return Task.Run(() => this.FindPath(start, goal, costFunction, defaultCost, maxTries, allowDiagonals));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a path between two points using this pathfinder's default settings or, alternatively, the supplied override settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start">The point to start path finding at</param>
|
||||||
|
/// <param name="goal">The point to find a path to</param>
|
||||||
|
/// <param name="costFunction">The function that determines the cost for each path point</param>
|
||||||
|
/// <param name="defaultCost">The default cost for each path point</param>
|
||||||
|
/// <param name="maxTries">The maximum amount of tries before path finding is aborted</param>
|
||||||
|
/// <param name="allowDiagonals">If diagonals should be looked at for path finding</param>
|
||||||
|
/// <returns>A stack of path points, where the top item is the first point to go to.</returns>
|
||||||
public Stack<T> FindPath(T start, T goal, GetCost costFunction = null, float? defaultCost = null, int? maxTries = null, bool? allowDiagonals = null) {
|
public Stack<T> FindPath(T start, T goal, GetCost costFunction = null, float? defaultCost = null, int? maxTries = null, bool? allowDiagonals = null) {
|
||||||
var stopwatch = Stopwatch.StartNew();
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
@ -94,8 +148,14 @@ namespace MLEM.Pathfinding {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A helper method to add two positions together.
|
||||||
|
/// </summary>
|
||||||
protected abstract T AddPositions(T first, T second);
|
protected abstract T AddPositions(T first, T second);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A helper method to get the Manhattan Distance between two points.
|
||||||
|
/// </summary>
|
||||||
protected abstract float GetManhattanDistance(T first, T second);
|
protected abstract float GetManhattanDistance(T first, T second);
|
||||||
|
|
||||||
private static Stack<T> CompilePath(PathPoint<T> current) {
|
private static Stack<T> CompilePath(PathPoint<T> current) {
|
||||||
|
@ -107,17 +167,48 @@ namespace MLEM.Pathfinding {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A cost function for a given path finding position.
|
||||||
|
/// If a path point should have the default cost, <see cref="AStar{T}.DefaultCost"/> should be returned.
|
||||||
|
/// If a path point should be unreachable, <see cref="AStar{T}.InfiniteCost"/> should be returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currPos">The current position in the path</param>
|
||||||
|
/// <param name="nextPos">The position we're trying to reach from the current position</param>
|
||||||
public delegate float GetCost(T currPos, T nextPos);
|
public delegate float GetCost(T currPos, T nextPos);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A point in a <see cref="AStar{T}"/> path
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of point used for this path</typeparam>
|
||||||
public class PathPoint<T> {
|
public class PathPoint<T> {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path point that this point originated from
|
||||||
|
/// </summary>
|
||||||
public readonly PathPoint<T> Parent;
|
public readonly PathPoint<T> Parent;
|
||||||
|
/// <summary>
|
||||||
|
/// The position of this path point
|
||||||
|
/// </summary>
|
||||||
public readonly T Pos;
|
public readonly T Pos;
|
||||||
|
/// <summary>
|
||||||
|
/// The F cost of this path point
|
||||||
|
/// </summary>
|
||||||
public readonly float F;
|
public readonly float F;
|
||||||
|
/// <summary>
|
||||||
|
/// The G cost of this path point
|
||||||
|
/// </summary>
|
||||||
public readonly float G;
|
public readonly float G;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new path point with the supplied settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pos">The point's position</param>
|
||||||
|
/// <param name="distance">The point's manhattan distance from the start point</param>
|
||||||
|
/// <param name="parent">The point's parent</param>
|
||||||
|
/// <param name="terrainCostForThisPos">The point's terrain cost, based on <see cref="AStar{T}.GetCost"/></param>
|
||||||
|
/// <param name="defaultCost">The default cost for a path point</param>
|
||||||
public PathPoint(T pos, float distance, PathPoint<T> parent, float terrainCostForThisPos, float defaultCost) {
|
public PathPoint(T pos, float distance, PathPoint<T> parent, float terrainCostForThisPos, float defaultCost) {
|
||||||
this.Pos = pos;
|
this.Pos = pos;
|
||||||
this.Parent = parent;
|
this.Parent = parent;
|
||||||
|
@ -126,12 +217,14 @@ namespace MLEM.Pathfinding {
|
||||||
this.F = this.G + distance * defaultCost;
|
this.F = this.G + distance * defaultCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool Equals(object obj) {
|
public override bool Equals(object obj) {
|
||||||
if (obj == this)
|
if (obj == this)
|
||||||
return true;
|
return true;
|
||||||
return obj is PathPoint<T> point && point.Pos.Equals(this.Pos);
|
return obj is PathPoint<T> point && point.Pos.Equals(this.Pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override int GetHashCode() {
|
public override int GetHashCode() {
|
||||||
return this.Pos.GetHashCode();
|
return this.Pos.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,25 @@ using Microsoft.Xna.Framework;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
|
|
||||||
namespace MLEM.Pathfinding {
|
namespace MLEM.Pathfinding {
|
||||||
|
/// <summary>
|
||||||
|
/// A 2-dimensional implementation of <see cref="AStar{T}"/> that uses <see cref="Point"/> for positions.
|
||||||
|
/// </summary>
|
||||||
public class AStar2 : AStar<Point> {
|
public class AStar2 : AStar<Point> {
|
||||||
|
|
||||||
private static readonly Point[] AdjacentDirs = Direction2Helper.Adjacent.Offsets().ToArray();
|
private static readonly Point[] AdjacentDirs = Direction2Helper.Adjacent.Offsets().ToArray();
|
||||||
private static readonly Point[] AllDirs = Direction2Helper.All.Offsets().ToArray();
|
private static readonly Point[] AllDirs = Direction2Helper.All.Offsets().ToArray();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public AStar2(GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) :
|
public AStar2(GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) :
|
||||||
base(AllDirs, AdjacentDirs, defaultCostFunction, defaultAllowDiagonals, defaultCost, defaultMaxTries) {
|
base(AllDirs, AdjacentDirs, defaultCostFunction, defaultAllowDiagonals, defaultCost, defaultMaxTries) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override Point AddPositions(Point first, Point second) {
|
protected override Point AddPositions(Point first, Point second) {
|
||||||
return first + second;
|
return first + second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override float GetManhattanDistance(Point first, Point second) {
|
protected override float GetManhattanDistance(Point first, Point second) {
|
||||||
return Math.Abs(second.X - first.X) + Math.Abs(second.Y - first.Y);
|
return Math.Abs(second.X - first.X) + Math.Abs(second.Y - first.Y);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ using System.Linq;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
namespace MLEM.Pathfinding {
|
namespace MLEM.Pathfinding {
|
||||||
|
/// <summary>
|
||||||
|
/// A 3-dimensional implementation of <see cref="AStar{T}"/> that uses <see cref="Vector3"/> for positions.
|
||||||
|
/// </summary>
|
||||||
public class AStar3 : AStar<Vector3> {
|
public class AStar3 : AStar<Vector3> {
|
||||||
|
|
||||||
private static readonly Vector3[] AdjacentDirs = {
|
private static readonly Vector3[] AdjacentDirs = {
|
||||||
|
@ -31,14 +34,17 @@ namespace MLEM.Pathfinding {
|
||||||
AllDirs = dirs.ToArray();
|
AllDirs = dirs.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public AStar3(GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) :
|
public AStar3(GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) :
|
||||||
base(AllDirs, AdjacentDirs, defaultCostFunction, defaultAllowDiagonals, defaultCost, defaultMaxTries) {
|
base(AllDirs, AdjacentDirs, defaultCostFunction, defaultAllowDiagonals, defaultCost, defaultMaxTries) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override Vector3 AddPositions(Vector3 first, Vector3 second) {
|
protected override Vector3 AddPositions(Vector3 first, Vector3 second) {
|
||||||
return first + second;
|
return first + second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override float GetManhattanDistance(Vector3 first, Vector3 second) {
|
protected override float GetManhattanDistance(Vector3 first, Vector3 second) {
|
||||||
return Math.Abs(second.X - first.X) + Math.Abs(second.Y - first.Y) + Math.Abs(second.Z - first.Z);
|
return Math.Abs(second.X - first.X) + Math.Abs(second.Y - first.Y) + Math.Abs(second.Z - first.Z);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,37 +6,70 @@ using MLEM.Extensions;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
|
|
||||||
namespace MLEM.Textures {
|
namespace MLEM.Textures {
|
||||||
|
/// <summary>
|
||||||
|
/// This class represents a texture with nine areas.
|
||||||
|
/// A nine patch texture is useful if a big area should be covered by a small texture that has a specific outline, like a gui panel texture. The center of the texture will be stretched, while the outline of the texture will remain at its original size, keeping aspect ratios alive.
|
||||||
|
/// </summary>
|
||||||
public class NinePatch : GenericDataHolder {
|
public class NinePatch : GenericDataHolder {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture region of this nine patch
|
||||||
|
/// </summary>
|
||||||
public readonly TextureRegion Region;
|
public readonly TextureRegion Region;
|
||||||
|
/// <summary>
|
||||||
|
/// The padding in each direction that marks where the outline area stops
|
||||||
|
/// </summary>
|
||||||
public readonly Padding Padding;
|
public readonly Padding Padding;
|
||||||
|
/// <summary>
|
||||||
|
/// The nine patches that result from the <see cref="Padding"/>
|
||||||
|
/// </summary>
|
||||||
public readonly Rectangle[] SourceRectangles;
|
public readonly Rectangle[] SourceRectangles;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new nine patch from a texture and a padding
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture to use</param>
|
||||||
|
/// <param name="padding">The padding that marks where the outline area stops</param>
|
||||||
public NinePatch(TextureRegion texture, Padding padding) {
|
public NinePatch(TextureRegion texture, Padding padding) {
|
||||||
this.Region = texture;
|
this.Region = texture;
|
||||||
this.Padding = padding;
|
this.Padding = padding;
|
||||||
this.SourceRectangles = this.CreateRectangles(this.Region.Area).ToArray();
|
this.SourceRectangles = this.CreateRectangles(this.Region.Area).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new nine patch from a texture and a padding
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture to use</param>
|
||||||
|
/// <param name="paddingLeft">The padding on the left edge</param>
|
||||||
|
/// <param name="paddingRight">The padding on the right edge</param>
|
||||||
|
/// <param name="paddingTop">The padding on the top edge</param>
|
||||||
|
/// <param name="paddingBottom">The padding on the bottom edge</param>
|
||||||
public NinePatch(TextureRegion texture, int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) :
|
public NinePatch(TextureRegion texture, int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) :
|
||||||
this(texture, new Padding(paddingLeft, paddingRight, paddingTop, paddingBottom)) {
|
this(texture, new Padding(paddingLeft, paddingRight, paddingTop, paddingBottom)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="NinePatch(TextureRegion, int, int, int, int)"/>
|
||||||
public NinePatch(Texture2D texture, int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) :
|
public NinePatch(Texture2D texture, int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) :
|
||||||
this(new TextureRegion(texture), paddingLeft, paddingRight, paddingTop, paddingBottom) {
|
this(new TextureRegion(texture), paddingLeft, paddingRight, paddingTop, paddingBottom) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new nine patch from a texture and a uniform padding
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture to use</param>
|
||||||
|
/// <param name="padding">The padding that each edge should have</param>
|
||||||
public NinePatch(Texture2D texture, int padding) : this(new TextureRegion(texture), padding) {
|
public NinePatch(Texture2D texture, int padding) : this(new TextureRegion(texture), padding) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="NinePatch(TextureRegion, int)"/>
|
||||||
public NinePatch(TextureRegion texture, int padding) : this(texture, padding, padding, padding, padding) {
|
public NinePatch(TextureRegion texture, int padding) : this(texture, padding, padding, padding, padding) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Rectangle> CreateRectangles(Rectangle area, float patchScale = 1) {
|
private IEnumerable<Rectangle> CreateRectangles(Rectangle area, float patchScale = 1) {
|
||||||
return this.CreateRectangles((RectangleF) area, patchScale).Select(r => (Rectangle) r);
|
return this.CreateRectangles((RectangleF) area, patchScale).Select(r => (Rectangle) r);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<RectangleF> CreateRectangles(RectangleF area, float patchScale = 1) {
|
internal IEnumerable<RectangleF> CreateRectangles(RectangleF area, float patchScale = 1) {
|
||||||
var pl = this.Padding.Left * patchScale;
|
var pl = this.Padding.Left * patchScale;
|
||||||
var pr = this.Padding.Right * patchScale;
|
var pr = this.Padding.Right * patchScale;
|
||||||
var pt = this.Padding.Top * patchScale;
|
var pt = this.Padding.Top * patchScale;
|
||||||
|
@ -62,8 +95,23 @@ namespace MLEM.Textures {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A set of extensions that allow for <see cref="NinePatch"/> rendering
|
||||||
|
/// </summary>
|
||||||
public static class NinePatchExtensions {
|
public static class NinePatchExtensions {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws a nine patch area using the given sprite batch
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="batch">The batch to draw with</param>
|
||||||
|
/// <param name="texture">The nine patch to draw</param>
|
||||||
|
/// <param name="destinationRectangle">The area that should be covered by this nine patch</param>
|
||||||
|
/// <param name="color">The color to use</param>
|
||||||
|
/// <param name="rotation">The rotation</param>
|
||||||
|
/// <param name="origin">The origin position</param>
|
||||||
|
/// <param name="effects">The effects that the sprite should have</param>
|
||||||
|
/// <param name="layerDepth">The depth</param>
|
||||||
|
/// <param name="patchScale">The scale of each area of the nine patch</param>
|
||||||
public static void Draw(this SpriteBatch batch, NinePatch texture, RectangleF destinationRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth, float patchScale = 1) {
|
public static void Draw(this SpriteBatch batch, NinePatch texture, RectangleF destinationRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth, float patchScale = 1) {
|
||||||
var dest = texture.CreateRectangles(destinationRectangle, patchScale);
|
var dest = texture.CreateRectangles(destinationRectangle, patchScale);
|
||||||
var count = 0;
|
var count = 0;
|
||||||
|
@ -74,14 +122,17 @@ namespace MLEM.Textures {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch,MLEM.Textures.NinePatch,MLEM.Misc.RectangleF,Microsoft.Xna.Framework.Color,float,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Graphics.SpriteEffects,float,float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, NinePatch texture, Rectangle destinationRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth, float patchScale = 1) {
|
public static void Draw(this SpriteBatch batch, NinePatch texture, Rectangle destinationRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth, float patchScale = 1) {
|
||||||
batch.Draw(texture, (RectangleF) destinationRectangle, color, rotation, origin, effects, layerDepth, patchScale);
|
batch.Draw(texture, (RectangleF) destinationRectangle, color, rotation, origin, effects, layerDepth, patchScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch,MLEM.Textures.NinePatch,MLEM.Misc.RectangleF,Microsoft.Xna.Framework.Color,float,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Graphics.SpriteEffects,float,float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, NinePatch texture, RectangleF destinationRectangle, Color color, float patchScale = 1) {
|
public static void Draw(this SpriteBatch batch, NinePatch texture, RectangleF destinationRectangle, Color color, float patchScale = 1) {
|
||||||
batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0, patchScale);
|
batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0, patchScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch,MLEM.Textures.NinePatch,MLEM.Misc.RectangleF,Microsoft.Xna.Framework.Color,float,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Graphics.SpriteEffects,float,float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, NinePatch texture, Rectangle destinationRectangle, Color color, float patchScale = 1) {
|
public static void Draw(this SpriteBatch batch, NinePatch texture, Rectangle destinationRectangle, Color color, float patchScale = 1) {
|
||||||
batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0, patchScale);
|
batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0, patchScale);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,59 +4,119 @@ using MLEM.Extensions;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
|
|
||||||
namespace MLEM.Textures {
|
namespace MLEM.Textures {
|
||||||
|
/// <summary>
|
||||||
|
/// This class represents a part of a texture.
|
||||||
|
/// </summary>
|
||||||
public class TextureRegion : GenericDataHolder {
|
public class TextureRegion : GenericDataHolder {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture that this region is a part of
|
||||||
|
/// </summary>
|
||||||
public readonly Texture2D Texture;
|
public readonly Texture2D Texture;
|
||||||
|
/// <summary>
|
||||||
|
/// The area that is covered by this texture region
|
||||||
|
/// </summary>
|
||||||
public readonly Rectangle Area;
|
public readonly Rectangle Area;
|
||||||
|
/// <summary>
|
||||||
|
/// The top left corner of this texture region
|
||||||
|
/// </summary>
|
||||||
public Point Position => this.Area.Location;
|
public Point Position => this.Area.Location;
|
||||||
|
/// <summary>
|
||||||
|
/// The x coordinate of the top left corner of this texture region
|
||||||
|
/// </summary>
|
||||||
public int U => this.Area.X;
|
public int U => this.Area.X;
|
||||||
|
/// <summary>
|
||||||
|
/// The y coordinate of the top left corner of this texture region
|
||||||
|
/// </summary>
|
||||||
public int V => this.Area.Y;
|
public int V => this.Area.Y;
|
||||||
|
/// <summary>
|
||||||
|
/// The size of this texture region
|
||||||
|
/// </summary>
|
||||||
public Point Size => this.Area.Size;
|
public Point Size => this.Area.Size;
|
||||||
|
/// <summary>
|
||||||
|
/// The width of this texture region
|
||||||
|
/// </summary>
|
||||||
public int Width => this.Area.Width;
|
public int Width => this.Area.Width;
|
||||||
|
/// <summary>
|
||||||
|
/// The height of this texture region
|
||||||
|
/// </summary>
|
||||||
public int Height => this.Area.Height;
|
public int Height => this.Area.Height;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new texture region from a texture and a rectangle which defines the region's area
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture to use</param>
|
||||||
|
/// <param name="area">The area that this texture region should cover</param>
|
||||||
public TextureRegion(Texture2D texture, Rectangle area) {
|
public TextureRegion(Texture2D texture, Rectangle area) {
|
||||||
this.Texture = texture;
|
this.Texture = texture;
|
||||||
this.Area = area;
|
this.Area = area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new texture region that spans the entire texture
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture to use</param>
|
||||||
public TextureRegion(Texture2D texture) : this(texture, new Rectangle(0, 0, texture.Width, texture.Height)) {
|
public TextureRegion(Texture2D texture) : this(texture, new Rectangle(0, 0, texture.Width, texture.Height)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new texture region based on a texture and area coordinates
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture to use</param>
|
||||||
|
/// <param name="u">The x coordinate of the top left corner of this area</param>
|
||||||
|
/// <param name="v">The y coordinate of the top left corner of this area</param>
|
||||||
|
/// <param name="width">The width of this area</param>
|
||||||
|
/// <param name="height">The height of this area</param>
|
||||||
public TextureRegion(Texture2D texture, int u, int v, int width, int height) : this(texture, new Rectangle(u, v, width, height)) {
|
public TextureRegion(Texture2D texture, int u, int v, int width, int height) : this(texture, new Rectangle(u, v, width, height)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new texture region based on a texture, a position and a size
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture to use</param>
|
||||||
|
/// <param name="uv">The top left corner of this area</param>
|
||||||
|
/// <param name="size">The size of this area</param>
|
||||||
public TextureRegion(Texture2D texture, Point uv, Point size) : this(texture, new Rectangle(uv, size)) {
|
public TextureRegion(Texture2D texture, Point uv, Point size) : this(texture, new Rectangle(uv, size)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class provides a set of extension methods for dealing with <see cref="TextureRegion"/>
|
||||||
|
/// </summary>
|
||||||
public static class TextureRegionExtensions {
|
public static class TextureRegionExtensions {
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SpriteBatch.Draw(Texture2D, Vector2, Rectangle?, Color, float, Vector2, Vector2, SpriteEffects, float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, TextureRegion texture, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
|
public static void Draw(this SpriteBatch batch, TextureRegion texture, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
|
||||||
batch.Draw(texture.Texture, position, texture.Area, color, rotation, origin, scale, effects, layerDepth);
|
batch.Draw(texture.Texture, position, texture.Area, color, rotation, origin, scale, effects, layerDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SpriteBatch.Draw(Texture2D, Vector2, Rectangle?, Color, float, Vector2, Vector2, SpriteEffects, float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, TextureRegion texture, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) {
|
public static void Draw(this SpriteBatch batch, TextureRegion texture, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) {
|
||||||
batch.Draw(texture, position, color, rotation, origin, new Vector2(scale), effects, layerDepth);
|
batch.Draw(texture, position, color, rotation, origin, new Vector2(scale), effects, layerDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SpriteBatch.Draw(Texture2D, Vector2, Rectangle?, Color, float, Vector2, Vector2, SpriteEffects, float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, TextureRegion texture, Rectangle destinationRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth) {
|
public static void Draw(this SpriteBatch batch, TextureRegion texture, Rectangle destinationRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth) {
|
||||||
batch.Draw(texture.Texture, destinationRectangle, texture.Area, color, rotation, origin, effects, layerDepth);
|
batch.Draw(texture.Texture, destinationRectangle, texture.Area, color, rotation, origin, effects, layerDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SpriteBatch.Draw(Texture2D, Vector2, Rectangle?, Color, float, Vector2, Vector2, SpriteEffects, float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, TextureRegion texture, RectangleF destinationRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth) {
|
public static void Draw(this SpriteBatch batch, TextureRegion texture, RectangleF destinationRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth) {
|
||||||
batch.Draw(texture.Texture, destinationRectangle, texture.Area, color, rotation, origin, effects, layerDepth);
|
batch.Draw(texture.Texture, destinationRectangle, texture.Area, color, rotation, origin, effects, layerDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SpriteBatch.Draw(Texture2D, Vector2, Rectangle?, Color, float, Vector2, Vector2, SpriteEffects, float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, TextureRegion texture, Vector2 position, Color color) {
|
public static void Draw(this SpriteBatch batch, TextureRegion texture, Vector2 position, Color color) {
|
||||||
batch.Draw(texture, position, color, 0, Vector2.Zero, Vector2.One, SpriteEffects.None, 0);
|
batch.Draw(texture, position, color, 0, Vector2.Zero, Vector2.One, SpriteEffects.None, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SpriteBatch.Draw(Texture2D, Vector2, Rectangle?, Color, float, Vector2, Vector2, SpriteEffects, float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, TextureRegion texture, Rectangle destinationRectangle, Color color) {
|
public static void Draw(this SpriteBatch batch, TextureRegion texture, Rectangle destinationRectangle, Color color) {
|
||||||
batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0);
|
batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SpriteBatch.Draw(Texture2D, Vector2, Rectangle?, Color, float, Vector2, Vector2, SpriteEffects, float)"/>
|
||||||
public static void Draw(this SpriteBatch batch, TextureRegion texture, RectangleF destinationRectangle, Color color) {
|
public static void Draw(this SpriteBatch batch, TextureRegion texture, RectangleF destinationRectangle, Color color) {
|
||||||
batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0);
|
batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,63 @@ using Microsoft.Xna.Framework.Graphics;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
|
|
||||||
namespace MLEM.Textures {
|
namespace MLEM.Textures {
|
||||||
|
/// <summary>
|
||||||
|
/// This class represents an atlas of <see cref="TextureRegion"/> objects that is uniform.
|
||||||
|
/// Uniform, in this case, means that the texture atlas' size is not determined by the width and height of the texture, but instead by the amount of sub-regions that the atlas has in the x and y direction.
|
||||||
|
/// Using a uniform texture atlas over a regular texture as an atlas allows for texture artists to create higher resolution textures without coordinates becoming off.
|
||||||
|
/// </summary>
|
||||||
public class UniformTextureAtlas : GenericDataHolder {
|
public class UniformTextureAtlas : GenericDataHolder {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture to use for this atlas
|
||||||
|
/// </summary>
|
||||||
public readonly Texture2D Texture;
|
public readonly Texture2D Texture;
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of sub-regions this atlas has in the x direction
|
||||||
|
/// </summary>
|
||||||
public readonly int RegionAmountX;
|
public readonly int RegionAmountX;
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of sub-regions this atlas has in the y direction
|
||||||
|
/// </summary>
|
||||||
public readonly int RegionAmountY;
|
public readonly int RegionAmountY;
|
||||||
|
/// <summary>
|
||||||
|
/// The width of each region, based on the texture's width and the amount of regions
|
||||||
|
/// </summary>
|
||||||
public readonly int RegionWidth;
|
public readonly int RegionWidth;
|
||||||
|
/// <summary>
|
||||||
|
/// The height of reach region, based on the texture's height and the amount of regions
|
||||||
|
/// </summary>
|
||||||
public readonly int RegionHeight;
|
public readonly int RegionHeight;
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the <see cref="TextureRegion"/> at this texture atlas's given index.
|
||||||
|
/// The index is zero-based, where rows come first and columns come second.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The zero-based texture index</param>
|
||||||
public TextureRegion this[int index] => this[index % this.RegionAmountX, index / this.RegionAmountX];
|
public TextureRegion this[int index] => this[index % this.RegionAmountX, index / this.RegionAmountX];
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the <see cref="TextureRegion"/> at this texture atlas' given region position
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The region's x and y location</param>
|
||||||
public TextureRegion this[Point point] => this[new Rectangle(point, new Point(1, 1))];
|
public TextureRegion this[Point point] => this[new Rectangle(point, new Point(1, 1))];
|
||||||
|
/// <inheritdoc cref="this[Point]"/>
|
||||||
public TextureRegion this[int x, int y] => this[new Point(x, y)];
|
public TextureRegion this[int x, int y] => this[new Point(x, y)];
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the <see cref="TextureRegion"/> at this texture atlas' given region position and size.
|
||||||
|
/// Note that the region size is not in pixels, but in region units.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rect">The region's area</param>
|
||||||
public TextureRegion this[Rectangle rect] => this.GetOrAddRegion(rect);
|
public TextureRegion this[Rectangle rect] => this.GetOrAddRegion(rect);
|
||||||
|
/// <inheritdoc cref="this[Rectangle]"/>
|
||||||
public TextureRegion this[int x, int y, int width, int height] => this[new Rectangle(x, y, width, height)];
|
public TextureRegion this[int x, int y, int width, int height] => this[new Rectangle(x, y, width, height)];
|
||||||
|
|
||||||
private readonly Dictionary<Rectangle, TextureRegion> regions = new Dictionary<Rectangle, TextureRegion>();
|
private readonly Dictionary<Rectangle, TextureRegion> regions = new Dictionary<Rectangle, TextureRegion>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new uniform texture atlas with the given texture and region amount.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">The texture to use for this atlas</param>
|
||||||
|
/// <param name="regionAmountX">The amount of texture regions in the x direction</param>
|
||||||
|
/// <param name="regionAmountY">The amount of texture regions in the y direction</param>
|
||||||
public UniformTextureAtlas(Texture2D texture, int regionAmountX, int regionAmountY) {
|
public UniformTextureAtlas(Texture2D texture, int regionAmountX, int regionAmountY) {
|
||||||
this.Texture = texture;
|
this.Texture = texture;
|
||||||
this.RegionAmountX = regionAmountX;
|
this.RegionAmountX = regionAmountX;
|
||||||
|
|
Loading…
Reference in a new issue