Ditto is a runtime Unreal Engine plugin for Combee-backed outfit and equipment state.
The plugin owns the outfit container, slot layout, equip and unequip transactions, and the handoff from inventory state to avatar presentation. The actual visual implementation is kept behind outfit part fragments, so a part can be driven by Mutable Customizable Objects, direct skeletal mesh replacement, or another backend added later.
Ditto currently provides:
- A replicated outfit container component:
UDittoOutfits. - A gameplay-tag based outfit layout:
FDittoOutfitLayout. - Item fragments for equip and unequip behavior:
UDittoFragment_OutfitPart. - Combee transactions for moving items between closets and outfit slots.
- Avatar callbacks through
IDittoOutfitAvatar. - Mutable Customizable Object outfit part implementations.
- Direct skeletal mesh attachment/replacement outfit part implementation.
Ditto does not implement a complete character customization UI, inventory database, or animation policy. Those remain project-level or plugin-level concerns built on top of the container and fragment flow.
The plugin descriptor enables:
- unreal-combee
- UE5Coro
Mutable
The module also depends on Combee use, presets, and transaction modules, plus Unreal
GameplayTags, MutableRuntime, and CustomizableObject.
UDittoOutfits is a UCombeeContainer subclass that represents the currently equipped
outfit slots for an owner.
It stores:
Avatar: the actor that receives visual outfit updates.Layout: the registered outfit slots and per-slot presentation data.
The component validates compatible items through UDittoFragment_OutfitPart. An item is
compatible with a slot when the item's Part tags include the registered slot tags.
FDittoOutfitLayout is registered at runtime by calling:
OutfitComponent->RegisterPart(PartTags, PartData);Each registered part contains:
Part: gameplay tags that describe the slot.ContainerIndex: the matching cell index inUDittoOutfits.PartData: anFInstancedStructconsumed by the outfit part implementation.
PartData is intentionally backend-specific. Mutable parts use
FDittoOutfitPartData_Mutable; attach parts use FDittoOutfitPartData_Attach.
An avatar actor can implement IDittoOutfitAvatar to configure and optionally intercept
outfit updates.
Important callbacks:
SetupOutfitAvatar: called on authority when an avatar is assigned. Register layout slots here.ReceiveOutfitUpdate: called on authority after a cell changes. Return true to fully handle the update yourself.ReceiveOutfitUpdateDeferred: called from container mutation flow on server and client. Return true to fully handle the visual update yourself.
If the avatar callbacks return false, UDittoOutfits falls back to the fragment's
Wear and TakeOff implementations.
UDittoFragment_OutfitPart is the base fragment placed on an equippable Combee item.
It contains:
Part: gameplay tags describing what slots the item can occupy.Closets: container component classes that can receive the item when it is unequipped.Wear: presentation hook for applying the item.TakeOff: presentation hook for removing the item.
OnUse decides whether the item is currently in an outfit container or another container:
- If used from
UDittoOutfits, it requestsUDittoTransaction_Unequip. - If used from another container, it requests
UDittoTransaction_Equip.
- A player uses an item that owns a
UDittoFragment_OutfitPart. OnUsebuilds an equip or unequip payload.UCombeeTransactionSubsystemruns the transaction.UDittoTransaction_Equipswaps the item into the matching outfit slot.UDittoTransaction_Unequipmoves the item back into a configured closet container.UDittoOutfitsreceives the cell/container mutation.- The avatar interface gets a chance to handle the update.
- If not handled, Ditto calls the outfit part fragment's
WearorTakeOff.
TakeOff is multicast by fragment class because the previous item instance may not exist
on every client. Implementations should use the supplied PartData and class default
values instead of relying on per-instance runtime state.
Files:
Outfit/Mutable/DittoFragment_OutfitPart_Mutable_CO.*Outfit/Mutable/DittoFragment_OutfitPart_Mutable_COI.*Outfit/Mutable/DittoOutfitPartData_Mutable.h
Use this backend for body parts that should participate in a Mutable Customizable Object graph, such as body, head, clothing, armor, hands, or feet.
FDittoOutfitPartData_Mutable supplies:
CustomizableComponentMeshComponentPartNameDefaultLink
UDittoFragment_OutfitPart_Mutable_CO selects the fragment's ObjectName for the Mutable
integer parameter named by PartName, updates the skeletal mesh asynchronously, and can
link an animation layer override. Taking the part off resets the Mutable parameter to its
default value and relinks DefaultLink.
UDittoFragment_OutfitPart_Mutable_COI follows the same parameter flow, but also copies
parameters from a configured UCustomizableObjectInstance.
Mutable parts must be compatible with the skeleton merge rules of Mutable. If a part owns an independent skeleton with conflicting bone parents, prefer the attach backend instead of forcing it into the Customizable Object graph.
Files:
Outfit/Attach/DittoFragment_OutfitPart_Attach.*Outfit/Attach/DittoOutfitPartData_Attach.h
Use this backend for equipment that should not be merged by Mutable, such as weapons, props, independent skeletal accessories, or socket-driven pieces.
FDittoOutfitPartData_Attach supplies:
MeshComponentDefaultMeshPartName
The current implementation uses MeshComponent and DefaultMesh. Wearing a part sets the
component's skeletal mesh to the fragment's Mesh and optionally applies AnimOverride.
Taking it off restores DefaultMesh.
This backend is the safer choice for weapons or props with their own skeleton hierarchy.
- Add
UDittoOutfitsto the actor that owns the equipment state. - Assign an avatar actor with
SetAvatar. - Implement
IDittoOutfitAvataron the avatar. - In
SetupOutfitAvatar, callRegisterPartfor each slot. - Build each slot's
PartDatawith the backend data struct needed by that slot. - Put a
UDittoFragment_OutfitPartsubclass on each equippable item. - Set the fragment's
Parttags so the item can match the registered slot. - Configure
Closetsso unequipped items have a valid destination container.
Example slot registration shape:
const auto PartData = FInstancedStruct::Make(FDittoOutfitPartData_Attach{
.MeshComponent = WeaponMeshComponent,
.DefaultMesh = DefaultWeaponMesh,
.PartName = TEXT("Weapon"),
});
OutfitComponent->RegisterPart(WeaponPartTags, PartData);Use Mutable CO or COI when:
- The part is a body/clothing module.
- It should affect the generated character mesh.
- It uses the same skeleton hierarchy expected by the Mutable graph.
- It needs Mutable parameters, material variants, or body-part composition.
Use Attach when:
- The part is a weapon, prop, or independent accessory.
- The part has its own skeletal hierarchy.
- The part should be attached to a socket or controlled by a dedicated component.
- Mutable skeleton merging reports conflicting bone parents.
UDittoOutfits::LayoutandAvatarare replicated.- Layout changes are authority-side operations.
- The default avatar update path runs through Combee mutation events.
TakeOffuses a reliable multicast helper because previous item instances may not be available on clients.
New presentation backends should subclass UDittoFragment_OutfitPart and define a matching
FInstancedStruct data type for slot-specific runtime references.
Good candidates for future backends:
- Static mesh socket attachments.
- Niagara or effect-only outfit parts.
- Multiple component swaps from one outfit item.
- Gameplay ability or attribute driven outfit parts.
Keep new backend logic self-contained in its own folder under Outfit/ and keep
cross-plugin dependencies explicit in the plugin descriptor and module rules.