diff --git a/Demos/AnimationDemo.cs b/Demos/AnimationDemo.cs index 8e8d712..4168dbf 100644 --- a/Demos/AnimationDemo.cs +++ b/Demos/AnimationDemo.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; @@ -15,12 +16,12 @@ namespace Demos { base.LoadContent(); var tex = LoadContent("Textures/Anim"); - + // create the four animations by supplying the time per frame, the texture and the four regions used - var downAnim = new SpriteAnimation(0.2F, tex, new Rectangle(0, 0, 8, 8), new Rectangle(0, 8, 8, 8), new Rectangle(0, 16, 8, 8), new Rectangle(0, 24, 8, 8)); - var upAnim = new SpriteAnimation(0.2F, tex, new Rectangle(8, 0, 8, 8), new Rectangle(8, 8, 8, 8), new Rectangle(8, 16, 8, 8), new Rectangle(8, 24, 8, 8)); - var leftAnim = new SpriteAnimation(0.2F, tex, new Rectangle(16, 0, 8, 8), new Rectangle(16, 8, 8, 8), new Rectangle(16, 16, 8, 8), new Rectangle(16, 24, 8, 8)); - var rightAnim = new SpriteAnimation(0.2F, tex, new Rectangle(24, 0, 8, 8), new Rectangle(24, 8, 8, 8), new Rectangle(24, 16, 8, 8), new Rectangle(24, 24, 8, 8)); + var downAnim = new SpriteAnimation(0.2F, tex, new Rectangle(0, 0, 8, 8), new Rectangle(0, 8, 8, 8), new Rectangle(0, 16, 8, 8), new Rectangle(0, 24, 8, 8)) {Name = "Down"}; + var upAnim = new SpriteAnimation(0.2F, tex, new Rectangle(8, 0, 8, 8), new Rectangle(8, 8, 8, 8), new Rectangle(8, 16, 8, 8), new Rectangle(8, 24, 8, 8)) {Name = "Up"}; + var leftAnim = new SpriteAnimation(0.2F, tex, new Rectangle(16, 0, 8, 8), new Rectangle(16, 8, 8, 8), new Rectangle(16, 16, 8, 8), new Rectangle(16, 24, 8, 8)) {Name = "Left"}; + var rightAnim = new SpriteAnimation(0.2F, tex, new Rectangle(24, 0, 8, 8), new Rectangle(24, 8, 8, 8), new Rectangle(24, 16, 8, 8), new Rectangle(24, 24, 8, 8)) {Name = "Right"}; // create a sprite animation group which manages a list of animations and figures out which one should // be playing right now based on supplied conditions @@ -32,6 +33,16 @@ namespace Demos { this.group.Add(upAnim, () => this.facing == 1); this.group.Add(leftAnim, () => this.facing == 2); this.group.Add(rightAnim, () => this.facing == 3); + + // you can also add a priority to an animation in the group (10 in this case, which is higher than the default of 0) + // if two animations' playing conditions are both true, then the one with the higher priority will be picked to play + // in this instance, a standing "animation" is displayed when we're facing down and also holding the space key + this.group.Add(new SpriteAnimation(1F, tex, new Rectangle(0, 0, 8, 8)) {Name = "DownStanding"}, () => this.facing == 0 && Input.IsKeyDown(Keys.Space), 10); + + // you can also add a callback to see when the animation used changes + this.group.OnAnimationChanged += (anim, newAnim) => { + Console.WriteLine("Changing anim from " + (anim?.Name ?? "None") + " to " + (newAnim?.Name ?? "None")); + }; } protected override void Update(GameTime gameTime) { diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index e85b18e..24300fe 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -213,7 +213,7 @@ namespace MLEM.Ui.Elements { this.sortedChildren.Clear(); this.sortedChildren.AddRange(this.Children); - this.sortedChildren.Sort((e1, e2) => e1.Priority.CompareTo(e1.Priority)); + this.sortedChildren.Sort((e1, e2) => e1.Priority.CompareTo(e2.Priority)); } } diff --git a/MLEM/Animations/SpriteAnimation.cs b/MLEM/Animations/SpriteAnimation.cs index b5399e1..0cb56cf 100644 --- a/MLEM/Animations/SpriteAnimation.cs +++ b/MLEM/Animations/SpriteAnimation.cs @@ -10,20 +10,25 @@ namespace MLEM.Animations { public AnimationFrame this[int index] => this.frames[index]; public AnimationFrame CurrentFrame { get { + // we might have overshot the end time by a little bit, so just return the last frame + if (this.TimeIntoAnimation >= this.TotalTime) + return this.frames[this.frames.Length - 1]; var accum = 0D; foreach (var frame in this.frames) { accum += frame.Seconds; if (accum >= this.TimeIntoAnimation) return frame; } - // we might have overshot the end time by a little bit, so just return the last frame - return this.frames[this.frames.Length - 1]; + // if we're here then the time is negative for some reason, so just return the first frame + Console.WriteLine("Test"); + return this.frames[0]; } } public TextureRegion CurrentRegion => this.CurrentFrame.Region; public readonly double TotalTime; public double TimeIntoAnimation { get; private set; } public bool IsFinished { get; private set; } + public string Name; public bool IsLooping = true; public Completed OnCompleted; diff --git a/MLEM/Animations/SpriteAnimationGroup.cs b/MLEM/Animations/SpriteAnimationGroup.cs index 1a7d0b1..1eed847 100644 --- a/MLEM/Animations/SpriteAnimationGroup.cs +++ b/MLEM/Animations/SpriteAnimationGroup.cs @@ -11,37 +11,55 @@ namespace MLEM.Animations { public SpriteAnimation CurrentAnimation => this.currAnimation?.Animation; public AnimationFrame CurrentFrame => this.CurrentAnimation?.CurrentFrame; public TextureRegion CurrentRegion => this.CurrentAnimation?.CurrentRegion; + public AnimationChanged OnAnimationChanged; - public SpriteAnimationGroup Add(SpriteAnimation anim, Func condition) { - this.animations.Add(new ConditionedAnimation(anim, condition)); + public SpriteAnimationGroup Add(SpriteAnimation anim, Func condition, int priority = 0) { + this.animations.Add(new ConditionedAnimation(anim, condition, priority)); + this.animations.Sort((a1, a2) => a1.Priority.CompareTo(a2.Priority)); return this; } public void Update(GameTime time) { - if (this.currAnimation == null || !this.currAnimation.ShouldPlay()) { - this.currAnimation = null; - foreach (var anim in this.animations) { - if (anim.ShouldPlay()) { - this.currAnimation = anim; - anim.Animation.Restart(); - break; - } - } + ConditionedAnimation animToPlay = null; + if (this.currAnimation != null && this.currAnimation.ShouldPlay()) + animToPlay = this.currAnimation; + foreach (var anim in this.animations) { + // if we find an animation with a lower priority then it means we can break + // because the list is sorted by priority + if (animToPlay != null && anim.Priority < animToPlay.Priority) + break; + if (anim.ShouldPlay()) + animToPlay = anim; } + if (animToPlay != this.currAnimation) { + this.OnAnimationChanged?.Invoke(this.currAnimation?.Animation, animToPlay?.Animation); + this.currAnimation = animToPlay; + if (animToPlay != null) + animToPlay.Animation.Restart(); + } + if (this.currAnimation != null) this.currAnimation.Animation.Update(time); } + public SpriteAnimation ByName(string name) { + return this.animations.Find(anim => anim.Animation.Name == name)?.Animation; + } + + public delegate void AnimationChanged(SpriteAnimation oldAnim, SpriteAnimation newAnim); + } internal class ConditionedAnimation { public readonly SpriteAnimation Animation; public readonly Func ShouldPlay; + public readonly int Priority; - public ConditionedAnimation(SpriteAnimation animation, Func shouldPlay) { + public ConditionedAnimation(SpriteAnimation animation, Func shouldPlay, int priority) { this.Animation = animation; this.ShouldPlay = shouldPlay; + this.Priority = priority; } }