diff --git a/MLEM/Animations/SpriteAnimation.cs b/MLEM/Animations/SpriteAnimation.cs index 7fd8b59..31c6146 100644 --- a/MLEM/Animations/SpriteAnimation.cs +++ b/MLEM/Animations/SpriteAnimation.cs @@ -138,6 +138,10 @@ namespace MLEM.Animations { this.IsPaused = false; } + /// + /// A callback for when a sprite animation is completed. + /// + /// The animation that has completed public delegate void Completed(SpriteAnimation animation); } diff --git a/MLEM/Animations/SpriteAnimationGroup.cs b/MLEM/Animations/SpriteAnimationGroup.cs index 1dce5df..c52bba8 100644 --- a/MLEM/Animations/SpriteAnimationGroup.cs +++ b/MLEM/Animations/SpriteAnimationGroup.cs @@ -98,6 +98,11 @@ namespace MLEM.Animations { } } + /// + /// A callback delegate for when a 's current animation changed. + /// + /// The previous animation + /// The new animation public delegate void AnimationChanged(SpriteAnimation oldAnim, SpriteAnimation newAnim); } diff --git a/MLEM/Extensions/ColorExtensions.cs b/MLEM/Extensions/ColorExtensions.cs index bd1feea..e8dc192 100644 --- a/MLEM/Extensions/ColorExtensions.cs +++ b/MLEM/Extensions/ColorExtensions.cs @@ -40,11 +40,11 @@ namespace MLEM.Extensions { } /// - /// Copies the alpha value from into this color. + /// Copies the alpha value from another color into this color. /// /// The color /// The color to copy the alpha from - /// The with 's alpha value + /// The first color with the second color's alpha value public static Color CopyAlpha(this Color color, Color other) { return color * (other.A / 255F); } diff --git a/MLEM/Extensions/GraphicsExtensions.cs b/MLEM/Extensions/GraphicsExtensions.cs index e5fc78b..53e2b36 100644 --- a/MLEM/Extensions/GraphicsExtensions.cs +++ b/MLEM/Extensions/GraphicsExtensions.cs @@ -75,17 +75,29 @@ namespace MLEM.Extensions { return new TargetContext(device, target); } + /// + /// Represents a context in which a is applied. + /// This class should be used with . + /// public struct TargetContext : IDisposable { private readonly GraphicsDevice device; private readonly RenderTargetBinding[] lastTargets; + /// + /// Creates a new target context with the given settings. + /// + /// The graphics device to apply the target on + /// The target to apply public TargetContext(GraphicsDevice device, RenderTarget2D target) { this.device = device; this.lastTargets = device.RenderTargetCount <= 0 ? null : device.GetRenderTargets(); device.SetRenderTarget(target); } + /// + /// Disposes this target context, which causes the graphics device's previous render targets to be re-applied. + /// public void Dispose() { this.device.SetRenderTargets(this.lastTargets); } diff --git a/MLEM/Extensions/NumberExtensions.cs b/MLEM/Extensions/NumberExtensions.cs index beb7843..3b92a72 100644 --- a/MLEM/Extensions/NumberExtensions.cs +++ b/MLEM/Extensions/NumberExtensions.cs @@ -24,7 +24,7 @@ namespace MLEM.Extensions { /// The first number to equate /// The second number to equate /// The equality tolerance - /// Whether or not and are different by at most + /// Whether or not the two values are different by at most tolerance public static bool Equals(this float first, float second, float tolerance) { return Math.Abs(first - second) <= tolerance; } diff --git a/MLEM/Input/InputHandler.cs b/MLEM/Input/InputHandler.cs index 2d40444..08c9f1a 100644 --- a/MLEM/Input/InputHandler.cs +++ b/MLEM/Input/InputHandler.cs @@ -9,47 +9,125 @@ using MLEM.Extensions; using MLEM.Misc; namespace MLEM.Input { + /// + /// 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. + /// public class InputHandler : GameComponent { + /// + /// Contains the keyboard state from the last update call + /// public KeyboardState LastKeyboardState { get; private set; } + /// + /// Contains the current keyboard state + /// public KeyboardState KeyboardState { get; private set; } + /// + /// Contains the keyboard keys that are currently being pressed + /// public Keys[] PressedKeys { get; private set; } + /// + /// Set this property to false to disable keyboard handling for this input handler. + /// public bool HandleKeyboard; + /// + /// Contains the mouse state from the last update call + /// public MouseState LastMouseState { get; private set; } + /// + /// Contains the current mouse state + /// public MouseState MouseState { get; private set; } + /// + /// Contains the current position of the mouse, extracted from + /// public Point MousePosition => this.MouseState.Position; + /// + /// Contains the position of the mouse from the last update call, extracted from + /// public Point LastMousePosition => this.LastMouseState.Position; + /// + /// Contains the current scroll wheel value, in increments of 120 + /// public int ScrollWheel => this.MouseState.ScrollWheelValue; + /// + /// Contains the scroll wheel value from the last update call, in increments of 120 + /// public int LastScrollWheel => this.LastMouseState.ScrollWheelValue; + /// + /// Set this property to false to disable mouse handling for this input handler. + /// public bool HandleMouse; private readonly GamePadState[] lastGamepads = new GamePadState[GamePad.MaximumGamePadCount]; private readonly GamePadState[] gamepads = new GamePadState[GamePad.MaximumGamePadCount]; + /// + /// Contains the amount of gamepads that are currently connected. + /// This property is automatically updated in + /// public int ConnectedGamepads { get; private set; } + /// + /// Set this property to false to disable keyboard handling for this input handler. + /// public bool HandleGamepads; + /// + /// Contains the touch state from the last update call + /// public TouchCollection LastTouchState { get; private set; } + /// + /// Contains the current touch state + /// public TouchCollection TouchState { get; private set; } + /// + /// Contains all of the gestures that have finished during the last update call. + /// To easily query these gestures, use + /// public readonly ReadOnlyCollection Gestures; private readonly List gestures = new List(); + /// + /// Set this property to false to disable touch handling for this input handler. + /// public bool HandleTouch; + /// + /// This is the amount of time that has to pass before the first keyboard repeat event is triggered. + /// + /// public TimeSpan KeyRepeatDelay = TimeSpan.FromSeconds(0.65); + /// + /// This is the amount of time that has to pass between keyboard repeat events. + /// + /// public TimeSpan KeyRepeatRate = TimeSpan.FromSeconds(0.05); + /// + /// Set this property to false to disable keyboard repeat event handling. + /// public bool HandleKeyboardRepeats = true; private DateTime heldKeyStart; private DateTime lastKeyRepeat; private bool triggerKeyRepeat; private Keys heldKey; + /// + /// Set this property to false to disable gamepad repeat event handling. + /// public bool HandleGamepadRepeats = true; private readonly DateTime[] heldGamepadButtonStarts = new DateTime[GamePad.MaximumGamePadCount]; private readonly DateTime[] lastGamepadButtonRepeats = new DateTime[GamePad.MaximumGamePadCount]; private readonly bool[] triggerGamepadButtonRepeat = new bool[GamePad.MaximumGamePadCount]; private readonly Buttons?[] heldGamepadButtons = new Buttons?[GamePad.MaximumGamePadCount]; + /// + /// Creates a new input handler with optional initial values. + /// + /// If keyboard input should be handled + /// If mouse input should be handled + /// If gamepad input should be handled + /// If touch input should be handled public InputHandler(bool handleKeyboard = true, bool handleMouse = true, bool handleGamepads = true, bool handleTouch = true) : base(null) { this.HandleKeyboard = handleKeyboard; this.HandleMouse = handleMouse; @@ -58,6 +136,10 @@ namespace MLEM.Input { this.Gestures = this.gestures.AsReadOnly(); } + /// + /// Updates this input handler, querying pressed and released keys and calculating repeat events. + /// Call this in your method. + /// public void Update() { if (this.HandleKeyboard) { this.LastKeyboardState = this.KeyboardState; @@ -151,34 +233,55 @@ namespace MLEM.Input { } } + /// public override void Update(GameTime gameTime) { this.Update(); } + /// + /// Returns the state of the indexth gamepad from the last update call + /// + /// The zero-based gamepad index + /// The state of the gamepad last update public GamePadState GetLastGamepadState(int index) { return this.lastGamepads[index]; } + /// + /// Returns the current state of the indexth gamepad + /// + /// The zero-based gamepad index + /// The current state of the gamepad public GamePadState GetGamepadState(int index) { return this.gamepads[index]; } + /// public bool IsKeyDown(Keys key) { return this.KeyboardState.IsKeyDown(key); } + /// public bool IsKeyUp(Keys key) { return this.KeyboardState.IsKeyUp(key); } + /// public bool WasKeyDown(Keys key) { return this.LastKeyboardState.IsKeyDown(key); } + /// public bool WasKeyUp(Keys key) { return this.LastKeyboardState.IsKeyUp(key); } + /// + /// 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. + /// + /// The key to query + /// If the key is pressed public bool IsKeyPressed(Keys key) { // if the queried key is the held key and a repeat should be triggered, return true if (this.HandleKeyboardRepeats && key == this.heldKey && this.triggerKeyRepeat) @@ -186,30 +289,62 @@ namespace MLEM.Input { return this.WasKeyUp(key) && this.IsKeyDown(key); } + /// + /// Returns whether the given modifier key is down. + /// + /// The modifier key + /// If the modifier key is down public bool IsModifierKeyDown(ModifierKey modifier) { return modifier.GetKeys().Any(this.IsKeyDown); } + /// + /// Returns whether the given mouse button is currently down. + /// + /// The button to query + /// Whether or not the queried button is down public bool IsMouseButtonDown(MouseButton button) { return this.MouseState.GetState(button) == ButtonState.Pressed; } + /// + /// Returns whether the given mouse button is currently up. + /// + /// The button to query + /// Whether or not the queried button is up public bool IsMouseButtonUp(MouseButton button) { return this.MouseState.GetState(button) == ButtonState.Released; } + /// + /// Returns whether the given mouse button was down the last update call. + /// + /// The button to query + /// Whether or not the queried button was down public bool WasMouseButtonDown(MouseButton button) { return this.LastMouseState.GetState(button) == ButtonState.Pressed; } + /// + /// Returns whether the given mouse button was up the last update call. + /// + /// The button to query + /// Whether or not the queried button was up public bool WasMouseButtonUp(MouseButton button) { return this.LastMouseState.GetState(button) == ButtonState.Released; } + /// + /// 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. + /// + /// The button to query + /// Whether the button is pressed public bool IsMouseButtonPressed(MouseButton button) { return this.WasMouseButtonUp(button) && this.IsMouseButtonDown(button); } + /// public bool IsGamepadButtonDown(Buttons button, int index = -1) { if (index < 0) { for (var i = 0; i < this.ConnectedGamepads; i++) @@ -220,6 +355,7 @@ namespace MLEM.Input { return this.GetGamepadState(index).IsButtonDown(button); } + /// public bool IsGamepadButtonUp(Buttons button, int index = -1) { if (index < 0) { for (var i = 0; i < this.ConnectedGamepads; i++) @@ -230,6 +366,7 @@ namespace MLEM.Input { return this.GetGamepadState(index).IsButtonUp(button); } + /// public bool WasGamepadButtonDown(Buttons button, int index = -1) { if (index < 0) { for (var i = 0; i < this.ConnectedGamepads; i++) @@ -240,6 +377,7 @@ namespace MLEM.Input { return this.GetLastGamepadState(index).IsButtonDown(button); } + /// public bool WasGamepadButtonUp(Buttons button, int index = -1) { if (index < 0) { for (var i = 0; i < this.ConnectedGamepads; i++) @@ -250,6 +388,13 @@ namespace MLEM.Input { return this.GetLastGamepadState(index).IsButtonUp(button); } + /// + /// 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. + /// + /// The button to query + /// The zero-based index of the gamepad, or -1 for any gamepad + /// Whether the given button is pressed public bool IsGamepadButtonPressed(Buttons button, int index = -1) { if (this.HandleGamepadRepeats) { if (index < 0) { @@ -263,6 +408,12 @@ namespace MLEM.Input { return this.WasGamepadButtonUp(button, index) && this.IsGamepadButtonDown(button, index); } + /// + /// Queries for a gesture of a given type that finished during the current update call. + /// + /// The type of gesture to query for + /// The resulting gesture sample, or default if there isn't one + /// True if a gesture of the type was found, otherwise false public bool GetGesture(GestureType type, out GestureSample sample) { foreach (var gesture in this.Gestures) { if (gesture.GestureType == type) { @@ -273,6 +424,14 @@ namespace MLEM.Input { return false; } + /// + /// Returns if a given control of any kind is down. + /// This is a helper function that can be passed a , or . + /// + /// The control whose down state to query + /// The index of the gamepad to query (if applicable), or -1 for any gamepad + /// Whether the given control is down + /// If the passed control isn't of a supported type public bool IsDown(object control, int index = -1) { if (control is Keys key) return this.IsKeyDown(key); @@ -283,6 +442,14 @@ namespace MLEM.Input { throw new ArgumentException(nameof(control)); } + /// + /// Returns if a given control of any kind is up. + /// This is a helper function that can be passed a , or . + /// + /// The control whose up state to query + /// The index of the gamepad to query (if applicable), or -1 for any gamepad + /// Whether the given control is down + /// If the passed control isn't of a supported type public bool IsUp(object control, int index = -1) { if (control is Keys key) return this.IsKeyUp(key); @@ -293,6 +460,14 @@ namespace MLEM.Input { throw new ArgumentException(nameof(control)); } + /// + /// Returns if a given control of any kind is pressed. + /// This is a helper function that can be passed a , or . + /// + /// The control whose pressed state to query + /// The index of the gamepad to query (if applicable), or -1 for any gamepad + /// Whether the given control is down + /// If the passed control isn't of a supported type public bool IsPressed(object control, int index = -1) { if (control is Keys key) return this.IsKeyPressed(key); @@ -303,18 +478,26 @@ namespace MLEM.Input { throw new ArgumentException(nameof(control)); } + /// public bool IsAnyDown(params object[] control) { return control.Any(c => this.IsDown(c)); } + /// public bool IsAnyUp(params object[] control) { return control.Any(c => this.IsUp(c)); } + /// public bool IsAnyPressed(params object[] control) { return control.Any(c => this.IsPressed(c)); } + /// + /// Helper function to enable gestures for a easily. + /// Note that, if other gestures were previously enabled, they will not get overridden. + /// + /// The gestures to enable public static void EnableGestures(params GestureType[] gestures) { foreach (var gesture in gestures) TouchPanel.EnabledGestures |= gesture; diff --git a/MLEM/Input/KeysExtensions.cs b/MLEM/Input/KeysExtensions.cs index 2db84fa..4400aca 100644 --- a/MLEM/Input/KeysExtensions.cs +++ b/MLEM/Input/KeysExtensions.cs @@ -4,10 +4,21 @@ using Microsoft.Xna.Framework.Input; using MLEM.Misc; namespace MLEM.Input { + /// + /// A set of extension methods for dealing with and + /// public static class KeysExtensions { + /// + /// All enum values of + /// public static readonly ModifierKey[] ModifierKeys = EnumHelper.GetValues().ToArray(); + /// + /// Returns all of the keys that the given modifier key represents + /// + /// The modifier key + /// All of the keys the modifier key represents public static IEnumerable GetKeys(this ModifierKey modifier) { switch (modifier) { case ModifierKey.Shift: @@ -25,6 +36,12 @@ namespace MLEM.Input { } } + /// + /// Returns the modifier key that the given key represents. + /// If there is no matching modifier key, is returned. + /// + /// The key to convert to a modifier key + /// The modifier key, or public static ModifierKey GetModifier(this Keys key) { foreach (var mod in ModifierKeys) { if (GetKeys(mod).Contains(key)) @@ -33,17 +50,38 @@ namespace MLEM.Input { return ModifierKey.None; } + /// + /// Returns whether the given key is a modifier key or not. + /// + /// The key + /// If the key is a modifier key public static bool IsModifier(this Keys key) { return GetModifier(key) != ModifierKey.None; } } + /// + /// 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. + /// public enum ModifierKey { + /// + /// No modifier key. Only used for + /// None, + /// + /// The shift modifier key. This represents Left Shift and Right Shift keys. + /// Shift, + /// + /// The control modifier key. This represents Left Control and Right Control. + /// Control, + /// + /// The alt modifier key. This represents Alt and Alt Graph. + /// Alt } diff --git a/MLEM/Input/MouseExtensions.cs b/MLEM/Input/MouseExtensions.cs index f62abb9..e312e61 100644 --- a/MLEM/Input/MouseExtensions.cs +++ b/MLEM/Input/MouseExtensions.cs @@ -4,10 +4,23 @@ using Microsoft.Xna.Framework.Input; using MLEM.Misc; namespace MLEM.Input { + /// + /// A set of extension methods for dealing with and + /// public static class MouseExtensions { + /// + /// All enum values of + /// public static readonly MouseButton[] MouseButtons = EnumHelper.GetValues().ToArray(); + /// + /// Returns the of the given mouse button. + /// + /// The mouse's current state + /// The button whose state to query + /// The state of the button + /// If a mouse button out of range is passed public static ButtonState GetState(this MouseState state, MouseButton button) { switch (button) { case MouseButton.Left: @@ -27,12 +40,31 @@ namespace MLEM.Input { } + /// + /// This enum is a list of possible mouse buttons. + /// It serves as a wrapper around 's button properties. + /// public enum MouseButton { + /// + /// The left mouse button, or + /// Left, + /// + /// The middle mouse button, or + /// Middle, + /// + /// The right mouse button, or + /// Right, + /// + /// The first extra mouse button, or + /// Extra1, + /// + /// The second extra mouse button, or + /// Extra2 } diff --git a/MLEM/Misc/AutoTiling.cs b/MLEM/Misc/AutoTiling.cs index 59594b4..268b140 100644 --- a/MLEM/Misc/AutoTiling.cs +++ b/MLEM/Misc/AutoTiling.cs @@ -2,8 +2,29 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace MLEM.Misc { + /// + /// This class contains a 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 . + /// public class AutoTiling { + /// + /// 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 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// 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 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); } + /// + /// A delegate function that determines if a given offset position connects to an auto-tile location. + /// + /// The x offset + /// The y offset public delegate bool ConnectsTo(int xOff, int yOff); } diff --git a/MLEM/Misc/Direction2.cs b/MLEM/Misc/Direction2.cs index 9da324c..b9d4bb4 100644 --- a/MLEM/Misc/Direction2.cs +++ b/MLEM/Misc/Direction2.cs @@ -5,37 +5,100 @@ using System.Linq; using Microsoft.Xna.Framework; namespace MLEM.Misc { + /// + /// An enum that represents two-dimensional directions. + /// Both straight and diagonal directions are supported. + /// public enum Direction2 { + /// + /// The up direction, or -y. + /// Up, + /// + /// The right direction, or +x. + /// Right, + /// + /// The down direction, or +y. + /// Down, + /// + /// The left direction, or -x. + /// Left, + /// + /// The up and right direction, or +x, -y. + /// UpRight, + /// + /// The down and right direction, or +x, +y. + /// DownRight, + /// + /// The down and left direction, or -x, +y. + /// DownLeft, + /// + /// The up and left direction, or -x, -y. + /// UpLeft, + /// + /// No direction. + /// None } + /// + /// A set of helper and extension methods for dealing with + /// public static class Direction2Helper { + /// + /// All enum values + /// public static readonly Direction2[] All = EnumHelper.GetValues().ToArray(); + /// + /// The through directions + /// public static readonly Direction2[] Adjacent = All.Where(IsAdjacent).ToArray(); + /// + /// The through directions + /// public static readonly Direction2[] Diagonals = All.Where(IsDiagonal).ToArray(); + /// + /// All directions except + /// public static readonly Direction2[] AllExceptNone = All.Where(dir => dir != Direction2.None).ToArray(); + /// + /// Returns if the given direction is considered an "adjacent" direction. + /// An adjacent direction is one that is not a diagonal. + /// + /// The direction to query + /// Whether the direction is adjacent public static bool IsAdjacent(this Direction2 dir) { return dir <= Direction2.Left; } + /// + /// Returns if the given direction is considered a diagonal direction. + /// + /// The direction to query + /// Whether the direction is diagonal public static bool IsDiagonal(this Direction2 dir) { return dir >= Direction2.UpRight && dir <= Direction2.UpLeft; } + /// + /// Returns the directional offset of a given direction. + /// The offset direction will be exactly one unit in each axis that the direction represents. + /// + /// The direction whose offset to query + /// The direction's offset public static Point Offset(this Direction2 dir) { switch (dir) { case Direction2.Up: @@ -59,11 +122,23 @@ namespace MLEM.Misc { } } + /// + /// Maps each direction in the given enumerable of directions to its . + /// + /// The direction enumerable + /// The directions' offsets, in the same order as the input directions. public static IEnumerable Offsets(this IEnumerable directions) { foreach (var dir in directions) yield return dir.Offset(); } + /// + /// 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. + /// + /// The direction whose opposite to get + /// The opposite of the direction public static Direction2 Opposite(this Direction2 dir) { switch (dir) { case Direction2.Up: @@ -87,11 +162,22 @@ namespace MLEM.Misc { } } + /// + /// Returns the angle of the direction in radians, where has an angle of 0. + /// + /// The direction whose angle to get + /// The direction's angle public static float Angle(this Direction2 dir) { var offset = dir.Offset(); return (float) Math.Atan2(offset.Y, offset.X); } + /// + /// Rotates the given direction clockwise and returns the resulting direction. + /// + /// The direction to rotate + /// Whether to rotate by 45 degrees. If this is false, the rotation is 90 degrees instead. + /// The rotated direction public static Direction2 RotateCw(this Direction2 dir, bool fortyFiveDegrees = false) { switch (dir) { case Direction2.Up: @@ -115,6 +201,12 @@ namespace MLEM.Misc { } } + /// + /// Rotates the given direction counter-clockwise and returns the resulting direction. + /// + /// The direction to rotate counter-clockwise + /// Whether to rotate by 45 degrees. If this is false, the rotation is 90 degrees instead. + /// The rotated direction public static Direction2 RotateCcw(this Direction2 dir, bool fortyFiveDegrees = false) { switch (dir) { case Direction2.Up: diff --git a/MLEM/Misc/EnumHelper.cs b/MLEM/Misc/EnumHelper.cs index 815e814..a984b36 100644 --- a/MLEM/Misc/EnumHelper.cs +++ b/MLEM/Misc/EnumHelper.cs @@ -4,11 +4,25 @@ using System.Linq; using Microsoft.Xna.Framework.Input; namespace MLEM.Misc { + /// + /// A helper class that allows easier usage of values. + /// public static class EnumHelper { + /// + /// All values of the enum. + /// public static readonly Buttons[] Buttons = GetValues().ToArray(); + /// + /// All values of the enum. + /// public static readonly Keys[] Keys = GetValues().ToArray(); + /// + /// Returns all of the values of the given enum type. + /// + /// The type whose enum to get + /// An enumerable of the values of the enum, in declaration order. public static IEnumerable GetValues() { return Enum.GetValues(typeof(T)).Cast(); } diff --git a/MLEM/Misc/GenericDataHolder.cs b/MLEM/Misc/GenericDataHolder.cs index beb75dc..39c789b 100644 --- a/MLEM/Misc/GenericDataHolder.cs +++ b/MLEM/Misc/GenericDataHolder.cs @@ -2,10 +2,19 @@ using System; using System.Collections.Generic; namespace MLEM.Misc { + /// + /// 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. + /// public class GenericDataHolder { private Dictionary data; + /// + /// Store a piece of generic data on this object. + /// + /// The key to store the data by + /// The data to store in the object public void SetData(string key, object data) { if (data == default) { if (this.data != null) @@ -17,12 +26,22 @@ namespace MLEM.Misc { } } + /// + /// Returns a piece of generic data of the given type on this object. + /// + /// The key that the data is stored by + /// The type of the data stored + /// The data, or default if it doesn't exist public T GetData(string key) { if (this.data != null && this.data.TryGetValue(key, out var val) && val is T t) return t; return default; } + /// + /// Returns all of the generic data that this object stores. + /// + /// The generic data on this object public IReadOnlyCollection GetDataKeys() { if (this.data == null) return Array.Empty(); diff --git a/MLEM/Misc/Padding.cs b/MLEM/Misc/Padding.cs index a1b202c..ef90620 100644 --- a/MLEM/Misc/Padding.cs +++ b/MLEM/Misc/Padding.cs @@ -1,15 +1,44 @@ using Microsoft.Xna.Framework; namespace MLEM.Misc { + /// + /// Represents a generic padding. + /// A padding is an object of data that stores an offset from each side of a rectangle or square. + /// public struct Padding { + /// + /// The amount of padding on the left side + /// public float Left; + /// + /// The amount of padding on the right side + /// public float Right; + /// + /// The amount of padding on the top side + /// public float Top; + /// + /// The amount of padding on the bottom side + /// public float Bottom; + /// + /// The total width of this padding, a sum of the left and right padding. + /// public float Width => this.Left + this.Right; + /// + /// The total height of this padding, a sum of the top and bottom padding. + /// public float Height => this.Top + this.Bottom; + /// + /// Create a new padding with the specified borders. + /// + /// The amount of padding on the left side + /// The amount of padding on the right side + /// The amount of padding on the top side + /// The amount of padding on the bottom side public Padding(float left, float right, float top, float bottom) { this.Left = left; this.Right = right; @@ -17,38 +46,58 @@ namespace MLEM.Misc { this.Bottom = bottom; } + /// + /// 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. + /// + /// The vector to convert + /// A padding based on the vector's values public static implicit operator Padding(Vector2 vec) { return new Padding(vec.X, vec.X, vec.Y, vec.Y); } + /// + /// Scales a padding by a scalar. + /// public static Padding operator *(Padding p, float scale) { return new Padding(p.Left * scale, p.Right * scale, p.Top * scale, p.Bottom * scale); } + /// + /// Adds two paddings together in a memberwise fashion. + /// 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); } + /// + /// Subtracts two paddings in a memberwise fashion. + /// 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); } + /// public static bool operator ==(Padding left, Padding right) { return left.Equals(right); } + /// public static bool operator !=(Padding left, Padding right) { return !(left == right); } + /// 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); } + /// public override bool Equals(object obj) { return obj is Padding other && this.Equals(other); } + /// public override int GetHashCode() { var hashCode = this.Left.GetHashCode(); hashCode = (hashCode * 397) ^ this.Right.GetHashCode(); diff --git a/MLEM/Misc/RectangleF.cs b/MLEM/Misc/RectangleF.cs index e709aa0..5a34ffe 100644 --- a/MLEM/Misc/RectangleF.cs +++ b/MLEM/Misc/RectangleF.cs @@ -4,26 +4,59 @@ using Microsoft.Xna.Framework; using MLEM.Extensions; namespace MLEM.Misc { + /// + /// Represents a float-based version of + /// [DataContract] public struct RectangleF : IEquatable { + /// + /// The empty rectangle, with an x, y, width and height of 0. + /// public static RectangleF Empty => default; + /// + /// The x position of the top left corner of this rectangle. + /// [DataMember] public float X; + /// + /// The y position of the top left corner of this rectangle. + /// [DataMember] public float Y; + /// + /// The width of this rectangle. + /// [DataMember] public float Width; + /// + /// The height of this rectangle. + /// [DataMember] public float Height; + /// public float Left => this.X; + /// + /// The x position of the bottom right corner of this rectangle. + /// public float Right => this.X + this.Width; + /// public float Top => this.Y; + /// + /// The y position of the bottom right corner of this rectangle. + /// public float Bottom => this.Y + this.Height; + /// + /// A boolean that is true if this rectangle is empty. + /// A rectangle is considered empty if the width or height is 0. + /// public bool IsEmpty => this.Width <= 0 || this.Height <= 0; + /// + /// The top left corner of this rectangle + /// public Vector2 Location { get => new Vector2(this.X, this.Y); set { @@ -31,6 +64,9 @@ namespace MLEM.Misc { this.Y = value.Y; } } + /// + /// The size, that is, the width and height of this rectangle. + /// public Vector2 Size { get => new Vector2(this.Width, this.Height); set { @@ -38,8 +74,18 @@ namespace MLEM.Misc { this.Height = value.Y; } } + /// + /// The center of this rectangle, based on the top left corner and the size. + /// public Vector2 Center => new Vector2(this.X + this.Width / 2, this.Y + this.Height / 2); + /// + /// Creates a new rectangle with the specified location and size + /// + /// The x coordinate of the top left corner of the rectangle + /// The y coordinate of the top left corner of the rectangle + /// The width of the rectangle + /// The height of the rectangle public RectangleF(float x, float y, float width, float height) { this.X = x; this.Y = y; @@ -47,6 +93,11 @@ namespace MLEM.Misc { this.Height = height; } + /// + /// Creates a new rectangle with the specified location and size vectors + /// + /// The top left corner of the rectangle + /// The size of the rectangle, where x represents width and the y represents height public RectangleF(Vector2 location, Vector2 size) { this.X = location.X; this.Y = location.Y; @@ -54,6 +105,13 @@ namespace MLEM.Misc { this.Height = size.Y; } + /// + /// 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. + /// + /// The first corner to use + /// The second corner to use + /// public static RectangleF FromCorners(Vector2 corner1, Vector2 corner2) { var minX = Math.Min(corner1.X, corner2.X); var minY = Math.Min(corner1.Y, corner2.Y); @@ -62,46 +120,65 @@ namespace MLEM.Misc { return new RectangleF(minX, minY, maxX - minX, maxY - minY); } + /// + /// Converts a float-based rectangle to an int-based rectangle, flooring each value in the process. + /// + /// The rectangle to convert + /// The resulting rectangle public static explicit operator Rectangle(RectangleF rect) { return new Rectangle(rect.X.Floor(), rect.Y.Floor(), rect.Width.Floor(), rect.Height.Floor()); } + /// + /// Converts an int-based rectangle to a float-based rectangle. + /// + /// The rectangle to convert + /// The resulting rectangle public static explicit operator RectangleF(Rectangle rect) { return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height); } + /// 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; } + /// public static bool operator !=(RectangleF a, RectangleF b) { return !(a == b); } + /// public bool Contains(float x, float y) { return this.X <= x && x < this.X + this.Width && this.Y <= y && y < this.Y + this.Height; } + /// public bool Contains(Vector2 value) { return this.Contains(value.X, value.Y); } + /// 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; } + /// public override bool Equals(object obj) { return obj is RectangleF f && this == f; } + /// public bool Equals(RectangleF other) { return this == other; } + /// public override int GetHashCode() { return (((17 * 23 + this.X.GetHashCode()) * 23 + this.Y.GetHashCode()) * 23 + this.Width.GetHashCode()) * 23 + this.Height.GetHashCode(); } + /// public void Inflate(float horizontalAmount, float verticalAmount) { this.X -= horizontalAmount; this.Y -= verticalAmount; @@ -109,10 +186,12 @@ namespace MLEM.Misc { this.Height += verticalAmount * 2; } + /// public bool Intersects(RectangleF value) { return value.Left < this.Right && this.Left < value.Right && value.Top < this.Bottom && this.Top < value.Bottom; } + /// public static RectangleF Intersect(RectangleF value1, RectangleF value2) { if (value1.Intersects(value2)) { var num1 = Math.Min(value1.X + value1.Width, value2.X + value2.Width); @@ -125,26 +204,31 @@ namespace MLEM.Misc { } } + /// public void Offset(float offsetX, float offsetY) { this.X += offsetX; this.Y += offsetY; } + /// public void Offset(Vector2 amount) { this.X += amount.X; this.Y += amount.Y; } + /// public override string ToString() { return "{X:" + this.X + " Y:" + this.Y + " Width:" + this.Width + " Height:" + this.Height + "}"; } + /// public static RectangleF Union(RectangleF value1, RectangleF value2) { var x = Math.Min(value1.X, value2.X); 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); } + /// public void Deconstruct(out float x, out float y, out float width, out float height) { x = this.X; y = this.Y; diff --git a/MLEM/Misc/TextInputWrapper.cs b/MLEM/Misc/TextInputWrapper.cs index 98394cb..e9bade5 100644 --- a/MLEM/Misc/TextInputWrapper.cs +++ b/MLEM/Misc/TextInputWrapper.cs @@ -6,9 +6,19 @@ using Microsoft.Xna.Framework.Input; using MLEM.Input; namespace MLEM.Misc { + /// + /// 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 for more info. + /// public abstract class TextInputWrapper { private static TextInputWrapper current; + /// + /// The current text input wrapper. + /// Set this value before starting your game if you want to use text input wrapping. + /// + /// public static TextInputWrapper Current { get { if (current == null) @@ -18,26 +28,59 @@ namespace MLEM.Misc { set => current = value; } + /// + /// Determines if this text input wrapper requires an on-screen keyboard. + /// + /// If this text input wrapper requires an on-screen keyboard public abstract bool RequiresOnScreenKeyboard(); + /// + /// Adds a text input listener to this text input wrapper. + /// The supplied listener will be called whenever a character is input. + /// + /// The game's window + /// The callback that should be called whenever a character is pressed public abstract void AddListener(GameWindow window, TextInputCallback callback); + /// + /// A delegate method that can be used for + /// + /// The object that sent the event. The used in most cases. + /// The key that was pressed + /// The character that corresponds to that key public delegate void TextInputCallback(object sender, Keys key, char character); + /// + /// 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. + /// + /// + /// This listener is initialized as follows: + /// + /// new TextInputWrapper.DesktopGl{TextInputEventArgs}((w, c) => w.TextInput += c); + /// + /// + /// public class DesktopGl : TextInputWrapper { private MemberInfo key; private MemberInfo character; private readonly Action> addListener; + /// + /// Creates a new DesktopGL-based text input wrapper + /// + /// The function that is used to add a text input listener public DesktopGl(Action> addListener) { this.addListener = addListener; } + /// public override bool RequiresOnScreenKeyboard() { return false; } + /// public override void AddListener(GameWindow window, TextInputCallback callback) { this.addListener(window, (sender, args) => { // the old versions of DesktopGL use a property here, while the @@ -69,32 +112,54 @@ namespace MLEM.Misc { } + /// + /// 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. + /// public class Mobile : TextInputWrapper { + /// public override bool RequiresOnScreenKeyboard() { return true; } + /// public override void AddListener(GameWindow window, TextInputCallback callback) { } } + /// + /// A text input wrapper that does nothing. + /// This can be used if no text input is required for the game. + /// public class None : TextInputWrapper { + /// public override bool RequiresOnScreenKeyboard() { return false; } + /// public override void AddListener(GameWindow window, TextInputCallback callback) { } } + /// + /// A primitive text input wrapper that is locked to the American keyboard localization. + /// Only use this text input wrapper if is unavailable for some reason. + /// + /// Note that, when using this text input wrapper, its method has to be called periodically. + /// public class Primitive : TextInputWrapper { private TextInputCallback callback; + /// + /// Updates this text input wrapper by querying pressed keys and sending corresponding input events. + /// + /// The input handler to use for text input querying public void Update(InputHandler handler) { var pressed = handler.KeyboardState.GetPressedKeys().Except(handler.LastKeyboardState.GetPressedKeys()); var shift = handler.IsModifierKeyDown(ModifierKey.Shift); @@ -105,10 +170,12 @@ namespace MLEM.Misc { } } + /// public override bool RequiresOnScreenKeyboard() { return false; } + /// public override void AddListener(GameWindow window, TextInputCallback callback) { this.callback += callback; } diff --git a/MLEM/Pathfinding/AStar.cs b/MLEM/Pathfinding/AStar.cs index 59cfc4c..24372ae 100644 --- a/MLEM/Pathfinding/AStar.cs +++ b/MLEM/Pathfinding/AStar.cs @@ -4,18 +4,61 @@ using System.Diagnostics; using System.Threading.Tasks; namespace MLEM.Pathfinding { + /// + /// This is an abstract implementation of the A* path finding algorithm. + /// This implementation is used by , a 2-dimensional A* path finding algorithm, and , a 3-dimensional A* path finding algorithm. + /// + /// The type of points used for this path public abstract class AStar { + /// + /// A value that represents an infinite path cost, or a cost for a location that cannot possibly be reached. + /// public static readonly float InfiniteCost = float.PositiveInfinity; + /// + /// The array of all directions that will be checked for path finding. + /// Note that this array is only used if is true. + /// public readonly T[] AllDirections; + /// + /// The array of all adjacent directions that will be checked for path finding. + /// Note that this array is only used if is false. + /// public readonly T[] AdjacentDirections; + /// + /// The default cost function that determines the cost for each path finding position. + /// public GetCost DefaultCostFunction; + /// + /// The default cost for a path point. + /// public float DefaultCost; + /// + /// The default amount of maximum tries that will be used before path finding is aborted. + /// public int DefaultMaxTries; + /// + /// Whether or not diagonal directions are considered while finding a path. + /// public bool DefaultAllowDiagonals; + /// + /// The amount of tries required for finding the last queried path + /// public int LastTriesNeeded { get; private set; } + /// + /// The amount of time required for finding the last queried path + /// public TimeSpan LastTimeNeeded { get; private set; } + /// + /// Creates a new A* pathfinder with the supplied default settings. + /// + /// All directions that should be checked + /// All adjacent directions that should be checked + /// The default function for cost determination of a path point + /// Whether or not diagonals should be allowed by default + /// The default cost for a path point + /// The default amount of tries before path finding is aborted protected AStar(T[] allDirections, T[] adjacentDirections, GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) { this.AllDirections = allDirections; this.AdjacentDirections = adjacentDirections; @@ -25,10 +68,21 @@ namespace MLEM.Pathfinding { this.DefaultAllowDiagonals = defaultAllowDiagonals; } + /// public Task> 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)); } + /// + /// Finds a path between two points using this pathfinder's default settings or, alternatively, the supplied override settings. + /// + /// The point to start path finding at + /// The point to find a path to + /// The function that determines the cost for each path point + /// The default cost for each path point + /// The maximum amount of tries before path finding is aborted + /// If diagonals should be looked at for path finding + /// A stack of path points, where the top item is the first point to go to. public Stack FindPath(T start, T goal, GetCost costFunction = null, float? defaultCost = null, int? maxTries = null, bool? allowDiagonals = null) { var stopwatch = Stopwatch.StartNew(); @@ -94,8 +148,14 @@ namespace MLEM.Pathfinding { return ret; } + /// + /// A helper method to add two positions together. + /// protected abstract T AddPositions(T first, T second); + /// + /// A helper method to get the Manhattan Distance between two points. + /// protected abstract float GetManhattanDistance(T first, T second); private static Stack CompilePath(PathPoint current) { @@ -107,17 +167,48 @@ namespace MLEM.Pathfinding { return path; } + /// + /// A cost function for a given path finding position. + /// If a path point should have the default cost, should be returned. + /// If a path point should be unreachable, should be returned. + /// + /// The current position in the path + /// The position we're trying to reach from the current position public delegate float GetCost(T currPos, T nextPos); } + /// + /// A point in a path + /// + /// The type of point used for this path public class PathPoint { + /// + /// The path point that this point originated from + /// public readonly PathPoint Parent; + /// + /// The position of this path point + /// public readonly T Pos; + /// + /// The F cost of this path point + /// public readonly float F; + /// + /// The G cost of this path point + /// public readonly float G; + /// + /// Creates a new path point with the supplied settings. + /// + /// The point's position + /// The point's manhattan distance from the start point + /// The point's parent + /// The point's terrain cost, based on + /// The default cost for a path point public PathPoint(T pos, float distance, PathPoint parent, float terrainCostForThisPos, float defaultCost) { this.Pos = pos; this.Parent = parent; @@ -126,12 +217,14 @@ namespace MLEM.Pathfinding { this.F = this.G + distance * defaultCost; } + /// public override bool Equals(object obj) { if (obj == this) return true; return obj is PathPoint point && point.Pos.Equals(this.Pos); } + /// public override int GetHashCode() { return this.Pos.GetHashCode(); } diff --git a/MLEM/Pathfinding/AStar2.cs b/MLEM/Pathfinding/AStar2.cs index 78529f6..a30f56e 100644 --- a/MLEM/Pathfinding/AStar2.cs +++ b/MLEM/Pathfinding/AStar2.cs @@ -4,19 +4,25 @@ using Microsoft.Xna.Framework; using MLEM.Misc; namespace MLEM.Pathfinding { + /// + /// A 2-dimensional implementation of that uses for positions. + /// public class AStar2 : AStar { private static readonly Point[] AdjacentDirs = Direction2Helper.Adjacent.Offsets().ToArray(); private static readonly Point[] AllDirs = Direction2Helper.All.Offsets().ToArray(); + /// public AStar2(GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) : base(AllDirs, AdjacentDirs, defaultCostFunction, defaultAllowDiagonals, defaultCost, defaultMaxTries) { } + /// protected override Point AddPositions(Point first, Point second) { return first + second; } + /// protected override float GetManhattanDistance(Point first, Point second) { return Math.Abs(second.X - first.X) + Math.Abs(second.Y - first.Y); } diff --git a/MLEM/Pathfinding/AStar3.cs b/MLEM/Pathfinding/AStar3.cs index b1de642..66a83a8 100644 --- a/MLEM/Pathfinding/AStar3.cs +++ b/MLEM/Pathfinding/AStar3.cs @@ -4,6 +4,9 @@ using System.Linq; using Microsoft.Xna.Framework; namespace MLEM.Pathfinding { + /// + /// A 3-dimensional implementation of that uses for positions. + /// public class AStar3 : AStar { private static readonly Vector3[] AdjacentDirs = { @@ -31,14 +34,17 @@ namespace MLEM.Pathfinding { AllDirs = dirs.ToArray(); } + /// public AStar3(GetCost defaultCostFunction, bool defaultAllowDiagonals, float defaultCost = 1, int defaultMaxTries = 10000) : base(AllDirs, AdjacentDirs, defaultCostFunction, defaultAllowDiagonals, defaultCost, defaultMaxTries) { } + /// protected override Vector3 AddPositions(Vector3 first, Vector3 second) { return first + 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); } diff --git a/MLEM/Textures/NinePatch.cs b/MLEM/Textures/NinePatch.cs index ec3a3e2..2f1aa3b 100644 --- a/MLEM/Textures/NinePatch.cs +++ b/MLEM/Textures/NinePatch.cs @@ -6,37 +6,70 @@ using MLEM.Extensions; using MLEM.Misc; namespace MLEM.Textures { + /// + /// 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. + /// public class NinePatch : GenericDataHolder { + /// + /// The texture region of this nine patch + /// public readonly TextureRegion Region; + /// + /// The padding in each direction that marks where the outline area stops + /// public readonly Padding Padding; + /// + /// The nine patches that result from the + /// public readonly Rectangle[] SourceRectangles; + /// + /// Creates a new nine patch from a texture and a padding + /// + /// The texture to use + /// The padding that marks where the outline area stops public NinePatch(TextureRegion texture, Padding padding) { this.Region = texture; this.Padding = padding; this.SourceRectangles = this.CreateRectangles(this.Region.Area).ToArray(); } + /// + /// Creates a new nine patch from a texture and a padding + /// + /// The texture to use + /// The padding on the left edge + /// The padding on the right edge + /// The padding on the top edge + /// The padding on the bottom edge public NinePatch(TextureRegion texture, int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) : this(texture, new Padding(paddingLeft, paddingRight, paddingTop, paddingBottom)) { } + /// public NinePatch(Texture2D texture, int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) : this(new TextureRegion(texture), paddingLeft, paddingRight, paddingTop, paddingBottom) { } + /// + /// Creates a new nine patch from a texture and a uniform padding + /// + /// The texture to use + /// The padding that each edge should have public NinePatch(Texture2D texture, int padding) : this(new TextureRegion(texture), padding) { } + /// public NinePatch(TextureRegion texture, int padding) : this(texture, padding, padding, padding, padding) { } - public IEnumerable CreateRectangles(Rectangle area, float patchScale = 1) { + private IEnumerable CreateRectangles(Rectangle area, float patchScale = 1) { return this.CreateRectangles((RectangleF) area, patchScale).Select(r => (Rectangle) r); } - public IEnumerable CreateRectangles(RectangleF area, float patchScale = 1) { + internal IEnumerable CreateRectangles(RectangleF area, float patchScale = 1) { var pl = this.Padding.Left * patchScale; var pr = this.Padding.Right * patchScale; var pt = this.Padding.Top * patchScale; @@ -62,8 +95,23 @@ namespace MLEM.Textures { } + /// + /// A set of extensions that allow for rendering + /// public static class NinePatchExtensions { + /// + /// Draws a nine patch area using the given sprite batch + /// + /// The batch to draw with + /// The nine patch to draw + /// The area that should be covered by this nine patch + /// The color to use + /// The rotation + /// The origin position + /// The effects that the sprite should have + /// The depth + /// The scale of each area of the nine patch 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 count = 0; @@ -74,14 +122,17 @@ namespace MLEM.Textures { } } + /// 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); } + /// 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); } + /// 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); } diff --git a/MLEM/Textures/TextureRegion.cs b/MLEM/Textures/TextureRegion.cs index 7f976aa..1177b45 100644 --- a/MLEM/Textures/TextureRegion.cs +++ b/MLEM/Textures/TextureRegion.cs @@ -4,59 +4,119 @@ using MLEM.Extensions; using MLEM.Misc; namespace MLEM.Textures { + /// + /// This class represents a part of a texture. + /// public class TextureRegion : GenericDataHolder { + /// + /// The texture that this region is a part of + /// public readonly Texture2D Texture; + /// + /// The area that is covered by this texture region + /// public readonly Rectangle Area; + /// + /// The top left corner of this texture region + /// public Point Position => this.Area.Location; + /// + /// The x coordinate of the top left corner of this texture region + /// public int U => this.Area.X; + /// + /// The y coordinate of the top left corner of this texture region + /// public int V => this.Area.Y; + /// + /// The size of this texture region + /// public Point Size => this.Area.Size; + /// + /// The width of this texture region + /// public int Width => this.Area.Width; + /// + /// The height of this texture region + /// public int Height => this.Area.Height; + /// + /// Creates a new texture region from a texture and a rectangle which defines the region's area + /// + /// The texture to use + /// The area that this texture region should cover public TextureRegion(Texture2D texture, Rectangle area) { this.Texture = texture; this.Area = area; } + /// + /// Creates a new texture region that spans the entire texture + /// + /// The texture to use public TextureRegion(Texture2D texture) : this(texture, new Rectangle(0, 0, texture.Width, texture.Height)) { } + /// + /// Creates a new texture region based on a texture and area coordinates + /// + /// The texture to use + /// The x coordinate of the top left corner of this area + /// The y coordinate of the top left corner of this area + /// The width of this area + /// The height of this area public TextureRegion(Texture2D texture, int u, int v, int width, int height) : this(texture, new Rectangle(u, v, width, height)) { } + /// + /// Creates a new texture region based on a texture, a position and a size + /// + /// The texture to use + /// The top left corner of this area + /// The size of this area public TextureRegion(Texture2D texture, Point uv, Point size) : this(texture, new Rectangle(uv, size)) { } } + /// + /// This class provides a set of extension methods for dealing with + /// public static class TextureRegionExtensions { + /// 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); } + /// 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); } + /// 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); } + /// 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); } + /// 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); } + /// public static void Draw(this SpriteBatch batch, TextureRegion texture, Rectangle destinationRectangle, Color color) { batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0); } + /// public static void Draw(this SpriteBatch batch, TextureRegion texture, RectangleF destinationRectangle, Color color) { batch.Draw(texture, destinationRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0); } diff --git a/MLEM/Textures/UniformTextureAtlas.cs b/MLEM/Textures/UniformTextureAtlas.cs index 8998b83..34855e6 100644 --- a/MLEM/Textures/UniformTextureAtlas.cs +++ b/MLEM/Textures/UniformTextureAtlas.cs @@ -5,21 +5,63 @@ using Microsoft.Xna.Framework.Graphics; using MLEM.Misc; namespace MLEM.Textures { + /// + /// This class represents an atlas of 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. + /// public class UniformTextureAtlas : GenericDataHolder { + /// + /// The texture to use for this atlas + /// public readonly Texture2D Texture; + /// + /// The amount of sub-regions this atlas has in the x direction + /// public readonly int RegionAmountX; + /// + /// The amount of sub-regions this atlas has in the y direction + /// public readonly int RegionAmountY; + /// + /// The width of each region, based on the texture's width and the amount of regions + /// public readonly int RegionWidth; + /// + /// The height of reach region, based on the texture's height and the amount of regions + /// public readonly int RegionHeight; + /// + /// Returns the at this texture atlas's given index. + /// The index is zero-based, where rows come first and columns come second. + /// + /// The zero-based texture index public TextureRegion this[int index] => this[index % this.RegionAmountX, index / this.RegionAmountX]; + /// + /// Returns the at this texture atlas' given region position + /// + /// The region's x and y location public TextureRegion this[Point point] => this[new Rectangle(point, new Point(1, 1))]; + /// public TextureRegion this[int x, int y] => this[new Point(x, y)]; + /// + /// Returns the at this texture atlas' given region position and size. + /// Note that the region size is not in pixels, but in region units. + /// + /// The region's area public TextureRegion this[Rectangle rect] => this.GetOrAddRegion(rect); + /// public TextureRegion this[int x, int y, int width, int height] => this[new Rectangle(x, y, width, height)]; private readonly Dictionary regions = new Dictionary(); + /// + /// Creates a new uniform texture atlas with the given texture and region amount. + /// + /// The texture to use for this atlas + /// The amount of texture regions in the x direction + /// The amount of texture regions in the y direction public UniformTextureAtlas(Texture2D texture, int regionAmountX, int regionAmountY) { this.Texture = texture; this.RegionAmountX = regionAmountX;