Skip to content

Persistent Identities ​

In Unreal Engine, actor names (e.g., StaticMeshActor_42 or BP_Chest_C_1) are volatile: they mutate when levels are re-saved, change when actors are moved to different streaming cells, and collide when levels are instanced.

Aegis Save eliminates name-based fragility by establishing Permanent, Bit-Exact 128-Bit GUID Identities across every persistent object in your game.


1. The Four Identity Archetypes ​

Identity ArchetypeSource of TruthLifecycle & BehaviorExample Use Case
1. Hand-Placed Level ActorAdopted ActorGuid (Serialized to package)Permanent across world re-partitioning, cell streaming, and cooked builds.Level-placed loot chests, doors, puzzles, world bosses.
2. Runtime Spawned ActorMonotonic Counter (Persisted in Global scope)Assigned at runtime via SpawnPersistentActor; recreated automatically on load.Dropped player weapons, dynamic campfires, vehicle spawns.
3. Registered Plain ObjectStable Label via MakePersistentIdFromLabelDeterministic 128-bit hash derived from a stable name string; persists non-actors.Inventory managers, quest journals, player stats models.
4. Instanced Level ActorCook-time Ancestor Composition (C-83)Level Instance transform and placement seeds composed deterministically into actor GUIDs.Reusable modular dungeon rooms, instanced enemy outposts.

2. Hand-Placed Actors & Cooked Read-Back (§6.0s) ​

When you place an actor in Unreal Editor and attach UAegisPersistenceComponent, Aegis automatically adopts the actor's editor-assigned ActorGuid into the component's own serialized PersistentId property.

The Cooked Build Hazard — And How Aegis Solves It ​

In Unreal Engine, AActor::ActorGuid is wrapped in WITH_EDITORONLY_DATA: it is stripped completely from cooked standalone and Shipping client builds.

Many marketplace plugins fail in Shipping builds because they rely on ActorGuid at runtime. Aegis Save solves this through Pre-Save Package Identity Stamping:

  1. During level editing and map saving (PreSave), UAegisPersistenceComponent captures the editor GUID and serializes it into the actor's persistent package data.
  2. In cooked client builds where actorGuid=N/A, UAegisPersistenceComponent recovers its adopted PersistentId bit-exact with HasValidId() == true (Section 6.0s Verified).
text
Editor Authoring:  [Details Panel] ──► Adopt ActorGuid ──► Serialize PersistentId into Map Package
                                                                    │
Cook Pipeline:     [Cook & Package] ──► ActorGuid Stripped          │
                                                                    ▼
Shipping Client:   [Loaded in Game] ◄── Read-Back Bit-Exact PersistentId (actorGuid = N/A)

3. Runtime Spawned Actors (SpawnPersistentActor) ​

Actors spawned during gameplay cannot use editor GUIDs because they do not exist in the level file. If you use standard SpawnActor, the actor will vanish when you reload a save.

To spawn an actor that survives save and load:

text
[Spawn Persistent Actor (Aegis)]
  ├── Class: BP_DroppedWeapon
  ├── Transform: HitLocation
  └── Return Value ──► [Set Weapon Ammo]
cpp
#include "Core/AegisSubsystem.h"

UAegisSubsystem* Aegis = UAegisSubsystem::Get(this);
AYourDroppedItem* SpawnedItem = Aegis->SpawnPersistentActor<AYourDroppedItem>(
    AYourDroppedItem::StaticClass(),
    SpawnTransform
);

Aegis assigns a deterministic, monotonically increasing 64-bit sequence counter stored in the save container's Global scope. When the save is reloaded, Aegis automatically re-instantiates the spawned actor class, reassigns its persistent identity, and restores its saved properties.


4. Plain Objects: Deterministic Labels ​

For UObjects that are not actors (such as custom UMG models, inventory state classes, or quest trackers), use MakePersistentIdFromLabel:

text
[Make Persistent Id From Label ("PlayerBackpack")]
                      │
                      ▼
[Register Persistent Object]
  ├── Object: InventoryObjectRef
  ├── Identity: (From Label)
  └── Scope: Player
cpp
#include "Core/AegisSubsystem.h"

UAegisSubsystem* Aegis = UAegisSubsystem::Get(this);
const FGuid BackpackId = Aegis->MakePersistentIdFromLabel(TEXT("PlayerBackpack"));

// Register under Player scope so it persists across map transitions
Aegis->RegisterPersistentObject(MyBackpackInstance, BackpackId, EAegisScope::Player);

Use Stable String Literals

Always pass constant string literals (e.g., TEXT("QuestManager") or TEXT("SkillTree")). Never generate labels using dynamic indices, timestamps, or display names that can change between game sessions.


5. Level Instances & Ancestor Composition (C-83) ​

When a designer places a Level Instance (ALevelInstance), all actors inside that sub-level exist in multiple places across the world. If each placed instance used identical authored GUIDs, saving would cause catastrophic identity collisions.

Aegis solves this with Cook-Time Deterministic Ancestor Composition:

  1. At cook time, UAegisPersistenceComponent::PreSave walks up the parent Level Instance hierarchy.
  2. It hashes the parent Level Instance placement seed with the actor's authored identity (C-83).
  3. Both Embedded (Partitioned) and Standalone (LevelStreaming) Level Instances generate unique, collision-free identities across streaming cycles (FO-27 / AC-214 Verified).

Next Steps ​

Aegis Save — Enterprise World Persistence for Unreal Engine 5.