// 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 the size of the sprite. /// public Point Size => TextureRegion.OriginalSize; /// /// 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 { return new Vector2(Origin.X / TextureRegion.OriginalSize.Width, Origin.Y / TextureRegion.OriginalSize.Height); } set { Origin = new Vector2(value.X * TextureRegion.OriginalSize.Width, value.Y * TextureRegion.OriginalSize.Height); } } /// /// 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; if (value.OriginNormalized.HasValue) { OriginNormalized = value.OriginNormalized.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 = textureRegion.OriginNormalized ?? Vector2.Zero; Depth = 0.0f; } /// /// Initializes a new instance of the class by copying property values from an existing sprite. /// /// /// Creates a shallow copy where the new sprite shares the same reference. /// The reference is copied but the object itself is not cloned. /// /// The sprite to copy from. /// Thrown if is . /// /// Thrown if the source texture of the of the provided source sprite has been disposed of. /// public Sprite(Sprite source) { ArgumentNullException.ThrowIfNull(source); ObjectDisposedException.ThrowIf(source.TextureRegion.Texture.IsDisposed, source.TextureRegion.Texture); _textureRegion = source._textureRegion; Alpha = source.Alpha; Color = source.Color; IsVisible = source.IsVisible; Effect = source.Effect; Depth = source.Depth; Origin = source.Origin; Tag = source.Tag; } /// /// 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.OriginalSize.Width, TextureRegion.OriginalSize.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; } /// /// Creates a shallow copy of this sprite with the same texture region and property values. /// /// /// The returned sprite shares the same reference. /// The reference is copied but the object itself is not cloned. /// /// A new instance with copied property values. /// /// Thrown if the source texture of the of this sprite has been disposed of. /// public Sprite Clone() { return new Sprite(this); } }