Skip to content

Commit ed2c2c4

Browse files
committed
Fix: SpawnArea deferred adding/removing system for GameObjects during update loop added.
Fix: GameObject Layer lock added. While a GameObject is managed by a SpawnArea the layer can no longer be changed. Nuget: Version changed from 5.2.0 to 5.3.0 and 5.3.0 Release Notes added.
1 parent f28df28 commit ed2c2c4

3 files changed

Lines changed: 695 additions & 506 deletions

File tree

ShapeEngine/Core/GameObject.cs

Lines changed: 100 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.ComponentModel;
12
using System.Numerics;
23
using ShapeEngine.Core.Structs;
34
using ShapeEngine.Geometry.RectDef;
@@ -13,6 +14,8 @@ namespace ShapeEngine.Core;
1314
/// </remarks>
1415
public abstract class GameObject : IUpdateable, IDrawable
1516
{
17+
#region Events
18+
1619
/// <summary>
1720
/// Occurs when this object is killed.
1821
/// <list type="bullet">
@@ -22,6 +25,7 @@ public abstract class GameObject : IUpdateable, IDrawable
2225
/// </list>
2326
/// </summary>
2427
public event Action<GameObject, string?, GameObject?>? OnKilled;
28+
2529
/// <summary>
2630
/// Occurs when this object is revived.
2731
/// <list type="bullet">
@@ -32,15 +36,68 @@ public abstract class GameObject : IUpdateable, IDrawable
3236
/// </summary>
3337
public event Action<GameObject, string?, GameObject?>? OnRevived;
3438

39+
#endregion
40+
41+
#region Properties
42+
3543
/// <summary>
3644
/// Gets or sets the transform (position, rotation, scale) of this object.
3745
/// </summary>
3846
public Transform2D Transform { get; set; }
47+
3948
/// <summary>
4049
/// Gets whether this object is dead (killed).
4150
/// </summary>
4251
public bool IsDead { get; private set; }
4352

53+
/// <summary>
54+
/// Gets a value indicating whether this object is currently managed by a <see cref="SpawnArea"/>.
55+
/// </summary>
56+
/// <value>
57+
/// <see langword="true"/> if the object has been added to and is actively managed by a <see cref="SpawnArea"/>; otherwise, <see langword="false"/>.
58+
/// </value>
59+
/// <remarks>
60+
/// <para>
61+
/// <b>Deferred Processing:</b> Because <see cref="SpawnArea"/> processes additions and removals using deferred logic,
62+
/// this state may not change immediately upon calling <see cref="SpawnArea.AddGameObject(GameObject)"/> or <see cref="SpawnArea.RemoveGameObject(GameObject)"/>.
63+
/// Instead, the value is updated once the object is actually processed (which can be delayed until the end of the current frame).
64+
/// </para>
65+
/// </remarks>
66+
public bool IsSpawned { get; private set; }
67+
68+
private uint currentLayer;
69+
70+
/// <summary>
71+
/// Gets or sets the area layer in which this object is stored and drawn. Higher layers are rendered on top of lower layers.
72+
/// </summary>
73+
/// <remarks>
74+
/// <para>
75+
/// <b>Restriction:</b> The layer <b>cannot</b> be changed while the object is spawned (i.e., when <see cref="IsSpawned"/> is <see langword="true"/>).
76+
/// </para>
77+
/// <para>
78+
/// You must check that <see cref="IsSpawned"/> is <see langword="false"/> (or remove the object from its <see cref="SpawnArea"/>)
79+
/// before setting this value. Attempting to modify the layer while spawned throws an <see cref="InvalidOperationException"/>.
80+
/// </para>
81+
/// </remarks>
82+
/// <exception cref="InvalidOperationException">Thrown when attempting to set the layer while <see cref="IsSpawned"/> is <see langword="true"/>.</exception>
83+
public uint Layer
84+
{
85+
get => currentLayer;
86+
set
87+
{
88+
if (IsSpawned)
89+
{
90+
throw new InvalidOperationException(
91+
"Layer cannot be changed while the GameObject is spawned. Remove it from the SpawnArea first.");
92+
}
93+
currentLayer = value;
94+
}
95+
}
96+
97+
#endregion
98+
99+
#region Public Methods
100+
44101
/// <summary>
45102
/// Gets the bounding box of this object in world space.
46103
/// </summary>
@@ -74,17 +131,14 @@ public abstract class GameObject : IUpdateable, IDrawable
74131
/// <param name="gameArea">The area of the game world.</param>
75132
/// <returns>True if drawing to game, otherwise false.</returns>
76133
public virtual bool IsDrawingToGame(Rect gameArea) => true;
134+
77135
/// <summary>
78136
/// Determines if this object should be drawn to the game UI. (default = false)
79137
/// </summary>
80138
/// <param name="gameUiArea">The area of the game UI.</param>
81139
/// <returns>True if drawing to game UI, otherwise false.</returns>
82140
public virtual bool IsDrawingToGameUI(Rect gameUiArea) => false;
83-
84-
/// <summary>
85-
/// Gets or sets the area layer this object is stored in. Higher layers are drawn on top of lower layers.
86-
/// </summary>
87-
public uint Layer { get; set; }
141+
88142
/// <summary>
89143
/// Called by the area to update the object's position based on the new parallax position.
90144
/// </summary>
@@ -103,6 +157,7 @@ public virtual void UpdateParallaxe(Vector2 newParallaxPosition) { }
103157
/// </summary>
104158
/// <param name="spawnArea">The spawn area this object is added to.</param>
105159
public virtual void OnSpawned(SpawnArea spawnArea){}
160+
106161
/// <summary>
107162
/// Called by the area once a game object is removed or dead.
108163
/// </summary>
@@ -136,20 +191,7 @@ public bool Kill(string? killMessage = null, GameObject? killer = null)
136191

137192
return false;
138193
}
139-
/// <summary>
140-
/// Called after the object is killed. Override for custom logic.
141-
/// </summary>
142-
/// <param name="killMessage">Optional message for the kill event.</param>
143-
/// <param name="killer">Optional killer object.</param>
144-
protected virtual void WasKilled(string? killMessage = null, GameObject? killer = null) { }
145-
/// <summary>
146-
/// Called before the object is killed. Override to prevent kill by returning false.
147-
/// </summary>
148-
/// <param name="killMessage">Optional message for the kill event.</param>
149-
/// <param name="killer">Optional killer object.</param>
150-
/// <returns>True to allow kill, false to prevent.</returns>
151-
protected virtual bool TryKill(string? killMessage = null, GameObject? killer = null) => true;
152-
194+
153195
/// <summary>
154196
/// Tries to revive this game object.
155197
/// </summary>
@@ -170,17 +212,56 @@ public bool Revive(string? reviveMessage = null, GameObject? reviver = null)
170212

