Skip to main content

DynamicRef Subsystem

Base:
UWorldSubsystem
Type:
UNDynamicRefSubsystem
Header File:
NexusDynamicRefs/Public/NDynamicRefSubsystem.h

A locator system that maintains a map that organizes UObject into predefined categories (ENDynamicRef), named buckets (FName), or FGameplayTag keys. Tag-keyed entries are stored in the same FName-bucket map under the tag's TagName, so every tag accessor is effectively a thin convenience wrapper over its *ByName equivalent.

Weak References 0.3.1

Registrations are stored as TWeakObjectPtr — the subsystem never keeps a registered object alive. When a tracked UObject is destroyed or garbage-collected without an explicit Remove*, its entry simply goes stale; it is not a dangling pointer. Every Blueprint-facing accessor accounts for this:

  • GetObjects* / GetActors* filter stale entries out of the returned array.
  • GetFirstObject* / GetLastObject* skip stale entries to return the first/last live object.
  • GetCount* prunes stale entries and returns the live count (it calls Compact() internally).
  • GetNames / GetTags omit buckets whose objects have all gone stale.

You still want a matching Remove* for anything you Add* manually — leaning on weak expiry leaves empty buckets lingering until the next accessor compacts them. The weak storage is a safety net against missed removals and unexpected destruction, not a substitute for balanced add/remove.

Getting Actors​

Accessing the referenced AActors can be done with minimal overhead.

note

In the above blueprint example, the UNDynamicRefComponent would need to have its Lifecycle set to InitializeComponent in order to ensure it is registered prior to a hypothetical BeginPlay() event.

UFunctions​

tip

The UNDynamicRefComponent automatically manages the registration lifecycle.

Adding References​

Add Object​

/**
* Add a reference by ENDynamicRef to a specified UObject.
* @remark Be careful with the manual add method. If you add it, you must remove it!
* @param DynamicRef The desired ENDynamicRef to add to.
* @param InObject The UObject to be referenced by the provided ENDynamicRef.
*/
void AddObject(ENDynamicRef DynamicRef, UObject* InObject);

Add Object (By Name)​

/**
* Add a reference by FName to a specified UObject.
* @remark Be careful with the manual add method. If you add it, you must remove it!
* @param Name The desired FName to add to.
* @param InObject The UObject to be referenced by the FName.
*/
void AddObjectByName(FName Name, UObject* InObject);

Add Objects​

/**
* Add a reference by ENDynamicRef to a TArray of UObjects.
* @remark Be careful with the manual add method. If you add it, you must remove it!
* @param DynamicRef The desired ENDynamicRef to add to.
* @param InObjects The TArray of UObjects to be referenced by the provided ENDynamicRef.
*/
void AddObjects(ENDynamicRef DynamicRef, TArray<UObject*> InObjects);

Add Objects (By Name)​

/**
* Add a reference by FName to a TArray of UObjects.
* @remark Be careful with the manual add method. If you add it, you must remove it!
* @param Name The desired FName to add to.
* @param InObjects The TArray of UObjects to be referenced by the FName.
*/
void AddObjectsByName(FName Name, TArray<UObject*> InObjects);

Removing References​

Remove Object​

/**
* Remove a reference by ENDynamicRef to a specified UObject.
* @param DynamicRef The desired ENDynamicRef to remove from.
* @param InObject The UObject to be having its reference removed by the provided ENDynamicRef.
*/
void RemoveObject(ENDynamicRef DynamicRef, UObject* InObject);

Remove Object (By Name)​

/**
* Remove a reference by FName to a specified UObject.
* @remark Be careful with the manual remove method, it should be used for things that you have manually added.
* @param Name The desired FName to remove from.
* @param InObject The UObject to be having its reference removed by the FName.
*/
void RemoveObjectByName(FName Name, UObject* InObject);

Remove Objects​

/**
* Remove a reference by ENDynamicRef to a TArray of UObjects.
* @param DynamicRef The desired ENDynamicRef to remove from.
* @param InObjects The TArray of UObjects to be having their references removed by the provided ENDynamicRef.
*/
void RemoveObjects(ENDynamicRef DynamicRef, TArray<UObject*> InObjects);

Remove Objects (By Name)​

/**
* Remove a reference by FName to a TArray of UObjects.
* @remark Be careful with the manual remove method, it should be used for things that you have manually added.
* @param Name The desired FName to remove from.
* @param InObjects The TArray of UObjects to be having their references removed by the FName.
*/
void RemoveObjectsByName(FName Name, TArray<UObject*> InObjects);

Accessing References​

Get Actors​

