1using System.Collections.ObjectModel;
14public sealed class SpriteManager : IDisposable
16 [Newtonsoft.Json.JsonIgnore]
17 internal DefinitionProvenance? DefinitionProvenance {
get;
set; }
19 private static readonly Lazy<SpriteManager> _instance =
new(() =>
new SpriteManager());
25 public static SpriteManager
Instance => _instance.Value;
27 internal readonly List<Sprite> _spriteList =
new();
28 private readonly
object _spriteListLock =
new();
30 private long _lastTick = EngineSimulationClock.GetCurrentTick();
37 private SpriteManager() { }
46 lock (_spriteListLock)
47 return new List<Sprite>(_spriteList).AsReadOnly();
56 private string _defaultCollisionProfile = CollisionProfileNames.Actor;
64 get => _defaultCollisionProfile;
67 if (
string.IsNullOrWhiteSpace(value))
68 throw new ArgumentException(
"Default collision profile cannot be empty.", nameof(value));
70 _defaultCollisionProfile = value;
73 #region public methods
89 string? collisionProfileName =
null)
91 var profileName =
string.IsNullOrWhiteSpace(collisionProfileName)
93 : collisionProfileName;
95 var sprite =
new Sprite(sceneLayer, frame, profileName);
97 PublishCreatedSprite(sprite);
118 PublishCreatedSprite(newSprite);
163 DefinitionProvenance =
null;
164 List<Sprite> tempSprites;
165 lock (_spriteListLock)
166 tempSprites =
new List<Sprite>(_spriteList);
167 foreach (
Sprite sprite
in tempSprites)
178 List<Sprite> snapshot;
179 lock (_spriteListLock)
180 snapshot =
new List<Sprite>(_spriteList);
182 foreach (
Sprite sprite
in snapshot)
191 internal bool HasVisibleSpriteInWorldRect(
195 lock (_spriteListLock)
197 for (
int i = 0; i < _spriteList.Count; i++)
199 var sprite = _spriteList[i];
200 if (sprite.Visible &&
201 ReferenceEquals(sprite.SceneLayer, sceneLayer) &&
202 sprite.VisualBoundsWorld.IntersectsWith(worldRect))
223 bool fullEnclosures =
false)
225 List<Sprite> retSprites =
new();
227 List<Sprite> snapshot;
228 lock (_spriteListLock)
229 snapshot =
new List<Sprite>(_spriteList);
231 foreach (
Sprite sprite
in snapshot)
233 if ((sceneLayer is
null) || (sprite.
SceneLayer == sceneLayer))
238 retSprites.Add(sprite);
243 retSprites.Add(sprite);
262 Rectangle viewRectPx,
264 bool fullEnclosures =
false)
266 var retSprites =
new List<Sprite>();
268 List<Sprite> snapshot;
269 lock (_spriteListLock)
270 snapshot =
new List<Sprite>(_spriteList);
272 foreach (var sprite
in snapshot)
274 if ((sceneLayer is
null) || (sprite.
SceneLayer == sceneLayer))
280 if (viewRectPx.Contains(rectScreen))
281 retSprites.Add(sprite);
285 if (rectScreen.IntersectsWith(viewRectPx))
286 retSprites.Add(sprite);
306 var retSprites =
new List<Sprite>();
308 List<Sprite> snapshot;
309 lock (_spriteListLock)
310 snapshot =
new List<Sprite>(_spriteList);
312 foreach (
Sprite sprite
in snapshot)
314 if ((sceneLayer is
null) || (sprite.
SceneLayer == sceneLayer))
317 retSprites.Add(sprite);
324 #endregion public methods
326 #region internal methods
328 internal void AddSprite(
Sprite sprite)
330 lock (_spriteListLock)
331 _spriteList.Add(sprite);
339 internal void RefreshCollisionProfiles(
SceneLayer sceneLayer)
341 ArgumentNullException.ThrowIfNull(sceneLayer);
343 List<Sprite> snapshot;
344 lock (_spriteListLock)
345 snapshot =
new List<Sprite>(_spriteList);
347 foreach (var sprite
in snapshot)
352 if (ReferenceEquals(sprite.SceneLayer, sceneLayer))
353 sprite.RefreshCollisionProfile();
357 internal void MoveSprites(
long tick)
359 if (tick <= _lastTick)
362 float duration = HighResTimer.GetDuration(_lastTick, tick);
364 lock (_spriteListLock)
366 for (
int i = 0; i < _spriteList.Count; i++)
368 var sprite = _spriteList[i];
370 if (sprite._pendingDispose)
373 sprite.Movement.AdvanceMovement(duration);
374 sprite.AdvanceResize(duration);
375 sprite.AdvanceJiggle(duration);
378 SweepDisposedSprites();
384 private void SweepDisposedSprites()
387 while (i < _spriteList.Count)
389 if (!_spriteList[i]._pendingDispose)
395 _spriteList[i].DisposeImmediate();
397 int last = _spriteList.Count - 1;
398 _spriteList[i] = _spriteList[last];
399 _spriteList.RemoveAt(last);
403 private void PublishCreatedSprite(Sprite sprite)
412 lock (_spriteListLock)
413 _spriteList.Remove(sprite);
414 sprite.DisposeImmediate();
419 #endregion internal methods
Sprite? GetSpriteByID(string ID)
Retrieves a sprite by its ID/nickname.
ReadOnlyCollection< Sprite > AllSprites
Gets a read-only collection of all sprites currently managed by the sprite manager.
List< Sprite > GetSpritesInViewRectRange(View view, Rectangle viewRectPx, SceneLayer? sceneLayer=null, bool fullEnclosures=false)
Gets all sprites within the specified view rectangle range.
Sprite CreateSprite(SceneLayer sceneLayer, Frame frame, string? id=null, string? collisionProfileName=null)
Creates a new sprite on the specified scene layer with the given frame.
string DefaultCollisionProfile
Gets or sets the scene collision profile assigned to newly created sprites when no profile is specifi...
List< Sprite > GetSpritesInWorldRectRange(Rectangle worldRect, SceneLayer? sceneLayer=null, bool fullEnclosures=false)
Gets all sprites within the specified world rectangle range.
Sprite CloneSprite(Sprite sprite)
Creates a copy of the specified sprite, preserving its current scene layer.
void Clear()
Removes and disposes all sprites currently managed by the sprite manager.
Sprite CloneSprite(Sprite sprite, SceneLayer sceneLayer)
Creates a clone of the specified sprite on the given scene layer.
void Dispose()
Releases all sprites currently managed by this instance.
bool SizeNewSpritesToSceneLayer
Gets or sets a value indicating whether new sprites should be automatically sized to their scene laye...
void Remove(string ID)
Removes and disposes the sprite with the specified ID.
Action< Sprite >? SpriteCreated
Event raised when a new sprite is created.
void Remove(Sprite sprite)
Removes and disposes the specified sprite.
static SpriteManager Instance
Gets the singleton instance of the sprite manager.
Sprite? CloneSprite(string id, SceneLayer sceneLayer)
Creates a clone of the sprite with the specified ID on the given scene layer.
List< Sprite > GetSpritesAtViewPixel(View view, Point viewPxlPt, SceneLayer? sceneLayer=null)
Gets all sprites at the specified view pixel coordinate.
Represents a movable visual element on a scene layer.
override void Dispose()
Releases the resources owned by this instance.
RectangleF GetVisualBoundsScreen(View view)
Gets the axis-aligned screen-pixel bounds enclosing the rotated sprite.
Rectangle VisualBoundsWorld
Gets the axis-aligned world-pixel bounds enclosing the rotated sprite.
override SceneLayer SceneLayer
string? Nickname
Gets or sets the optional lookup name.
Represents a rendered view of a scene, combining a camera position with a viewport configuration to c...
Represents a single layer within a Scene, containing a grid of tiles and supporting various coordinat...
Represents the source Tilesheet and its coordinates to render on a destination.