Combines
Documentation Unreal Engine Player Combat Inventory
Combines turn one item dropped onto another into new items: two herbs make a mixed herb, poison and arrows make poison arrows. Write them as recipe rows in a Combine Set, or as a Blueprint class.
Set it up
5 minutes- 1
Write the recipe
Right-click in the Content Browser, UPC > Items > Combine Set, name itCombineSet_MyCraftingand add a Recipes row: Item A and Item BItem_UPC_Herb, Results oneItem_UPC_MixedHerb, Seconds 1.DetailsCombineSet_MyCraftingRecipes
Recipes1 Array elementIndex [ 0 ]Item AItem_UPC_HerbCount A1Item BItem_UPC_HerbCount B1Results1 Array elementIndex [ 0 ]ItemItem_UPC_MixedHerbCount1Order MattersRequired Tags0 Gameplay TagsSeconds1Combines
Combines0 Array elementsYour own set with one row: two herbs make one mixed herb, a second each. - 2
List it on a config
DuplicateDemo/Items/Inventory/InventoryConfig_UPC_SurvivalGridasInventoryConfig_MyGridand replaceCombineSet_UPC_Demoin its Combine Sets with your set.DetailsInventoryConfig_MyGridCombines
Combine Sets1 Array elementIndex [ 0 ]CombineSet_MyCraftingTiming
Combine At OnceYour copy of the survival grid config, with your set in place of the demo's. Combine At Once stays off, so each combine waits its Seconds behind a progress bar. - 3
Give the demo player the config and two herbs
OnBP_UPC_Character's InventoryComponent, set Config toInventoryConfig_MyGridand addItem_UPC_Herbto Starting Items twice (herbs do not stack, so each lies in its own cell). - 4
Open your config on the grid screen
InBPC_UPC_InventoryScreens(Demo/UI/Widgets/Inventory, onBP_UPC_PlayerController), add an Inventory Screen Classes row in Class Defaults:InventoryConfig_MyGridwithWBP_UPC_InventoryScreen_SurvivalGrid. Without a row the open world list opens, which pauses the game, so a timed combine cannot land (Inventory Screens). - 5
Drop one herb on the other
Press Play inLVL_UPC_Demo, press I and drag one herb onto the other. A progress bar fills on its tile and a second later a mixed herb takes their place. Combine in one herb's actions, then the other herb, does the same.
The demo character has what combines need. Yours needs a UPC Equipment Manager (without it nothing combines) and a call to Notify Ability System Ready (without it a combine finishes at once, with no progress bar; Character Setup). A combine runs inside one inventory, so a drop between the inventory and a chest never combines.
Recipes: the rows you fill
| Setting | Does |
|---|---|
| Item A, Count A | First item and the units one combine uses. |
| Item B, Count B | Second item and units; may equal Item A (two herbs). |
| Results | What one combine makes, each an item and count (an empty bottle can come back). The first result goes where the config's Combine Result Place says (default: onto the item it was combined onto). When that combine leaves a stack behind or the result does not fit, and for every other result, it lands as a pickup does: same-item stacks first, then the first free place in container order, else past the end of the first carried container that takes new items. A result goes on the belt only into a slot its ingredient emptied, if the Filter allows. |
| Order Matters | On: only Item A dropped onto Item B combines. Off (default): either way round. |
| Required Tags | Tags the character must hold (a learned skill, a workbench zone), added and removed by your code (a trigger zone). Without them the drop is refused with UPC.Inventory.Refused.Locked. Empty needs none. |
| Seconds | Time per combine behind a progress bar; moving stops it. 0 (default), or a config with Combine At Once, makes it instant. A combine of your own sets time with Unit Timing. |
A drop runs the row as many times as the dragged units and other stack allow. The demo's Dip Arrows turns one poison and five arrows into five poison arrows, so twelve arrows dragged onto three flasks run it twice: ten poison arrows, two arrows and a flask left. A row can require a workbench and return the bottle:
Details
CombineSet_MyWorkbench
Recipes
Recipes1 Array element
Recipes
1 Array element
Index [ 0 ]
Index [ 0 ]
Item A
Item_MyHerb
Count A
2
Item B
Item_MyBottle
Count B
1
Results2 Array elements
Results
2 Array elements
Index [ 0 ]
Index [ 0 ]
Item
Item_MyPotion
Count
1
Index [ 1 ]
Index [ 1 ]
Item
Item_MyEmptyBottle
Count
1
Order Matters
Required Tags
1 Gameplay Tag
MyGame.State.AtWorkbench
Seconds
2
Combines
Combines
0 Array elements
A set with one row: two herbs and a bottle make a potion at a workbench, and the empty bottle comes back. The row is turned down with Locked until the character holds the tag.
A combine of your own
For what a row cannot say (rounds counted into a magazine), make a Blueprint child of UPC Inventory Combine and list it in a set's Combines. Characters share one copy, so keep no state in it. The demo's Load Holder and Coat Weapon are two to copy (Inventory Rules and Combines in Blueprint).
| Function | Answers |
|---|---|
| Matches | Whether it handles Item dropped onto Target, from the two items. |
| Get Units | Units the drop makes now, 0 to the context's Count; 0 refuses, Out Reason a tag under UPC.Inventory.Refused. Default: every unit asked. |
| Apply Unit | Makes one unit, on the server. |
| Unit Timing | Optional: seconds per unit, sounds, and whether moving stops it (default half a second, stopped by moving). |
Matches gets the two items. Get Units and Apply Unit get a Context: the Inventory, the dropped Item, the Target it landed on (not a node's Target pin) and Count, the units still asked for. One Apply Unit is one unit, here one round loaded. Ten rounds dropped on a magazine holding 25 of 30: Get Units gets Count 10 and answers 5 (the room left), so five units run, asking again before each with Count 5, then 4, down to 1.
A magazine, step by step
These steps build on the quick start's
InventoryConfig_MyGrid and BP_UPC_Character.- Make
Item_MyRound(Max Stack Size 60),Item_MyMagazine(Max Stack Size 1, so each magazine is its own item) and a gameplay tagMyGame.Ammo.Loaded. Each magazine counts its rounds in that tag as a stat tag, a count kept on one owned item. - Make
BP_MagazineCombinewith Instance Editable variablesRoundandMagazine(UPC Item Definition) andCapacity(Integer, 30), and override the three functions:
BP_MagazineCombine · Matches
Matches
Item
Target
Get Item Def
Target
Return Value
Round
==
Get Item Def
Target
Return Value
Magazine
==
AND
Return Node
Return Value
BP_MagazineCombine · Get Units: the least of the rounds dragged, the rounds in the stack and the room left
Get Units
Context
Break UPC Inventory Combine Context
UPC Inventory Combine Context
Inventory
Item
Target
Count
Get Item Stack Count
Target
Instance
Return Value
MIN
Capacity
Get Stat Tag Stack Count
Target
TagMyGame.Ammo.Loaded
Return Value
-
MIN
Return Node
Out ReasonUPC.Inventory.Refused.Full
Return Value
BP_MagazineCombine · Apply Unit: one round into the magazine
Apply Unit
Context
Break UPC Inventory Combine Context
UPC Inventory Combine Context
Inventory
Item
Target
Count
Add Stat Tag Stack
Target is UPC Inventory Item Instance
Target
TagMyGame.Ammo.Loaded
Stack Count1
Consume Item Instance
Target is UPC Inventory
Target
Instance
Count1
Return Value
- Make
CombineSet_MyShooter, add your combine to its Combines, and pick your two items:
Details
CombineSet_MyShooter
Recipes
Recipes
0 Array elements
Combines
Combines1 Array element
Combines
1 Array element
Index [ 0 ]BP_MagazineCombine
Index [ 0 ]
BP_MagazineCombine
Round
Item_MyRound
Magazine
Item_MyMagazine
Capacity
30
Unit Timing
Unit Timing
Seconds
0.5
Cancel on Move
Your own set with the magazine combine listed. Each entry carries its own Unit Timing: half a second per round, stopped by moving, unless you change it here.
- Add
CombineSet_MyShootertoInventoryConfig_MyGrid's Combine Sets, below your crafting set. InBP_UPC_Character's Starting Items, add oneItem_MyMagazineand tenItem_MyRoundentries (they join into one stack of 10). Drag the rounds onto the magazine on the grid screen to load it, one round every half second.
AdvancedTest it without a screen
Give
BP_UPC_Character two graphs: B sends the Request Move a screen's drag sends, and the load's end prints the rounds loaded:BP_UPC_Character · B drops the rounds on the magazine
B
Pressed
Released
Key
Inventory Component
Find First Item by Definition
Target
Item DefItem_MyRound
Return Value
Find First Item by Definition
Target
Item DefItem_MyMagazine
Return Value
Get Item Placement
Target
Item
Out Placement
Return Value
Request Move
Target is UPC Inventory
Target
Item
Count10
Destination
BP_UPC_Character · print the load when it ends
On Equip Sequence Ended (EquipmentManagerComponent)
Cancelled
Reason
Inventory Component
Find First Item by Definition
Target
Item DefItem_MyMagazine
Return Value
Get Stat Tag Stack Count
Target
TagMyGame.Ammo.Loaded
Return Value
Print String
In String
Press Play, then B. Five seconds later the print shows 10; walk off mid-load and it shows the rounds loaded so far.
Combine from a button, and show it
| Node | Does |
|---|---|
| Request Combine | Combines the whole stack of Item with Target, an item wherever it is (poison reaches the sword in hand). |
| Can Combine (UPC Inventory Statics) | Request Combine's check: Out Results is what one combine of a recipe makes, Out Reason why not. |
| On Combined (inventory) | As units land: the two items, the recipe or combine, and Units landed together (1 per unit of a timed combine, the full count when all land at once). |
| On Equip Step Started (equipment manager) | As each unit starts, with its seconds, for a progress bar. |
| On Equip Sequence Ended | The load finished or stopped, with a reason. |
An item's Details panel lists recipes naming it under Combines With ("Herb + Herb -> Mixed Herb"). The screens' Combine mode is on Inventory Screens.
Debug it
Log LogUPCInventory Verbose names each request the server turned down.AdvancedHow a combine runs
- A drop or Request Combine asks the config's Combine Sets in order, each set's recipes before its combines. The first match decides, ahead of going into a bag, joining a stack or trading places.
- Can Place answers Combine with the units allowed, or Refused with the reason, and Request Move runs the combine as one equipment change, a unit at a time.
- Before each unit the inventory asks again: Get Units for a combine, the stacks and Required Tags for a recipe. A 0 stops the rest with its reason, so leaving a workbench mid-load stops it with
Locked. Units already made stay, whatever stops the load. - A load also stops before its next unit when the player moves (Unit Timing's Cancel on Move), a screen calls the equipment manager's Request Cancel Sequence with either item, either item leaves the inventory, the character gains one of the equipment action's Cancel Tags or
UPC.State.EquipmentBlocked, or another animation cuts it off. On Equip Sequence Ended gives a reason underUPC.Equipment.Cancelled, or the combine's own. - Every unit lands together, with no step events and no unit sounds, when the unit time is 0, the config's Combine At Once is on (Containers and Rules), or the character has no equipment action. On Combined then fires once, with the full count.
- Get Playing Step answers how far the current unit has got, 0 to 1, and how many have landed, for a widget opened mid-load.
- On Equip Step Started's step carries the target as Source Item, the dropped item as Used Item, and the unit's seconds as Timing, counted from Start Time.
- One equipment change runs at a time, so the belt refuses a press during a load, and Block Actions While Running on the equipment action shuts the action graph for the load. A unit Get Units refuses as it lands has already played its commit sound.
- The inventory judges reach and Access Tags when the player asks, not before each unit.
- A result lands in stacks within its Max Stack Size. A row with several results judges each against the inventory as it stands.
- Find Combine Recipe returns a set's row for two items, and Get Recipes For Item every row of a config's sets that uses or makes an item, for a tooltip or a recipe book.
- A drop asks about the item under the dropped cell, whatever the dragged item's size, so a wide box of rounds still loads a magazine at a grid's edge.
- Saving a set warns in the Message Log about a row or a result naming no item, a row making nothing, and two rows taking the same pair (the first wins). Saving a config warns about a result no container taking new items lets in.
AdvancedMore combines to write
- Magazines by caliber. List one combine per caliber, or have Matches compare a caliber tag both items carry in their Slot Tags.
- A key opening a locked box. Get Units answers 1 while the key and the box share a lock tag in their Slot Tags (
MyGame.Lock.Crypt) and the box lacks aMyGame.Box.Unlockedstat tag, else 0 withUPC.Inventory.Refused.NotAdmitted. Apply Unit adds that stat tag and consumes the key.
The demo
Details
CombineSet_UPC_Demo
Recipes
Recipes2 Array elements
Recipes
2 Array elements
Index [ 0 ]
Index [ 0 ]
Item A
Item_UPC_Herb
Count A
1
Item B
Item_UPC_Herb
Count B
1
Results1 Array element
Results
1 Array element
Index [ 0 ]
Index [ 0 ]
Item
Item_UPC_MixedHerb
Count
1
Order Matters
Required Tags
0 Gameplay Tags
Seconds
1
Index [ 1 ]
Index [ 1 ]
Item A
Item_UPC_Poison
Count A
1
Item B
Item_UPC_Arrow
Count B
5
Results1 Array element
Results
1 Array element
Index [ 0 ]
Index [ 0 ]
Item
Item_UPC_PoisonArrow
Count
5
Order Matters
Required Tags
0 Gameplay Tags
Seconds
1
Combines
Combines2 Array elements
Combines
2 Array elements
Index [ 0 ]BP_UPC_Combine_LoadHolder
Index [ 0 ]
BP_UPC_Combine_LoadHolder
Unit Timing
Unit Timing
Seconds
0.3
Start Sound
None
Commit Sound
None
Cancel on Move
Index [ 1 ]BP_UPC_Combine_CoatWeapon
Index [ 1 ]
BP_UPC_Combine_CoatWeapon
Poison
Item_UPC_Poison
Weapon Tags
1 Gameplay Tag
UPC.Slot.Weapon
Poisoned Tag
UPC.Inventory.Stat.Poisoned
Strikes
3
Unit Timing
Unit Timing
Seconds
1.5
Start Sound
None
Commit Sound
None
Cancel on Move
The set all three demo styles list. Mix Herbs and Dip Arrows are plain rows; Load Holder and Coat Weapon are Blueprint combines.
The party RPG and open world list combine at once; the survival grid shows a progress bar.
MultiplayerWhat runs where
- The server makes every unit; the owning player's machine asks the same sets, so its Can Place and Can Combine match the server early. It plays its copy of the load from the server's step about half a round trip later and stops with the server's reason.
- On Combined fires on the server and the owning player's machine, once per landing.
C++C++
The magazine as a C++ subclass:
UCLASS()
class UMyMagazineCombine : public UUPCInventoryCombine
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Magazine")
TObjectPtr<UUPCItemDefinition> Round;
UPROPERTY(EditAnywhere, Category = "Magazine")
TObjectPtr<UUPCItemDefinition> Magazine;
UPROPERTY(EditAnywhere, Category = "Magazine")
int32 Capacity = 30;
virtual bool Matches_Implementation(const UUPCInventoryItemInstance* Item, const UUPCInventoryItemInstance* Target) const override
{
return Item->GetItemDef() == Round && Target->GetItemDef() == Magazine;
}
virtual int32 GetUnits_Implementation(const FUPCInventoryCombineContext& Context, FGameplayTag& OutReason) const override
{
const int32 Room = Capacity - Context.Target->GetStatTagStackCount(TAG_MyGame_Ammo_Loaded);
const int32 Units = FMath::Min3(Context.Count, Context.Inventory->GetItemStackCount(Context.Item), Room);
if (Units < 1)
{
OutReason = TAG_UPC_Inventory_Refused_Full;
}
return Units;
}
virtual void ApplyUnit_Implementation(const FUPCInventoryCombineContext& Context) const override
{
Context.Target->AddStatTagStack(TAG_MyGame_Ammo_Loaded, 1);
Context.Inventory->ConsumeItemInstance(Context.Item, 1);
}
};Inventory/UPCInventoryTypes.hdeclares the refusal tags;TAG_MyGame_Ammo_Loadedis your own native tag.UUPCInventoryConfig::CombineSetslistsUUPCCombineSetassets, each holdingFUPCCombineReciperows inRecipesand instancedUUPCInventoryCombineobjects inCombines.UUPCInventoryConfig::FindCombinereturns the first match as anFUPCCombineMatch.- The load is one
FUPCEquipStepwithRepeatsset to the units,SourceItemthe target andUsedItemthe dropped item. UUPCInventoryComponent::CanCombinefills theFUPCCombineMatch;OnCombinedcarries anFUPCCombineOutcome.
AdvancedTroubleshooting
- A drop merges or swaps instead. Nothing in the config's Combine Sets takes the pair, or the character uses another config.
- Nothing happens. Another equipment change is running, or the pawn has no UPC Equipment Manager (a warning under
LogUPCInventory). - A drop is refused. Print Out Reason from Can Combine or Can Place:
NoCombine,NotEnough(a stack holds too few for one combine),Locked(Required Tags missing), or a reason your combine sets, such asFull. - A load stops at once. Get Units answered 0 for the next unit, or the player moved during a combine that stops on a move.