/**
* Gets an array of AActor dynamically associated with the provided ENDynamicRef.
* @note This method will only return AActor objects, filtering out any non-AActor UObject.
* @param DynamicRef The desired ENDynamicRef to access.
* @return An array of UObject.
*/
TArray<AActor*> GetActors(const ENDynamicRef DynamicRef);

Get Actors (By Name)​

/**
* Gets an array of AActor dynamically associated with the provided FName.
* @note This method will only return AActor objects, filtering out any non-AActor UObject.
* @param Name The desired FName to access.
* @return An array of UObject.
*/
TArray<AActor*> GetActorsByName(FName Name);

Get Objects​

/**
* Gets an array of UObject dynamically associated with the provided ENDynamicRef.
* @param DynamicRef The desired ENDynamicRef to access.
* @return An array of UObject.
*/
TArray<UObject*> GetObjects(const ENDynamicRef DynamicRef);

Get Objects (By Name)​

/**
* Gets an array of UObject dynamically associated with the provided FName.
* @param Name The desired FName to access.
* @return An array of UObject.
*/
TArray<UObject*> GetObjectsByName(FName Name);

Get First Actor​

/**
* Retrieves the first/oldest AActor associated with a specified ENDynamicRef.
* @param DynamicRef The ENDynamicRef collection to iterate.
* @return A pointer to the first AActor found for the specified ENDynamicRef, or nullptr if no actors are found.
*/
AActor* GetFirstActor(const ENDynamicRef DynamicRef);

Get First Actor (By Name)​

/**
* Retrieves the first/oldest AActor associated with a specified FName.
* @param Name The FName collection to iterate.
* @return A pointer to the first AActor found for the specified ENDynamicRef, or nullptr if no actors are found.
*/
AActor* GetFirstActorByName(FName Name);

Get First Object​

/**
* Gets the first/oldest UObject associated with the provided ENDynamicRef.
* @param DynamicRef The desired ENDynamicRef collection to access.
* @return The first UObject in the collection.
*/
UObject* GetFirstObject(const ENDynamicRef DynamicRef);

Get First Object (By Name)​

/**
* Gets the first/oldest UObject associated with the provided FName.
* @param Name The desired FName to access.
* @return The first UObject in the collection.
*/
UObject* GetFirstObjectByName(FName Name);

Get Last Actor​

/**
* Retrieves the last/newest AActor associated with a specified ENDynamicRef.
* @param DynamicRef The ENDynamicRef collection to iterate.
* @return A pointer to the last AActor found for the specified ENDynamicRef, or nullptr if no actors are found.
*/
AActor* GetLastActor(const ENDynamicRef DynamicRef);

Get Last Actor (By Name)​

/**
* Retrieves the last/newest AActor associated with a specified FName.
* @param Name The FName collection to iterate.
* @return A pointer to the last AActor found for the specified FName, or nullptr if no actors are found.
*/
AActor* GetLastActorByName(FName Name);

Get Last Object​

/**
* Gets the last/newest UObject associated with the provided ENDynamicRef.
* @param DynamicRef The desired ENDynamicRef collection to access.
* @return The last UObject in the collection.
*/
UObject* GetLastObject(const ENDynamicRef DynamicRef);

Get Last Object (By Name)​

/**
* Gets the last/newest UObject associated with the provided FName.
* @param Name The desired FName type to access.
* @return The last UObject in the collection.
*/
UObject* GetLastObjectByName(FName Name);

Tag References🚧0.3.0🚧​

FGameplayTag accessors operate on the same named map as the *ByName calls — the tag's TagName is the key. There are no tag-specific Add* / Remove* calls: register a tag via the UNDynamicRefComponent's TagReferences (or by calling the matching *ByName overload with Tag.GetTagName()). Tags that fail IsValid() are treated as a no-op and return empty/null.

Get Actors (By Tag)​

/**
* Gets an array of AActor dynamically associated with the provided FGameplayTag.
* @note This method will only return AActor objects, filtering out any non-AActor UObject.
* @param Tag The desired FGameplayTag to access.
* @return An array of AActor.
*/
TArray<AActor*> GetActorsByTag(FGameplayTag Tag);

Get Objects (By Tag)​

/**
* Gets an array of UObject dynamically associated with the provided FGameplayTag.
* @param Tag The desired FGameplayTag to access.
* @return An array of UObject.
*/
TArray<UObject*> GetObjectsByTag(FGameplayTag Tag);

Get First Actor (By Tag)​

