Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ProwlEngine/Prowl/llms.txt

Use this file to discover all available pages before exploring further.

EngineObject is the root base class for every first-class object in the Prowl runtime — GameObject, MonoBehaviour, Scene, assets, and more all derive from it. It establishes a globally unique runtime identity via InstanceID, optional persistent asset identity via AssetID, a human-readable Name, a two-phase destruction system (DestroyLater / DestroyImmediate), and a deep-clone API backed by Prowl’s attribute-driven cloning infrastructure. Understanding EngineObject is essential before working with the scene hierarchy or writing editor tooling.
EngineObject carries [CloneBehavior(CloneBehavior.Reference)] at the class level, which means the cloning system treats any EngineObject reference as shared by default. Subclasses override this by applying their own [CloneBehavior] attribute — for example, MonoBehaviour uses CloneBehavior.ChildObject.

Identity Properties

InstanceID
int
A monotonically incrementing integer assigned at construction time. Guaranteed unique within a single process lifetime. Never persisted to disk — it changes every time an object is created, including after deserialization.
public int InstanceID => _instanceID;
Debug.Log($"Object ID: {go.InstanceID}");
AssetID
Guid
The persistent asset GUID. Guid.Empty for runtime-only objects (GameObjects that were never saved as assets). Set by the asset pipeline when an object is loaded from disk.
[HideInInspector]
[CloneField(CloneFieldFlags.Skip)]
public Guid AssetID = Guid.Empty;
FileID
ushort
A sub-file identifier used when multiple objects are packed into a single asset file. 0 when the object is the sole resident of its file.
[HideInInspector]
[CloneField(CloneFieldFlags.Skip)]
public ushort FileID = 0;
Name
string
Human-readable display name. Defaults to "New" + GetType().Name at construction, then overridden by the caller. Persisted to disk via the serialization header.
[HideInInspector]
public string Name;
IsDestroyed
bool
Set to true by either destroy path. The equality operators (== / !=) treat any two destroyed EngineObjects as equal and treat a destroyed object as equal to null. Check this flag before using a cached reference.
[HideInInspector, SerializeIgnore]
[CloneField(CloneFieldFlags.Skip)]
public bool IsDestroyed = false;
if (myObject == null || myObject.IsDestroyed)
    return; // safe guard

Lifecycle Methods

CreatedInstance (virtual)

Called automatically at the end of the EngineObject base constructor, before Name is applied. Override in subclasses to perform initialization that must happen at the moment of allocation.
public virtual void CreatedInstance() { }

DestroyLater

Marks the object as destroyed and pushes it onto the deferred destruction stack. The object is not disposed immediately; disposal happens the next time EngineObject.HandleDestroyed() is called (typically at the end of the frame by the scene manager).
public void DestroyLater()
Prefer DestroyLater during gameplay. It avoids mutating collections while iterating them and keeps destruction deterministic within a frame boundary.
// Schedule an enemy for removal at end-of-frame
enemy.DestroyLater();

DestroyImmediate

Marks the object as destroyed and calls OnDispose() synchronously. Use with care — calling this while the object’s list is being iterated can cause collection-modification exceptions.
public void DestroyImmediate()
DestroyImmediate bypasses the deferred queue. Use it only in editor tooling, teardown sequences where no iteration is in progress, or when you explicitly need synchronous cleanup.

HandleDestroyed (static)

Drains the deferred destruction stack, calling OnDispose() on every object that was queued by DestroyLater(). Called automatically by SceneManager.Clear() and should be called by the engine host after each frame if objects may have been deferred.
public static void HandleDestroyed()
// Typical end-of-frame cleanup (done internally by SceneManager)
EngineObject.HandleDestroyed();

OnDispose (virtual)

Override in subclasses to release resources when the object is destroyed. The default implementation is empty. GameObject uses this to recursively destroy children and components; MonoBehaviour uses it to detach from its GameObject.
public virtual void OnDispose() { }

OnValidate (virtual)

Called by the editor inspector when a serialized field is modified. Override to enforce invariants (e.g. clamping a value or updating a cached computation).
public virtual void OnValidate() { }

Clone API

Prowl’s cloning system is attribute-driven and supports deep copying with configurable field handling. EngineObject implements ICloneExplicit to coordinate the two-pass clone operation.

