Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
SpriteManager.cs
1using System.Collections.ObjectModel;
2using System.Drawing;
8
10
14public sealed class SpriteManager : IDisposable
15{
16 [Newtonsoft.Json.JsonIgnore]
17 internal DefinitionProvenance? DefinitionProvenance { get; set; }
18
19 private static readonly Lazy<SpriteManager> _instance = new(() => new SpriteManager());
20
25 public static SpriteManager Instance => _instance.Value;
26
27 internal readonly List<Sprite> _spriteList = new();
28 private readonly object _spriteListLock = new();
29
30 private long _lastTick = EngineSimulationClock.GetCurrentTick();
31
35 public event Action<Sprite>? SpriteCreated;
36
37 private SpriteManager() { }
38
42 public ReadOnlyCollection<Sprite> AllSprites
43 {
44 get
45 {
46 lock (_spriteListLock)
47 return new List<Sprite>(_spriteList).AsReadOnly();
48 }
49 }
50
54 public bool SizeNewSpritesToSceneLayer { get; set; } = true;
55
56 private string _defaultCollisionProfile = CollisionProfileNames.Actor;
57
63 {
64 get => _defaultCollisionProfile;
65 set
66 {
67 if (string.IsNullOrWhiteSpace(value))
68 throw new ArgumentException("Default collision profile cannot be empty.", nameof(value));
69
70 _defaultCollisionProfile = value;
71 }
72 }
73 #region public methods
74
86 SceneLayer sceneLayer,
87 Frame frame,
88 string? id = null,
89 string? collisionProfileName = null)
90 {
91 var profileName = string.IsNullOrWhiteSpace(collisionProfileName)
93 : collisionProfileName;
94
95 var sprite = new Sprite(sceneLayer, frame, profileName);
96 sprite.Nickname = id;
97 PublishCreatedSprite(sprite);
98 return sprite;
99 }
100
106 public Sprite CloneSprite(Sprite sprite) => CloneSprite(sprite, sprite.SceneLayer);
107
114 public Sprite CloneSprite(Sprite sprite, SceneLayer sceneLayer)
115 {
116 Sprite newSprite = new Sprite(sprite, sceneLayer);
117
118 PublishCreatedSprite(newSprite);
119 return newSprite;
120 }
121
128 public Sprite? CloneSprite(string id, SceneLayer sceneLayer)
129 {
130 Sprite? sprite = GetSpriteByID(id);
131
132 if (sprite != null)
133 return CloneSprite(sprite, sceneLayer);
134
135 return null;
136 }
137
142 public void Remove(Sprite sprite)
143 {
144 sprite.Dispose();
145 }
146
151 public void Remove(string ID)
152 {
153 Sprite? sprite = GetSpriteByID(ID);
154 if (sprite != null)
155 Remove(sprite);
156 }
157
161 public void Clear()
162 {
163 DefinitionProvenance = null;
164 List<Sprite> tempSprites;
165 lock (_spriteListLock)
166 tempSprites = new List<Sprite>(_spriteList);
167 foreach (Sprite sprite in tempSprites)
168 Remove(sprite);
169 }
170
176 public Sprite? GetSpriteByID(string ID)
177 {
178 List<Sprite> snapshot;
179 lock (_spriteListLock)
180 snapshot = new List<Sprite>(_spriteList);
181
182 foreach (Sprite sprite in snapshot)
183 {
184 if (sprite.Nickname == ID)
185 return sprite;
186 }
187
188 return null;
189 }
190
191 internal bool HasVisibleSpriteInWorldRect(
192 Rectangle worldRect,
193 SceneLayer sceneLayer)
194 {
195 lock (_spriteListLock)
196 {
197 for (int i = 0; i < _spriteList.Count; i++)
198 {
199 var sprite = _spriteList[i];
200 if (sprite.Visible &&
201 ReferenceEquals(sprite.SceneLayer, sceneLayer) &&
202 sprite.VisualBoundsWorld.IntersectsWith(worldRect))
203 {
204 return true;
205 }
206 }
207 }
208
209 return false;
210 }
211
212 // world
220 public List<Sprite> GetSpritesInWorldRectRange(
221 Rectangle worldRect,
222 SceneLayer? sceneLayer = null,
223 bool fullEnclosures = false)
224 {
225 List<Sprite> retSprites = new();
226
227 List<Sprite> snapshot;
228 lock (_spriteListLock)
229 snapshot = new List<Sprite>(_spriteList);
230
231 foreach (Sprite sprite in snapshot)
232 {
233 if ((sceneLayer is null) || (sprite.SceneLayer == sceneLayer))
234 {
235 if (fullEnclosures)
236 {
237 if (worldRect.Contains(sprite.VisualBoundsWorld))
238 retSprites.Add(sprite);
239 }
240 else
241 {
242 if (sprite.VisualBoundsWorld.IntersectsWith(worldRect))
243 retSprites.Add(sprite);
244 }
245 }
246 }
247
248 return retSprites;
249 }
250
251 // screen
260 public List<Sprite> GetSpritesInViewRectRange(
261 View view,
262 Rectangle viewRectPx,
263 SceneLayer? sceneLayer = null,
264 bool fullEnclosures = false)
265 {
266 var retSprites = new List<Sprite>();
267
268 List<Sprite> snapshot;
269 lock (_spriteListLock)
270 snapshot = new List<Sprite>(_spriteList);
271
272 foreach (var sprite in snapshot)
273 {
274 if ((sceneLayer is null) || (sprite.SceneLayer == sceneLayer))
275 {
276 var rectScreen = sprite.GetVisualBoundsScreen(view).ToPixelAlignedRect();
277
278 if (fullEnclosures)
279 {
280 if (viewRectPx.Contains(rectScreen))
281 retSprites.Add(sprite);
282 }
283 else
284 {
285 if (rectScreen.IntersectsWith(viewRectPx))
286 retSprites.Add(sprite);
287 }
288 }
289 }
290
291 return retSprites;
292 }
293
301 public List<Sprite> GetSpritesAtViewPixel(
302 View view,
303 Point viewPxlPt,
304 SceneLayer? sceneLayer = null)
305 {
306 var retSprites = new List<Sprite>();
307
308 List<Sprite> snapshot;
309 lock (_spriteListLock)
310 snapshot = new List<Sprite>(_spriteList);
311
312 foreach (Sprite sprite in snapshot)
313 {
314 if ((sceneLayer is null) || (sprite.SceneLayer == sceneLayer))
315 {
316 if (sprite.GetVisualBoundsScreen(view).Contains(viewPxlPt))
317 retSprites.Add(sprite);
318 }
319 }
320
321 return retSprites;
322 }
323
324 #endregion public methods
325
326 #region internal methods
327
328 internal void AddSprite(Sprite sprite)
329 {
330 lock (_spriteListLock)
331 _spriteList.Add(sprite);
332 }
333
339 internal void RefreshCollisionProfiles(SceneLayer sceneLayer)
340 {
341 ArgumentNullException.ThrowIfNull(sceneLayer);
342
343 List<Sprite> snapshot;
344 lock (_spriteListLock)
345 snapshot = new List<Sprite>(_spriteList);
346
347 foreach (var sprite in snapshot)
348 {
349 if (sprite is null)
350 continue;
351
352 if (ReferenceEquals(sprite.SceneLayer, sceneLayer))
353 sprite.RefreshCollisionProfile();
354 }
355 }
356
357 internal void MoveSprites(long tick)
358 {
359 if (tick <= _lastTick)
360 return;
361
362 float duration = HighResTimer.GetDuration(_lastTick, tick);
363
364 lock (_spriteListLock)
365 {
366 for (int i = 0; i < _spriteList.Count; i++)
367 {
368 var sprite = _spriteList[i];
369
370 if (sprite._pendingDispose)
371 continue;
372
373 sprite.Movement.AdvanceMovement(duration);
374 sprite.AdvanceResize(duration);
375 sprite.AdvanceJiggle(duration);
376 }
377
378 SweepDisposedSprites();
379 }
380
381 _lastTick = tick;
382 }
383
384 private void SweepDisposedSprites()
385 {
386 int i = 0;
387 while (i < _spriteList.Count)
388 {
389 if (!_spriteList[i]._pendingDispose)
390 {
391 i++;
392 continue;
393 }
394
395 _spriteList[i].DisposeImmediate();
396
397 int last = _spriteList.Count - 1;
398 _spriteList[i] = _spriteList[last];
399 _spriteList.RemoveAt(last);
400 }
401 }
402
403 private void PublishCreatedSprite(Sprite sprite)
404 {
405 try
406 {
407 SpriteCreated?.Invoke(sprite);
408 }
409 catch
410 {
411 // The caller cannot roll back an instance that never returned from CreateSprite/CloneSprite.
412 lock (_spriteListLock)
413 _spriteList.Remove(sprite);
414 sprite.DisposeImmediate();
415 throw;
416 }
417 }
418
419 #endregion internal methods
420
424 public void Dispose()
425 {
426 Clear();
427 }
428}
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.
Definition Sprite.cs:20
override void Dispose()
Releases the resources owned by this instance.
Definition Sprite.cs:669
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.
Definition Sprite.cs:356
override SceneLayer SceneLayer
Definition Sprite.cs:624
string? Nickname
Gets or sets the optional lookup name.
Definition Tile.cs:107
Represents a rendered view of a scene, combining a camera position with a viewport configuration to c...
Definition View.cs:14
Represents a single layer within a Scene, containing a grid of tiles and supporting various coordinat...
Definition SceneLayer.cs:40
Represents the source Tilesheet and its coordinates to render on a destination.
Definition Frame.cs:12