Sprite.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // Copyright (c) Craftwork Games. All rights reserved.
  2. // Licensed under the MIT license.
  3. // See LICENSE file in the project root for full license information.
  4. using System;
  5. using System.Linq;
  6. using Microsoft.Xna.Framework;
  7. using Microsoft.Xna.Framework.Graphics;
  8. namespace MonoGame.Extended.Graphics;
  9. /// <summary>
  10. /// Represents a drawable 2D texture region with additional properties for rendering, such as position, scale,
  11. /// rotation, and color.
  12. /// </summary>
  13. public class Sprite : IColorable
  14. {
  15. private Texture2DRegion _textureRegion;
  16. /// <summary>
  17. /// Gets or sets a value indicating whether this sprite is visible.
  18. /// </summary>
  19. public bool IsVisible { get; set; }
  20. /// <summary>
  21. /// Gets or sets the color mask used when rendering this sprite.
  22. /// </summary>
  23. /// <remarks>
  24. /// The color mask is applied to the sprite's texture by multiplying each pixel's color value with the specified
  25. /// color. For example, setting the color to `Color.Red` will make the sprite appear as a red tint over its
  26. /// texture.
  27. /// </remarks>
  28. public Color Color { get; set; }
  29. /// <summary>
  30. /// Gets or sets the alpha transparency value used when rendering this sprite.
  31. /// </summary>
  32. /// <remarks>
  33. /// The alpha value should be between 0 (fully transparent) and 1 (fully opaque).
  34. /// </remarks>
  35. public float Alpha { get; set; }
  36. /// <summary>
  37. /// Gets or sets the layer depth used when rendering this sprite.
  38. /// </summary>
  39. /// <remarks>
  40. /// Sprites with higher depth values are rendered on top of those with lower depth values.
  41. /// </remarks>
  42. public float Depth { get; set; }
  43. /// <summary>
  44. /// Gets or sets the sprite effects to apply when rendering this sprite.
  45. /// </summary>
  46. /// <remarks>
  47. /// This specifies the desired horizontal and/or vertical flip effect of the sprite.
  48. /// </remarks>
  49. public SpriteEffects Effect { get; set; }
  50. /// <summary>
  51. /// Gets or Sets an object that contains user defined data about this sprite.
  52. /// </summary>
  53. public object Tag { get; set; }
  54. /// <summary>
  55. /// Gets the size of the sprite.
  56. /// </summary>
  57. public Point Size => TextureRegion.OriginalSize;
  58. /// <summary>
  59. /// Gets or sets the origin of this sprite.
  60. /// </summary>
  61. /// <remarks>
  62. /// The origin is relative to the bounds of the texture region and represents the point around which the sprite is
  63. /// rotated and scaled.
  64. /// </remarks>
  65. public Vector2 Origin { get; set; }
  66. /// <summary>
  67. /// Gets or sets the normalized origin of this sprite relative to its texture region.
  68. /// </summary>
  69. /// <remarks>
  70. /// The normalized origin represents the origin as a fraction of the texture region's UV coordinates,
  71. /// where (0, 0) is the top-left corner and (1, 1) is the bottom-right corner.
  72. /// </remarks>
  73. public Vector2 OriginNormalized
  74. {
  75. get { return new Vector2(Origin.X / TextureRegion.OriginalSize.Width, Origin.Y / TextureRegion.OriginalSize.Height); }
  76. set { Origin = new Vector2(value.X * TextureRegion.OriginalSize.Width, value.Y * TextureRegion.OriginalSize.Height); }
  77. }
  78. /// <summary>
  79. /// Gets or sets the source texture region of this sprite.
  80. /// </summary>
  81. /// <exception cref="ArgumentNullException">Thrown when setting to a null texture region.</exception>
  82. /// <exception cref="ObjectDisposedException">
  83. /// Thrown if the source texture of the assigned <see cref="Texture2DRegion"/> has been disposed of when setting.
  84. /// </exception>
  85. public Texture2DRegion TextureRegion
  86. {
  87. get => _textureRegion;
  88. set
  89. {
  90. ArgumentNullException.ThrowIfNull(value);
  91. if (value.Texture.IsDisposed)
  92. {
  93. throw new ObjectDisposedException(nameof(value), $"The source {nameof(Texture2D)} of the {nameof(TextureRegion)} was disposed prior to setting this property.");
  94. }
  95. _textureRegion = value;
  96. if (value.OriginNormalized.HasValue)
  97. {
  98. OriginNormalized = value.OriginNormalized.Value;
  99. }
  100. }
  101. }
  102. /// <summary>
  103. /// Initializes a new instance of the <see cref="Sprite"/> class with the specified texture.
  104. /// </summary>
  105. /// <param name="texture">The source texture of the sprite.</param>
  106. /// <exception cref="ArgumentNullException">
  107. /// Thrown if the <paramref name="texture"/> parameter is <see langword="null"/>.
  108. /// </exception>
  109. /// <exception cref="ObjectDisposedException">
  110. /// Thrown if the <paramref name="texture"/> parameter was disposed of prior.
  111. /// </exception>
  112. public Sprite(Texture2D texture)
  113. : this(new Texture2DRegion(texture))
  114. {
  115. }
  116. /// <summary>
  117. /// Initializes a new instance of the <see cref="Sprite"/> class with the specified texture region.
  118. /// The sprite represents a renderable 2D image defined by the given texture region.
  119. /// </summary>
  120. /// <param name="textureRegion">The source texture region of the sprite.</param>
  121. /// <exception cref="ArgumentNullException">
  122. /// Thrown if the <paramref name="textureRegion"/> parameter is <see langword="null"/>.
  123. /// </exception>
  124. /// <exception cref="ObjectDisposedException">
  125. /// Thrown if the source texture of the <paramref name="textureRegion"/> parameter has been disposed of.
  126. /// </exception>
  127. public Sprite(Texture2DRegion textureRegion)
  128. {
  129. ArgumentNullException.ThrowIfNull(textureRegion);
  130. if (textureRegion.Texture.IsDisposed)
  131. {
  132. throw new ObjectDisposedException(nameof(textureRegion), $"The source {nameof(Texture2D)} of the {nameof(textureRegion)} was disposed prior.");
  133. }
  134. _textureRegion = textureRegion;
  135. Alpha = 1.0f;
  136. Color = Color.White;
  137. IsVisible = true;
  138. Effect = SpriteEffects.None;
  139. OriginNormalized = textureRegion.OriginNormalized ?? Vector2.Zero;
  140. Depth = 0.0f;
  141. }
  142. /// <summary>
  143. /// Initializes a new instance of the <see cref="Sprite"/> class by copying property values from an existing sprite.
  144. /// </summary>
  145. /// <remarks>
  146. /// Creates a shallow copy where the new sprite shares the same <see cref="TextureRegion"/> reference.
  147. /// The <see cref="Tag"/> reference is copied but the object itself is not cloned.
  148. /// </remarks>
  149. /// <param name="source">The sprite to copy from.</param>
  150. /// <exception cref="ArgumentNullException">Thrown if <paramref name="source"/> is <see langword="null"/>.</exception>
  151. /// <exception cref="ObjectDisposedException">
  152. /// Thrown if the source texture of the <see cref="TextureRegion"/> of the provided source sprite has been disposed of.
  153. /// </exception>
  154. public Sprite(Sprite source)
  155. {
  156. ArgumentNullException.ThrowIfNull(source);
  157. ObjectDisposedException.ThrowIf(source.TextureRegion.Texture.IsDisposed, source.TextureRegion.Texture);
  158. _textureRegion = source._textureRegion;
  159. Alpha = source.Alpha;
  160. Color = source.Color;
  161. IsVisible = source.IsVisible;
  162. Effect = source.Effect;
  163. Depth = source.Depth;
  164. Origin = source.Origin;
  165. Tag = source.Tag;
  166. }
  167. /// <summary>
  168. /// Gets the bounding rectangle of the sprite in world/screen coordinates.
  169. /// </summary>
  170. /// <param name="transform">The transformation of the sprite.</param>
  171. /// <returns>The bounding rectangle of the sprite in world/screen coordinates.</returns>
  172. public RectangleF GetBoundingRectangle(Transform2 transform)
  173. {
  174. return GetBoundingRectangle(transform.Position, transform.Rotation, transform.Scale);
  175. }
  176. /// <summary>
  177. /// Gets the bounding rectangle of the sprite in world/screen coordinates.
  178. /// </summary>
  179. /// <param name="position">The xy-coordinate position of the sprite in world/screen coordinates.</param>
  180. /// <param name="rotation">The rotation, in radians, of the sprite.</param>
  181. /// <param name="scale">The scale of the sprite.</param>
  182. /// <returns>The bounding rectangle of the sprite in world/screen coordinates.</returns>
  183. public RectangleF GetBoundingRectangle(Vector2 position, float rotation, Vector2 scale)
  184. {
  185. var corners = GetCorners(position, rotation, scale);
  186. var min = new Vector2(corners.Min(i => i.X), corners.Min(i => i.Y));
  187. var max = new Vector2(corners.Max(i => i.X), corners.Max(i => i.Y));
  188. return new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y);
  189. }
  190. /// <summary>
  191. /// Gets the corner points of the sprite in world/screen coordinates.
  192. /// </summary>
  193. /// <param name="position">The xy-coordinate position of the sprite in world/screen coordinates.</param>
  194. /// <param name="rotation">The rotation, in radians, of the sprite.</param>
  195. /// <param name="scale">The scale of the sprite.</param>
  196. /// <returns>The corner points of the sprite in world/screen coordinates.</returns>
  197. public Vector2[] GetCorners(Vector2 position, float rotation, Vector2 scale)
  198. {
  199. var min = -Origin;
  200. var max = min + new Vector2(TextureRegion.OriginalSize.Width, TextureRegion.OriginalSize.Height);
  201. var offset = position;
  202. if (scale != Vector2.One)
  203. {
  204. min *= scale;
  205. max = max * scale;
  206. }
  207. var corners = new Vector2[4];
  208. corners[0] = min;
  209. corners[1] = new Vector2(max.X, min.Y);
  210. corners[2] = max;
  211. corners[3] = new Vector2(min.X, max.Y);
  212. if (rotation != 0)
  213. {
  214. var matrix = Matrix.CreateRotationZ(rotation);
  215. for (var i = 0; i < 4; i++)
  216. {
  217. corners[i] = Vector2.Transform(corners[i], matrix);
  218. }
  219. }
  220. for (var i = 0; i < 4; i++)
  221. {
  222. corners[i] += offset;
  223. }
  224. return corners;
  225. }
  226. /// <summary>
  227. /// Creates a shallow copy of this sprite with the same texture region and property values.
  228. /// </summary>
  229. /// <remarks>
  230. /// The returned sprite shares the same <see cref="TextureRegion"/> reference.
  231. /// The <see cref="Tag"/> reference is copied but the object itself is not cloned.
  232. /// </remarks>
  233. /// <returns>A new <see cref="Sprite"/> instance with copied property values.</returns>
  234. /// <exception cref="ObjectDisposedException">
  235. /// Thrown if the source texture of the <see cref="TextureRegion"/> of this sprite has been disposed of.
  236. /// </exception>
  237. public Sprite Clone()
  238. {
  239. return new Sprite(this);
  240. }
  241. }