mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-25 14:08:34 +01:00
Compare commits
No commits in common. "96f0c517574f49d5be16f82638656572ad2da813" and "d5ec0b8001d96796518b016785fa0474e5952afa" have entirely different histories.
96f0c51757
...
d5ec0b8001
9 changed files with 114 additions and 203 deletions
|
@ -14,14 +14,12 @@ Additions
|
||||||
- Added ColorExtensions.Multiply
|
- Added ColorExtensions.Multiply
|
||||||
- Added SoundEffectInstanceHandler.Stop
|
- Added SoundEffectInstanceHandler.Stop
|
||||||
- Added TextureRegion.OffsetCopy
|
- Added TextureRegion.OffsetCopy
|
||||||
- Added RectangleF.DistanceSquared and RectangleF.Distance
|
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
- Generify GenericFont's string drawing
|
- Generify GenericFont's string drawing
|
||||||
- Added InputHandler mouse and touch position querying that preserves the game's viewport
|
- Added InputHandler mouse and touch position querying that preserves the game's viewport
|
||||||
- Added float version of GetRandomWeightedEntry
|
- Added float version of GetRandomWeightedEntry
|
||||||
- Allow LinkCode to specify a color to draw with
|
- Allow LinkCode to specify a color to draw with
|
||||||
- Allow better control over the order and layout of a Keybind's combinations
|
|
||||||
|
|
||||||
Fixes
|
Fixes
|
||||||
- **Fixed a formatting Code only knowing about the last Token that it is applied in**
|
- **Fixed a formatting Code only knowing about the last Token that it is applied in**
|
||||||
|
@ -45,10 +43,6 @@ Improvements
|
||||||
- Update elements less aggressively when changing a ui system's style
|
- Update elements less aggressively when changing a ui system's style
|
||||||
- Automatically update all elements when changing a ui system's viewport
|
- Automatically update all elements when changing a ui system's viewport
|
||||||
- Allow setting a default color for clickable links in UiStyle
|
- Allow setting a default color for clickable links in UiStyle
|
||||||
- Allow ElementHelper's KeybindButton to query a combination at a given index
|
|
||||||
- Automatically select the first element when a dropdown is opened in auto nav mode
|
|
||||||
- Improved gamepad navigation by employing angles between elements
|
|
||||||
- Prefer elements that have the same parent as the currently selected element when using gamepad navigation
|
|
||||||
|
|
||||||
Fixes
|
Fixes
|
||||||
- Fixed paragraph links having incorrect hover locations when using special text alignments
|
- Fixed paragraph links having incorrect hover locations when using special text alignments
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
# Made with MLEM
|
# Made with MLEM
|
||||||
- [A Breath of Spring Air](https://ellpeck.itch.io/a-breath-of-spring-air), a short platformer ([Source](https://git.ellpeck.de/Ellpeck/GreatSpringGameJam))
|
- [A Breath of Spring Air](https://ellpeck.itch.io/a-breath-of-spring-air), a short platformer ([Source](https://git.ellpeck.de/Ellpeck/GreatSpringGameJam))
|
||||||
- [Don't Wake Up](https://ellpeck.itch.io/dont-wake-up), a short puzzle game ([Source](https://github.com/Ellpeck/DontLetGo))
|
- [Don't Wake Up](https://ellpeck.itch.io/dont-wake-up), a short puzzle game ([Source](https://github.com/Ellpeck/DontLetGo))
|
||||||
- [Pong clone](https://github.com/luanfagu/pong), a very simple pong clone ([Source](https://github.com/luanfagu/pong))
|
|
||||||
- [Tiny Life](https://tinylifegame.com), an isometric life simulation game ([Modding API](https://github.com/Ellpeck/TinyLifeExampleMod))
|
- [Tiny Life](https://tinylifegame.com), an isometric life simulation game ([Modding API](https://github.com/Ellpeck/TinyLifeExampleMod))
|
||||||
|
|
||||||
If you created a game with the help of MLEM, you can get it added to this list by submitting it on the [issue tracker](https://github.com/Ellpeck/MLEM/issues). If its source is public, other people will be able to use your project as an example, too!
|
If you created a game with the help of MLEM, you can get it added to this list by submitting it on the [issue tracker](https://github.com/Ellpeck/MLEM/issues). If its source is public, other people will be able to use your project as an example, too!
|
||||||
|
@ -43,4 +42,4 @@ There are several other libraries and tools that work well in combination with M
|
||||||
- [GameBundle](https://github.com/Ellpeck/GameBundle), a tool that packages MonoGame and other .NET Core applications into several distributable formats
|
- [GameBundle](https://github.com/Ellpeck/GameBundle), a tool that packages MonoGame and other .NET Core applications into several distributable formats
|
||||||
- [MonoGame.Extended](https://github.com/craftworkgames/MonoGame.Extended), a package that also provides several additional features for MonoGame
|
- [MonoGame.Extended](https://github.com/craftworkgames/MonoGame.Extended), a package that also provides several additional features for MonoGame
|
||||||
- [Coroutine](https://github.com/Ellpeck/Coroutine), a package that implements Unity-style coroutines for any project
|
- [Coroutine](https://github.com/Ellpeck/Coroutine), a package that implements Unity-style coroutines for any project
|
||||||
- [Illumilib](https://github.com/Ellpeck/Illumilib), a simple keyboard and mouse lighting library with support for Razer, Logitech and Corsair devices
|
- [Illumilib](https://github.com/Ellpeck/Illumilib), a simple keyboard and mouse lighting library with support for Razer, Logitech and Corsair devices
|
|
@ -43,12 +43,6 @@ namespace MLEM.Ui.Elements {
|
||||||
this.OnAreaUpdated += e => this.Panel.PositionOffset = new Vector2(0, e.Area.Height / this.Scale);
|
this.OnAreaUpdated += e => this.Panel.PositionOffset = new Vector2(0, e.Area.Height / this.Scale);
|
||||||
this.OnOpenedOrClosed += e => this.Priority = this.IsOpen ? 10000 : 0;
|
this.OnOpenedOrClosed += e => this.Priority = this.IsOpen ? 10000 : 0;
|
||||||
this.OnPressed += e => this.IsOpen = !this.IsOpen;
|
this.OnPressed += e => this.IsOpen = !this.IsOpen;
|
||||||
this.GetGamepadNextElement = (dir, usualNext) => {
|
|
||||||
// Force navigate down to our first child if we're open
|
|
||||||
if (this.IsOpen && dir == Direction2.Down)
|
|
||||||
return this.Panel.GetChildren().FirstOrDefault(c => c.CanBeSelected) ?? usualNext;
|
|
||||||
return usualNext;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -60,8 +54,6 @@ namespace MLEM.Ui.Elements {
|
||||||
// Since the dropdown causes elements to be over each other,
|
// Since the dropdown causes elements to be over each other,
|
||||||
// usual gamepad code doesn't apply
|
// usual gamepad code doesn't apply
|
||||||
element.GetGamepadNextElement = (dir, usualNext) => {
|
element.GetGamepadNextElement = (dir, usualNext) => {
|
||||||
if (dir == Direction2.Left || dir == Direction2.Right)
|
|
||||||
return null;
|
|
||||||
if (dir == Direction2.Up) {
|
if (dir == Direction2.Up) {
|
||||||
var prev = element.GetOlderSibling();
|
var prev = element.GetOlderSibling();
|
||||||
return prev ?? this;
|
return prev ?? this;
|
||||||
|
|
|
@ -117,9 +117,8 @@ namespace MLEM.Ui.Elements {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="Button"/> that acts as a way to input a custom value for a <see cref="Keybind"/>.
|
/// Creates a <see cref="Button"/> that acts as a way to input a custom value for a <see cref="Keybind"/>.
|
||||||
/// Note that only the <see cref="Keybind.Combination"/> at index <paramref name="index"/> of the given keybind is displayed and edited, all others are ignored.
|
/// Note that only the first <see cref="Keybind.Combination"/> of the given keybind is displayed and edited, all others are ignored. The exception is that, if <paramref name="unbindKey"/> is set, unbinding the keybind clears all combinations.
|
||||||
/// Inputting custom keybinds using this element supports <see cref="ModifierKey"/>-based modifiers and any <see cref="GenericInput"/>-based keys.
|
/// Inputting custom keybinds using this element supports <see cref="ModifierKey"/>-based modifiers and any <see cref="GenericInput"/>-based keys.
|
||||||
/// The keybind button's current state can be retrieved using the "Active" <see cref="GenericDataHolder.GetData{T}"/> key, which stores a bool that indicates whether the button is currently waiting for a new value to be assigned.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="anchor">The button's anchor</param>
|
/// <param name="anchor">The button's anchor</param>
|
||||||
/// <param name="size">The button's size</param>
|
/// <param name="size">The button's size</param>
|
||||||
|
@ -129,12 +128,15 @@ namespace MLEM.Ui.Elements {
|
||||||
/// <param name="unbindKey">An optional generic input that allows the keybind value to be unbound, clearing all combinations</param>
|
/// <param name="unbindKey">An optional generic input that allows the keybind value to be unbound, clearing all combinations</param>
|
||||||
/// <param name="unboundPlaceholder">A placeholder text that is displayed if the keybind is unbound</param>
|
/// <param name="unboundPlaceholder">A placeholder text that is displayed if the keybind is unbound</param>
|
||||||
/// <param name="inputName">An optional function to give each input a display name that is easier to read. If this is null, <see cref="GenericInput.ToString"/> is used.</param>
|
/// <param name="inputName">An optional function to give each input a display name that is easier to read. If this is null, <see cref="GenericInput.ToString"/> is used.</param>
|
||||||
/// <param name="index">The index in the <paramref name="keybind"/> that the button should display. Note that, if the index is greater than the amount of combinations, combinations entered using this button will be stored at an earlier index.</param>
|
|
||||||
/// <returns>A keybind button with the given settings</returns>
|
/// <returns>A keybind button with the given settings</returns>
|
||||||
public static Button KeybindButton(Anchor anchor, Vector2 size, Keybind keybind, InputHandler inputHandler, string activePlaceholder, GenericInput unbindKey = default, string unboundPlaceholder = "", Func<GenericInput, string> inputName = null, int index = 0) {
|
public static Button KeybindButton(Anchor anchor, Vector2 size, Keybind keybind, InputHandler inputHandler, string activePlaceholder, GenericInput unbindKey = default, string unboundPlaceholder = "", Func<GenericInput, string> inputName = null) {
|
||||||
string GetCurrentName() => keybind.TryGetCombination(index, out var combination) ? combination.ToString(" + ", inputName) : unboundPlaceholder;
|
string GetCurrentName() {
|
||||||
|
var combination = keybind.GetCombinations().FirstOrDefault();
|
||||||
|
return combination?.ToString(" + ", inputName) ?? unboundPlaceholder;
|
||||||
|
}
|
||||||
|
|
||||||
var button = new Button(anchor, size, GetCurrentName());
|
var button = new Button(anchor, size, GetCurrentName());
|
||||||
|
var active = false;
|
||||||
var activeNext = false;
|
var activeNext = false;
|
||||||
button.OnPressed = e => {
|
button.OnPressed = e => {
|
||||||
button.Text.Text = activePlaceholder;
|
button.Text.Text = activePlaceholder;
|
||||||
|
@ -142,24 +144,22 @@ namespace MLEM.Ui.Elements {
|
||||||
};
|
};
|
||||||
button.OnUpdated = (e, time) => {
|
button.OnUpdated = (e, time) => {
|
||||||
if (activeNext) {
|
if (activeNext) {
|
||||||
button.SetData("Active", true);
|
active = true;
|
||||||
activeNext = false;
|
activeNext = false;
|
||||||
} else if (button.GetData<bool>("Active")) {
|
} else if (active) {
|
||||||
if (unbindKey != default && inputHandler.IsPressed(unbindKey)) {
|
if (unbindKey != default && inputHandler.IsPressed(unbindKey)) {
|
||||||
keybind.Remove((c, i) => i == index);
|
keybind.Clear();
|
||||||
button.Text.Text = unboundPlaceholder;
|
button.Text.Text = unboundPlaceholder;
|
||||||
button.SetData("Active", false);
|
active = false;
|
||||||
} else if (inputHandler.InputsPressed.Length > 0) {
|
} else if (inputHandler.InputsPressed.Length > 0) {
|
||||||
var key = inputHandler.InputsPressed.FirstOrDefault(i => !i.IsModifier());
|
var key = inputHandler.InputsPressed.FirstOrDefault(i => !i.IsModifier());
|
||||||
if (key != default) {
|
if (key != default) {
|
||||||
var mods = inputHandler.InputsDown.Where(i => i.IsModifier());
|
var mods = inputHandler.InputsDown.Where(i => i.IsModifier());
|
||||||
keybind.Remove((c, i) => i == index).Insert(index, key, mods.ToArray());
|
keybind.Remove((c, i) => i == 0).Add(key, mods.ToArray());
|
||||||
button.Text.Text = GetCurrentName();
|
button.Text.Text = GetCurrentName();
|
||||||
button.SetData("Active", false);
|
active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
button.Text.Text = GetCurrentName();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return button;
|
return button;
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Input;
|
using Microsoft.Xna.Framework.Input;
|
||||||
using Microsoft.Xna.Framework.Input.Touch;
|
using Microsoft.Xna.Framework.Input.Touch;
|
||||||
using MLEM.Extensions;
|
|
||||||
using MLEM.Input;
|
using MLEM.Input;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
using MLEM.Ui.Elements;
|
using MLEM.Ui.Elements;
|
||||||
|
@ -357,11 +355,11 @@ namespace MLEM.Ui {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the next element that should be selected during gamepad navigation, based on the <see cref="Direction2"/> that we're looking for elements in.
|
/// Returns the next element that should be selected during gamepad navigation, based on the <see cref="RectangleF"/> that we're looking for elements in.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="direction">The direction that we're looking for next elements in</param>
|
/// <param name="searchArea">The area that we're looking for next elements in</param>
|
||||||
/// <returns>The first element found in that area</returns>
|
/// <returns>The first element found in that area</returns>
|
||||||
protected virtual Element GetGamepadNextElement(Direction2 direction) {
|
protected virtual Element GetGamepadNextElement(RectangleF searchArea) {
|
||||||
if (this.ActiveRoot == null)
|
if (this.ActiveRoot == null)
|
||||||
return null;
|
return null;
|
||||||
var children = this.ActiveRoot.Element.GetChildren(c => !c.IsHidden, true, true).Append(this.ActiveRoot.Element);
|
var children = this.ActiveRoot.Element.GetChildren(c => !c.IsHidden, true, true).Append(this.ActiveRoot.Element);
|
||||||
|
@ -369,17 +367,14 @@ namespace MLEM.Ui {
|
||||||
return children.FirstOrDefault(c => c.CanBeSelected);
|
return children.FirstOrDefault(c => c.CanBeSelected);
|
||||||
} else {
|
} else {
|
||||||
Element closest = null;
|
Element closest = null;
|
||||||
float closestDistSq = 0;
|
float closestDist = 0;
|
||||||
foreach (var child in children) {
|
foreach (var child in children) {
|
||||||
if (!child.CanBeSelected || child == this.SelectedElement)
|
if (!child.CanBeSelected || child == this.SelectedElement || !searchArea.Intersects(child.Area))
|
||||||
continue;
|
continue;
|
||||||
var (xOffset, yOffset) = child.Area.Center - this.SelectedElement.Area.Center;
|
var dist = Vector2.Distance(child.Area.Center, this.SelectedElement.Area.Center);
|
||||||
if (Math.Abs(direction.Angle() - Math.Atan2(yOffset, xOffset)) >= MathHelper.PiOver2 - Element.Epsilon)
|
if (closest == null || dist < closestDist) {
|
||||||
continue;
|
|
||||||
var distSq = child.Area.DistanceSquared(this.SelectedElement.Area);
|
|
||||||
if (closest == null || distSq < closestDistSq) {
|
|
||||||
closest = child;
|
closest = child;
|
||||||
closestDistSq = distSq;
|
closestDist = dist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return closest;
|
return closest;
|
||||||
|
@ -388,7 +383,28 @@ namespace MLEM.Ui {
|
||||||
|
|
||||||
private void HandleGamepadNextElement(Direction2 dir) {
|
private void HandleGamepadNextElement(Direction2 dir) {
|
||||||
this.IsAutoNavMode = true;
|
this.IsAutoNavMode = true;
|
||||||
var next = this.GetGamepadNextElement(dir);
|
RectangleF searchArea = default;
|
||||||
|
if (this.SelectedElement?.Root != null) {
|
||||||
|
searchArea = this.SelectedElement.Area;
|
||||||
|
var (_, _, width, height) = this.System.Viewport;
|
||||||
|
switch (dir) {
|
||||||
|
case Direction2.Down:
|
||||||
|
searchArea.Height += height;
|
||||||
|
break;
|
||||||
|
case Direction2.Left:
|
||||||
|
searchArea.X -= width;
|
||||||
|
searchArea.Width += width;
|
||||||
|
break;
|
||||||
|
case Direction2.Right:
|
||||||
|
searchArea.Width += width;
|
||||||
|
break;
|
||||||
|
case Direction2.Up:
|
||||||
|
searchArea.Y -= height;
|
||||||
|
searchArea.Height += height;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var next = this.GetGamepadNextElement(searchArea);
|
||||||
if (this.SelectedElement != null)
|
if (this.SelectedElement != null)
|
||||||
next = this.SelectedElement.GetGamepadNextElement(dir, next);
|
next = this.SelectedElement.GetGamepadNextElement(dir, next);
|
||||||
if (next != null)
|
if (next != null)
|
||||||
|
|
|
@ -47,28 +47,7 @@ namespace MLEM.Input {
|
||||||
|
|
||||||
/// <inheritdoc cref="Add(MLEM.Input.GenericInput,MLEM.Input.GenericInput[])"/>
|
/// <inheritdoc cref="Add(MLEM.Input.GenericInput,MLEM.Input.GenericInput[])"/>
|
||||||
public Keybind Add(GenericInput key, ModifierKey modifier) {
|
public Keybind Add(GenericInput key, ModifierKey modifier) {
|
||||||
foreach (var mod in modifier.GetKeys())
|
return this.Add(key, modifier.GetKeys().Select(m => (GenericInput) m).ToArray());
|
||||||
this.Add(key, mod);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Inserts a new key combination into the given <paramref name="index"/> of this keybind's combinations that can optionally be pressed for the keybind to trigger.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The index to insert this combination into.</param>
|
|
||||||
/// <param name="key">The key to be pressed.</param>
|
|
||||||
/// <param name="modifiers">The modifier keys that have to be held down.</param>
|
|
||||||
/// <returns>This keybind, for chaining.</returns>
|
|
||||||
public Keybind Insert(int index, GenericInput key, params GenericInput[] modifiers) {
|
|
||||||
this.combinations = this.combinations.Take(index).Append(new Combination(key, modifiers)).Concat(this.combinations.Skip(index)).ToArray();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Insert(int,MLEM.Input.GenericInput,MLEM.Input.GenericInput[])"/>
|
|
||||||
public Keybind Insert(int index, GenericInput key, ModifierKey modifier) {
|
|
||||||
foreach (var mod in modifier.GetKeys().Reverse())
|
|
||||||
this.Insert(index, key, mod);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -155,22 +134,6 @@ namespace MLEM.Input {
|
||||||
yield return combination;
|
yield return combination;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to retrieve the combination at the given <paramref name="index"/> within this keybind.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The index of the combination to retrieve.</param>
|
|
||||||
/// <param name="combination">The combination, or default if this method returns false.</param>
|
|
||||||
/// <returns>Whether the combination could be successfully retrieved or the index was out of bounds of this keybind's combination collection.</returns>
|
|
||||||
public bool TryGetCombination(int index, out Combination combination) {
|
|
||||||
if (index >= 0 && index < this.combinations.Length) {
|
|
||||||
combination = this.combinations[index];
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
combination = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts this keybind into an easily human-readable string.
|
/// Converts this keybind into an easily human-readable string.
|
||||||
/// When using <see cref="ToString()"/>, this method is used with <paramref name="joiner"/> set to ", ".
|
/// When using <see cref="ToString()"/>, this method is used with <paramref name="joiner"/> set to ", ".
|
||||||
|
|
|
@ -105,6 +105,49 @@ 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) {
|
||||||
|
var minX = Math.Min(corner1.X, corner2.X);
|
||||||
|
var minY = Math.Min(corner1.Y, corner2.Y);
|
||||||
|
var maxX = Math.Max(corner1.X, corner2.X);
|
||||||
|
var maxY = Math.Max(corner1.Y, corner2.Y);
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Equals(RectangleF)"/>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Equals(RectangleF)"/>
|
||||||
|
public static bool operator !=(RectangleF a, RectangleF b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="Rectangle.Contains(float, float)"/>
|
/// <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;
|
||||||
|
@ -153,92 +196,6 @@ namespace MLEM.Misc {
|
||||||
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.Offset(float, float)"/>
|
|
||||||
public void Offset(float offsetX, float offsetY) {
|
|
||||||
this.X += offsetX;
|
|
||||||
this.Y += offsetY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Rectangle.Offset(Vector2)"/>
|
|
||||||
public void Offset(Vector2 amount) {
|
|
||||||
this.X += amount.X;
|
|
||||||
this.Y += amount.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the suqared distance between this rectangle and the <paramref name="value"/>.
|
|
||||||
/// The returned value is the smallest squared distance between any two edges or corners of the two rectangles.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The rectangle to calculate the squared distance to.</param>
|
|
||||||
/// <returns>The squared distance between the two rectangles.</returns>
|
|
||||||
public float DistanceSquared(RectangleF value) {
|
|
||||||
// we calculate the distance based on the quadrants that the other rectangle is in using 8 cases:
|
|
||||||
// 1 7 4
|
|
||||||
// 3 T 6
|
|
||||||
// 2 8 5
|
|
||||||
var valueIsAbove = value.Bottom < this.Top;
|
|
||||||
var valueIsBelow = value.Top > this.Bottom;
|
|
||||||
if (value.Right < this.Left) {
|
|
||||||
if (valueIsAbove) // 1
|
|
||||||
return Vector2.DistanceSquared(new Vector2(value.Right, value.Bottom), new Vector2(this.Left, this.Top));
|
|
||||||
if (valueIsBelow) // 2
|
|
||||||
return Vector2.DistanceSquared(new Vector2(value.Right, value.Top), new Vector2(this.Left, this.Bottom));
|
|
||||||
return (this.Left - value.Right) * (this.Left - value.Right); // 3
|
|
||||||
} else if (value.Left > this.Right) {
|
|
||||||
if (valueIsAbove) // 4
|
|
||||||
return Vector2.DistanceSquared(new Vector2(value.Left, value.Bottom), new Vector2(this.Right, this.Top));
|
|
||||||
if (valueIsBelow) // 5
|
|
||||||
return Vector2.DistanceSquared(new Vector2(value.Left, value.Top), new Vector2(this.Right, this.Bottom));
|
|
||||||
return (value.Left - this.Right) * (value.Left - this.Right); // 6
|
|
||||||
} else if (valueIsAbove) {
|
|
||||||
return (this.Top - value.Bottom) * (this.Top - value.Bottom); // 7
|
|
||||||
} else if (valueIsBelow) {
|
|
||||||
return (value.Top - this.Bottom) * (value.Top - this.Bottom); // 8
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the distance between this rectangle and the <paramref name="value"/>.
|
|
||||||
/// The returned value is the smallest distance between any two edges or corners of the two rectangles.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The rectangle to calculate the distance to.</param>
|
|
||||||
/// <returns>The distance between the two rectangles.</returns>
|
|
||||||
public float Distance(RectangleF value) {
|
|
||||||
return (float) Math.Sqrt(this.DistanceSquared(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Returns a string that represents the current object.</summary>
|
|
||||||
/// <returns>A string that represents the current object.</returns>
|
|
||||||
public override string ToString() {
|
|
||||||
return "{X:" + this.X + " Y:" + this.Y + " Width:" + this.Width + " Height:" + this.Height + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Rectangle.Deconstruct"/>
|
|
||||||
public void Deconstruct(out float x, out float y, out float width, out float height) {
|
|
||||||
x = this.X;
|
|
||||||
y = this.Y;
|
|
||||||
width = this.Width;
|
|
||||||
height = this.Height;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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) {
|
|
||||||
return new Rectangle(rect.X.Floor(), rect.Y.Floor(), rect.Width.Floor(), rect.Height.Floor());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Rectangle.Union(Rectangle, Rectangle)"/>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Rectangle.Intersect(Rectangle, Rectangle)"/>
|
/// <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)) {
|
||||||
|
@ -252,38 +209,37 @@ namespace MLEM.Misc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="Rectangle.Offset(float, float)"/>
|
||||||
/// Creates a new rectangle based on two corners that form a bounding box.
|
public void Offset(float offsetX, float offsetY) {
|
||||||
/// The resulting rectangle will encompass both corners as well as all of the space between them.
|
this.X += offsetX;
|
||||||
/// </summary>
|
this.Y += offsetY;
|
||||||
/// <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) {
|
|
||||||
var minX = Math.Min(corner1.X, corner2.X);
|
|
||||||
var minY = Math.Min(corner1.Y, corner2.Y);
|
|
||||||
var maxX = Math.Max(corner1.X, corner2.X);
|
|
||||||
var maxY = Math.Max(corner1.Y, corner2.Y);
|
|
||||||
return new RectangleF(minX, minY, maxX - minX, maxY - minY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="Rectangle.Offset(Vector2)"/>
|
||||||
/// Converts an int-based rectangle to a float-based rectangle.
|
public void Offset(Vector2 amount) {
|
||||||
/// </summary>
|
this.X += amount.X;
|
||||||
/// <param name="rect">The rectangle to convert</param>
|
this.Y += amount.Y;
|
||||||
/// <returns>The resulting rectangle</returns>
|
|
||||||
public static explicit operator RectangleF(Rectangle rect) {
|
|
||||||
return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="Equals(RectangleF)"/>
|
/// <summary>Returns a string that represents the current object.</summary>
|
||||||
public static bool operator ==(RectangleF a, RectangleF b) {
|
/// <returns>A string that represents the current object.</returns>
|
||||||
return a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height;
|
public override string ToString() {
|
||||||
|
return "{X:" + this.X + " Y:" + this.Y + " Width:" + this.Width + " Height:" + this.Height + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="Equals(RectangleF)"/>
|
/// <inheritdoc cref="Rectangle.Union(Rectangle, Rectangle)"/>
|
||||||
public static bool operator !=(RectangleF a, RectangleF b) {
|
public static RectangleF Union(RectangleF value1, RectangleF value2) {
|
||||||
return !(a == b);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Rectangle.Deconstruct"/>
|
||||||
|
public void Deconstruct(out float x, out float y, out float width, out float height) {
|
||||||
|
x = this.X;
|
||||||
|
y = this.Y;
|
||||||
|
width = this.Width;
|
||||||
|
height = this.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
# Made with MLEM
|
# Made with MLEM
|
||||||
- [A Breath of Spring Air](https://ellpeck.itch.io/a-breath-of-spring-air), a short platformer ([Source](https://git.ellpeck.de/Ellpeck/GreatSpringGameJam))
|
- [A Breath of Spring Air](https://ellpeck.itch.io/a-breath-of-spring-air), a short platformer ([Source](https://git.ellpeck.de/Ellpeck/GreatSpringGameJam))
|
||||||
- [Don't Wake Up](https://ellpeck.itch.io/dont-wake-up), a short puzzle game ([Source](https://github.com/Ellpeck/DontLetGo))
|
- [Don't Wake Up](https://ellpeck.itch.io/dont-wake-up), a short puzzle game ([Source](https://github.com/Ellpeck/DontLetGo))
|
||||||
- [Pong clone](https://github.com/luanfagu/pong), a very simple pong clone ([Source](https://github.com/luanfagu/pong))
|
|
||||||
- [Tiny Life](https://tinylifegame.com), an isometric life simulation game ([Modding API](https://github.com/Ellpeck/TinyLifeExampleMod))
|
- [Tiny Life](https://tinylifegame.com), an isometric life simulation game ([Modding API](https://github.com/Ellpeck/TinyLifeExampleMod))
|
||||||
|
|
||||||
If you created a game with the help of MLEM, you can get it added to this list by submitting it on the [issue tracker](https://github.com/Ellpeck/MLEM/issues). If its source is public, other people will be able to use your project as an example, too!
|
If you created a game with the help of MLEM, you can get it added to this list by submitting it on the [issue tracker](https://github.com/Ellpeck/MLEM/issues). If its source is public, other people will be able to use your project as an example, too!
|
||||||
|
@ -43,4 +42,4 @@ There are several other libraries and tools that work well in combination with M
|
||||||
- [GameBundle](https://github.com/Ellpeck/GameBundle), a tool that packages MonoGame and other .NET Core applications into several distributable formats
|
- [GameBundle](https://github.com/Ellpeck/GameBundle), a tool that packages MonoGame and other .NET Core applications into several distributable formats
|
||||||
- [MonoGame.Extended](https://github.com/craftworkgames/MonoGame.Extended), a package that also provides several additional features for MonoGame
|
- [MonoGame.Extended](https://github.com/craftworkgames/MonoGame.Extended), a package that also provides several additional features for MonoGame
|
||||||
- [Coroutine](https://github.com/Ellpeck/Coroutine), a package that implements Unity-style coroutines for any project
|
- [Coroutine](https://github.com/Ellpeck/Coroutine), a package that implements Unity-style coroutines for any project
|
||||||
- [Illumilib](https://github.com/Ellpeck/Illumilib), a simple keyboard and mouse lighting library with support for Razer, Logitech and Corsair devices
|
- [Illumilib](https://github.com/Ellpeck/Illumilib), a simple keyboard and mouse lighting library with support for Razer, Logitech and Corsair devices
|
|
@ -304,14 +304,6 @@ namespace Sandbox {
|
||||||
var newPanel = new Panel(Anchor.TopLeft, new Vector2(200, 100), new Vector2(10, 10));
|
var newPanel = new Panel(Anchor.TopLeft, new Vector2(200, 100), new Vector2(10, 10));
|
||||||
newPanel.AddChild(new Button(Anchor.TopLeft, new Vector2(100, 20), "Text", "Tooltip text"));
|
newPanel.AddChild(new Button(Anchor.TopLeft, new Vector2(100, 20), "Text", "Tooltip text"));
|
||||||
this.UiSystem.Add("Panel", newPanel);
|
this.UiSystem.Add("Panel", newPanel);
|
||||||
|
|
||||||
var keybind = new Keybind(MouseButton.Left, ModifierKey.Shift);
|
|
||||||
var keybindPanel = new Panel(Anchor.BottomRight, new Vector2(100, 100), new Vector2(5));
|
|
||||||
for (var i = 0; i < 3; i++) {
|
|
||||||
var button = keybindPanel.AddChild(ElementHelper.KeybindButton(Anchor.AutoLeft, new Vector2(0.5F, 12), keybind, Input, "Press", Keys.Escape, index: i));
|
|
||||||
button.Text.TextScale = 0.1F;
|
|
||||||
}
|
|
||||||
this.UiSystem.Add("Keybinds", keybindPanel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void DoUpdate(GameTime gameTime) {
|
protected override void DoUpdate(GameTime gameTime) {
|
||||||
|
|
Loading…
Reference in a new issue