mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-15 15:09:29 +00:00
Update animated sprite so the constructor api is similar to original MGE (#906)
* Update animated sprite so the constructor api is similar to original MGE * Fix tests from changes
This commit is contained in:
committed by
GitHub
parent
98ee05f58a
commit
2ad7a9d3db
@@ -8,10 +8,10 @@ namespace MonoGame.Extended.Animations
|
||||
public AnimationComponent(Game game)
|
||||
: base(game)
|
||||
{
|
||||
Animations = new List<Animation>();
|
||||
Animations = new List<AnimationController>();
|
||||
}
|
||||
|
||||
public List<Animation> Animations { get; }
|
||||
public List<AnimationController> Animations { get; }
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
|
||||
+9
-9
@@ -8,12 +8,12 @@ using Microsoft.Xna.Framework;
|
||||
namespace MonoGame.Extended.Animations;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an animation with various control features such as play, pause, stop, looping, reversing, and
|
||||
/// ping-pong effects.
|
||||
/// Represents an animation controller with features to play, pause, stop, reset, and set the state of
|
||||
/// animation playback such as looping, reversing, and ping-pong effects.
|
||||
/// </summary>
|
||||
public class Animation : IAnimation
|
||||
public class AnimationController : IAnimationController
|
||||
{
|
||||
private readonly IAnimationDefinition _definition;
|
||||
private readonly IAnimation _definition;
|
||||
private int _direction;
|
||||
|
||||
/// <summary>
|
||||
@@ -44,7 +44,7 @@ public class Animation : IAnimation
|
||||
public double Speed { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<IAnimation, AnimationEventTrigger> OnAnimationEvent;
|
||||
public event Action<IAnimationController, AnimationEventTrigger> OnAnimationEvent;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeSpan CurrentFrameTimeRemaining { get; private set; }
|
||||
@@ -56,10 +56,10 @@ public class Animation : IAnimation
|
||||
public int FrameCount => _definition.FrameCount;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Animation"/> class with the specified definition.
|
||||
/// Initializes a new instance of the <see cref="AnimationController"/> class with the specified definition.
|
||||
/// </summary>
|
||||
/// <param name="definition">The definition of the animation.</param>
|
||||
public Animation(IAnimationDefinition definition)
|
||||
public AnimationController(IAnimation definition)
|
||||
{
|
||||
_definition = definition;
|
||||
|
||||
@@ -101,7 +101,7 @@ public class Animation : IAnimation
|
||||
{
|
||||
if (startingFrame < 0 || startingFrame >= _definition.FrameCount)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(startingFrame), $"{nameof(startingFrame)} cannot be less than zero or greater than or equal to the total number of frames in this {nameof(Animation)}");
|
||||
throw new ArgumentOutOfRangeException(nameof(startingFrame), $"{nameof(startingFrame)} cannot be less than zero or greater than or equal to the total number of frames in this {nameof(AnimationController)}");
|
||||
}
|
||||
|
||||
// Cannot play something that is already playing
|
||||
@@ -134,7 +134,7 @@ public class Animation : IAnimation
|
||||
{
|
||||
if (index < 0 || index >= _definition.FrameCount)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), $"{nameof(index)} cannot be less than zero or greater than or equal to the total number of frames in this {nameof(Animation)}");
|
||||
throw new ArgumentOutOfRangeException(nameof(index), $"{nameof(index)} cannot be less than zero or greater than or equal to the total number of frames in this {nameof(AnimationController)}");
|
||||
}
|
||||
|
||||
CurrentFrame = index;
|
||||
@@ -12,14 +12,14 @@ namespace MonoGame.Extended.Animations;
|
||||
public class AnimationEvent : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the animation associated with the event.
|
||||
/// Gets the animation controller associated with the event.
|
||||
/// </summary>
|
||||
public IAnimation Animation { get; }
|
||||
public IAnimationController Animation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trigger that caused the event.
|
||||
/// </summary>
|
||||
public AnimationEventTrigger Trigger { get; }
|
||||
|
||||
internal AnimationEvent(IAnimation animation, AnimationEventTrigger trigger) => (Animation, Trigger) = (animation, trigger);
|
||||
internal AnimationEvent(IAnimationController animation, AnimationEventTrigger trigger) => (Animation, Trigger) = (animation, trigger);
|
||||
}
|
||||
|
||||
@@ -3,60 +3,25 @@
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Animations;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface for an animation with various control features such as play, pause, stop, looping, reversing, and ping-pong effects.
|
||||
/// Defines the interface for an animation, specifying properties of the animation such as frames, looping, reversing,
|
||||
/// and ping-pong effects.
|
||||
/// </summary>
|
||||
public interface IAnimation : IDisposable
|
||||
public interface IAnimation
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the animation is paused.
|
||||
/// Gets the name of the animation.
|
||||
/// </summary>
|
||||
bool IsPaused { get; }
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the animation is currently animating.
|
||||
/// Gets the read-only collection of frames in the animation.
|
||||
/// </summary>
|
||||
bool IsAnimating { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the animation should loop.
|
||||
/// </summary>
|
||||
bool IsLooping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the animation is reversed.
|
||||
/// </summary>
|
||||
bool IsReversed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the animation should ping-pong (reverse direction at the ends).
|
||||
/// </summary>
|
||||
bool IsPingPong { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the speed of the animation.
|
||||
/// </summary>
|
||||
/// <value>The speed cannot be less than zero.</value>
|
||||
double Speed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action to perform when an animation event is triggered.
|
||||
/// </summary>
|
||||
event Action<IAnimation, AnimationEventTrigger> OnAnimationEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time remaining for the current frame.
|
||||
/// </summary>
|
||||
TimeSpan CurrentFrameTimeRemaining { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the current frame of the animation.
|
||||
/// </summary>
|
||||
int CurrentFrame { get; }
|
||||
ReadOnlySpan<IAnimationFrame> Frames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of frames in the animation.
|
||||
@@ -64,86 +29,17 @@ public interface IAnimation : IDisposable
|
||||
int FrameCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the animation to a specified frame.
|
||||
/// Gets a value indicating whether the animation should loop.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the frame to set.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when the <paramref name="index"/> parameter is less than zero or greater than or equal to the total
|
||||
/// number of frames.
|
||||
/// </exception>
|
||||
void SetFrame(int index);
|
||||
bool IsLooping { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Plays the animation from the beginning.
|
||||
/// Gets a value indicating whether the animation is reversed.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully started; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Play();
|
||||
bool IsReversed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Plays the animation from a specified starting frame.
|
||||
/// Gets a value indicating whether the animation should ping-pong (reverse direction at the ends).
|
||||
/// </summary>
|
||||
/// <param name="startingFrame">The frame to start the animation from.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully started; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when the <paramref name="startingFrame"/> parameter is less than zero or greater than or equal to the
|
||||
/// total number of frames.
|
||||
/// </exception>
|
||||
bool Play(int startingFrame);
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the animation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully paused; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Pause();
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the animation.
|
||||
/// </summary>
|
||||
/// <param name="resetFrameDuration">If set to <see langword="true"/>, resets the frame duration.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully paused; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Pause(bool resetFrameDuration);
|
||||
|
||||
/// <summary>
|
||||
/// Unpauses the animation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully unpaused; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Unpause();
|
||||
|
||||
/// <summary>
|
||||
/// Unpauses the animation.
|
||||
/// </summary>
|
||||
/// <param name="advanceToNextFrame">If set to <see langword="true"/>, advances to the next frame.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully unpaused; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Unpause(bool advanceToNextFrame);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the animation.
|
||||
/// </summary>
|
||||
/// <param name="gameTime">A snapshot of the timing values for the current update cycle.</param>
|
||||
void Update(GameTime gameTime);
|
||||
|
||||
/// <summary>
|
||||
/// Stops the animation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully stopped; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Stop();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the animation to its initial state.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
bool IsPingPong { get; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) Craftwork Games. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Animations;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface for an animation controller with features to play, pause, stop, reset, and set the state of
|
||||
/// animation playback such as looping, reversing, and ping-pong effects.
|
||||
/// </summary>
|
||||
public interface IAnimationController : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this animation controller has been disposed of.
|
||||
/// </summary>
|
||||
bool IsDisposed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the animation is paused.
|
||||
/// </summary>
|
||||
bool IsPaused { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the animation is currently animating.
|
||||
/// </summary>
|
||||
bool IsAnimating { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the animation should loop.
|
||||
/// </summary>
|
||||
bool IsLooping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the animation is reversed.
|
||||
/// </summary>
|
||||
bool IsReversed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the animation should ping-pong (reverse direction at the ends).
|
||||
/// </summary>
|
||||
bool IsPingPong { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the speed of the animation.
|
||||
/// </summary>
|
||||
/// <value>The speed cannot be less than zero.</value>
|
||||
double Speed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action to perform when an animation event is triggered.
|
||||
/// </summary>
|
||||
event Action<IAnimationController, AnimationEventTrigger> OnAnimationEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time remaining for the current frame.
|
||||
/// </summary>
|
||||
TimeSpan CurrentFrameTimeRemaining { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the current frame of the animation.
|
||||
/// </summary>
|
||||
int CurrentFrame { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of frames in the animation.
|
||||
/// </summary>
|
||||
int FrameCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the animation to a specified frame.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the frame to set.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when the <paramref name="index"/> parameter is less than zero or greater than or equal to the total
|
||||
/// number of frames.
|
||||
/// </exception>
|
||||
void SetFrame(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Plays the animation from the beginning.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully started; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Play();
|
||||
|
||||
/// <summary>
|
||||
/// Plays the animation from a specified starting frame.
|
||||
/// </summary>
|
||||
/// <param name="startingFrame">The frame to start the animation from.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully started; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when the <paramref name="startingFrame"/> parameter is less than zero or greater than or equal to the
|
||||
/// total number of frames.
|
||||
/// </exception>
|
||||
bool Play(int startingFrame);
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the animation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully paused; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Pause();
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the animation.
|
||||
/// </summary>
|
||||
/// <param name="resetFrameDuration">If set to <see langword="true"/>, resets the frame duration.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully paused; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Pause(bool resetFrameDuration);
|
||||
|
||||
/// <summary>
|
||||
/// Unpauses the animation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully unpaused; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Unpause();
|
||||
|
||||
/// <summary>
|
||||
/// Unpauses the animation.
|
||||
/// </summary>
|
||||
/// <param name="advanceToNextFrame">If set to <see langword="true"/>, advances to the next frame.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully unpaused; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Unpause(bool advanceToNextFrame);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the animation.
|
||||
/// </summary>
|
||||
/// <param name="gameTime">A snapshot of the timing values for the current update cycle.</param>
|
||||
void Update(GameTime gameTime);
|
||||
|
||||
/// <summary>
|
||||
/// Stops the animation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the animation was successfully stopped; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
bool Stop();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the animation to its initial state.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) Craftwork Games. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MonoGame.Extended.Animations;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface for an animation definition, specifying properties of the animation such as frames, looping,
|
||||
/// reversing, and ping-pong effects.
|
||||
/// </summary>
|
||||
public interface IAnimationDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the animation.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the read-only collection of frames in the animation.
|
||||
/// </summary>
|
||||
ReadOnlySpan<IAnimationFrame> Frames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of frames in the animation.
|
||||
/// </summary>
|
||||
int FrameCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the animation should loop.
|
||||
/// </summary>
|
||||
bool IsLooping { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the animation is reversed.
|
||||
/// </summary>
|
||||
bool IsReversed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the animation should ping-pong (reverse direction at the ends).
|
||||
/// </summary>
|
||||
bool IsPingPong { get; }
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.Animations;
|
||||
|
||||
@@ -12,30 +14,66 @@ namespace MonoGame.Extended.Graphics;
|
||||
/// </summary>
|
||||
public class AnimatedSprite : Sprite
|
||||
{
|
||||
private readonly SpriteSheet _spriteSheet;
|
||||
private IAnimation _animation;
|
||||
|
||||
private readonly Texture2DRegion[] _regions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the animation used by this animated sprite.
|
||||
/// Gets the animation controller used to control the current animation of this animated sprite.
|
||||
/// </summary>
|
||||
public IAnimation Animation { get; }
|
||||
public IAnimationController Controller { get; private set; }
|
||||
|
||||
internal AnimatedSprite(SpriteSheetAnimationDefinition definition, Texture2DRegion[] regions)
|
||||
: base(regions[0])
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AnimatedSprite"/> class with the specified
|
||||
/// <see cref="SpriteSheet"/>.
|
||||
/// </summary>
|
||||
/// <param name="spriteSheet">The <see cref="SpriteSheet"/> that contains the animations.</param>
|
||||
public AnimatedSprite(SpriteSheet spriteSheet)
|
||||
: base(spriteSheet.TextureAtlas[0])
|
||||
{
|
||||
Animation = new Animation(definition);
|
||||
_regions = regions;
|
||||
ArgumentNullException.ThrowIfNull(spriteSheet);
|
||||
_spriteSheet = spriteSheet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AnimatedSprite"/> class with the specified
|
||||
/// <see cref="SpriteSheet"/>.
|
||||
/// </summary>
|
||||
/// <param name="spriteSheet">The <see cref="SpriteSheet"/> that contains the animations.</param>
|
||||
/// <param name="initialAnimation">The initial animation to play</param>
|
||||
public AnimatedSprite(SpriteSheet spriteSheet, string initialAnimation) :this(spriteSheet)
|
||||
{
|
||||
SpriteSheetAnimation definition = spriteSheet.GetAnimation(initialAnimation);
|
||||
Controller = new AnimationController(definition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the animation to use for this animated sprite.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the animation.</param>
|
||||
/// <returns>The <see cref="IAnimationController"/> of the animation.</returns>
|
||||
/// <exception cref="KeyNotFoundException">
|
||||
/// Thrown if the source spritesheet does not contain an animation a name that matches the <paramref name="name"/> parameter.
|
||||
/// </exception>
|
||||
public IAnimationController SetAnimation(string name)
|
||||
{
|
||||
_animation = _spriteSheet.GetAnimation(name);
|
||||
Controller = new AnimationController(_animation);
|
||||
return Controller;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Update(GameTime gameTime)
|
||||
{
|
||||
int index = Animation.CurrentFrame;
|
||||
Animation.Update(gameTime);
|
||||
int index = Controller.CurrentFrame;
|
||||
Controller.Update(gameTime);
|
||||
|
||||
// If the current frame changed during the update, change the texture region
|
||||
if (index != Animation.CurrentFrame)
|
||||
if (index != Controller.CurrentFrame)
|
||||
{
|
||||
TextureRegion = _regions[Animation.CurrentFrame];
|
||||
int regionIndex = _animation.Frames[Controller.CurrentFrame].FrameIndex;
|
||||
TextureRegion = _spriteSheet.TextureAtlas[regionIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace MonoGame.Extended.Graphics;
|
||||
/// </summary>
|
||||
public class SpriteSheet
|
||||
{
|
||||
private readonly Dictionary<string, SpriteSheetAnimationDefinition> _animations = new Dictionary<string, SpriteSheetAnimationDefinition>();
|
||||
private readonly Dictionary<string, SpriteSheetAnimation> _animations = new Dictionary<string, SpriteSheetAnimation>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of animations defined in the sprite sheet.
|
||||
@@ -57,31 +57,22 @@ public class SpriteSheet
|
||||
/// <returns>A new <see cref="Sprite"/> instance.</returns>
|
||||
public Sprite CreateSprite(string regionName) => TextureAtlas.CreateSprite(regionName);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an animated sprite from the specified animation name.
|
||||
/// </summary>
|
||||
/// <param name="animationName">The name of the animation.</param>
|
||||
/// <returns>A new <see cref="AnimatedSprite"/> instance.</returns>
|
||||
public AnimatedSprite CreateAnimatedSprite(string animationName)
|
||||
{
|
||||
SpriteSheetAnimationDefinition animationDefinition = _animations[animationName];
|
||||
Texture2DRegion[] regions = TextureAtlas.GetRegions(animationDefinition.Frames);
|
||||
return new AnimatedSprite(animationDefinition, regions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a new animation for the sprite sheet.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the animation.</param>
|
||||
/// <param name="buildAction">The action to build the animation definition.</param>
|
||||
public void DefineAnimation(string name, Action<SpriteSheetAnimationDefinitionBuilder> buildAction)
|
||||
public void DefineAnimation(string name, Action<SpriteSheetAnimationBuilder> buildAction)
|
||||
{
|
||||
SpriteSheetAnimationDefinitionBuilder builder = new SpriteSheetAnimationDefinitionBuilder(name, this);
|
||||
SpriteSheetAnimationBuilder builder = new SpriteSheetAnimationBuilder(name, this);
|
||||
buildAction(builder);
|
||||
SpriteSheetAnimationDefinition definition = builder.Build();
|
||||
SpriteSheetAnimation definition = builder.Build();
|
||||
_animations.Add(name, definition);
|
||||
}
|
||||
|
||||
public SpriteSheetAnimation GetAnimation(string name) => _animations[name];
|
||||
|
||||
/// <summary>
|
||||
/// Removes the animation definition with the specified name.
|
||||
/// </summary>
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ using MonoGame.Extended.Animations;
|
||||
|
||||
namespace MonoGame.Extended.Graphics;
|
||||
|
||||
internal class SpriteSheetAnimationDefinition : IAnimationDefinition
|
||||
public class SpriteSheetAnimation : IAnimation
|
||||
{
|
||||
private readonly SpriteSheetAnimationFrame[] _frames;
|
||||
|
||||
@@ -18,7 +18,7 @@ internal class SpriteSheetAnimationDefinition : IAnimationDefinition
|
||||
public bool IsReversed { get; }
|
||||
public bool IsPingPong { get; }
|
||||
|
||||
internal SpriteSheetAnimationDefinition(string name, SpriteSheetAnimationFrame[] frames, bool isLooping, bool isReversed, bool isPingPong)
|
||||
internal SpriteSheetAnimation(string name, SpriteSheetAnimationFrame[] frames, bool isLooping, bool isReversed, bool isPingPong)
|
||||
{
|
||||
Name = name;
|
||||
IsLooping = isLooping;
|
||||
+15
-15
@@ -8,9 +8,9 @@ using System.Collections.Generic;
|
||||
namespace MonoGame.Extended.Graphics;
|
||||
|
||||
/// <summary>
|
||||
/// A builder class for creating <see cref="SpriteSheetAnimationDefinition"/> instances.
|
||||
/// A builder class for creating <see cref="SpriteSheetAnimation"/> instances.
|
||||
/// </summary>
|
||||
public sealed class SpriteSheetAnimationDefinitionBuilder
|
||||
public sealed class SpriteSheetAnimationBuilder
|
||||
{
|
||||
private readonly string _name;
|
||||
private readonly SpriteSheet _spriteSheet;
|
||||
@@ -19,7 +19,7 @@ public sealed class SpriteSheetAnimationDefinitionBuilder
|
||||
private bool _isReversed;
|
||||
private bool _isPingPong;
|
||||
|
||||
internal SpriteSheetAnimationDefinitionBuilder(string name, SpriteSheet spriteSheet)
|
||||
internal SpriteSheetAnimationBuilder(string name, SpriteSheet spriteSheet)
|
||||
{
|
||||
_name = name;
|
||||
_spriteSheet = spriteSheet;
|
||||
@@ -30,8 +30,8 @@ public sealed class SpriteSheetAnimationDefinitionBuilder
|
||||
/// </summary>
|
||||
/// <param name="regionIndex">The index of the region in the sprite sheet.</param>
|
||||
/// <param name="duration">The duration of the frame.</param>
|
||||
/// <returns>The <see cref="SpriteSheetAnimationDefinitionBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationDefinitionBuilder AddFrame(int regionIndex, TimeSpan duration)
|
||||
/// <returns>The <see cref="SpriteSheetAnimationBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationBuilder AddFrame(int regionIndex, TimeSpan duration)
|
||||
{
|
||||
SpriteSheetAnimationFrame frame = new SpriteSheetAnimationFrame(regionIndex, duration);
|
||||
_frames.Add(frame);
|
||||
@@ -43,8 +43,8 @@ public sealed class SpriteSheetAnimationDefinitionBuilder
|
||||
/// </summary>
|
||||
/// <param name="regionName">The name of the region in the sprite sheet.</param>
|
||||
/// <param name="duration">The duration of the frame.</param>
|
||||
/// <returns>The <see cref="SpriteSheetAnimationDefinitionBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationDefinitionBuilder AddFrame(string regionName, TimeSpan duration)
|
||||
/// <returns>The <see cref="SpriteSheetAnimationBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationBuilder AddFrame(string regionName, TimeSpan duration)
|
||||
{
|
||||
int index = _spriteSheet.TextureAtlas.GetIndexOfRegion(regionName);
|
||||
return AddFrame(index, duration);
|
||||
@@ -54,8 +54,8 @@ public sealed class SpriteSheetAnimationDefinitionBuilder
|
||||
/// Sets whether the animation should loop.
|
||||
/// </summary>
|
||||
/// <param name="isLooping">If set to <c>true</c>, the animation will loop.</param>
|
||||
/// <returns>The <see cref="SpriteSheetAnimationDefinitionBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationDefinitionBuilder IsLooping(bool isLooping)
|
||||
/// <returns>The <see cref="SpriteSheetAnimationBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationBuilder IsLooping(bool isLooping)
|
||||
{
|
||||
_isLooping = isLooping;
|
||||
return this;
|
||||
@@ -65,8 +65,8 @@ public sealed class SpriteSheetAnimationDefinitionBuilder
|
||||
/// Sets whether the animation should be reversed.
|
||||
/// </summary>
|
||||
/// <param name="isReversed">If set to <c>true</c>, the animation will play in reverse.</param>
|
||||
/// <returns>The <see cref="SpriteSheetAnimationDefinitionBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationDefinitionBuilder IsReversed(bool isReversed)
|
||||
/// <returns>The <see cref="SpriteSheetAnimationBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationBuilder IsReversed(bool isReversed)
|
||||
{
|
||||
_isReversed = isReversed;
|
||||
return this;
|
||||
@@ -76,13 +76,13 @@ public sealed class SpriteSheetAnimationDefinitionBuilder
|
||||
/// Sets whether the animation should ping-pong (reverse direction at the ends).
|
||||
/// </summary>
|
||||
/// <param name="isPingPong">If set to <c>true</c>, the animation will ping-pong.</param>
|
||||
/// <returns>The <see cref="SpriteSheetAnimationDefinitionBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationDefinitionBuilder IsPingPong(bool isPingPong)
|
||||
/// <returns>The <see cref="SpriteSheetAnimationBuilder"/> instance for chaining.</returns>
|
||||
public SpriteSheetAnimationBuilder IsPingPong(bool isPingPong)
|
||||
{
|
||||
_isPingPong = isPingPong;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal SpriteSheetAnimationDefinition Build() =>
|
||||
new SpriteSheetAnimationDefinition(_name, _frames.ToArray(), _isLooping, _isReversed, _isPingPong);
|
||||
internal SpriteSheetAnimation Build() =>
|
||||
new SpriteSheetAnimation(_name, _frames.ToArray(), _isLooping, _isReversed, _isPingPong);
|
||||
}
|
||||
@@ -169,6 +169,11 @@ namespace MonoGame.Extended
|
||||
return Subtract(first, second);
|
||||
}
|
||||
|
||||
public static Vector2 operator -(Vector2 first, SizeF second)
|
||||
{
|
||||
return Subtract(first, second);
|
||||
}
|
||||
|
||||
public static SizeF operator /(SizeF size, float value)
|
||||
{
|
||||
return new SizeF(size.Width / value, size.Height / value);
|
||||
|
||||
@@ -18,7 +18,7 @@ public class AnimationTests
|
||||
public TimeSpan Duration { get; set; }
|
||||
}
|
||||
|
||||
private class TestAnimationDefinition : IAnimationDefinition
|
||||
private class TestAnimation : IAnimation
|
||||
{
|
||||
private readonly IAnimationFrame[] _frames;
|
||||
public string Name { get; set; }
|
||||
@@ -28,11 +28,11 @@ public class AnimationTests
|
||||
public bool IsReversed { get; set; }
|
||||
public bool IsPingPong { get; set; }
|
||||
|
||||
public TestAnimationDefinition(IAnimationFrame[] frames) => _frames = frames;
|
||||
public TestAnimation(IAnimationFrame[] frames) => _frames = frames;
|
||||
}
|
||||
|
||||
private readonly IAnimationDefinition _definition;
|
||||
private readonly Animation _animation;
|
||||
private readonly IAnimation _animation;
|
||||
private readonly IAnimationController _animationController;
|
||||
|
||||
public AnimationTests()
|
||||
{
|
||||
@@ -41,94 +41,95 @@ public class AnimationTests
|
||||
new TestAnimationFrame { FrameIndex = 0, Duration = TimeSpan.FromSeconds(1) },
|
||||
new TestAnimationFrame { FrameIndex = 1, Duration = TimeSpan.FromSeconds(1) }
|
||||
};
|
||||
_definition = new TestAnimationDefinition(frames)
|
||||
_animation = new TestAnimation(frames)
|
||||
{
|
||||
FrameCount = frames.Length,
|
||||
IsLooping = false,
|
||||
IsReversed = false,
|
||||
IsPingPong = false
|
||||
};
|
||||
_animation = new Animation(_definition);
|
||||
|
||||
_animationController = new AnimationController(_animation);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Play_ShouldStartAnimation()
|
||||
{
|
||||
var result = _animation.Play();
|
||||
var result = _animationController.Play();
|
||||
|
||||
Assert.True(result);
|
||||
Assert.True(_animation.IsAnimating);
|
||||
Assert.Equal(0, _animation.CurrentFrame);
|
||||
Assert.True(_animationController.IsAnimating);
|
||||
Assert.Equal(0, _animationController.CurrentFrame);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Play_ShouldThrowException_ForInvalidFrame()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _animation.Play(-1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _animation.Play(10));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _animationController.Play(-1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _animationController.Play(10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pause_ShouldPauseAnimation()
|
||||
{
|
||||
_animation.Play();
|
||||
var result = _animation.Pause();
|
||||
_animationController.Play();
|
||||
var result = _animationController.Pause();
|
||||
|
||||
Assert.True(result);
|
||||
Assert.True(_animation.IsPaused);
|
||||
Assert.True(_animationController.IsPaused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pause_ShouldResetFrameDuration_WhenSpecified()
|
||||
{
|
||||
_animation.Play();
|
||||
_animation.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0.5)));
|
||||
_animationController.Play();
|
||||
_animationController.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0.5)));
|
||||
|
||||
var result = _animation.Pause(true);
|
||||
var result = _animationController.Pause(true);
|
||||
|
||||
Assert.True(result);
|
||||
Assert.Equal(_definition.Frames[0].Duration, _animation.CurrentFrameTimeRemaining);
|
||||
Assert.Equal(_animation.Frames[0].Duration, _animationController.CurrentFrameTimeRemaining);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnPause_ShouldResumeAnimation()
|
||||
{
|
||||
_animation.Play();
|
||||
_animation.Pause();
|
||||
var result = _animation.Unpause();
|
||||
_animationController.Play();
|
||||
_animationController.Pause();
|
||||
var result = _animationController.Unpause();
|
||||
|
||||
Assert.True(result);
|
||||
Assert.False(_animation.IsPaused);
|
||||
Assert.False(_animationController.IsPaused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnPause_ShouldAdvanceFrame_WhenSpecified()
|
||||
{
|
||||
_animation.Play();
|
||||
_animation.Pause();
|
||||
var result = _animation.Unpause(true);
|
||||
_animationController.Play();
|
||||
_animationController.Pause();
|
||||
var result = _animationController.Unpause(true);
|
||||
|
||||
Assert.True(result);
|
||||
Assert.Equal(1, _animation.CurrentFrame);
|
||||
Assert.Equal(1, _animationController.CurrentFrame);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stop_ShouldStopAnimation()
|
||||
{
|
||||
_animation.Play();
|
||||
var result = _animation.Stop();
|
||||
_animationController.Play();
|
||||
var result = _animationController.Stop();
|
||||
|
||||
Assert.True(result);
|
||||
Assert.False(_animation.IsAnimating);
|
||||
Assert.False(_animationController.IsAnimating);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_ShouldAdvanceFrame()
|
||||
{
|
||||
_animation.Play();
|
||||
_animation.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1.1)));
|
||||
_animationController.Play();
|
||||
_animationController.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1.1)));
|
||||
|
||||
Assert.Equal(1, _animation.CurrentFrame);
|
||||
Assert.Equal(1, _animationController.CurrentFrame);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -139,7 +140,7 @@ public class AnimationTests
|
||||
bool animationLoopTriggered = false;
|
||||
bool animationCompletedTriggered = false;
|
||||
|
||||
_animation.OnAnimationEvent += (anim, trigger) =>
|
||||
_animationController.OnAnimationEvent += (anim, trigger) =>
|
||||
{
|
||||
switch (trigger)
|
||||
{
|
||||
@@ -158,63 +159,63 @@ public class AnimationTests
|
||||
}
|
||||
};
|
||||
|
||||
_animation.Play();
|
||||
_animation.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1.1)));
|
||||
_animationController.Play();
|
||||
_animationController.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1.1)));
|
||||
|
||||
Assert.True(frameBeginTriggered);
|
||||
Assert.True(frameEndTriggered);
|
||||
|
||||
_animation.IsLooping = true;
|
||||
_animation.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1.1)));
|
||||
_animationController.IsLooping = true;
|
||||
_animationController.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1.1)));
|
||||
Assert.True(animationLoopTriggered);
|
||||
|
||||
_animation.IsLooping = false;
|
||||
_animation.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(2)));
|
||||
_animationController.IsLooping = false;
|
||||
_animationController.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(2)));
|
||||
Assert.True(animationCompletedTriggered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_ShouldLoopAnimation()
|
||||
{
|
||||
_animation.IsLooping = true;
|
||||
_animation.Play();
|
||||
_animation.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(2.1)));
|
||||
_animationController.IsLooping = true;
|
||||
_animationController.Play();
|
||||
_animationController.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(2.1)));
|
||||
|
||||
Assert.Equal(0, _animation.CurrentFrame);
|
||||
Assert.Equal(0, _animationController.CurrentFrame);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reset_ShouldResetAnimation()
|
||||
{
|
||||
_animation.Play();
|
||||
_animation.Reset();
|
||||
_animationController.Play();
|
||||
_animationController.Reset();
|
||||
|
||||
Assert.False(_animation.IsAnimating);
|
||||
Assert.True(_animation.IsPaused);
|
||||
Assert.Equal(0, _animation.CurrentFrame);
|
||||
Assert.False(_animationController.IsAnimating);
|
||||
Assert.True(_animationController.IsPaused);
|
||||
Assert.Equal(0, _animationController.CurrentFrame);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFrame_ShouldChangeCurrentFrame()
|
||||
{
|
||||
_animation.SetFrame(1);
|
||||
_animationController.SetFrame(1);
|
||||
|
||||
Assert.Equal(1, _animation.CurrentFrame);
|
||||
Assert.Equal(_definition.Frames[1].Duration, _animation.CurrentFrameTimeRemaining);
|
||||
Assert.Equal(1, _animationController.CurrentFrame);
|
||||
Assert.Equal(_animation.Frames[1].Duration, _animationController.CurrentFrameTimeRemaining);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFrame_ShouldThrowException_ForInvalidFrame()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _animation.SetFrame(-1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _animation.SetFrame(10));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _animationController.SetFrame(-1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _animationController.SetFrame(10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispose_ShouldDisposeAnimation()
|
||||
{
|
||||
_animation.Dispose();
|
||||
_animationController.Dispose();
|
||||
|
||||
Assert.True(_animation.IsDisposed);
|
||||
Assert.True(_animationController.IsDisposed);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user