> 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/creating-fragments.md).

# Creating Fragments

## Overview

Fragments are the building blocks of the attachment system. Every visual, audio, or gameplay effect is implemented as a fragment subclass. This guide covers creating custom fragments in both C++ and Blueprint.

## Fragment Base Class

All fragments derive from `UModularAttachmentFragment`:

```cpp
UCLASS(Abstract, Blueprintable, BlueprintType, EditInlineNew, DefaultToInstanced)
class UModularAttachmentFragment : public UObject
```

### Methods to Override

| Method                  | Required                    | Purpose                                                        |
| ----------------------- | --------------------------- | -------------------------------------------------------------- |
| `CollectSoftRefs()`     | Yes (if you have soft refs) | Declare `TSoftObjectPtr` / `TSoftClassPtr` for async streaming |
| `ApplyToComponent()`    | Yes                         | Create runtime components / apply modifications                |
| `RemoveFromComponent()` | Yes                         | Clean up / destroy what was created                            |

All three are `BlueprintNativeEvent` — implement `_Implementation` in C++ or override directly in Blueprint.

## C++ Example — Niagara VFX Fragment

```cpp
// ModularAttachmentFragment_Niagara.h

#pragma once

#include "ModularAttachmentFragment.h"
#include "NiagaraSystem.h"
#include "ModularAttachmentFragment_Niagara.generated.h"

UCLASS(DisplayName = "Niagara VFX Fragment")
class UModularAttachmentFragment_Niagara : public UModularAttachmentFragment
{
    GENERATED_BODY()

public:

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Niagara")
    TSoftObjectPtr<UNiagaraSystem> NiagaraSystem;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Niagara")
    FName AttachSocketName;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Niagara")
    FTransform RelativeTransform;

    // Phase 2: Declare soft refs for streaming
    virtual void CollectSoftRefs_Implementation(
        TArray<FSoftObjectPath>& OutSoftReferences) override
    {
        if (!NiagaraSystem.IsNull())
        {
            OutSoftReferences.Add(NiagaraSystem.ToSoftObjectPath());
        }
    }

    // Phase 3: Apply — assets are guaranteed loaded at this point
    virtual FSpawnedFragmentData ApplyToComponent_Implementation(
        UModularAttachmentComponent* TargetComponent) override
    {
        FSpawnedFragmentData Data;
        Data.FragmentTag = FragmentTag;

        UNiagaraComponent* NiagaraComp = UNiagaraFunctionLibrary::SpawnSystemAttached(
            NiagaraSystem.Get(),
            TargetComponent,
            AttachSocketName,
            RelativeTransform.GetLocation(),
            RelativeTransform.GetRotation().Rotator(),
            EAttachLocation::KeepRelativeOffset,
            false
        );

        if (NiagaraComp)
        {
            Data.SpawnedComponents.Add(NiagaraComp);
        }
        return Data;
    }

    // Cleanup
    virtual void RemoveFromComponent_Implementation(
        UModularAttachmentComponent* TargetComponent,
        const FSpawnedFragmentData& FragmentData) override
    {
        for (const TWeakObjectPtr<UActorComponent>& Comp : FragmentData.SpawnedComponents)
        {
            if (Comp.IsValid())
            {
                Comp->DestroyComponent();
            }
        }
    }
};
```

## Blueprint Example

1. **Content Browser** → Right-Click → **Modular Attachment System** → **Attachment Fragment Blueprint**
2. Set parent class to `ModularAttachmentFragment` (or a C++ subclass like `_StaticMesh`)
3. Open the Blueprint

### Override CollectSoftRefs

1. **Add Event** → `CollectSoftRefs`
2. Use **Make Soft Object Path** for each asset
3. **Add** each path to the `OutSoftReferences` array

### Override ApplyToComponent

1. **Add Event** → `ApplyToComponent`
2. Use `TargetComponent→GetOwner()` to access the owning actor
3. Spawn runtime components via **Add Component by Class**
4. **Make SpawnedFragmentData** — fill `FragmentTag` and `SpawnedComponents`
5. **Return** the struct

### Override RemoveFromComponent

1. **Add Event** → `RemoveFromComponent`
2. Iterate `FragmentData→SpawnedComponents`
3. Call **Destroy Component** on each

## Fragment Nesting

Fragments can contain child fragments for complex compositions:

```cpp
UPROPERTY(EditAnywhere, Instanced, Category = "Children")
TArray<TObjectPtr<UModularAttachmentFragment>> AttachedFragments;
```

Example — a scope attachment with multiple visual parts:

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

The system recursively applies and removes all children when the parent is applied/removed.

Child fragments automatically receive their parent’s mesh via `GetParentMeshComponent()`. If a child spawns its own mesh, that mesh is propagated to its children instead. See [Parent Mesh Propagation](broken://pages/e5bd5eb82f54f22a5a9f2b2e6fd25d451895e291#parent-mesh-propagation).

## Editor Outline Support

When you click a fragment node in the graph editor, the editor highlights the corresponding mesh in the viewport with a selection outline. To enable this for custom fragments:

1. Override `GetSpawnedMeshComponent()` in your fragment
2. Return the `USceneComponent*` you spawned (e.g., your mesh component)
3. The editor casts it to `UPrimitiveComponent` and applies `SelectionOverrideDelegate`

If you don’t override this, clicking your node will show no outline.

## Best Practices

| Practice                                                    | Reason                                                                                               |
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Always implement `CollectSoftRefs`                          | Without it, assets won’t be loaded when `ApplyToComponent` runs                                      |
| Use `Mesh.LoadSynchronous()` not `Mesh.Get()`               | `.Get()` can return null if the lazy pointer isn’t resolved yet; `LoadSynchronous()` always resolves |
| Return **all** spawned components in `FSpawnedFragmentData` | Ensures proper cleanup on removal                                                                    |
| Override `GetSpawnedMeshComponent()` if you spawn a mesh    | Enables child propagation and editor outline                                                         |
| Use `GetParentMeshComponent()` to choose attach target      | Attach to parent’s mesh if nested, fall back to the component                                        |
| Use `FragmentTag` for identification                        | Enables `GetFragmentByTag()` and selective event routing                                             |
| Keep fragments atomic                                       | One mesh per fragment, one effect per fragment — compose via nesting                                 |
| Use `EditInlineNew` / `Instanced`                           | Allows inline editing in the Definition's Details panel                                              |

## Next Steps

* [Gameplay Tags](/dante-docs/modular-attachment-system/gameplay-tags.md) — assign fragment tags from the native hierarchy
* [API Reference](/dante-docs/modular-attachment-system/api-reference.md) — full `UModularAttachmentFragment` method signatures