/**
* Retrieves the first/oldest AActor associated with a specified FGameplayTag.
* @param Tag The FGameplayTag collection to iterate.
* @return A pointer to the first AActor found for the specified FGameplayTag, or nullptr if no actors are found.
*/
AActor* GetFirstActorByTag(FGameplayTag Tag);

Get First Object (By Tag)​

/**
* Gets the first/oldest UObject associated with the provided FGameplayTag.
* @param Tag The desired FGameplayTag to access.
* @return The first UObject in the collection.
*/
UObject* GetFirstObjectByTag(FGameplayTag Tag);

Get Last Actor (By Tag)​

/**
* Retrieves the last/newest AActor associated with a specified FGameplayTag.
* @param Tag The FGameplayTag collection to iterate.
* @return A pointer to the last AActor found for the specified FGameplayTag, or nullptr if no actors are found.
*/
AActor* GetLastActorByTag(FGameplayTag Tag);

Get Last Object (By Tag)​

/**
* Gets the last/newest UObject associated with the provided FGameplayTag.
* @param Tag The desired FGameplayTag to access.
* @return The last UObject in the collection.
*/
UObject* GetLastObjectByTag(FGameplayTag Tag);

Tag Containers🚧0.3.0🚧​

Set-style accessors that operate on an FGameplayTagContainer. *ByAnyTags returns the union of every supplied tag's bucket (deduplicated); *ByAllTags returns the intersection (a UObject must appear under every requested tag). Invalid tags in the container are skipped for the Any variants; for the All variants, an invalid or absent tag short-circuits the result to empty.

Get Objects (By Any Tags)​

/**
* Gets the union of UObjects registered under any of the supplied FGameplayTags. Results are deduplicated.
* @param Tags The FGameplayTagContainer whose tags' buckets should be unioned.
* @return An array of UObject. Empty if no provided tag has a registered bucket.
*/
TArray<UObject*> GetObjectsByAnyTags(const FGameplayTagContainer& Tags);

Get Actors (By Any Tags)​

/**
* Gets the union of AActors registered under any of the supplied FGameplayTags.
* Results are deduplicated and non-AActor UObjects are filtered out.
* @param Tags The FGameplayTagContainer whose tags' buckets should be unioned.
* @return An array of AActor.
*/
TArray<AActor*> GetActorsByAnyTags(const FGameplayTagContainer& Tags);

Get Count (By Any Tags)​

/**
* @param Tags The FGameplayTagContainer whose tags' buckets should be unioned.
* @return The number of unique UObjects registered under any of the supplied tags.
*/
int32 GetCountByAnyTags(const FGameplayTagContainer& Tags);

Get Objects (By All Tags)​

/**
* Gets the intersection of UObjects registered under every supplied FGameplayTag.
* A UObject must appear under every requested tag to be returned.
* @param Tags The FGameplayTagContainer whose tags' buckets should be intersected.
* @return An array of UObject. Empty if any provided tag has no registered bucket.
*/
TArray<UObject*> GetObjectsByAllTags(const FGameplayTagContainer& Tags);

Get Actors (By All Tags)​

/**
* Gets the intersection of AActors registered under every supplied FGameplayTag.
* Non-AActor UObjects are filtered out.
* @param Tags The FGameplayTagContainer whose tags' buckets should be intersected.
* @return An array of AActor.
*/
TArray<AActor*> GetActorsByAllTags(const FGameplayTagContainer& Tags);

Get Count (By All Tags)​

/**
* @param Tags The FGameplayTagContainer whose tags' buckets should be intersected.
* @return The number of UObjects registered under every supplied tag.
*/
int32 GetCountByAllTags(const FGameplayTagContainer& Tags);

Utilities​

Get Count​

/**
* Retrieves the count of UObjects associated with a specified ENDynamicRef collection.
* @param DynamicRef The desired ENDynamicRef collection.
* @return The number of UObjects associated with the specified ENDynamicRef collection. Returns 0 for NDR_None.
*/
int32 GetCount(const ENDynamicRef DynamicRef);

Get Count (By Name)​

/**
* Retrieves the count of UObjects associated with a specified FName collection.
* @param Name The desired FName collection.
* @return The number of UObjects associated with the specified FName collection.
*/
int32 GetCountByName(FName Name);

Get Count (By Tag)🚧0.3.0🚧​

/**
* Retrieves the count of UObjects associated with a specified FGameplayTag collection.
* @param Tag The desired FGameplayTag collection.
* @return The number of UObjects associated with the specified FGameplayTag collection.
*/
int32 GetCountByTag(FGameplayTag Tag);

Get Dynamic Refs​

