Smart Objects

Intro

A Smart Object is an object in the world that fully contains all of the data and logic necessary to tell characters in the world how to interact with it. It has a set number of Smart Object Slots that can be occupied by characters at one time, with reservations managed by the Game Server. This has been set up to be similar to the built-in Smart Objects feature present in Unreal Engine 5, but it is not exactly the same and has been custom-tailored to fit into our game code.

System

SmartObjectSlotComponent

This is a scene component with a transform that a character detects and gets to to interact with an object and run the custom object logic. It’s the component that will drive the logic of that one NPC. It contains these variables:

Instance variables:

Static variables:

API:

Some presets are available in Game/Systems/SmartObject/Slots to show typical use cases, like SOS_SimpleAnim and SOS_SimpleEmote . See Using the Smart Object system

SmartObjectCondition

These are UObjects that will be added to a SmartObjectSlotComponent . To be able to be set directly on the component, they should use the EditInlineNew UCLASS tag, and the container variable the Instanced UPROPERTY tag . The base slot component class triggers the events as appropriate. The API is:

Base set of smart object conditions:

To get a comprehensive list, have a look at SOS_AllCoreFunctions in Game/Systems/SmartObject

SmartObjectFunction

Just like smart object conditions, those are objects stored on the slot component which triggers the events as appropriate. The API is:

Some smart object functions examples:

To get a comprehensive list, have a look at SOS_AllCoreFunctions in Game/Sytems/SmartObject

SmartObjectManager

This is a singleton actor that can be gotten from AConanGameState that is used to get access to smart object slots. Internally, smart object slots might be destroyed at runtime to save some resources on the server so going through the manager allows to construct them on the fly.

API

SmartObjectCoordinatorInterface (optional)

This is for any coordination logic between the slots. It should be implemented in the actor itself.

The API:

Any other bindings can be done directly to the slot delegates themselves, like binding to SmartObjectSlotComponent::OnStateChanged from SetSlotState . But you should rather bind to the actual logic that changed.

In addition to placeables, we could also have other things like resource nodes or in-game elements such as dungeon doors use the SmartObjectInterface and have their own logic to return slots (resource nodes could generate them on the fly).

Peripheral systems

Displaying available slots

This should be useful from the ThrallSystemComponent and the BuildSystemComponent .

They would call GetAvailableSmartObjectSlots from the SmartObjectManager and call DisplayPreview on the linked CDO slot component, which should show a ghost humanoid preview of what the thrall would do on the slot.

When using the ThrallSystemComponent , when the brush comes in range of the smart object slot, it would be snapped in place of the ghost humanoid preview.

Choosing to interact with a slot

This is the part that chooses which smart object slot to interact with

Thralls : The player will see the available slots when moving/placing the thrall and can assign them directly

Players : This should be part of the interaction logic of the parent actor. For instance, for placeables, if there is no other logic tied to interacting, it should scan for the available slot components and try to reach the closest available one. Placeables should make sure that there is only one behavior tied to interact (for instance the hidden bookshelf should only have the interaction to open it, not to read a book in front of it)

NPCs : The way NPCs choose to interact with smart object depends on which system relies on smart objects (purge behaviors, the tavern, a potential settlements system…), but it should involve querying nearby available slots. They also need to be notified when slots become available.

Assigning a thrall to a smart object slot should be considered his new home location, and a reference to the smart object should be saved in the thrall component alongside the home location variable.

This way, when a thrall is released from the slot (like for instance by being attacked by an enemy or when put in following mode), the thrall can instantly reserve the slot so no other thrall is assigned to it, and then it can naturally go back to the smart object slot when done with its other AI tasks.

Getting to the slot

There are different ways of getting to a slot:

All those ways are handled by systems external to the smart object system. When the smart object is detected, it is put in the Reserved state until the character gets to it. When the system that gets the character to the slot is happy with itself, it then calls BeginUse that puts the slot in the InUse state.

Forced movement

For the player flow typically (but also NPC AI), interacting with a smart object will make you enter a forced movement towards the smart object slot location. This logic is triggered with TryForceMoveToSmartObject . It has the parameters:

Additionally: