> For the complete documentation index, see [llms.txt](https://dante-3.gitbook.io/dante-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dante-3.gitbook.io/dante-docs/modular-attachment-system/modular-attachment-fragment.md).

# Modular Attachment Fragment

**Header:** `ModularAttachmentSystem/Fragment/ModularAttachmentFragment.h`\
**Module:** `ModularAttachmentSystem`\
**Parent:** `UObject`\
**Specifiers:** `Abstract`, `BlueprintType`, `Blueprintable`, `DefaultToInstanced`, `EditInlineNew`

## Overview

`UModularAttachmentFragment` is the **smallest unit of behavior** in the Modular Attachment System. Each fragment subclass does exactly one thing: spawn a mesh, apply a material, modify a stat, play a sound, attach a VFX system, etc.

Fragments are:

* **Single-responsibility** — one fragment = one effect
* **Self-contained** — declares its own soft refs, applies its own logic, cleans up after itself
* **Nestable** — fragments can contain child fragments via `AttachedFragments`
* **Tag-identified** — each fragment has a `FragmentTag` for lookup and event routing
* **Blueprint-extensible** — all lifecycle methods are `BlueprintNativeEvent`

## Properties

### `FragmentTag`

```cpp
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Modular Attachment System")
FGameplayTag FragmentTag;
```

Identifies this fragment in the subsystem registry and event routing. Used by `GetFragmentByTag()` and `GetSpawnedComponentsByFragmentTag()` on the owning component.

{% hint style="info" %}
**Best practice:** Always assign a tag. Without it, the fragment cannot be looked up at runtime and won't receive targeted events.
{% endhint %}

### `AttachedFragments`

```cpp
UPROPERTY(EditDefaultsOnly, Instanced, Category = "Modular Attachment System")
TArray<TObjectPtr<UModularAttachmentFragment>> AttachedFragments;
```

Child fragments that are applied together with this one. The component's `ApplyFragmentTree()` walks parent → children recursively, giving each fragment its own `FSpawnedFragmentData` entry.

Example tree:

```
ScopeBodyFragment          (StaticMesh — housing)
├── LensFragment           (StaticMesh — glass)
├── ReticleFragment        (Custom — draws reticle decal)
└── ZoomModifierFragment   (Custom — modifies FOV on aim)
```

Removing the parent removes all children automatically.

### Editor-Only Properties

| Property                  | Type        | Description                                          |
| ------------------------- | ----------- | ---------------------------------------------------- |
| `EditorGraphPosition`     | `FIntPoint` | Persisted node position in the fragment graph editor |
| `bHasEditorGraphPosition` | `bool`      | Whether graph position has been set                  |

## Lifecycle Methods (BlueprintNativeEvent)

All three are the core override points. Blueprint overrides the event node directly; C++ overrides the `_Implementation` suffix.

### `CollectSoftRefs`

```cpp
UFUNCTION(BlueprintNativeEvent, DisplayName = "Collect Soft Refs")
TArray<FSoftObjectPath> CollectSoftRefs() const;
```

Called during **Phase 2** of the load pipeline. Return all `TSoftObjectPtr` / `TSoftClassPtr` assets that must be streamed before `ApplyToComponent` runs.

The base implementation propagates to children — it collects soft refs from all `AttachedFragments` recursively.

**C++ override:**

```cpp
TArray<FSoftObjectPath> UMyFragment::CollectSoftRefs_Implementation() const
{
    TArray<FSoftObjectPath> Refs = Super::CollectSoftRefs_Implementation();
    if (!MyMesh.IsNull())
    {
        Refs.AddUnique(MyMesh.ToSoftObjectPath());
    }
    return Refs;
}
```

**Blueprint override:**

1. Override **Collect Soft Refs** event
2. Call **Parent: Collect Soft Refs** to get child refs
3. Use **Make Soft Object Path** → **Add** for each asset
4. Return the array

{% hint style="danger" %}
**Critical:** If you skip this step, `TSoftObjectPtr::Get()` will return `nullptr` inside `ApplyToComponent` because the asset was never streamed.
{% endhint %}

### `ApplyToComponent`

```cpp
UFUNCTION(BlueprintNativeEvent, DisplayName = "Apply To Component")
FSpawnedFragmentData ApplyToComponent(UModularAttachmentComponent* Component);
```

Called during **Phase 3** — all assets declared in `CollectSoftRefs` are guaranteed loaded at this point. Create runtime components, apply modifications, and return everything in `FSpawnedFragmentData`.

The base implementation only sets `FragmentTag` on the output struct. Child fragments are applied separately by the component's `ApplyFragmentTree` — you do **not** need to recurse into `AttachedFragments` manually.

**C++ override:**

```cpp
FSpawnedFragmentData UMyFragment::ApplyToComponent_Implementation(UModularAttachmentComponent* Component)
{
    FSpawnedFragmentData Data = Super::ApplyToComponent_Implementation(Component);

    AActor* Owner = Component->GetOwner();
    UStaticMeshComponent* MeshComp = NewObject<UStaticMeshComponent>(Owner);
    MeshComp->SetStaticMesh(MyMesh.Get());  // guaranteed loaded
    MeshComp->RegisterComponent();
    MeshComp->AttachToComponent(Component, FAttachmentTransformRules::KeepRelativeTransform);

    Data.SpawnedComponents.Add(MeshComp);
    return Data;
}
```

**Blueprint override:**

1. Override **Apply To Component** event
2. Get the owning actor via `Component → Get Owner`
3. **Add Component by Class** to spawn runtime components
4. Fill `FSpawnedFragmentData` with `FragmentTag` and all spawned components
5. Return the struct

### `RemoveFromComponent`

```cpp
UFUNCTION(BlueprintNativeEvent, DisplayName = "Remove From Component")
void RemoveFromComponent(UModularAttachmentComponent* Component, const FSpawnedFragmentData& Data);
```

Called on unload/swap. The base implementation destroys all components listed in `Data.SpawnedComponents`. Override only if you need custom cleanup (e.g., reverting a material, clearing a timer).

## Hierarchy Navigation

| Method                 | Returns                               | Description                                                                    |
| ---------------------- | ------------------------------------- | ------------------------------------------------------------------------------ |
| `GetParentFragment()`  | `UModularAttachmentFragment*`         | Walks Outer chain — returns the containing fragment, or `nullptr` if top-level |
| `GetOwnerDefinition()` | `UModularAttachmentDefinition*`       | Walks up the Outer chain to find the containing `UPrimaryDataAsset`            |
| `GetChildFragments()`  | `TArray<UModularAttachmentFragment*>` | Returns the `AttachedFragments` array (direct children only)                   |

**Example — accessing the parent from a child fragment:**

```cpp
if (UModularAttachmentFragment* Parent = GetParentFragment())
{
    UE_LOG(LogTemp, Log, TEXT("My parent tag: %s"), *Parent->FragmentTag.ToString());
}
```

## Next Steps

* [UModularAttachmentDefinition](/dante-docs/modular-attachment-system/modular-attachment-definition.md) — data asset that composes fragments
* [UModularAttachmentComponent](/dante-docs/modular-attachment-system/modular-attachment-component.md) — runtime mount point
* [Creating Fragments](/dante-docs/modular-attachment-system/creating-fragments.md) — step-by-step authoring guide