/** @return All ENDynamicRef slots that currently have at least one registered object. */
TArray<ENDynamicRef> GetDynamicRefs() const;

Returns only the populated slots, which is useful for tooling — for example, the Developer Overlay iterates this list to render one row per active slot.

Get Names​

/** @return All FName buckets that currently have at least one registered object. */
TArray<FName> GetNames() const;

Same shape as GetDynamicRefs, but for the free-form FName buckets backed by the named-collection map.

Get Tags🚧0.3.0🚧​

/**
* @return All FGameplayTags whose corresponding FName bucket currently has at least one registered object.
* @remark Bucket FNames that do not resolve to a known FGameplayTag (e.g. raw names added via the FName API) are skipped.
*/
TArray<FGameplayTag> GetTags() const;

GetTags is a filtered view of GetNames — only buckets whose key resolves to a registered FGameplayTag via UGameplayTagsManager::RequestGameplayTag are returned. Raw FName buckets that were never registered as tags are intentionally omitted.

Native-Only Fast Paths​

Two families of native-only accessors skip the safety checks performed by their Blueprint-exposed counterparts. They exist for tight inner loops where the caller has already guaranteed the slot/bucket exists and is non-empty.

Unchecked First/Last​

MethodEquivalent To
GetFirstObjectUnsafe(ENDynamicRef)GetFirstObject without nullptr/empty checks.
GetFirstObjectByNameUnsafe(FName)GetFirstObjectByName without nullptr/empty checks.
GetFirstObjectByTagUnsafe(FGameplayTag) 🚧0.3.0🚧GetFirstObjectByTag without validity/empty checks.
GetLastObjectUnsafe(ENDynamicRef)GetLastObject without nullptr/empty checks.
GetLastObjectByNameUnsafe(FName)GetLastObjectByName without nullptr/empty checks.
GetLastObjectByTagUnsafe(FGameplayTag) 🚧0.3.0🚧GetLastObjectByTag without validity/empty checks.
warning

The *Unsafe variants will dereference into an empty array if you call them on a slot/bucket with no registered objects — only use them from native code paths that already gated the lookup with GetCount or by subscribing to the registration delegates below.

Because references are weak (see the Weak References note at the top of this page), the *Unsafe accessors also skip the stale-entry check their safe counterparts perform: if the slot's first/last entry has been destroyed without removal, the Unsafe call returns nullptr even though the slot is non-empty. Prefer the safe GetFirstObject* / GetLastObject* whenever stale entries are possible.

Collection References​

Return a const FNDynamicRefCollection& directly, avoiding the per-call TArray copy that GetObjects* performs. Prefer these when you only need to read the backing storage. The collection stores TWeakObjectPtr entries and is not compacted by these accessors, so iterate with .Get() null-checks (or call its HasObjects() / GetObjectsCopy() / GetFirstValid() / GetLastValid() helpers) to skip any stale entries.

MethodReturns
GetObjectCollectionRefUnsafe(ENDynamicRef)The collection for the slot. No bounds check on DynamicRef.
GetObjectCollectionByNameRefUnsafe(FName)The collection for the bucket. FindChecked — asserts if Name is not present.
GetObjectCollectionByTagRefUnsafe(FGameplayTag) 🚧0.3.0🚧The collection for the tag's TagName bucket. FindChecked — asserts if the tag is not present.
warning

The returned reference is only valid until the next mutation of the underlying map. Any Add* / Remove* call may rehash the named map or reallocate the slot's backing array and invalidate the reference. Do not cache it across frames, and do not access it from multiple threads.

Delegates​

The subsystem fires four native multicast delegates that broadcast every registration change. The shipped Developer Overlay listens on all four to keep its UI in sync without polling — mirror that pattern when you build custom diagnostic UIs.

DelegateSignatureFires When
OnAdded(ENDynamicRef, UObject*)An object is registered under a slot.
OnRemoved(ENDynamicRef, UObject*)An object is unregistered from a slot.
OnAddedByName(FName, UObject*)An object is registered under a named bucket.
OnRemovedByName(FName, UObject*)An object is unregistered from a named bucket.

The delegates are native (not BlueprintAssignable) — bind from C++ via OnAdded.AddUObject(...) and remove with RemoveAll(this) in your teardown path.

The add/remove broadcasts are symmetric: OnAdded / OnAddedByName only fire when an object is newly registered — a duplicate Add* of an already-present UObject is a silent no-op — and OnRemoved / OnRemovedByName only fire when the call actually removes something. Attempting to remove a UObject that was never registered to a slot/bucket is a silent no-op rather than a spurious broadcast, so external listeners always see a balanced add/remove pair.