diff --git a/.editorconfig b/.editorconfig index 9f90c20d..73797762 100644 --- a/.editorconfig +++ b/.editorconfig @@ -43,7 +43,7 @@ tab_width = 4 ################################################################################ ### New line preferences ################################################################################ -end_of_line = crlf +end_of_line = lf ###############################################################################3 ### diff --git a/source/MonoGame.Extended.Content.Pipeline/TextureAtlases/TexturePackerWriter.cs b/source/MonoGame.Extended.Content.Pipeline/TextureAtlases/TexturePackerWriter.cs index 735d817b..35833d8f 100644 --- a/source/MonoGame.Extended.Content.Pipeline/TextureAtlases/TexturePackerWriter.cs +++ b/source/MonoGame.Extended.Content.Pipeline/TextureAtlases/TexturePackerWriter.cs @@ -25,11 +25,11 @@ namespace MonoGame.Extended.Content.Pipeline.TextureAtlases var regionName = Path.ChangeExtension(region.Filename, null); Debug.Assert(regionName != null, "regionName != null"); - writer.Write(regionName); writer.Write(region.Frame.X); writer.Write(region.Frame.Y); writer.Write(region.Frame.Width); writer.Write(region.Frame.Height); + writer.Write(regionName); } } diff --git a/source/MonoGame.Extended.Input/InputListeners/InputListenerComponent.cs b/source/MonoGame.Extended.Input/InputListeners/InputListenerComponent.cs index 302f6b5d..7a76b6a2 100644 --- a/source/MonoGame.Extended.Input/InputListeners/InputListenerComponent.cs +++ b/source/MonoGame.Extended.Input/InputListeners/InputListenerComponent.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework; namespace MonoGame.Extended.Input.InputListeners { - public class InputListenerComponent : GameComponent, IUpdate + public class InputListenerComponent : GameComponent, IUpdateable { private readonly List _listeners; @@ -34,4 +34,4 @@ namespace MonoGame.Extended.Input.InputListeners GamePadListener.CheckConnections(); } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/AnimationComponent.cs b/source/MonoGame.Extended/AnimationComponent.cs index 28a0e576..0573334c 100644 --- a/source/MonoGame.Extended/AnimationComponent.cs +++ b/source/MonoGame.Extended/AnimationComponent.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using Microsoft.Xna.Framework; -using MonoGame.Extended.Sprites; namespace MonoGame.Extended.Animations { @@ -27,4 +26,4 @@ namespace MonoGame.Extended.Animations Animations.RemoveAll(a => a.IsDisposed); } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/Animations/Animation.cs b/source/MonoGame.Extended/Animations/Animation.cs new file mode 100644 index 00000000..5d647391 --- /dev/null +++ b/source/MonoGame.Extended/Animations/Animation.cs @@ -0,0 +1,279 @@ +// 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; + +/// +/// Represents an animation with various control features such as play, pause, stop, looping, reversing, and +/// ping-pong effects. +/// +public class Animation : IAnimation +{ + private readonly IAnimationDefinition _definition; + private int _direction; + + /// + /// Gets a value that indicates whether this animation has been disposed of. + /// + public bool IsDisposed { get; protected set; } + + /// + public bool IsPaused { get; private set; } + + /// + public bool IsAnimating { get; private set; } + + /// + public bool IsLooping { get; set; } + + /// + public bool IsReversed + { + get => _direction == -1; + set => _direction = value ? -1 : 1; + } + + /// + public bool IsPingPong { get; set; } + + /// + public double Speed { get; set; } + + /// + public event Action OnAnimationEvent; + + /// + public TimeSpan CurrentFrameTimeRemaining { get; private set; } + + /// + public int CurrentFrame { get; private set; } + + /// + public int FrameCount => _definition.FrameCount; + + /// + /// Initializes a new instance of the class with the specified definition. + /// + /// The definition of the animation. + public Animation(IAnimationDefinition definition) + { + _definition = definition; + + // Set initial properties but keep original values in the definition cached for Reset() + IsLooping = definition.IsLooping; + IsReversed = definition.IsReversed; + IsPingPong = definition.IsPingPong; + Speed = 1.0f; + } + + /// + public bool Pause() => Pause(false); + + /// + public bool Pause(bool resetFrameDuration) + { + // We can only pause something that is animating and not already paused. This is to prevent situations + // that could accidentally reset frame duration if it was set to true. + if (!IsAnimating || IsPaused) + { + return false; + } + + IsPaused = true; + + if (resetFrameDuration) + { + CurrentFrameTimeRemaining = _definition.Frames[CurrentFrame].Duration; + } + + return true; + } + + /// + public bool Play() => Play(0); + + /// + public bool Play(int startingFrame) + { + 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)}"); + } + + // Cannot play something that is already playing + if (IsAnimating) + { + return false; + } + + IsAnimating = true; + CurrentFrame = startingFrame; + CurrentFrameTimeRemaining = _definition.Frames[CurrentFrame].Duration; + return true; + } + + /// + public void Reset() + { + IsReversed = _definition.IsReversed; + IsPingPong = _definition.IsPingPong; + IsLooping = _definition.IsLooping; + IsAnimating = false; + IsPaused = true; + Speed = 1.0d; + CurrentFrame = IsReversed ? _definition.FrameCount - 1 : 0; + CurrentFrameTimeRemaining = _definition.Frames[CurrentFrame].Duration; + } + + /// + public void SetFrame(int index) + { + 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)}"); + } + + CurrentFrame = index; + CurrentFrameTimeRemaining = _definition.Frames[CurrentFrame].Duration; + OnAnimationEvent?.Invoke(this, AnimationEventTrigger.FrameBegin); + } + + /// + public bool Stop() => Stop(AnimationEventTrigger.AnimationStopped); + + private bool Stop(AnimationEventTrigger trigger) + { + // We can't stop something that's not animating. This is to prevent accidentally invoking OnAnimationEnd + if (!IsAnimating) + { + return false; + } + + IsAnimating = false; + IsPaused = true; + OnAnimationEvent?.Invoke(this, trigger); + return true; + } + + /// + public bool Unpause() => Unpause(false); + + /// + public bool Unpause(bool advanceToNextFrame) + { + // We can't unpause something that's not animating and also isn't paused. This is to prevent improper usage + // that could accidentally advance to the next frame if it was set to true. + if (!IsAnimating || !IsPaused) + { + return false; + } + + IsPaused = false; + + if (advanceToNextFrame) + { + _ = AdvanceFrame(); + } + + return true; + } + + /// + public void Update(GameTime gameTime) + { + TimeSpan elapsedTime = gameTime.ElapsedGameTime; + TimeSpan remainingTime = TimeSpan.Zero; + + if (!IsAnimating || IsPaused) + { + return; + } + + CurrentFrameTimeRemaining -= elapsedTime * Speed; + + while (CurrentFrameTimeRemaining <= TimeSpan.Zero) + { + remainingTime += -CurrentFrameTimeRemaining; + + // End the current frame + OnAnimationEvent?.Invoke(this, AnimationEventTrigger.FrameEnd); + + if (!AdvanceFrame()) + { + break; + } + + CurrentFrameTimeRemaining -= remainingTime; + remainingTime = TimeSpan.Zero; + } + } + + private bool AdvanceFrame() + { + // Increment the current frame + CurrentFrame += _direction; + + // Ensure frame is in bounds + if (CurrentFrame < 0 || CurrentFrame >= _definition.FrameCount) + { + // Is this a looping animation? + if (IsLooping) + { + // Is this a standard loop or is it a ping pong? + if (IsPingPong) + { + _direction = -_direction; + CurrentFrame += _direction * 2; + } + else + { + CurrentFrame = IsReversed ? _definition.FrameCount - 1 : 0; + } + + // We looped + OnAnimationEvent?.Invoke(this, AnimationEventTrigger.AnimationLoop); + } + else + { + // No looping and we've reached the end, stop the animation + CurrentFrame -= _direction; + Stop(AnimationEventTrigger.AnimationCompleted); + return false; + } + } + + CurrentFrameTimeRemaining = _definition.Frames[CurrentFrame].Duration; + OnAnimationEvent?.Invoke(this, AnimationEventTrigger.FrameBegin); + return true; + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// + /// + /// When overriding this method, check if is or + /// . Only dispose of other managed resources when it is . + /// + /// + /// + /// Indicates whether this was called from or the finalizer. + protected virtual void Dispose(bool disposing) + { + if (IsDisposed) + { + return; + } + + IsDisposed = true; + } +} diff --git a/source/MonoGame.Extended/Animations/AnimationEvent.cs b/source/MonoGame.Extended/Animations/AnimationEvent.cs new file mode 100644 index 00000000..1c932ed8 --- /dev/null +++ b/source/MonoGame.Extended/Animations/AnimationEvent.cs @@ -0,0 +1,25 @@ +// 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; + +/// +/// Represents an event that occurs during an animation. +/// +public class AnimationEvent : EventArgs +{ + /// + /// Gets the animation associated with the event. + /// + public IAnimation Animation { get; } + + /// + /// Gets the trigger that caused the event. + /// + public AnimationEventTrigger Trigger { get; } + + internal AnimationEvent(IAnimation animation, AnimationEventTrigger trigger) => (Animation, Trigger) = (animation, trigger); +} diff --git a/source/MonoGame.Extended/Animations/AnimationEventTrigger.cs b/source/MonoGame.Extended/Animations/AnimationEventTrigger.cs new file mode 100644 index 00000000..7e50c383 --- /dev/null +++ b/source/MonoGame.Extended/Animations/AnimationEventTrigger.cs @@ -0,0 +1,42 @@ +// Copyright (c) Craftwork Games. All rights reserved. +// Licensed under the MIT license. +// See LICENSE file in the project root for full license information. + +namespace MonoGame.Extended.Animations; + +/// +/// Specifies the trigger for an animation event. +/// +public enum AnimationEventTrigger +{ + /// + /// Triggered at the beginning of a frame. + /// + FrameBegin, + + /// + /// Triggered at the end of a frame. + /// + FrameEnd, + + /// + /// Triggered when a frame is skipped. + /// + FrameSkipped, + + /// + /// Triggered when the animation loops. + /// + AnimationLoop, + + /// + /// Triggered when the animation completes. + /// + AnimationCompleted, + + /// + /// Triggered when the animation stops. + /// + AnimationStopped, +} + diff --git a/source/MonoGame.Extended/Animations/IAnimation.cs b/source/MonoGame.Extended/Animations/IAnimation.cs new file mode 100644 index 00000000..0224914c --- /dev/null +++ b/source/MonoGame.Extended/Animations/IAnimation.cs @@ -0,0 +1,149 @@ +// 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; + +/// +/// Defines the interface for an animation with various control features such as play, pause, stop, looping, reversing, and ping-pong effects. +/// +public interface IAnimation : IDisposable +{ + /// + /// Gets a value indicating whether the animation is paused. + /// + bool IsPaused { get; } + + /// + /// Gets a value indicating whether the animation is currently animating. + /// + bool IsAnimating { get; } + + /// + /// Gets or sets a value indicating whether the animation should loop. + /// + bool IsLooping { get; set; } + + /// + /// Gets or sets a value indicating whether the animation is reversed. + /// + bool IsReversed { get; set; } + + /// + /// Gets or sets a value indicating whether the animation should ping-pong (reverse direction at the ends). + /// + bool IsPingPong { get; set; } + + /// + /// Gets or sets the speed of the animation. + /// + /// The speed cannot be less than zero. + double Speed { get; set; } + + /// + /// Gets or sets the action to perform when an animation event is triggered. + /// + event Action OnAnimationEvent; + + /// + /// Gets the time remaining for the current frame. + /// + TimeSpan CurrentFrameTimeRemaining { get; } + + /// + /// Gets the index of the current frame of the animation. + /// + int CurrentFrame { get; } + + /// + /// Gets the total number of frames in the animation. + /// + int FrameCount { get; } + + /// + /// Sets the animation to a specified frame. + /// + /// The index of the frame to set. + /// + /// Thrown when the parameter is less than zero or greater than or equal to the total + /// number of frames. + /// + void SetFrame(int index); + + /// + /// Plays the animation from the beginning. + /// + /// + /// if the animation was successfully started; otherwise, . + /// + bool Play(); + + /// + /// Plays the animation from a specified starting frame. + /// + /// The frame to start the animation from. + /// + /// if the animation was successfully started; otherwise, . + /// + /// + /// Thrown when the parameter is less than zero or greater than or equal to the + /// total number of frames. + /// + bool Play(int startingFrame); + + /// + /// Pauses the animation. + /// + /// + /// if the animation was successfully paused; otherwise, . + /// + bool Pause(); + + /// + /// Pauses the animation. + /// + /// If set to , resets the frame duration. + /// + /// if the animation was successfully paused; otherwise, . + /// + bool Pause(bool resetFrameDuration); + + /// + /// Unpauses the animation. + /// + /// + /// if the animation was successfully unpaused; otherwise, . + /// + bool Unpause(); + + /// + /// Unpauses the animation. + /// + /// If set to , advances to the next frame. + /// + /// if the animation was successfully unpaused; otherwise, . + /// + bool Unpause(bool advanceToNextFrame); + + /// + /// Updates the animation. + /// + /// A snapshot of the timing values for the current update cycle. + void Update(GameTime gameTime); + + /// + /// Stops the animation. + /// + /// + /// if the animation was successfully stopped; otherwise, . + /// + bool Stop(); + + /// + /// Resets the animation to its initial state. + /// + void Reset(); +} diff --git a/source/MonoGame.Extended/Animations/IAnimationDefinition.cs b/source/MonoGame.Extended/Animations/IAnimationDefinition.cs new file mode 100644 index 00000000..fa48ae5b --- /dev/null +++ b/source/MonoGame.Extended/Animations/IAnimationDefinition.cs @@ -0,0 +1,44 @@ +// 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; + +/// +/// Defines the interface for an animation definition, specifying properties of the animation such as frames, looping, +/// reversing, and ping-pong effects. +/// +public interface IAnimationDefinition +{ + /// + /// Gets the name of the animation. + /// + string Name { get; } + + /// + /// Gets the read-only collection of frames in the animation. + /// + ReadOnlySpan Frames { get; } + + /// + /// Gets the total number of frames in the animation. + /// + int FrameCount { get; } + + /// + /// Gets a value indicating whether the animation should loop. + /// + bool IsLooping { get; } + + /// + /// Gets a value indicating whether the animation is reversed. + /// + bool IsReversed { get; } + + /// + /// Gets a value indicating whether the animation should ping-pong (reverse direction at the ends). + /// + bool IsPingPong { get; } +} diff --git a/source/MonoGame.Extended/Animations/IAnimationFrame.cs b/source/MonoGame.Extended/Animations/IAnimationFrame.cs new file mode 100644 index 00000000..4bab2805 --- /dev/null +++ b/source/MonoGame.Extended/Animations/IAnimationFrame.cs @@ -0,0 +1,23 @@ +// 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; + +/// +/// Defines the interface for an animation frame, specifying the frame index and its duration. +/// +public interface IAnimationFrame +{ + /// + /// Gets the frame index represented by this animation frame. + /// + int FrameIndex { get; } + + /// + /// Gets the total duration this frame should be displayed during an animation. + /// + TimeSpan Duration { get; } +} diff --git a/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasJsonContentTypeReader.cs b/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasJsonContentTypeReader.cs index 50ea6d60..e7058964 100644 --- a/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasJsonContentTypeReader.cs +++ b/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasJsonContentTypeReader.cs @@ -26,11 +26,11 @@ namespace MonoGame.Extended.Content.ContentReaders for (var i = 0; i < regionCount; i++) { atlas.CreateRegion( - ContentReaderExtensions.RemoveExtension(texturePackerFile.Regions[i].Filename), texturePackerFile.Regions[i].Frame.X, texturePackerFile.Regions[i].Frame.Y, texturePackerFile.Regions[i].Frame.Width, - texturePackerFile.Regions[i].Frame.Height); + texturePackerFile.Regions[i].Frame.Height, + ContentReaderExtensions.RemoveExtension(texturePackerFile.Regions[i].Filename)); } return atlas; diff --git a/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasReader.cs b/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasReader.cs index b7603d90..6ba935c2 100644 --- a/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasReader.cs +++ b/source/MonoGame.Extended/Content/ContentReaders/Texture2DAtlasReader.cs @@ -17,11 +17,11 @@ namespace MonoGame.Extended.Content.ContentReaders for (var i = 0; i < regionCount; i++) { atlas.CreateRegion( - reader.ReadString(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), - reader.ReadInt32()); + reader.ReadInt32(), + reader.ReadString()); } return atlas; diff --git a/source/MonoGame.Extended/FramesPerSecondCounter.cs b/source/MonoGame.Extended/FramesPerSecondCounter.cs index 432f1ac6..8ae77569 100644 --- a/source/MonoGame.Extended/FramesPerSecondCounter.cs +++ b/source/MonoGame.Extended/FramesPerSecondCounter.cs @@ -3,18 +3,58 @@ using Microsoft.Xna.Framework; namespace MonoGame.Extended { - public class FramesPerSecondCounter : IUpdate + public class FramesPerSecondCounter : IUpdateable { + private bool _enabled; + private int _updateOrder; + private static readonly TimeSpan _oneSecondTimeSpan = new TimeSpan(0, 0, 1); private int _framesCounter; private TimeSpan _timer = _oneSecondTimeSpan; + /// + public bool Enabled + { + get => _enabled; + set + { + if (_enabled == value) + { + return; + } + _enabled = value; + EnabledChanged?.Invoke(this, EventArgs.Empty); + } + } + + /// + public int UpdateOrder + { + get => _updateOrder; + set + { + if (_updateOrder == value) + { + return; + } + _updateOrder= value; + EnabledChanged?.Invoke(this, EventArgs.Empty); + } + } + + /// + public event EventHandler EnabledChanged; + + /// + public event EventHandler UpdateOrderChanged; + public FramesPerSecondCounter() { } public int FramesPerSecond { get; private set; } + public void Update(GameTime gameTime) { _timer += gameTime.ElapsedGameTime; @@ -31,4 +71,4 @@ namespace MonoGame.Extended _framesCounter++; } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/Graphics/AnimatedSprite.cs b/source/MonoGame.Extended/Graphics/AnimatedSprite.cs new file mode 100644 index 00000000..15096f1f --- /dev/null +++ b/source/MonoGame.Extended/Graphics/AnimatedSprite.cs @@ -0,0 +1,41 @@ +// Copyright (c) Craftwork Games. All rights reserved. +// Licensed under the MIT license. +// See LICENSE file in the project root for full license information. + +using Microsoft.Xna.Framework; +using MonoGame.Extended.Animations; + +namespace MonoGame.Extended.Graphics; + +/// +/// Represents an animated sprite that can play, pause, and update animations. +/// +public class AnimatedSprite : Sprite +{ + private readonly Texture2DRegion[] _regions; + + /// + /// Gets the animation used by this animated sprite. + /// + public IAnimation Animation { get; } + + internal AnimatedSprite(SpriteSheetAnimationDefinition definition, Texture2DRegion[] regions) + : base(regions[0]) + { + Animation = new Animation(definition); + _regions = regions; + } + + /// + public void Update(GameTime gameTime) + { + int index = Animation.CurrentFrame; + Animation.Update(gameTime); + + // If the current frame changed during the update, change the texture region + if (index != Animation.CurrentFrame) + { + TextureRegion = _regions[Animation.CurrentFrame]; + } + } +} diff --git a/source/MonoGame.Extended/Graphics/Sprite.cs b/source/MonoGame.Extended/Graphics/Sprite.cs new file mode 100644 index 00000000..096f8951 --- /dev/null +++ b/source/MonoGame.Extended/Graphics/Sprite.cs @@ -0,0 +1,229 @@ +// 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 System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MonoGame.Extended.Graphics; + +/// +/// Represents a drawable 2D texture region with additional properties for rendering, such as position, scale, +/// rotation, and color. +/// +public class Sprite : IColorable +{ + private Texture2DRegion _textureRegion; + + /// + /// Gets or sets a value indicating whether this sprite is visible. + /// + public bool IsVisible { get; set; } + + /// + /// Gets or sets the color mask used when rendering this sprite. + /// + /// + /// The color mask is applied to the sprite's texture by multiplying each pixel's color value with the specified + /// color. For example, setting the color to `Color.Red` will make the sprite appear as a red tint over its + /// texture. + /// + public Color Color { get; set; } + + /// + /// Gets or sets the alpha transparency value used when rendering this sprite. + /// + /// + /// The alpha value should be between 0 (fully transparent) and 1 (fully opaque). + /// + public float Alpha { get; set; } + + /// + /// Gets or sets the layer depth used when rendering this sprite. + /// + /// + /// Sprites with higher depth values are rendered on top of those with lower depth values. + /// + public float Depth { get; set; } + + /// + /// Gets or sets the sprite effects to apply when rendering this sprite. + /// + /// + /// This specifies the desired horizontal and/or vertical flip effect of the sprite. + /// + public SpriteEffects Effect { get; set; } + + /// + /// Gets or Sets an object that contains user defined data about this sprite. + /// + public object Tag { get; set; } + + /// + /// Gets or sets the origin of this sprite. + /// + /// + /// The origin is relative to the bounds of the texture region and represents the point around which the sprite is + /// rotated and scaled. + /// + public Vector2 Origin { get; set; } + + /// + /// Gets or sets the normalized origin of this sprite relative to its texture region. + /// + /// + /// The normalized origin represents the origin as a fraction of the texture region's UV coordinates, + /// where (0, 0) is the top-left corner and (1, 1) is the bottom-right corner. + /// + public Vector2 OriginNormalized + { + get + { + float normalizedX = (Origin.X - TextureRegion.LeftUV) / (TextureRegion.RightUV - TextureRegion.LeftUV); + float normalizedY = (Origin.Y - TextureRegion.TopUV) / (TextureRegion.BottomUV - TextureRegion.TopUV); + return new Vector2(normalizedX, normalizedY); + } + set + { + float actualX = value.X * (TextureRegion.RightUV - TextureRegion.LeftUV) + TextureRegion.LeftUV; + float actualY = value.Y * (TextureRegion.BottomUV - TextureRegion.TopUV) + TextureRegion.TopUV; + Origin = new Vector2(actualX, actualY); + } + } + + /// + /// Gets or sets the source texture region of this sprite. + /// + /// Thrown when setting to a null texture region. + /// + /// Thrown if the source texture of the assigned has been disposed of when setting. + /// + public Texture2DRegion TextureRegion + { + get => _textureRegion; + set + { + ArgumentNullException.ThrowIfNull(value); + if (value.Texture.IsDisposed) + { + throw new ObjectDisposedException(nameof(value), $"The source {nameof(Texture2D)} of the {nameof(TextureRegion)} was disposed prior to setting this property."); + } + _textureRegion = value; + } + } + + /// + /// Initializes a new instance of the class with the specified texture. + /// + /// The source texture of the sprite. + /// + /// Thrown if the parameter is . + /// + /// + /// Thrown if the parameter was disposed of prior. + /// + public Sprite(Texture2D texture) + : this(new Texture2DRegion(texture)) + { + } + + + /// + /// Initializes a new instance of the class with the specified texture region. + /// The sprite represents a renderable 2D image defined by the given texture region. + /// + /// The source texture region of the sprite. + /// + /// Thrown if the parameter is . + /// + /// + /// Thrown if the source texture of the parameter has been disposed of. + /// + public Sprite(Texture2DRegion textureRegion) + { + ArgumentNullException.ThrowIfNull(textureRegion); + if (textureRegion.Texture.IsDisposed) + { + throw new ObjectDisposedException(nameof(textureRegion), $"The source {nameof(Texture2D)} of the {nameof(textureRegion)} was disposed prior."); + } + + _textureRegion = textureRegion; + + Alpha = 1.0f; + Color = Color.White; + IsVisible = true; + Effect = SpriteEffects.None; + OriginNormalized = new Vector2(0.5f, 0.5f); + Depth = 0.0f; + } + + /// + /// Gets the bounding rectangle of the sprite in world/screen coordinates. + /// + /// The transformation of the sprite. + /// The bounding rectangle of the sprite in world/screen coordinates. + public RectangleF GetBoundingRectangle(Transform2 transform) + { + return GetBoundingRectangle(transform.Position, transform.Rotation, transform.Scale); + } + + /// + /// Gets the bounding rectangle of the sprite in world/screen coordinates. + /// + /// The xy-coordinate position of the sprite in world/screen coordinates. + /// The rotation, in radians, of the sprite. + /// The scale of the sprite. + /// The bounding rectangle of the sprite in world/screen coordinates. + public RectangleF GetBoundingRectangle(Vector2 position, float rotation, Vector2 scale) + { + var corners = GetCorners(position, rotation, scale); + var min = new Vector2(corners.Min(i => i.X), corners.Min(i => i.Y)); + var max = new Vector2(corners.Max(i => i.X), corners.Max(i => i.Y)); + return new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y); + } + + /// + /// Gets the corner points of the sprite in world/screen coordinates. + /// + /// The xy-coordinate position of the sprite in world/screen coordinates. + /// The rotation, in radians, of the sprite. + /// The scale of the sprite. + /// The corner points of the sprite in world/screen coordinates. + public Vector2[] GetCorners(Vector2 position, float rotation, Vector2 scale) + { + var min = -Origin; + var max = min + new Vector2(TextureRegion.Width, TextureRegion.Height); + var offset = position; + + if (scale != Vector2.One) + { + min *= scale; + max = max * scale; + } + + var corners = new Vector2[4]; + corners[0] = min; + corners[1] = new Vector2(max.X, min.Y); + corners[2] = max; + corners[3] = new Vector2(min.X, max.Y); + + if (rotation != 0) + { + var matrix = Matrix.CreateRotationZ(rotation); + + for (var i = 0; i < 4; i++) + { + corners[i] = Vector2.Transform(corners[i], matrix); + } + } + + for (var i = 0; i < 4; i++) + { + corners[i] += offset; + } + + return corners; + } +} diff --git a/source/MonoGame.Extended/Graphics/SpriteBatch.Extensions.cs b/source/MonoGame.Extended/Graphics/SpriteBatch.Extensions.cs index 487a2924..63b7e225 100644 --- a/source/MonoGame.Extended/Graphics/SpriteBatch.Extensions.cs +++ b/source/MonoGame.Extended/Graphics/SpriteBatch.Extensions.cs @@ -2,20 +2,29 @@ // Licensed under the MIT license. // See LICENSE file in the project root for full license information. - using System; -using System.Reflection; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace MonoGame.Extended.Graphics; +/// +/// Provides extension methods for the class. +/// public static class SpriteBatchExtensions { #region ----------------------------NinePatch----------------------------- private static readonly Rectangle[] _patchCache = new Rectangle[9]; private static Rectangle _rect = default; + /// + /// Draws a nine-patch region to the sprite batch. + /// + /// The sprite batch. + /// The nine-patch region. + /// The destination rectangle. + /// The color to tint the nine-patch region. + /// An optional clipping rectangle. public static void Draw(this SpriteBatch spriteBatch, NinePatch ninePatchRegion, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle = null) { CreateDestinationPatches(ninePatchRegion, destinationRectangle); @@ -44,8 +53,75 @@ public static class SpriteBatchExtensions #endregion -------------------------NinePatch----------------------------- + #region ----------------------------Sprite----------------------------- + /// + /// Draws a sprite to the sprite batch. + /// + /// The sprite to draw. + /// The sprite batch. + /// The position to draw the sprite. + /// The rotation of the sprite. + /// The scale of the sprite. + public static void Draw(this Sprite sprite, SpriteBatch spriteBatch, Vector2 position, float rotation, Vector2 scale) + { + Draw(spriteBatch, sprite, position, rotation, scale); + } + + /// + /// Draws a sprite to the sprite batch with a transform. + /// + /// The sprite batch. + /// The sprite to draw. + /// The transform to apply to the sprite. + public static void Draw(this SpriteBatch spriteBatch, Sprite sprite, Transform2 transform) + { + Draw(spriteBatch, sprite, transform.Position, transform.Rotation, transform.Scale); + } + + /// + /// Draws a sprite to the sprite batch. + /// + /// The sprite batch. + /// The sprite to draw. + /// The position to draw the sprite. + /// The rotation of the sprite. + public static void Draw(this SpriteBatch spriteBatch, Sprite sprite, Vector2 position, float rotation = 0) + { + Draw(spriteBatch, sprite, position, rotation, Vector2.One); + } + + /// + /// Draws a sprite to the sprite batch. + /// + /// The sprite batch. + /// The sprite to draw. + /// The position to draw the sprite. + /// The rotation of the sprite. + /// The scale of the sprite. + public static void Draw(this SpriteBatch spriteBatch, Sprite sprite, Vector2 position, float rotation, Vector2 scale) + { + if (sprite == null) throw new ArgumentNullException(nameof(sprite)); + + if (sprite.IsVisible) + { + var texture = sprite.TextureRegion.Texture; + var sourceRectangle = sprite.TextureRegion.Bounds; + spriteBatch.Draw(texture, position, sourceRectangle, sprite.Color * sprite.Alpha, rotation, sprite.Origin, scale, sprite.Effect, sprite.Depth); + } + } + #endregion -------------------------Sprite----------------------------- + #region ----------------------------Texture2D----------------------------- + /// + /// Draws a texture to the sprite batch with optional clipping. + /// + /// The sprite batch. + /// The texture to draw. + /// The source rectangle. + /// The destination rectangle. + /// The color to tint the texture. + /// An optional clipping rectangle. public static void Draw(this SpriteBatch spriteBatch, Texture2D texture, Rectangle sourceRectangle, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle) { if (!ClipRectangles(ref sourceRectangle, ref destinationRectangle, clippingRectangle)) @@ -61,11 +137,32 @@ public static class SpriteBatchExtensions #region ----------------------------TextureRegion----------------------------- + /// + /// Draws a texture region to the sprite batch. + /// + /// The sprite batch. + /// The texture region to draw. + /// The position to draw the texture region. + /// The color to tint the texture region. + /// An optional clipping rectangle. public static void Draw(this SpriteBatch spriteBatch, Texture2DRegion textureRegion, Vector2 position, Color color, Rectangle? clippingRectangle = null) { Draw(spriteBatch, textureRegion, position, color, 0, Vector2.Zero, Vector2.One, SpriteEffects.None, 0, clippingRectangle); } + /// + /// Draws a texture region to the sprite batch with specified parameters. + /// + /// The sprite batch. + /// The texture region to draw. + /// The position to draw the texture region. + /// The color to tint the texture region. + /// The rotation of the texture region. + /// The origin of the texture region. + /// The scale of the texture region. + /// The sprite effects to apply. + /// The layer depth. + /// An optional clipping rectangle. public static void Draw(this SpriteBatch spriteBatch, Texture2DRegion textureRegion, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth, Rectangle? clippingRectangle = null) { @@ -92,13 +189,19 @@ public static class SpriteBatchExtensions spriteBatch.Draw(textureRegion.Texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth); } + /// + /// Draws a texture region to the sprite batch. + /// + /// The sprite batch. + /// The texture region to draw. + /// The destination rectangle. + /// The color to tint the texture region. + /// An optional clipping rectangle. public static void Draw(this SpriteBatch spriteBatch, Texture2DRegion textureRegion, Rectangle destinationRectangle, Color color, Rectangle? clippingRectangle = null) { Draw(spriteBatch, textureRegion.Texture, textureRegion.Bounds, destinationRectangle, color, clippingRectangle); } - - #endregion -------------------------TextureRegion----------------------------- #region ----------------------------Utilities----------------------------- diff --git a/source/MonoGame.Extended/Graphics/SpriteSheet.cs b/source/MonoGame.Extended/Graphics/SpriteSheet.cs new file mode 100644 index 00000000..1d65a292 --- /dev/null +++ b/source/MonoGame.Extended/Graphics/SpriteSheet.cs @@ -0,0 +1,92 @@ +// 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 System.Collections.Generic; + +namespace MonoGame.Extended.Graphics; + +/// +/// Represents a sprite sheet containing textures and animations. +/// +public class SpriteSheet +{ + private readonly Dictionary _animations = new Dictionary(); + + /// + /// Gets the number of animations defined in the sprite sheet. + /// + public int AnimationCount => _animations.Count; + + /// + /// Gets the name of the sprite sheet. + /// + public string Name { get; } + + /// + /// Gets the texture atlas associated with the sprite sheet. + /// + public Texture2DAtlas TextureAtlas { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the sprite sheet. + /// The texture atlas to use for the sprite sheet. + /// Thrown when the is null. + public SpriteSheet(string name, Texture2DAtlas textureAtlas) + { + ArgumentNullException.ThrowIfNull(textureAtlas); + + TextureAtlas = textureAtlas; + Name = name; + } + + /// + /// Creates a sprite from the specified region index. + /// + /// The index of the region in the texture atlas. + /// A new instance. + public Sprite CreateSprite(int regionIndex) => TextureAtlas.CreateSprite(regionIndex); + + /// + /// Creates a sprite from the specified region name. + /// + /// The name of the region in the texture atlas. + /// A new instance. + public Sprite CreateSprite(string regionName) => TextureAtlas.CreateSprite(regionName); + + /// + /// Creates an animated sprite from the specified animation name. + /// + /// The name of the animation. + /// A new instance. + public AnimatedSprite CreateAnimatedSprite(string animationName) + { + SpriteSheetAnimationDefinition animationDefinition = _animations[animationName]; + Texture2DRegion[] regions = TextureAtlas.GetRegions(animationDefinition.Frames); + return new AnimatedSprite(animationDefinition, regions); + } + + /// + /// Defines a new animation for the sprite sheet. + /// + /// The name of the animation. + /// The action to build the animation definition. + public void DefineAnimation(string name, Action buildAction) + { + SpriteSheetAnimationDefinitionBuilder builder = new SpriteSheetAnimationDefinitionBuilder(name, this); + buildAction(builder); + SpriteSheetAnimationDefinition definition = builder.Build(); + _animations.Add(name, definition); + } + + /// + /// Removes the animation definition with the specified name. + /// + /// The name of the animation to remove. + /// true if the animation was successfully removed; otherwise, false. + public bool RemoveAnimationDefinition(string name) => _animations.Remove(name); +} + diff --git a/source/MonoGame.Extended/Graphics/SpriteSheetAnimationDefinition.cs b/source/MonoGame.Extended/Graphics/SpriteSheetAnimationDefinition.cs new file mode 100644 index 00000000..2375401b --- /dev/null +++ b/source/MonoGame.Extended/Graphics/SpriteSheetAnimationDefinition.cs @@ -0,0 +1,29 @@ +// 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 MonoGame.Extended.Animations; + +namespace MonoGame.Extended.Graphics; + +internal class SpriteSheetAnimationDefinition : IAnimationDefinition +{ + private readonly SpriteSheetAnimationFrame[] _frames; + + public string Name { get; } + public ReadOnlySpan Frames => _frames; + public int FrameCount => _frames.Length; + public bool IsLooping { get; } + public bool IsReversed { get; } + public bool IsPingPong { get; } + + internal SpriteSheetAnimationDefinition(string name, SpriteSheetAnimationFrame[] frames, bool isLooping, bool isReversed, bool isPingPong) + { + Name = name; + IsLooping = isLooping; + IsReversed = isReversed; + IsPingPong = isPingPong; + _frames = frames; + } +} diff --git a/source/MonoGame.Extended/Graphics/SpriteSheetAnimationDefinitionBuilder.cs b/source/MonoGame.Extended/Graphics/SpriteSheetAnimationDefinitionBuilder.cs new file mode 100644 index 00000000..53ce1273 --- /dev/null +++ b/source/MonoGame.Extended/Graphics/SpriteSheetAnimationDefinitionBuilder.cs @@ -0,0 +1,88 @@ +// 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 System.Collections.Generic; + +namespace MonoGame.Extended.Graphics; + +/// +/// A builder class for creating instances. +/// +public sealed class SpriteSheetAnimationDefinitionBuilder +{ + private readonly string _name; + private readonly SpriteSheet _spriteSheet; + private readonly List _frames = new List(); + private bool _isLooping; + private bool _isReversed; + private bool _isPingPong; + + internal SpriteSheetAnimationDefinitionBuilder(string name, SpriteSheet spriteSheet) + { + _name = name; + _spriteSheet = spriteSheet; + } + + /// + /// Adds a frame to the animation using the region index and duration. + /// + /// The index of the region in the sprite sheet. + /// The duration of the frame. + /// The instance for chaining. + public SpriteSheetAnimationDefinitionBuilder AddFrame(int regionIndex, TimeSpan duration) + { + SpriteSheetAnimationFrame frame = new SpriteSheetAnimationFrame(regionIndex, duration); + _frames.Add(frame); + return this; + } + + /// + /// Adds a frame to the animation using the region name and duration. + /// + /// The name of the region in the sprite sheet. + /// The duration of the frame. + /// The instance for chaining. + public SpriteSheetAnimationDefinitionBuilder AddFrame(string regionName, TimeSpan duration) + { + int index = _spriteSheet.TextureAtlas.GetIndexOfRegion(regionName); + return AddFrame(index, duration); + } + + /// + /// Sets whether the animation should loop. + /// + /// If set to true, the animation will loop. + /// The instance for chaining. + public SpriteSheetAnimationDefinitionBuilder IsLooping(bool isLooping) + { + _isLooping = isLooping; + return this; + } + + /// + /// Sets whether the animation should be reversed. + /// + /// If set to true, the animation will play in reverse. + /// The instance for chaining. + public SpriteSheetAnimationDefinitionBuilder IsReversed(bool isReversed) + { + _isReversed = isReversed; + return this; + } + + /// + /// Sets whether the animation should ping-pong (reverse direction at the ends). + /// + /// If set to true, the animation will ping-pong. + /// The instance for chaining. + public SpriteSheetAnimationDefinitionBuilder IsPingPong(bool isPingPong) + { + _isPingPong = isPingPong; + return this; + } + + internal SpriteSheetAnimationDefinition Build() => + new SpriteSheetAnimationDefinition(_name, _frames.ToArray(), _isLooping, _isReversed, _isPingPong); +} diff --git a/source/MonoGame.Extended/Graphics/SpriteSheetAnimationFrame.cs b/source/MonoGame.Extended/Graphics/SpriteSheetAnimationFrame.cs new file mode 100644 index 00000000..db569504 --- /dev/null +++ b/source/MonoGame.Extended/Graphics/SpriteSheetAnimationFrame.cs @@ -0,0 +1,34 @@ +// 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 System.Diagnostics; +using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.Animations; + +namespace MonoGame.Extended.Graphics; + +/// +/// Represents a single frame within a sprite sheet animation, including its index, display duration, and texture +/// region. +/// +[DebuggerDisplay("{Index} {Duration}")] +internal class SpriteSheetAnimationFrame : IAnimationFrame +{ + /// + /// Gets the index of the frame in the overall sprite sheet. + /// + public int FrameIndex { get; } + + /// + /// Gets the total duration this frame should be displayed during an animation. + /// + public TimeSpan Duration { get; } + + internal SpriteSheetAnimationFrame(int index, TimeSpan duration) + { + FrameIndex = index; + Duration = duration; + } +} diff --git a/source/MonoGame.Extended/Graphics/Texture2DAtlas.cs b/source/MonoGame.Extended/Graphics/Texture2DAtlas.cs index da57a972..daf766ba 100644 --- a/source/MonoGame.Extended/Graphics/Texture2DAtlas.cs +++ b/source/MonoGame.Extended/Graphics/Texture2DAtlas.cs @@ -1,8 +1,13 @@ +// 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 System.Collections; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using MonoGame.Extended.Animations; namespace MonoGame.Extended.Graphics; @@ -110,21 +115,21 @@ public class Texture2DAtlas : IEnumerable /// The width, in pixels, of the region. /// The height, in pixels, of the region. /// The created texture region. - public Texture2DRegion CreateRegion(int x, int y, int width, int height) => CreateRegion(null, new Rectangle(x, y, width, height)); + public Texture2DRegion CreateRegion(int x, int y, int width, int height) => CreateRegion(new Rectangle(x, y, width, height), null); /// /// Creates a new texture region with the specified name and adds it to this atlas. /// - /// The name of the texture region. /// The x-coordinate of the region. /// The y-coordinate of the region. /// The width, in pixels, of the region. /// The height, in pixels, of the region. + /// The name of the texture region. /// The created texture region. /// /// Thrown if a region with the same name as the parameter already exists in this atlas. /// - public Texture2DRegion CreateRegion(string name, int x, int y, int width, int height) => CreateRegion(name, new Rectangle(x, y, width, height)); + public Texture2DRegion CreateRegion(int x, int y, int width, int height, string name) => CreateRegion(new Rectangle(x, y, width, height), name); /// /// Creates a new texture region and adds it to this atlas. @@ -132,39 +137,39 @@ public class Texture2DAtlas : IEnumerable /// The location of the region. /// The size, in pixels, of the region. /// The created texture region. - public Texture2DRegion CreateRegion(Point location, Size size) => CreateRegion(null, new Rectangle(location.X, location.Y, size.Width, size.Height)); + public Texture2DRegion CreateRegion(Point location, Size size) => CreateRegion(new Rectangle(location.X, location.Y, size.Width, size.Height), null); /// /// Creates a new texture region with the specified name and adds it to this atlas. /// - /// The name of the texture region. /// The location of the region. /// The size, in pixels, of the region. + /// The name of the texture region. /// The created texture region. /// /// Thrown if a region with the same name as the parameter already exists in this atlas. /// - public Texture2DRegion CreateRegion(string name, Point location, Size size) => CreateRegion(name, new Rectangle(location.X, location.Y, size.Width, size.Height)); + public Texture2DRegion CreateRegion(string name, Point location, Size size) => CreateRegion(new Rectangle(location.X, location.Y, size.Width, size.Height), name); /// /// Creates a new texture region and adds it to this atlas. /// /// The bounds of the region. /// The created texture region. - public Texture2DRegion CreateRegion(Rectangle bounds) => CreateRegion(null, bounds); + public Texture2DRegion CreateRegion(Rectangle bounds) => CreateRegion(bounds, null); /// /// Creates a new texture region with the specified name and adds it to this atlas. /// - /// The name of the texture region. /// The bounds of the region. + /// The name of the texture region. /// The created texture region. /// /// Thrown if a region with the same name as the parameter already exists in this atlas. /// - public Texture2DRegion CreateRegion(string name, Rectangle bounds) + public Texture2DRegion CreateRegion(Rectangle bounds, string name) { - Texture2DRegion region = new Texture2DRegion(Texture, name, bounds); + Texture2DRegion region = new Texture2DRegion(Texture, bounds, name); AddRegion(region); return region; } @@ -274,6 +279,17 @@ public class Texture2DAtlas : IEnumerable return regions; } + + internal Texture2DRegion[] GetRegions(ReadOnlySpan frames) + { + Texture2DRegion[] regions = new Texture2DRegion[frames.Length]; + for (int i = 0; i < frames.Length; i++) + { + regions[i] = GetRegion(frames[i].FrameIndex); + } + + return regions; + } /// /// Gets the regions with the specified names. @@ -354,6 +370,35 @@ public class Texture2DAtlas : IEnumerable _regionsByName.Add(region.Name, region); } + /// + /// Creates a new using the region from this atlas at the specified index. + /// + /// The index of the region to use. + /// The created using the region at the specified index. + /// + /// Throw if the value of the is less than zero or is greater than or equal to the total + /// number of regions in this atlas. + /// + public Sprite CreateSprite(int regionIndex) + { + Texture2DRegion region = GetRegion(regionIndex); + return new Sprite(region); + } + + /// + /// Creates a new using the region from this atlas with the specified name. + /// + /// The name of the region to use. + /// The created using the region with the specified name. + /// + /// Thrown if this atlas does not contain a region with a name that matches the parameter. + /// + public Sprite CreateSprite(string regionName) + { + Texture2DRegion region = GetRegion(regionName); + return new Sprite(region); + } + private bool RemoveRegion(Texture2DRegion region) => _regionsByIndex.Remove(region) && _regionsByName.Remove(region.Name); /// diff --git a/source/MonoGame.Extended/Graphics/Texture2DRegion.cs b/source/MonoGame.Extended/Graphics/Texture2DRegion.cs index 8d3e4939..a1212ae7 100644 --- a/source/MonoGame.Extended/Graphics/Texture2DRegion.cs +++ b/source/MonoGame.Extended/Graphics/Texture2DRegion.cs @@ -89,7 +89,7 @@ public class Texture2DRegion /// Thrown if has been disposed prior. /// public Texture2DRegion(Texture2D texture) - : this(texture, null, 0, 0, texture.Width, texture.Height) { } + : this(texture, 0, 0, texture.Width, texture.Height, null) { } /// /// Initializes a new instance of the class representing the entire texture with the @@ -104,7 +104,7 @@ public class Texture2DRegion /// Thrown if has been disposed prior. /// public Texture2DRegion(Texture2D texture, string name) - : this(texture, name, texture.Bounds.X, texture.Bounds.Y, texture.Bounds.Width, texture.Bounds.Height) { } + : this(texture, 0, 0, texture.Bounds.Width, texture.Bounds.Height, null) { } /// /// Initializes a new instance of the class with the specified region of the texture. @@ -118,7 +118,7 @@ public class Texture2DRegion /// Thrown if has been disposed prior. /// public Texture2DRegion(Texture2D texture, Rectangle region) - : this(texture, null, region.X, region.Y, region.Width, region.Height) { } + : this(texture, region.X, region.Y, region.Width, region.Height, null) { } /// /// Initializes a new instance of the class with the specified region of the texture. @@ -135,41 +135,41 @@ public class Texture2DRegion /// Thrown if has been disposed prior. /// public Texture2DRegion(Texture2D texture, int x, int y, int width, int height) - : this(texture, null, x, y, width, height) { } + : this(texture, x, y, width, height, null) { } /// /// Initializes a new instance of the class with the specified region of the texture and /// name. /// /// The texture to create the region from. - /// The name of the texture region. /// The region of the texture to use. + /// The name of the texture region. /// /// Thrown if is . /// /// /// Thrown if has been disposed prior. /// - public Texture2DRegion(Texture2D texture, string name, Rectangle region) - : this(texture, name, region.X, region.Y, region.Width, region.Height) { } + public Texture2DRegion(Texture2D texture, Rectangle region, string name) + : this(texture, region.X, region.Y, region.Width, region.Height, name) { } /// /// Initializes a new instance of the class with the specified region of the texture and /// name. /// /// The texture to create the region from. - /// The name of the texture region. /// The top-left x-coordinate of the region within the texture. /// The top-left y-coordinate of the region within the texture. /// The width, in pixels, of the region. /// The height, in pixels, of the region. + /// The name of the texture region. /// /// Thrown if is . /// /// /// Thrown if has been disposed prior. /// - public Texture2DRegion(Texture2D texture, string name, int x, int y, int width, int height) + public Texture2DRegion(Texture2D texture,int x, int y, int width, int height, string name) { ArgumentNullException.ThrowIfNull(texture); if (texture.IsDisposed) diff --git a/source/MonoGame.Extended/Graphics/TextureRegion.Extensions.cs b/source/MonoGame.Extended/Graphics/TextureRegion.Extensions.cs index ca4d9aa9..4b9a7752 100644 --- a/source/MonoGame.Extended/Graphics/TextureRegion.Extensions.cs +++ b/source/MonoGame.Extended/Graphics/TextureRegion.Extensions.cs @@ -25,14 +25,14 @@ public static class TextureRegionExtensions /// Thrown if source texture of the has been disposed prior. /// public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, Rectangle region) => - textureRegion.GetSubregion(null, region.X, region.Y, region.Width, region.Height); + textureRegion.GetSubregion(region.X, region.Y, region.Width, region.Height, null); /// /// Gets a subregion of the specified texture region using the provided rectangle and name. /// /// The texture region to get the subregion from. - /// The name of the new subregion. /// The rectangle defining the subregion. + /// The name of the new subregion. /// A new representing the specified subregion. /// /// Thrown if is . @@ -41,7 +41,7 @@ public static class TextureRegionExtensions /// Thrown if source texture of the has been disposed prior. /// public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, string name, Rectangle region) => - textureRegion.GetSubregion(name, region.X, region.Y, region.Width, region.Height); + textureRegion.GetSubregion(region.X, region.Y, region.Width, region.Height, name); /// /// Gets a subregion of the specified texture region using the provided coordinates and dimensions. @@ -59,17 +59,17 @@ public static class TextureRegionExtensions /// Thrown if source texture of the has been disposed prior. /// public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, int x, int y, int width, int height) => - textureRegion.GetSubregion(null, x, y, width, height); + textureRegion.GetSubregion(x, y, width, height, null); /// /// Gets a subregion of the specified texture region using the provided name, coordinates, and dimensions. /// /// The texture region to get the subregion from. - /// The name of the new subregion. /// The top-left x-coordinate of the subregion within the texture region. /// The top-left y-coordinate of the subregion within the texture region. /// The width, in pixels, of the subregion. /// The height, in pixels, of the subregion. + /// The name of the new subregion. /// A new representing the specified subregion. /// /// Thrown if is . @@ -77,7 +77,7 @@ public static class TextureRegionExtensions /// /// Thrown if source texture of the has been disposed prior. /// - public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, string name, int x, int y, int width, int height) + public static Texture2DRegion GetSubregion(this Texture2DRegion textureRegion, int x, int y, int width, int height, string name) { ArgumentNullException.ThrowIfNull(textureRegion); @@ -87,7 +87,7 @@ public static class TextureRegionExtensions } Rectangle region = textureRegion.Bounds.GetRelativeRectangle(x, y, width, height); - return new Texture2DRegion(textureRegion.Texture, name, region); + return new Texture2DRegion(textureRegion.Texture, region, name); } /// diff --git a/source/MonoGame.Extended/IUpdate.cs b/source/MonoGame.Extended/IUpdate.cs deleted file mode 100644 index 6240e047..00000000 --- a/source/MonoGame.Extended/IUpdate.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.Xna.Framework; - -namespace MonoGame.Extended -{ - public interface IUpdate - { - void Update(GameTime gameTime); - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/Sprites/AnimatedSprite.cs b/source/MonoGame.Extended/Sprites/AnimatedSprite.cs deleted file mode 100644 index a6bf3935..00000000 --- a/source/MonoGame.Extended/Sprites/AnimatedSprite.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Linq; -using Microsoft.Xna.Framework; - -namespace MonoGame.Extended.Sprites -{ - public class AnimatedSprite : Sprite - { - private readonly SpriteSheet _spriteSheet; - private SpriteSheetAnimation _currentAnimation; - - public AnimatedSprite(SpriteSheet spriteSheet, string playAnimation = null) - : base(spriteSheet.TextureAtlas[0]) - { - _spriteSheet = spriteSheet; - - if (playAnimation != null) - Play(playAnimation); - } - - public SpriteSheetAnimation Play(string name, Action onCompleted = null) - { - if (_currentAnimation == null || _currentAnimation.IsComplete || _currentAnimation.Name != name) - { - var cycle = _spriteSheet.Cycles[name]; - var keyFrames = cycle.Frames.Select(f => _spriteSheet.TextureAtlas[f.Index]).ToArray(); - _currentAnimation = new SpriteSheetAnimation(name, keyFrames, cycle.FrameDuration, cycle.IsLooping, cycle.IsReversed, cycle.IsPingPong); - - if(_currentAnimation != null) - _currentAnimation.OnCompleted = onCompleted; - } - - return _currentAnimation; - } - - public void Update(float deltaTime) - { - if (_currentAnimation != null && !_currentAnimation.IsComplete) - { - _currentAnimation.Update(deltaTime); - TextureRegion = _currentAnimation.CurrentFrame; - } - } - - public void Update(GameTime gameTime) - { - Update(gameTime.GetElapsedSeconds()); - } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/Sprites/Animation.cs b/source/MonoGame.Extended/Sprites/Animation.cs deleted file mode 100644 index cd6e092a..00000000 --- a/source/MonoGame.Extended/Sprites/Animation.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using Microsoft.Xna.Framework; - -namespace MonoGame.Extended.Sprites -{ - public abstract class Animation : IUpdate, IDisposable - { - private readonly bool _disposeOnComplete; - private readonly Action _onCompleteAction; - private bool _isComplete; - - protected Animation(Action onCompleteAction, bool disposeOnComplete) - { - _onCompleteAction = onCompleteAction; - _disposeOnComplete = disposeOnComplete; - IsPaused = false; - } - - public bool IsComplete - { - get { return _isComplete; } - protected set - { - if (_isComplete != value) - { - _isComplete = value; - - if (_isComplete) - { - _onCompleteAction?.Invoke(); - - if (_disposeOnComplete) - Dispose(); - } - } - } - } - - public bool IsDisposed { get; private set; } - public bool IsPlaying => !IsPaused && !IsComplete; - public bool IsPaused { get; private set; } - public float CurrentTime { get; protected set; } - - public virtual void Dispose() - { - IsDisposed = true; - } - - public void Update(GameTime gameTime) - { - Update(gameTime.GetElapsedSeconds()); - } - - public void Play() - { - IsPaused = false; - } - - public void Pause() - { - IsPaused = true; - } - - public void Stop() - { - Pause(); - Rewind(); - } - - public void Rewind() - { - CurrentTime = 0; - } - - protected abstract bool OnUpdate(float deltaTime); - - public void Update(float deltaTime) - { - if (!IsPlaying) - return; - - CurrentTime += deltaTime; - IsComplete = OnUpdate(deltaTime); - } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/Sprites/ISpriteBatchDrawable.cs b/source/MonoGame.Extended/Sprites/ISpriteBatchDrawable.cs deleted file mode 100644 index eedcf6fd..00000000 --- a/source/MonoGame.Extended/Sprites/ISpriteBatchDrawable.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.Graphics; - -namespace MonoGame.Extended.Sprites -{ - public interface ISpriteBatchDrawable - { - bool IsVisible { get; } - Texture2DRegion TextureRegion { get; } - Vector2 Position { get; } - float Rotation { get; } - Vector2 Scale { get; } - Color Color { get; } - Vector2 Origin { get; } - SpriteEffects Effect { get; } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/Sprites/Sprite.cs b/source/MonoGame.Extended/Sprites/Sprite.cs deleted file mode 100644 index 84247a77..00000000 --- a/source/MonoGame.Extended/Sprites/Sprite.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Linq; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.Graphics; - -namespace MonoGame.Extended.Sprites -{ - public class Sprite : IColorable - { - private Texture2DRegion _textureRegion; - - public Sprite(Texture2DRegion textureRegion) - { - if (textureRegion == null) throw new ArgumentNullException(nameof(textureRegion)); - - _textureRegion = textureRegion; - - Alpha = 1.0f; - Color = Color.White; - IsVisible = true; - Effect = SpriteEffects.None; - OriginNormalized = new Vector2(0.5f, 0.5f); - Depth = 0.0f; - } - - public Sprite(Texture2D texture) - : this(new Texture2DRegion(texture)) - { - } - - public float Alpha { get; set; } - public float Depth { get; set; } - public object Tag { get; set; } - - public Vector2 OriginNormalized - { - get => new Vector2(Origin.X/TextureRegion.Width, Origin.Y/TextureRegion.Height); - set => Origin = new Vector2(value.X*TextureRegion.Width, value.Y*TextureRegion.Height); - } - - public Color Color { get; set; } - - public RectangleF GetBoundingRectangle(Transform2 transform) - { - return GetBoundingRectangle(transform.Position, transform.Rotation, transform.Scale); - } - - public RectangleF GetBoundingRectangle(Vector2 position, float rotation, Vector2 scale) - { - var corners = GetCorners(position, rotation, scale); - var min = new Vector2(corners.Min(i => i.X), corners.Min(i => i.Y)); - var max = new Vector2(corners.Max(i => i.X), corners.Max(i => i.Y)); - return new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y); - } - - public bool IsVisible { get; set; } - public Vector2 Origin { get; set; } - public SpriteEffects Effect { get; set; } - - public Texture2DRegion TextureRegion - { - get => _textureRegion; - set - { - if (value == null) - throw new InvalidOperationException("TextureRegion cannot be null"); - - // preserve the origin if the texture size changes - var originNormalized = OriginNormalized; - _textureRegion = value; - OriginNormalized = originNormalized; - } - } - - public Vector2[] GetCorners(Vector2 position, float rotation, Vector2 scale) - { - var min = -Origin; - var max = min + new Vector2(TextureRegion.Width, TextureRegion.Height); - var offset = position; - - if (scale != Vector2.One) - { - min *= scale; - max = max * scale; - } - - var corners = new Vector2[4]; - corners[0] = min; - corners[1] = new Vector2(max.X, min.Y); - corners[2] = max; - corners[3] = new Vector2(min.X, max.Y); - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (rotation != 0) - { - var matrix = Matrix.CreateRotationZ(rotation); - - for (var i = 0; i < 4; i++) - corners[i] = Vector2.Transform(corners[i], matrix); - } - - for (var i = 0; i < 4; i++) - corners[i] += offset; - - return corners; - } - } -} diff --git a/source/MonoGame.Extended/Sprites/SpriteExtensions.cs b/source/MonoGame.Extended/Sprites/SpriteExtensions.cs deleted file mode 100644 index 983065b4..00000000 --- a/source/MonoGame.Extended/Sprites/SpriteExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace MonoGame.Extended.Sprites -{ - public static class SpriteExtensions - { - public static void Draw(this Sprite sprite, SpriteBatch spriteBatch, Vector2 position, float rotation, Vector2 scale) - { - Draw(spriteBatch, sprite, position, rotation, scale); - } - - public static void Draw(this SpriteBatch spriteBatch, Sprite sprite, Transform2 transform) - { - Draw(spriteBatch, sprite, transform.Position, transform.Rotation, transform.Scale); - } - - public static void Draw(this SpriteBatch spriteBatch, Sprite sprite, Vector2 position, float rotation = 0) - { - Draw(spriteBatch, sprite, position, rotation, Vector2.One); - } - - public static void Draw(this SpriteBatch spriteBatch, Sprite sprite, Vector2 position, float rotation, Vector2 scale) - { - if (sprite == null) throw new ArgumentNullException(nameof(sprite)); - - if (sprite.IsVisible) - { - var texture = sprite.TextureRegion.Texture; - var sourceRectangle = sprite.TextureRegion.Bounds; - spriteBatch.Draw(texture, position, sourceRectangle, sprite.Color*sprite.Alpha, rotation, sprite.Origin, scale, sprite.Effect, sprite.Depth); - } - } - } -} diff --git a/source/MonoGame.Extended/Sprites/SpriteSheet.cs b/source/MonoGame.Extended/Sprites/SpriteSheet.cs deleted file mode 100644 index b3f22e95..00000000 --- a/source/MonoGame.Extended/Sprites/SpriteSheet.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using MonoGame.Extended.Graphics; -using MonoGame.Extended.TextureAtlases; - -namespace MonoGame.Extended.Sprites -{ - public class SpriteSheet - { - public SpriteSheet() - { - Cycles = new Dictionary(); - } - - public Texture2DAtlas TextureAtlas { get; set; } - public Dictionary Cycles { get; set; } - - public SpriteSheetAnimation CreateAnimation(string name) - { - var cycle = Cycles[name]; - var keyFrames = cycle.Frames - .Select(f => TextureAtlas[f.Index]) - .ToArray(); - - return new SpriteSheetAnimation(name, keyFrames, cycle.FrameDuration, cycle.IsLooping, cycle.IsReversed, cycle.IsPingPong); - } - } -} diff --git a/source/MonoGame.Extended/Sprites/SpriteSheetAnimation.cs b/source/MonoGame.Extended/Sprites/SpriteSheetAnimation.cs deleted file mode 100644 index 4d39db60..00000000 --- a/source/MonoGame.Extended/Sprites/SpriteSheetAnimation.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Linq; -using MonoGame.Extended.Graphics; - -namespace MonoGame.Extended.Sprites -{ - public class SpriteSheetAnimation : Animation - { - public const float DefaultFrameDuration = 0.2f; - - public SpriteSheetAnimation(string name, Texture2DAtlas textureAtlas, float frameDuration = DefaultFrameDuration, - bool isLooping = true, bool isReversed = false, bool isPingPong = false) - : this(name, textureAtlas.ToArray(), frameDuration, isLooping, isReversed, isPingPong) - { - } - - public SpriteSheetAnimation(string name, Texture2DRegion[] keyFrames, float frameDuration = DefaultFrameDuration, - bool isLooping = true, bool isReversed = false, bool isPingPong = false) - : base(null, false) - { - Name = name; - KeyFrames = keyFrames; - FrameDuration = frameDuration; - IsLooping = isLooping; - IsReversed = isReversed; - IsPingPong = isPingPong; - CurrentFrameIndex = IsReversed ? KeyFrames.Length - 1 : 0; - } - - public SpriteSheetAnimation(string name, Texture2DRegion[] keyFrames, SpriteSheetAnimationData data) - : this(name, keyFrames, data.FrameDuration, data.IsLooping, data.IsReversed, data.IsPingPong) - { - } - - public string Name { get; } - public Texture2DRegion[] KeyFrames { get; } - public float FrameDuration { get; set; } - public bool IsLooping { get; set; } - public bool IsReversed { get; set; } - public bool IsPingPong { get; set; } - public new bool IsComplete => CurrentTime >= AnimationDuration; - - public float AnimationDuration => IsPingPong - ? (KeyFrames.Length*2 - (IsLooping ? 2 : 1))*FrameDuration - : KeyFrames.Length*FrameDuration; - - public Texture2DRegion CurrentFrame => KeyFrames[CurrentFrameIndex]; - public int CurrentFrameIndex { get; private set; } - - public float FramesPerSecond - { - get => 1.0f/FrameDuration; - set => FrameDuration = value/1.0f; - } - - public Action OnCompleted { get; set; } - - protected override bool OnUpdate(float deltaTime) - { - if (IsComplete) - { - if (IsLooping) - CurrentTime %= AnimationDuration; - else - OnCompleted?.Invoke(); - } - - if (KeyFrames.Length == 1) - { - CurrentFrameIndex = 0; - return IsComplete; - } - - var frameIndex = (int) (CurrentTime/FrameDuration); - var length = KeyFrames.Length; - - if (IsPingPong) - { - if (IsComplete) - frameIndex = 0; - else - { - frameIndex = frameIndex % (length * 2 - 2); - - if (frameIndex >= length) - frameIndex = length - 2 - (frameIndex - length); - } - } - - if (IsLooping) - { - if (IsReversed) - { - frameIndex = frameIndex%length; - frameIndex = length - frameIndex - 1; - } - else - frameIndex = frameIndex%length; - } - else - frameIndex = IsReversed ? Math.Max(length - frameIndex - 1, 0) : Math.Min(length - 1, frameIndex); - - CurrentFrameIndex = frameIndex; - return IsComplete; - } - } -} diff --git a/source/MonoGame.Extended/Sprites/SpriteSheetAnimationCycle.cs b/source/MonoGame.Extended/Sprites/SpriteSheetAnimationCycle.cs deleted file mode 100644 index d9e86740..00000000 --- a/source/MonoGame.Extended/Sprites/SpriteSheetAnimationCycle.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; - -namespace MonoGame.Extended.Sprites -{ - public class SpriteSheetAnimationCycle - { - public SpriteSheetAnimationCycle() - { - Frames = new List(); - } - - public float FrameDuration { get; set; } = 0.2f; - public List Frames { get; set; } - public bool IsLooping { get; set; } - public bool IsReversed { get; set; } - public bool IsPingPong { get; set; } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/Sprites/SpriteSheetAnimationData.cs b/source/MonoGame.Extended/Sprites/SpriteSheetAnimationData.cs deleted file mode 100644 index 0bdf89d9..00000000 --- a/source/MonoGame.Extended/Sprites/SpriteSheetAnimationData.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace MonoGame.Extended.Sprites -{ - public class SpriteSheetAnimationData - { - public SpriteSheetAnimationData(int[] frameIndicies, float frameDuration = 0.2f, bool isLooping = true, - bool isReversed = false, bool isPingPong = false) - { - FrameIndicies = frameIndicies; - FrameDuration = frameDuration; - IsLooping = isLooping; - IsReversed = isReversed; - IsPingPong = isPingPong; - } - - public int[] FrameIndicies { get; } - public float FrameDuration { get; } - public bool IsLooping { get; } - public bool IsReversed { get; } - public bool IsPingPong { get; } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/Sprites/SpriteSheetAnimationFrame.cs b/source/MonoGame.Extended/Sprites/SpriteSheetAnimationFrame.cs deleted file mode 100644 index 2de8e39b..00000000 --- a/source/MonoGame.Extended/Sprites/SpriteSheetAnimationFrame.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Diagnostics; -using System.Text.Json; -using System.Text.Json.Serialization; - - -namespace MonoGame.Extended.Sprites -{ - [JsonConverter(typeof(SpriteSheetAnimationFrameJsonConverter))] - [DebuggerDisplay("{Index} {Duration}")] - public class SpriteSheetAnimationFrame - { - public SpriteSheetAnimationFrame(int index, float duration = 0.2f) - { - Index = index; - Duration = duration; - } - - public int Index { get; set; } - public float Duration { get; set; } - } - - public class SpriteSheetAnimationFrameJsonConverter : JsonConverter - { - /// - public override SpriteSheetAnimationFrame Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - switch (reader.TokenType) - { - case JsonTokenType.Number: - var index = reader.GetInt32(); - return new SpriteSheetAnimationFrame(index); - - case JsonTokenType.StartObject: - var frame = JsonSerializer.Deserialize(ref reader, options); - return frame; - - case JsonTokenType.Null: - return null; - - default: - throw new JsonException(); - } - } - - public override void Write(Utf8JsonWriter writer, SpriteSheetAnimationFrame value, JsonSerializerOptions options) - { - ArgumentNullException.ThrowIfNull(writer); - JsonSerializer.Serialize(writer, value, options); - } - } -} diff --git a/source/MonoGame.Extended/Timers/GameTimer.cs b/source/MonoGame.Extended/Timers/GameTimer.cs index 6a3acfe0..9fc6bcc2 100644 --- a/source/MonoGame.Extended/Timers/GameTimer.cs +++ b/source/MonoGame.Extended/Timers/GameTimer.cs @@ -3,8 +3,46 @@ using Microsoft.Xna.Framework; namespace MonoGame.Extended.Timers { - public abstract class GameTimer : IUpdate + public abstract class GameTimer : IUpdateable { + private bool _enabled; + private int _updateOrder; + /// + public bool Enabled + { + get => _enabled; + set + { + if (_enabled == value) + { + return; + } + _enabled = value; + EnabledChanged?.Invoke(this, EventArgs.Empty); + } + } + + /// + public int UpdateOrder + { + get => _updateOrder; + set + { + if (_updateOrder == value) + { + return; + } + _updateOrder = value; + EnabledChanged?.Invoke(this, EventArgs.Empty); + } + } + + /// + public event EventHandler EnabledChanged; + + /// + public event EventHandler UpdateOrderChanged; + protected GameTimer(double intervalSeconds) : this(TimeSpan.FromSeconds(intervalSeconds)) { @@ -20,6 +58,7 @@ namespace MonoGame.Extended.Timers public TimeSpan CurrentTime { get; protected set; } public TimerState State { get; protected set; } + public void Update(GameTime gameTime) { if (State != TimerState.Started) @@ -33,6 +72,7 @@ namespace MonoGame.Extended.Timers public event EventHandler Stopped; public event EventHandler Paused; + public void Start() { State = TimerState.Started; @@ -62,4 +102,4 @@ namespace MonoGame.Extended.Timers protected abstract void OnStopped(); protected abstract void OnUpdate(GameTime gameTime); } -} \ No newline at end of file +} diff --git a/tests/MonoGame.Extended.Entities.Tests/AspectBuilderTests.cs b/tests/MonoGame.Extended.Entities.Tests/AspectBuilderTests.cs index 1b3e9710..c24d1d80 100644 --- a/tests/MonoGame.Extended.Entities.Tests/AspectBuilderTests.cs +++ b/tests/MonoGame.Extended.Entities.Tests/AspectBuilderTests.cs @@ -1,6 +1,6 @@ using System; using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.Sprites; +using MonoGame.Extended.Graphics; using Xunit; namespace MonoGame.Extended.Entities.Tests diff --git a/tests/MonoGame.Extended.Entities.Tests/AspectTests.cs b/tests/MonoGame.Extended.Entities.Tests/AspectTests.cs index b06780ba..805da9d2 100644 --- a/tests/MonoGame.Extended.Entities.Tests/AspectTests.cs +++ b/tests/MonoGame.Extended.Entities.Tests/AspectTests.cs @@ -1,5 +1,5 @@ using System.Collections.Specialized; -using MonoGame.Extended.Sprites; +using MonoGame.Extended.Graphics; using Xunit; namespace MonoGame.Extended.Entities.Tests diff --git a/tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs b/tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs index abf1a16d..4343cfa4 100644 --- a/tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs +++ b/tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using MonoGame.Extended.Sprites; +using MonoGame.Extended.Graphics; using Xunit; namespace MonoGame.Extended.Entities.Tests diff --git a/tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs b/tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs index 89538041..9b3fef94 100644 --- a/tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs +++ b/tests/MonoGame.Extended.Entities.Tests/ComponentTypeTests.cs @@ -1,7 +1,4 @@ -using MonoGame.Extended.Sprites; -using Xunit; - -namespace MonoGame.Extended.Entities.Tests +namespace MonoGame.Extended.Entities.Tests { //public class ComponentTypeTests //{ @@ -17,4 +14,4 @@ namespace MonoGame.Extended.Entities.Tests // Assert.Equal(componentType.Id, componentType.GetHashCode()); // } //} -} \ No newline at end of file +} diff --git a/tests/MonoGame.Extended.Entities.Tests/WorldManagerTests.cs b/tests/MonoGame.Extended.Entities.Tests/WorldManagerTests.cs index 7620f1e8..7e53b5e6 100644 --- a/tests/MonoGame.Extended.Entities.Tests/WorldManagerTests.cs +++ b/tests/MonoGame.Extended.Entities.Tests/WorldManagerTests.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using Microsoft.Xna.Framework; using MonoGame.Extended.Entities.Systems; -using MonoGame.Extended.Sprites; -using Xunit; namespace MonoGame.Extended.Entities.Tests; @@ -42,8 +40,8 @@ public class WorldManagerTests private class DummySystem : EntitySystem, IUpdateSystem, IDrawSystem { - public List AddedEntitiesId { get; } = new (); - public List RemovedEntitiesId { get; } = new (); + public List AddedEntitiesId { get; } = new(); + public List RemovedEntitiesId { get; } = new(); public DummySystem() : base(Aspect.All(typeof(DummyComponent))) { } diff --git a/tests/MonoGame.Extended.Tests/Animations/AnimationTests.cs b/tests/MonoGame.Extended.Tests/Animations/AnimationTests.cs new file mode 100644 index 00000000..8dfcb905 --- /dev/null +++ b/tests/MonoGame.Extended.Tests/Animations/AnimationTests.cs @@ -0,0 +1,220 @@ +// 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; +using Xunit; +using MonoGame.Extended.Animations; + +namespace MonoGame.Extended.Tests.Animations; + + +public class AnimationTests +{ + private class TestAnimationFrame : IAnimationFrame + { + public int FrameIndex { get; set; } + public TimeSpan Duration { get; set; } + } + + private class TestAnimationDefinition : IAnimationDefinition + { + private readonly IAnimationFrame[] _frames; + public string Name { get; set; } + public ReadOnlySpan Frames => _frames; + public int FrameCount { get; set; } + public bool IsLooping { get; set; } + public bool IsReversed { get; set; } + public bool IsPingPong { get; set; } + + public TestAnimationDefinition(IAnimationFrame[] frames) => _frames = frames; + } + + private readonly IAnimationDefinition _definition; + private readonly Animation _animation; + + public AnimationTests() + { + var frames = new IAnimationFrame[] + { + new TestAnimationFrame { FrameIndex = 0, Duration = TimeSpan.FromSeconds(1) }, + new TestAnimationFrame { FrameIndex = 1, Duration = TimeSpan.FromSeconds(1) } + }; + _definition = new TestAnimationDefinition(frames) + { + FrameCount = frames.Length, + IsLooping = false, + IsReversed = false, + IsPingPong = false + }; + _animation = new Animation(_definition); + } + + [Fact] + public void Play_ShouldStartAnimation() + { + var result = _animation.Play(); + + Assert.True(result); + Assert.True(_animation.IsAnimating); + Assert.Equal(0, _animation.CurrentFrame); + } + + [Fact] + public void Play_ShouldThrowException_ForInvalidFrame() + { + Assert.Throws(() => _animation.Play(-1)); + Assert.Throws(() => _animation.Play(10)); + } + + [Fact] + public void Pause_ShouldPauseAnimation() + { + _animation.Play(); + var result = _animation.Pause(); + + Assert.True(result); + Assert.True(_animation.IsPaused); + } + + [Fact] + public void Pause_ShouldResetFrameDuration_WhenSpecified() + { + _animation.Play(); + _animation.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0.5))); + + var result = _animation.Pause(true); + + Assert.True(result); + Assert.Equal(_definition.Frames[0].Duration, _animation.CurrentFrameTimeRemaining); + } + + [Fact] + public void UnPause_ShouldResumeAnimation() + { + _animation.Play(); + _animation.Pause(); + var result = _animation.Unpause(); + + Assert.True(result); + Assert.False(_animation.IsPaused); + } + + [Fact] + public void UnPause_ShouldAdvanceFrame_WhenSpecified() + { + _animation.Play(); + _animation.Pause(); + var result = _animation.Unpause(true); + + Assert.True(result); + Assert.Equal(1, _animation.CurrentFrame); + } + + [Fact] + public void Stop_ShouldStopAnimation() + { + _animation.Play(); + var result = _animation.Stop(); + + Assert.True(result); + Assert.False(_animation.IsAnimating); + } + + [Fact] + public void Update_ShouldAdvanceFrame() + { + _animation.Play(); + _animation.Update(new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1.1))); + + Assert.Equal(1, _animation.CurrentFrame); + } + + [Fact] + public void Update_ShouldTriggerAnimationEvents() + { + bool frameBeginTriggered = false; + bool frameEndTriggered = false; + bool animationLoopTriggered = false; + bool animationCompletedTriggered = false; + + _animation.OnAnimationEvent += (anim, trigger) => + { + switch (trigger) + { + case AnimationEventTrigger.FrameBegin: + frameBeginTriggered = true; + break; + case AnimationEventTrigger.FrameEnd: + frameEndTriggered = true; + break; + case AnimationEventTrigger.AnimationLoop: + animationLoopTriggered = true; + break; + case AnimationEventTrigger.AnimationCompleted: + animationCompletedTriggered = true; + break; + } + }; + + _animation.Play(); + _animation.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))); + Assert.True(animationLoopTriggered); + + _animation.IsLooping = false; + _animation.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))); + + Assert.Equal(0, _animation.CurrentFrame); + } + + [Fact] + public void Reset_ShouldResetAnimation() + { + _animation.Play(); + _animation.Reset(); + + Assert.False(_animation.IsAnimating); + Assert.True(_animation.IsPaused); + Assert.Equal(0, _animation.CurrentFrame); + } + + [Fact] + public void SetFrame_ShouldChangeCurrentFrame() + { + _animation.SetFrame(1); + + Assert.Equal(1, _animation.CurrentFrame); + Assert.Equal(_definition.Frames[1].Duration, _animation.CurrentFrameTimeRemaining); + } + + [Fact] + public void SetFrame_ShouldThrowException_ForInvalidFrame() + { + Assert.Throws(() => _animation.SetFrame(-1)); + Assert.Throws(() => _animation.SetFrame(10)); + } + + [Fact] + public void Dispose_ShouldDisposeAnimation() + { + _animation.Dispose(); + + Assert.True(_animation.IsDisposed); + } +} diff --git a/tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj b/tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj index c12f9d2c..c82747f7 100644 --- a/tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj +++ b/tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj @@ -1,5 +1,11 @@  + + + + + + diff --git a/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs b/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs deleted file mode 100644 index 7a9bc372..00000000 --- a/tests/MonoGame.Extended.Tests/Sprites/SpriteSheetAnimationTests.cs +++ /dev/null @@ -1,911 +0,0 @@ -using System; -using Microsoft.Xna.Framework; -using MonoGame.Extended.Graphics; -using MonoGame.Extended.Sprites; -using MonoGame.Extended.TextureAtlases; -using Xunit; - -namespace MonoGame.Extended.Tests.Sprites -{ - public class SpriteSheetAnimationTests - { - [Theory] - [InlineData(0, 0)] - [InlineData(0, 0.9f)] - [InlineData(1, 1f)] - [InlineData(1, 1.9f)] - [InlineData(0, 2f)] - [InlineData(0, 2.9f)] - [InlineData(1, 3f)] - [InlineData(0, 4f)] - [InlineData(1, 5f)] - public void Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Fact] - public void Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Theory] - [InlineData(0, 0.9f)] - [InlineData(1, 1f)] - [InlineData(1, 1.1f)] - [InlineData(1, 1.9f)] - public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Theory] - [InlineData(1, 2f)] - [InlineData(1, 3f)] - [InlineData(1, 4f)] - public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.True(isCompleteFired); - } - - [Fact] - public void Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.True(isCompleteFired); - - isCompleteFired = false; // Reset isCompleteFired for next execution - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); // Event is not fired again as animation was already completed - } - - [Theory] - [InlineData(1, 0)] - [InlineData(1, 0.9f)] - [InlineData(0, 1f)] - [InlineData(0, 1.9f)] - [InlineData(1, 2f)] - [InlineData(1, 2.9f)] - [InlineData(0, 3f)] - [InlineData(1, 4f)] - [InlineData(0, 5f)] - public void Reversed_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - true, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Fact] - public void Reversed_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - true, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Theory] - [InlineData(1, 0.9f)] - [InlineData(0, 1f)] - [InlineData(0, 1.1f)] - [InlineData(0, 1.9f)] - public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Theory] - [InlineData(0, 2f)] - [InlineData(0, 3f)] - [InlineData(0, 4f)] - public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.True(isCompleteFired); - } - - [Fact] - public void Reversed_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.True(isCompleteFired); - - isCompleteFired = false; // Reset isCompleteFired for next execution - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); // Event is not fired again as animation was already completed; - } - - [Theory] - [InlineData(0, 0)] - [InlineData(0, 0.9f)] - [InlineData(1, 1f)] - [InlineData(1, 1.9f)] - [InlineData(0, 2f)] - [InlineData(0, 2.9f)] - [InlineData(1, 3f)] - [InlineData(0, 4f)] - [InlineData(1, 5f)] - [InlineData(0, 6f)] - [InlineData(1, 7f)] - [InlineData(0, 8f)] - public void PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - true, - false, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Fact] - public void PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - true, - false, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Theory] - [InlineData(0, 0.9f)] - [InlineData(1, 1f)] - [InlineData(1, 1.9f)] - [InlineData(0, 2f)] - [InlineData(0, 2.9f)] - public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false, - false, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Theory] - [InlineData(0, 3f)] - [InlineData(0, 4f)] - [InlineData(0, 5f)] - public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false, - false, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.True(isCompleteFired); - } - - [Fact] - public void PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - var textureRegion2D3 = new Texture2DRegion("Region 3", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2, textureRegion2D3 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1, 2 }, - 1, - false, - false, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.True(isCompleteFired); - - isCompleteFired = false; // Reset isCompleteFired for next execution - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); // Event is not fired again as animation was already completed - } - - [Theory] - [InlineData(1, 0)] - [InlineData(1, 0.9f)] - [InlineData(0, 1f)] - [InlineData(0, 1.9f)] - [InlineData(1, 2f)] - [InlineData(1, 2.9f)] - [InlineData(0, 3f)] - [InlineData(1, 4f)] - [InlineData(0, 5f)] - [InlineData(1, 6f)] - [InlineData(0, 7f)] - [InlineData(1, 8f)] - public void Reversed_PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - true, - true, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Fact] - public void Reversed_PingPong_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete_Over_Multiple_Updates() - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - true, - true, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Theory] - [InlineData(1, 0.9f)] - [InlineData(0, 1f)] - [InlineData(0, 1.9f)] - [InlineData(1, 2f)] - [InlineData(1, 2.9f)] - public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Not_Complete(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false, - true, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - } - - [Theory] - [InlineData(1, 3f)] - [InlineData(1, 4f)] - [InlineData(1, 5f)] - public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached(int expectedTextureRegionIndex, float time) - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1 }, - 1, - false, - true, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(time)); - - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[expectedTextureRegionIndex], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.True(isCompleteFired); - } - - [Fact] - public void Reversed_PingPong_Non_Looping_SpriteSheetAnimation_Should_Return_Correct_Frame_And_Complete_When_AnimationDuration_Is_Reached_Over_Multiple_Updates() - { - var textureRegion2D1 = new Texture2DRegion("Region 1", new Rectangle()); - var textureRegion2D2 = new Texture2DRegion("Region 2", new Rectangle()); - var textureRegion2D3 = new Texture2DRegion("Region 3", new Rectangle()); - - var textureRegions = new[] { textureRegion2D1, textureRegion2D2, textureRegion2D3 }; - - var spriteSheetAnimationData = new SpriteSheetAnimationData( - new[] { 0, 1, 2 }, - 1, - false, - true, - true - ); - - var spriteSheetAnimation = new SpriteSheetAnimation("Test", textureRegions, spriteSheetAnimationData); - - var isCompleteFired = false; - spriteSheetAnimation.OnCompleted += () => isCompleteFired = true; - - spriteSheetAnimation.Play(); - - var gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(0)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[0], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[1], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); - Assert.False(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); - - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.True(isCompleteFired); - - isCompleteFired = false; // Reset isCompleteFired for next execution - gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - spriteSheetAnimation.Update(gameTime); - - Assert.Equal(textureRegions[2], spriteSheetAnimation.CurrentFrame); - Assert.True(spriteSheetAnimation.IsComplete); - Assert.False(isCompleteFired); // Event is not fired again as animation was already completed - } - } -} diff --git a/tests/MonoGame.Extended.Tests/Sprites/SpriteTests.cs b/tests/MonoGame.Extended.Tests/Sprites/SpriteTests.cs deleted file mode 100644 index 307e2727..00000000 --- a/tests/MonoGame.Extended.Tests/Sprites/SpriteTests.cs +++ /dev/null @@ -1,91 +0,0 @@ -//using Microsoft.Xna.Framework; -//using Microsoft.Xna.Framework.Graphics; -//using MonoGame.Extended.Sprites; -//using MonoGame.Extended.TextureAtlases; -//using NSubstitute; -//using Xunit; - -//namespace MonoGame.Extended.Tests.Sprites -//{ -// -// public class SpriteTests -// { -// [Fact] -// public void Sprite_BoundingRectangleAfterPosition_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = Substitute.For(graphicsDevice, 50, 200); -// var sprite = new Sprite(texture); - -// Assert.Equal(new RectangleF(375, 140, 50, 200), sprite.GetBoundingRectangle(new Vector2(400, 240), 0, Vector2.One)); -// } - -// [Fact] -// public void Sprite_BoundingRectangleAfterOrigin_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = Substitute.For(graphicsDevice, 50, 200); -// var sprite = new Sprite(texture) { OriginNormalized = new Vector2(1.0f, 1.0f) }; - -// Assert.Equal(new RectangleF(-50, -200, 50, 200), sprite.GetBoundingRectangle(Vector2.Zero, 0, Vector2.One)); -// } - -// [Fact] -// public void Sprite_BoundingRectangleAfterScale_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = Substitute.For(graphicsDevice, 50, 200); -// var sprite = new Sprite(texture); - -// Assert.Equal(new RectangleF(-50, -200, 100, 400), sprite.GetBoundingRectangle(Vector2.Zero, 0, Vector2.One * 2.0f)); -// } - -// [Fact] -// public void Sprite_BoundingRectangleAfterRotation_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = Substitute.For(graphicsDevice, 50, 200); -// var sprite = new Sprite(texture); - -// AssertExtensions.AreApproximatelyEqual(new RectangleF(-100, -25, 200, 50), sprite.GetBoundingRectangle(Vector2.Zero, MathHelper.ToRadians(90), Vector2.One * 2.0f)); -// } - -// [Fact] -// public void Sprite_TextureRegionIsFullTextureWhenTextureConstructorIsUsed_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = Substitute.For(graphicsDevice, 100, 200); -// var sprite = new Sprite(texture); - -// Assert.Equal(new Rectangle(0, 0, 100, 200), sprite.TextureRegion.Bounds); -// } - -// [Fact] -// public void Sprite_DefaultOriginIsCentre_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = Substitute.For(graphicsDevice, 100, 200); -// var sprite = new Sprite(texture); - -// Assert.Equal(new Vector2(0.5f, 0.5f), sprite.OriginNormalized); -// Assert.Equal(new Vector2(50, 100), sprite.Origin); -// } - -// [Fact] -// public void Sprite_PreserveNormalizedOriginWhenTextureRegionChanges_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = Substitute.For(graphicsDevice, 100, 100); -// var textureRegion = new TextureRegion2D(texture, 10, 20, 30, 40); -// var sprite = new Sprite(textureRegion); - -// Assert.Equal(new Vector2(0.5f, 0.5f), sprite.OriginNormalized); -// Assert.Equal(new Vector2(15, 20), sprite.Origin); - -// sprite.TextureRegion = new TextureRegion2D(texture, 30, 40, 50, 60); - -// Assert.Equal(new Vector2(0.5f, 0.5f), sprite.OriginNormalized); -// Assert.Equal(new Vector2(25, 30), sprite.Origin); -// } -// } -//} diff --git a/tests/MonoGame.Extended.Tests/TestGraphicsDevice.cs b/tests/MonoGame.Extended.Tests/TestGraphicsDevice.cs deleted file mode 100644 index 8a8741b1..00000000 --- a/tests/MonoGame.Extended.Tests/TestGraphicsDevice.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.Xna.Framework.Graphics; - -namespace MonoGame.Extended.Tests -{ - public class TestGraphicsDevice : GraphicsDevice - { - public TestGraphicsDevice() - : base(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, new PresentationParameters()) - { - } - } -} \ No newline at end of file diff --git a/tests/MonoGame.Extended.Tests/TestHelper.cs b/tests/MonoGame.Extended.Tests/TestHelper.cs deleted file mode 100644 index b6a02bb5..00000000 --- a/tests/MonoGame.Extended.Tests/TestHelper.cs +++ /dev/null @@ -1,27 +0,0 @@ -//using Microsoft.Xna.Framework; -//using Microsoft.Xna.Framework.Graphics; -//using Xunit; - -//namespace MonoGame.Extended.Tests -//{ -// public static class TestHelper -// { -// public static void AreEqual(Vector3 a, Vector3 b, double delta) -// { -// Assert.Equal(a.X, b.X, delta); -// Assert.Equal(a.Y, b.Y, delta); -// Assert.Equal(a.Z, b.Z, delta); -// } - -// public static GraphicsDevice CreateGraphicsDevice() -// { -// return new GraphicsDevice( -// GraphicsAdapter.DefaultAdapter, -// GraphicsProfile.HiDef, -// new PresentationParameters()) -// { -// Viewport = new Viewport(0, 0, 800, 480) -// }; -// } -// } -//} \ No newline at end of file diff --git a/tests/MonoGame.Extended.Tests/TextureAtlases/TextureAtlasTests.cs b/tests/MonoGame.Extended.Tests/TextureAtlases/TextureAtlasTests.cs deleted file mode 100644 index 13d9dfc0..00000000 --- a/tests/MonoGame.Extended.Tests/TextureAtlases/TextureAtlasTests.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Xna.Framework.Graphics; -using MonoGame.Extended.TextureAtlases; -using Xunit; - -namespace MonoGame.Extended.Tests.TextureAtlases -{ - //public class TextureAtlasTests : IDisposable - //{ - // private readonly TestGame _game; - - // public TextureAtlasTests() - // { - // _game = new TestGame(); - // } - - // public void Dispose() - // { - // _game.Dispose(); - // } - - // [Fact] - // public void TextureAtlas_CreateRegion_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) - // { - // var atlas = new TextureAtlas(null, texture); - - // var region = atlas.CreateRegion("region0", 10, 20, 30, 40); - - // Assert.Same(texture, region.Texture); - // Assert.Equal(10, region.X); - // Assert.Equal(20, region.Y); - // Assert.Equal(30, region.Width); - // Assert.Equal(40, region.Height); - // } - // } - - // [Fact] - // public void TextureAtlas_GetRegionsByIndex_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) - // { - // var atlas = new TextureAtlas(null, texture); - - // var region0 = atlas.CreateRegion("region0", 10, 20, 30, 40); - // var region1 = atlas.CreateRegion("region1", 50, 60, 35, 45); - - // Assert.Same(region0, atlas[0]); - // Assert.Same(region1, atlas[1]); - // Assert.Same(region0, atlas.GetRegion(0)); - // Assert.Same(region1, atlas.GetRegion(1)); - // } - // } - - - // [Fact] - // public void TextureAtlas_GetRegionsByName_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) - // { - // var atlas = new TextureAtlas(null, texture); - - // var region0 = atlas.CreateRegion("region0", 10, 20, 30, 40); - // var region1 = atlas.CreateRegion("region1", 50, 60, 35, 45); - - // Assert.Same(region0, atlas["region0"]); - // Assert.Same(region1, atlas["region1"]); - // Assert.Same(region0, atlas.GetRegion("region0")); - // Assert.Same(region1, atlas.GetRegion("region1")); - // } - // } - - // [Fact] - // public void TextureAtlas_RemoveRegions_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) - // { - // var atlas = new TextureAtlas(null, texture); - - // var region0 = atlas.CreateRegion("region0", 10, 20, 30, 40); - // var region1 = atlas.CreateRegion("region1", 50, 60, 35, 45); - // var region2 = atlas.CreateRegion("region2", 32, 33, 34, 35); - - // Assert.Same(texture, atlas.Texture); - // Assert.Equal(3, atlas.RegionCount); - // Assert.Equal(atlas.RegionCount, atlas.Regions.Count()); - // Assert.Same(region1, atlas[1]); - - // atlas.RemoveRegion(1); - - // Assert.Equal(2, atlas.Regions.Count()); - // Assert.Same(region0, atlas[0]); - // Assert.Same(region2, atlas[1]); - - // atlas.RemoveRegion("region0"); - - // Assert.Single(atlas.Regions); - // Assert.Same(region2, atlas[0]); - // } - // } - - // [Fact] - // public void TextureAtlas_CreateRegionThatAlreadyExistsThrowsException_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) - // { - // var atlas = new TextureAtlas(null, texture); - - // atlas.CreateRegion("region0", 10, 20, 30, 40); - // Assert.Throws(() => atlas.CreateRegion("region0", 50, 60, 35, 45)); - // } - // } - - // [Fact] - // public void TextureAtlas_GetRegion_InvalidIndexThrowsException_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) - // { - // var atlas = new TextureAtlas(null, texture); - - // atlas.CreateRegion("region0", 10, 20, 30, 40); - // Assert.Throws(() => atlas.GetRegion(-1)); - // } - // } - - // [Fact] - // public void TextureAtlas_GetRegion_InvalidNameThrowsException_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) - // { - // var atlas = new TextureAtlas(null, texture); - - // atlas.CreateRegion("region0", 10, 20, 30, 40); - // Assert.Throws(() => atlas.GetRegion("region1")); - // } - // } - - // [Fact] - // public void TextureAtlas_EnumerateRegions_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 100, 200)) - // { - // var atlas = new TextureAtlas(null, texture); - - // var regions = new TextureRegion2D[3]; - // regions[0] = atlas.CreateRegion("region0", 10, 20, 30, 40); - // regions[1] = atlas.CreateRegion("region1", 50, 60, 35, 45); - // regions[2] = atlas.CreateRegion("region2", 32, 33, 34, 35); - // var index = 0; - - // foreach (var region in atlas) - // { - // Assert.Same(region, regions[index]); - // index++; - // } - // } - // } - - // [Fact] - // public void TextureAtlas_Create_WithDefaultParameters_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 50, 100) {Name = "testTexture"}) - // { - // var atlas = TextureAtlas.Create(null, texture, 25, 50); - - // Assert.Equal(4, atlas.RegionCount); - // Assert.True(atlas.Regions.All(i => i.Width == 25)); - // Assert.True(atlas.Regions.All(i => i.Height == 50)); - // Assert.True(atlas.Regions.All(i => ReferenceEquals(i.Texture, texture))); - // Assert.True(atlas.Regions.All(i => i.Name.StartsWith(texture.Name))); - // } - // } - - // [Fact] - // public void TextureAtlas_Create_WithMaxRegionCount_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 64, 64)) - // { - // var atlas = TextureAtlas.Create(null, texture, 32, 32, maxRegionCount: 3); - - // Assert.Equal(3, atlas.RegionCount); - // } - // } - - // [Fact] - // public void TextureAtlas_Create_WithMargin_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 24, 24)) - // { - // var atlas = TextureAtlas.Create(null, texture, 10, 10, margin: 2); - - // Assert.Equal(4, atlas.RegionCount); - // Assert.True(atlas.Regions.All(i => i.Width == 10 && i.Height == 10)); - // Assert.Equal(2, atlas[0].X); - // Assert.Equal(2, atlas[0].Y); - // Assert.Equal(12, atlas[3].X); - // Assert.Equal(12, atlas[3].Y); - // } - // } - - // [Fact] - // public void TextureAtlas_Create_WithSpacing_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 24, 24)) - // { - // var atlas = TextureAtlas.Create(null, texture, 10, 10, spacing: 2); - - // Assert.Equal(4, atlas.RegionCount); - // Assert.True(atlas.Regions.All(i => i.Width == 10 && i.Height == 10)); - // Assert.Equal(0, atlas[0].X); - // Assert.Equal(0, atlas[0].Y); - // Assert.Equal(12, atlas[3].X); - // Assert.Equal(12, atlas[3].Y); - // } - // } - - // [Fact] - // public void TextureAtlas_Create_WithMarginAndSpacing_Test() - // { - // using (var texture = new Texture2D(_game.GraphicsDevice, 28, 28)) - // { - // var atlas = TextureAtlas.Create(null, texture, 10, 10, margin: 3, spacing: 2); - - // Assert.Equal(4, atlas.RegionCount); - // Assert.True(atlas.Regions.All(i => i.Width == 10 && i.Height == 10)); - // Assert.Equal(3, atlas[0].X); - // Assert.Equal(3, atlas[0].Y); - // Assert.Equal(15, atlas[3].X); - // Assert.Equal(15, atlas[3].Y); - // } - // } - //} -} diff --git a/tests/MonoGame.Extended.Tests/TextureAtlases/TextureRegion2DTests.cs b/tests/MonoGame.Extended.Tests/TextureAtlases/TextureRegion2DTests.cs deleted file mode 100644 index 1274b100..00000000 --- a/tests/MonoGame.Extended.Tests/TextureAtlases/TextureRegion2DTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -//using Microsoft.Xna.Framework.Graphics; -//using MonoGame.Extended.TextureAtlases; -//using Xunit; - -//namespace MonoGame.Extended.Tests.TextureAtlases -//{ -// -// public class TextureRegion2DTests -// { -// [Fact] -// public void TextureRegion2D_FromTexture_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = new Texture2D(graphicsDevice, 100, 200); -// var textureRegion = new TextureRegion2D(texture); - -// Assert.AreSame(texture, textureRegion.Texture); -// Assert.Equal(0, textureRegion.X); -// Assert.Equal(0, textureRegion.Y); -// Assert.Equal(100, textureRegion.Width); -// Assert.Equal(200, textureRegion.Height); -// Assert.IsNull(textureRegion.Tag); -// } - -// [Fact] -// public void TextureRegion2D_Specified_Test() -// { -// var graphicsDevice = TestHelper.CreateGraphicsDevice(); -// var texture = new Texture2D(graphicsDevice, 100, 200); -// var textureRegion = new TextureRegion2D(texture, 10, 20, 30, 40); - -// Assert.AreSame(texture, textureRegion.Texture); -// Assert.Equal(10, textureRegion.X); -// Assert.Equal(20, textureRegion.Y); -// Assert.Equal(30, textureRegion.Width); -// Assert.Equal(40, textureRegion.Height); -// Assert.IsNull(textureRegion.Tag); -// } -// } -//} \ No newline at end of file