171213
return false;
172214
}
215+
216+
#endregion
217+
218+
#region Protected Methods
219+
220+
/// <summary>
221+
/// Called after the object is killed. Override for custom logic.
222+
/// </summary>
223+
/// <param name="killMessage">Optional message for the kill event.</param>
224+
/// <param name="killer">Optional killer object.</param>
225+
protected virtual void WasKilled(string? killMessage = null, GameObject? killer = null) { }
226+
227+
/// <summary>
228+
/// Called before the object is killed. Override to prevent kill by returning false.
229+
/// </summary>
230+
/// <param name="killMessage">Optional message for the kill event.</param>
231+
/// <param name="killer">Optional killer object.</param>
232+
/// <returns>True to allow kill, false to prevent.</returns>
233+
protected virtual bool TryKill(string? killMessage = null, GameObject? killer = null) => true;
234+
173235
/// <summary>
174236
/// Called after the object is revived. Override for custom logic.
175237
/// </summary>
176238
/// <param name="reviveMessage">Optional message for the revive event.</param>
177239
/// <param name="reviver">Optional reviver object.</param>
178240
protected virtual void WasRevived(string? reviveMessage = null, GameObject? reviver = null) { }
241+
179242
/// <summary>
180243
/// Called before the object is revived. Override to prevent revive by returning false.
181244
/// </summary>
182245
/// <param name="reviveMessage">Optional message for the revive event.</param>
183246
/// <param name="reviver">Optional reviver object.</param>
184247
/// <returns>True to allow revive, false to prevent.</returns>
185248
protected virtual bool TryRevive(string? reviveMessage = null, GameObject? reviver = null) => true;
249+
250+
#endregion
251+
252+
#region Internal
253+
254+
internal void ResolveOnSpawned(SpawnArea spawnArea)
255+
{
256+
IsSpawned = true;
257+
OnSpawned(spawnArea);
258+
}
259+
260+
internal void ResolveOnDespawned(SpawnArea spawnArea)
261+
{
262+
IsSpawned = false;
263+
OnDespawned(spawnArea);
264+
}
265+
266+
#endregion
186267
}

0 commit comments

Comments
 (0)