mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-22 04:53:29 +01:00
Added UiAnimation system
This commit is contained in:
parent
985dc74376
commit
d48b7e2e71
4 changed files with 150 additions and 14 deletions
|
@ -38,6 +38,7 @@ Removals
|
||||||
### MLEM.Ui
|
### MLEM.Ui
|
||||||
Additions
|
Additions
|
||||||
- Added AutoInlineCenter and AutoInlineBottom anchors
|
- Added AutoInlineCenter and AutoInlineBottom anchors
|
||||||
|
- Added UiAnimation system
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
- Increased Element area calculation recursion limit to 64
|
- Increased Element area calculation recursion limit to 64
|
||||||
|
|
|
@ -164,21 +164,13 @@ namespace Demos {
|
||||||
PositionOffset = new Vector2(0, 1)
|
PositionOffset = new Vector2(0, 1)
|
||||||
});
|
});
|
||||||
// Another button that shows animations!
|
// Another button that shows animations!
|
||||||
var fancyHoverTimer = 0D;
|
this.root.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.5F, 10), "Fancy Hover") {
|
||||||
var fancyButton = this.root.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.5F, 10), "Fancy Hover") {
|
|
||||||
PositionOffset = new Vector2(0, 1),
|
PositionOffset = new Vector2(0, 1),
|
||||||
OnUpdated = (e, time) => {
|
MouseEnterAnimation = new UiAnimation(0.15, (a, e, p) => e.ScaleTransform(1 + Easings.OutSine(p) * 0.05F)),
|
||||||
if (e.IsMouseOver && fancyHoverTimer <= 0.5F)
|
MouseExitAnimation = new UiAnimation(0.15, (a, e, p) => e.ScaleTransform(1 + Easings.OutSine.ReverseOutput()(p) * 0.05F)) {
|
||||||
return;
|
Finished = (a, e) => e.Transform = Matrix.Identity
|
||||||
if (fancyHoverTimer > 0) {
|
|
||||||
fancyHoverTimer -= time.ElapsedGameTime.TotalSeconds * 3;
|
|
||||||
e.ScaleTransform(1 + (float) Math.Sin(fancyHoverTimer * MathHelper.Pi) * 0.05F);
|
|
||||||
} else {
|
|
||||||
e.Transform = Matrix.Identity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
fancyButton.OnMouseEnter += e => fancyHoverTimer = 1;
|
|
||||||
this.root.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.5F, 10), "Transform Ui", "This button causes the entire ui to be transformed (both in positioning, rotation and scale)") {
|
this.root.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.5F, 10), "Transform Ui", "This button causes the entire ui to be transformed (both in positioning, rotation and scale)") {
|
||||||
OnPressed = element => {
|
OnPressed = element => {
|
||||||
if (element.Root.Transform == Matrix.Identity) {
|
if (element.Root.Transform == Matrix.Identity) {
|
||||||
|
|
|
@ -379,6 +379,14 @@ namespace MLEM.Ui.Elements {
|
||||||
this.SetAreaDirty();
|
this.SetAreaDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="UiAnimation"/> that is played when the mouse enters this element, in <see cref="OnMouseEnter"/>.
|
||||||
|
/// </summary>
|
||||||
|
public StyleProp<UiAnimation> MouseEnterAnimation;
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="UiAnimation"/> that is played when the mouse exits this element, in <see cref="OnMouseExit"/>.
|
||||||
|
/// </summary>
|
||||||
|
public StyleProp<UiAnimation> MouseExitAnimation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that is called after this element is drawn, but before its children are drawn
|
/// Event that is called after this element is drawn, but before its children are drawn
|
||||||
|
@ -490,6 +498,12 @@ namespace MLEM.Ui.Elements {
|
||||||
/// Use <see cref="AddChild{T}"/> or <see cref="RemoveChild"/> to manipulate this list while calling all of the necessary callbacks.
|
/// Use <see cref="AddChild{T}"/> or <see cref="RemoveChild"/> to manipulate this list while calling all of the necessary callbacks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly IList<Element> Children;
|
protected readonly IList<Element> Children;
|
||||||
|
/// <summary>
|
||||||
|
/// A list of all of the <see cref="UiAnimation"/> instances that are currently playing.
|
||||||
|
/// You can modify this collection through <see cref="PlayAnimation"/> and <see cref="StopAnimation"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly List<UiAnimation> PlayingAnimations = new List<UiAnimation>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sorted version of <see cref="Children"/>. The children are sorted by their <see cref="Priority"/>.
|
/// A sorted version of <see cref="Children"/>. The children are sorted by their <see cref="Priority"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -541,8 +555,16 @@ namespace MLEM.Ui.Elements {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
|
|
||||||
this.Children = new ReadOnlyCollection<Element>(this.children);
|
this.Children = new ReadOnlyCollection<Element>(this.children);
|
||||||
this.GetTabNextElement = (backward, next) => next;
|
this.GetTabNextElement += (backward, next) => next;
|
||||||
this.GetGamepadNextElement = (dir, next) => next;
|
this.GetGamepadNextElement += (dir, next) => next;
|
||||||
|
this.OnMouseEnter += e => {
|
||||||
|
if (e.MouseEnterAnimation.HasValue())
|
||||||
|
e.PlayAnimation(e.MouseEnterAnimation);
|
||||||
|
};
|
||||||
|
this.OnMouseExit += e => {
|
||||||
|
if (e.MouseExitAnimation.HasValue())
|
||||||
|
e.PlayAnimation(e.MouseExitAnimation);
|
||||||
|
};
|
||||||
|
|
||||||
this.SetAreaDirty();
|
this.SetAreaDirty();
|
||||||
this.SetSortedChildrenDirty();
|
this.SetSortedChildrenDirty();
|
||||||
|
@ -1028,6 +1050,14 @@ namespace MLEM.Ui.Elements {
|
||||||
public virtual void Update(GameTime time) {
|
public virtual void Update(GameTime time) {
|
||||||
this.System.InvokeOnElementUpdated(this, time);
|
this.System.InvokeOnElementUpdated(this, time);
|
||||||
|
|
||||||
|
for (var i = this.PlayingAnimations.Count - 1; i >= 0; i--) {
|
||||||
|
var anim = this.PlayingAnimations[i];
|
||||||
|
if (anim.Update(this, time)) {
|
||||||
|
anim.OnFinished(this);
|
||||||
|
this.PlayingAnimations.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update all sorted children, not just relevant ones, because they might become relevant or irrelevant through updates
|
// update all sorted children, not just relevant ones, because they might become relevant or irrelevant through updates
|
||||||
foreach (var child in this.SortedChildren) {
|
foreach (var child in this.SortedChildren) {
|
||||||
if (child.System != null)
|
if (child.System != null)
|
||||||
|
@ -1170,6 +1200,33 @@ namespace MLEM.Ui.Elements {
|
||||||
return this.CanBeMoused && this.DisplayArea.Contains(position) ? this : null;
|
return this.CanBeMoused && this.DisplayArea.Contains(position) ? this : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plays the given <see cref="UiAnimation"/> on this element, causing it to be added to the <see cref="PlayingAnimations"/> and updated in <see cref="Update"/>.
|
||||||
|
/// If the given <paramref name="animation"/> is already playing on this element, it will be restarted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="animation">The animation to play.</param>
|
||||||
|
public virtual void PlayAnimation(UiAnimation animation) {
|
||||||
|
if (this.PlayingAnimations.Contains(animation)) {
|
||||||
|
// if we're already playing this animation, just restart it
|
||||||
|
animation.OnFinished(this);
|
||||||
|
} else {
|
||||||
|
this.PlayingAnimations.Add(animation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops the given <see cref="UiAnimation"/> on this element, causing it to be removed from the <see cref="PlayingAnimations"/> and <see cref="UiAnimation.OnFinished"/> to be invoked.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="animation">The animation to stop.</param>
|
||||||
|
/// <returns>Whether the animation was present in this element's <see cref="PlayingAnimations"/>.</returns>
|
||||||
|
public virtual bool StopAnimation(UiAnimation animation) {
|
||||||
|
if (this.PlayingAnimations.Remove(animation)) {
|
||||||
|
animation.OnFinished(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||||
[Obsolete("Dispose will be removed in a future update. To unregister custom event handlers, use OnRemovedFromUi instead.")]
|
[Obsolete("Dispose will be removed in a future update. To unregister custom event handlers, use OnRemovedFromUi instead.")]
|
||||||
public virtual void Dispose() {
|
public virtual void Dispose() {
|
||||||
|
|
86
MLEM.Ui/UiAnimation.cs
Normal file
86
MLEM.Ui/UiAnimation.cs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using MLEM.Misc;
|
||||||
|
using MLEM.Ui.Elements;
|
||||||
|
|
||||||
|
namespace MLEM.Ui {
|
||||||
|
/// <summary>
|
||||||
|
/// A ui animation is a simple timed event that an <see cref="Element"/> in a <see cref="UiSystem"/> can use to play a visual or other type of animation.
|
||||||
|
/// To use ui animations, you can use <see cref="Element.PlayAnimation"/>, or one of the built-in style properties like <see cref="Element.MouseEnterAnimation"/> or <see cref="Element.MouseExitAnimation"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class UiAnimation : GenericDataHolder {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total time that this ui animation plays for.
|
||||||
|
/// </summary>
|
||||||
|
public readonly TimeSpan TotalTime;
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="AnimationFunction"/> that is invoked every <see cref="Update"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly AnimationFunction Function;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An event that is raised when this ui animation is (re)started in <see cref="Update"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Action<UiAnimation, Element> Started;
|
||||||
|
/// <summary>
|
||||||
|
/// An event that is raised when this ui animation is stopped or finished through <see cref="OnFinished"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Action<UiAnimation, Element> Finished;
|
||||||
|
/// <summary>
|
||||||
|
/// The current time that this ui animation has been playing for, out of the <see cref="TotalTime"/>.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan CurrentTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new ui animation with the given settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seconds">The amount of seconds that this ui animation should play for.</param>
|
||||||
|
/// <param name="function">The <see cref="AnimationFunction"/> that is invoked every <see cref="Update"/>.</param>
|
||||||
|
public UiAnimation(double seconds, AnimationFunction function) : this(TimeSpan.FromSeconds(seconds), function) {}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new ui animation with the given settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="totalTime">The <see cref="TotalTime"/> that this ui animation should play for.</param>
|
||||||
|
/// <param name="function">The <see cref="AnimationFunction"/> that is invoked every <see cref="Update"/>.</param>
|
||||||
|
public UiAnimation(TimeSpan totalTime, AnimationFunction function) {
|
||||||
|
this.TotalTime = totalTime;
|
||||||
|
this.Function = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates this ui animation, invoking its <see cref="Started"/> event if necessary, increasing its <see cref="CurrentTime"/> and invoking its <see cref="Function"/>.
|
||||||
|
/// This method is called by an <see cref="Element"/> in <see cref="Element.Update"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element">The element that this ui animation is attached to.</param>
|
||||||
|
/// <param name="time">The game's current time.</param>
|
||||||
|
/// <returns>Whether this animation is ready to finish, that is, if its <see cref="CurrentTime"/> is greater than or equal to its <see cref="TotalTime"/>.</returns>
|
||||||
|
public virtual bool Update(Element element, GameTime time) {
|
||||||
|
if (this.CurrentTime <= TimeSpan.Zero)
|
||||||
|
this.Started?.Invoke(this, element);
|
||||||
|
|
||||||
|
this.CurrentTime += time.ElapsedGameTime;
|
||||||
|
this.Function?.Invoke(this, element, this.CurrentTime.Ticks / (float) this.TotalTime.Ticks);
|
||||||
|
|
||||||
|
return this.CurrentTime >= this.TotalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Causes this ui animation's <see cref="Finished"/> event to be raised, and sets the <see cref="CurrentTime"/> to <see cref="TimeSpan.Zero"/>.
|
||||||
|
/// This allows the animation to play from the start again.
|
||||||
|
/// This method is invoked automatically when <see cref="Update"/> returns <see langword="true"/> in <see cref="Element.Update"/>, as well as in <see cref="Element.StopAnimation"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element"></param>
|
||||||
|
public virtual void OnFinished(Element element) {
|
||||||
|
this.Finished?.Invoke(this, element);
|
||||||
|
this.CurrentTime = TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate method used by <see cref="UiAnimation.Function"/>.
|
||||||
|
/// </summary>
|
||||||
|
public delegate void AnimationFunction(UiAnimation animation, Element element, float timePercentage);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue