How-To: Drop and Pick Up Items
Goal: drop an entry into the world as a pickup actor, author loot by hand, and pick it up slot-by-slot or all at once with partial-success feedback.
PickupActorClass on the Visuals fragment) and How-To: Route UI Requests.1. Create a pickup actor class
ACrimsonPickupActorBase replicates an FCrimsonInventoryPickup (Instances with live item objects, Templates as definition + count, Currencies) and resolves its mesh from the dropped item's PickupMesh, or from BagMeshOverride when it holds more than one slot. It destroys itself when emptied.
Blueprint Class -> `CrimsonPickupActorBase`, name it BP_Pickup. Assign Bag Mesh Override (used for multi-item drops). Override On Pickup Initialized for a spawn effect. Then on each item's Item Visuals fragment set Pickup Actor Class = BP_Pickup and Pickup Mesh.
BP_Pickup appears at the drop offset showing the item's mesh.UCrimsonInventoryItemFragment_Visuals on the item definition. With PickupActorClass unset nothing spawns at all and the entry is still removed - the drop looks like it deleted the item. With PickupMesh unset the actor spawns invisible.If your pickup actor simulates physics, that mesh also needs simple collision: a body carrying only complex (per-triangle) collision has nothing to rest on and falls through the world within a few frames, which looks identical to nothing having spawned. Check
StaticMesh -> Collision -> Add Simple Collision when a dropped item vanishes.2. Drop from an inventory (server)
| Call | Result |
|---|---|
DropItemByEntry(EntryId, Quantity, Location) | One pickup with one FCrimsonPickupInstance. A bag drops with its Contents subtree. |
DropItemsByDefinition(ItemDef, Num, Location) | Consumes by definition across entries, then drops. |
DropEntriesAsBag(EntryIds, Location, BagActorClass) | One pickup holding several entries (uses BagMeshOverride). |
DropCurrency(CurrencyType, Amount, Location) | Broadcasts Crimson.Inventory.Message.CurrencyDropped; spawning is up to the listener. |
Every drop also broadcasts Crimson.Inventory.Message.ItemDropped (FCrimsonInventory_Message_ItemDropped: DroppedInstance, Quantity, DropLocation) for VFX or analytics. From the client use the routing component's DropItem, which lands in DropItemByEntry.
Client: Find Routing Component -> Drop Item (Target, Entry Id, Count, Drop Location = pawn location + forward 100). Server: Drop Item By Entry* directly.
x2, a pickup spawns, and OperationResult reports Operation = Drop, bSuccess = true.3. Author loot by hand
Place BP_Pickup in the level and fill Pickup Inventory -> Templates (Item Def, Stack Count) and Currencies (Currency Data Table row, Stack Count). At runtime, Set Crimson Pickup Inventory On Actor (server) replaces the contents; Merge Pickup Inventories combines two.
BagMeshOverride when it has more than one slot and the single item's mesh otherwise.4. Take from a pickup
TakeSlot(SlotType, SlotIndex, RequestedQuantity, Destination) and TakeAll(Destination) run on the server and return FCrimsonPickupTakeResult (bAllTaken, QuantityTaken, QuantityRemaining, FailureReason). A full inventory yields a partial take; what remains stays in the pickup. Template rows whose item carries UCrimsonInventoryItemFragment_GrantCurrency convert to currency on add instead of occupying a slot.
Client: Request Pickup (Player Controller, Pickup Actor) for take-all, or Find Routing Component -> Take Pickup Slot (Slot Type = Template, Slot Index, Requested Quantity, Destination) for one slot. Bind On Pickup Contents Changed on the actor to refresh a loot window.
OperationResult shows TakePickupAll with bSuccess = false and a reason, one slot moved, and the pickup remains with one slot.5. Save and restore a dropped pickup (optional)
GetPickupSaveData flattens the carried contents into an FCrimsonInventoryPickupSaveData, and ApplyPickupSaveData rebuilds them. Data in, data out: this plugin never talks to a save system, so hand the struct to whichever one your project uses.
FCrimsonInventoryPickup holds live item-instance pointers. A serialized pointer to a runtime subobject resolves to nothing on load, so the pickup comes back EMPTY - no mesh, and no interaction prompt either, because a pickup only offers interaction options while it holds something. The flattened struct exists for exactly that reason.The same trap applies one level down: a save archive skips untagged properties recursively, so any struct you nest inside the snapshot needs
SaveGame on its own members or it serializes as empty.Get Pickup Save Data returns the struct and Apply Pickup Save Data takes it back, both on the pickup actor. Apply is authority-only - call it on the server after your save system has respawned the actor.
See also
- How-To: Nest Bags
- How-To: Route UI Requests
- How-To: Manage Currency