Clone

Creates a deep copy of this object using the Prowl cloning infrastructure.
public EngineObject Clone()
returns
EngineObject
A newly allocated object with all fields deep-copied according to their [CloneField] and [CloneBehavior] attributes.
EngineObject copy = original.Clone();

CopyTo

Copies data from this object into an existing target of the same type.
public void CopyTo(EngineObject target)
target
EngineObject
required
The destination object. Must be the same concrete type as the source.
sourceObject.CopyTo(targetObject);

OnSetupCloneTargets (virtual, protected)

Override to customize the first pass of cloning, where the clone graph is constructed and target objects are linked. Called internally; only needed for advanced scenarios.
protected virtual void OnSetupCloneTargets(object target, ICloneTargetSetup setup) { }

OnCopyDataTo (virtual, protected)

Override to customize the second pass of cloning, where field values are actually copied. Called internally after all targets are established.
protected virtual void OnCopyDataTo(object target, ICloneOperation operation) { }

Cloning Attributes

Applied to a class to set the default clone strategy for all instances of that type.
[CloneBehavior(CloneBehavior.Reference)]   // copy by reference (no deep copy)
[CloneBehavior(CloneBehavior.ChildObject)] // always deep-copy
[CloneBehavior(CloneBehavior.Default)]     // use automatic heuristics
ValueMeaning
DefaultDetermined automatically from type attributes and context.
ReferenceThe reference is copied as-is; the object itself is not cloned.
ChildObjectThe object is owned by its parent and deep-copied.
Applied to individual fields to refine cloning behavior regardless of the class-level setting.
[CloneField(CloneFieldFlags.Skip)]              // never copied
[CloneField(CloneFieldFlags.IdentityRelevant)]  // skipped when PreserveIdentity is true
[CloneField(CloneFieldFlags.DontSkip)]          // force-copied even if other hints say skip
FlagEffect
NoneNo special handling.
IdentityRelevantField encodes identity (e.g. _instanceID); skipped in identity-preserving copies.
SkipAlways omitted from the clone.
DontSkipOverrides SerializeIgnore and similar skip hints.

Static Search Methods

FindObjectsOfType<T>

Searches the active scene for all GameObjects and MonoBehaviour components that are assignable to T.
public static T?[] FindObjectsOfType<T>() where T : EngineObject
returns
T?[]
Array of all matching objects. May contain null entries if a match slot could not be cast.
// Find all active enemy scripts
EnemyAI[] enemies = EngineObject.FindObjectsOfType<EnemyAI>();
FindObjectsOfType iterates the entire scene graph every call. Cache the result rather than calling it in Update.

FindObjectByID<T>

Finds an EngineObject by its runtime InstanceID.
public static T? FindObjectByID<T>(int id) where T : EngineObject
id
int
required
The InstanceID to search for.
returns
T?
The matching object cast to T, or null if not found or the cast fails.
GameObject? found = EngineObject.FindObjectByID<GameObject>(savedID);

FindObjectByIdentifier<T>

Finds an EngineObject by its persistent Guid identifier. Useful after deserialization, where InstanceIDs have changed but Guids are stable.
public static T? FindObjectByIdentifier<T>(Guid identifier) where T : EngineObject
identifier
Guid
required
The persistent GUID to search for. On GameObject this is GameObject.Identifier; on MonoBehaviour this is MonoBehaviour.Identifier.
returns
T?
The matching object cast to T, or null if not found.
MonoBehaviour? comp = EngineObject.FindObjectByIdentifier<MonoBehaviour>(savedGuid);

Equality and Null Safety

EngineObject overrides == and != to treat destroyed objects as null-equivalent:
public static bool operator ==(EngineObject left, EngineObject right)
public static bool operator !=(EngineObject left, EngineObject right)
ComparisonResult
null == nulltrue
null == destroyedtrue
destroyed == destroyedtrue
alive == alive (same ref)true
alive == alive (different ref)false
The extension method Null() provides a concise null-or-destroyed check:
// Extension on EngineObject (defined in EngineObjectExtensions)
public static bool Null(this EngineObject obj) => obj == null || obj.IsDestroyed;
if (myObject.Null())
    return; // object is gone

Build docs developers (and LLMs) love