Chance's Modular Interaction System

:eyes: Introduction:

This system is built to empower designers to quickly and easily implement interactable objects into their game. The system is built to support objects having one or many unique interactions and manage priority when multiple interactions are present.

Key Features:

  • Supports multiple interactions per object
  • Dynamic UI that updates based on available interactions
  • Supports press, hold, and repeated tap interactions
  • Easily extendable with new interaction types

Build Your First Interaction (Chance’s Modular Interaction System)

:play_button: Getting Started:

Prerequisites-

In order to use Chance’s Modular Interaction system you will need to download it from GitHub through the following link: Chances-Interact-System GitHub

Setup Instructions-

  1. Open the project

  2. Once in the project you can begin testing with the provided example interactables or migrate the interaction system folder to your own project.

  3. When migrating system to a new project be sure to bring with or create a new config folder for interaction gameplay tags

  4. On your character add the BPC_InteractionSystem component. This allows them to manage and handle interactables

  5. On any object that is going to be interactable implement the BPI_Interactable Interface

  6. Finally you can begin filling out each new function/event that was added by the interface.

  7. Regardless of whether an interactable object has 1 interaction or 100, the gameplay tag container must contain at least one gameplay tag. It will also need a gameplay tag for each unique interaction you plan to create

  8. Use a switch case based off the gameplay tags to implement different interactions. This will be needed at each step of the process.

  9. Finally once this is completed drag the object into the world test what you have created

:laptop: Technical Breakdown:

F_Interaction Item:

Is a structure that is used to represent an object and one of its interactions.

BPI_Interactable:

BPI_Interactable is an interface that is implemented by any object that will have interactions built onto it and will be interacted with via the player.

Get Interact Options: is a function that will return a gameplay tag container. This container is used to provide a list of all the interaction tags that can be used to call unique interactions on an object.

Get Can Interact: is a function that is used for an object (querier input) to query whether or not a given interaction (gameplay tag) can be used to call a valid interaction. In practice this is used in conjunction with a switch case so the queried interaction can have unique code based on the context of the interaction.

Get Interaction Type: is a function that is used to return whether a queried interaction requires a single press, repeated taps, or a long hold in order to complete. The function also returns a float that can be used to pass the requirement value to complete the queried interaction. This should also utilize a switch to allow for the managing of multiple interactions utilizing different requirements.

Get Interact Display Message: is a function that returns the text type. It is used to return a description or tooltip about what will happen when the player interacts. Some examples would be “Open Door”, “Pickpocket”, or “Kick”. In implementation this function should use a switch so that different interacts may return different display messages.

Get Completion Percentage: is a function that is used to query against an interaction how close to completion it is. This will traditionally be used by hold or tap events. The output of this function is a float. This function in implementation should use a switch so each unique interaction can have code built around finding and utilizing its return percentage

On Interact: is an event that is used to call the actual interaction code. This may be picking up an object or opening a door. The function takes inputs of Instigator (the interactor), interaction system component (the interactor’s BPC_InteractionSystem), Interaction, and bool action value.
In implementation, this function should use a switch to execute the correct interaction logic based on the passed interaction tag.

Cancel All Interactions: Is an event that can be called to end or break all interactions on an object. For example if a player were to get caught lockpicking the interaction should be cancelled and the player should be kicked out.

Interaction System Component:

The variables Is Interacting and Is In Sub Interact are used to control and block interaction logic throughout the system. It is recommended based on what you are building to replace these bools with a gameplay tag that can be attached to the owning player and queried against.

Function Query If Interactable: takes an input of type object (Query Object). Clear the array Interactions and Owners List.

  1. If Query Object implements interactable interface or if any of its components implement the interface. If none do immediately return the function.

    1. On the Query Object call Get Interact Options then break the gameplay tag container. For each gameplay tag call Get Can Interact. If can interact is true add the object and the queried interaction as an Interaction Item struct to the Possible Interactions and Owners array.

    2. Get all components by interface BPI_Interactable- For each component call Get Interact Options then break the gameplay tag container. For each gameplay tag call Get Can Interact. If can interact is true add the object and the queried interaction as an Interaction Item struct to the Possible Interactions and Owners array.

  2. On function return output bool (has valid interactions) is set based on if the array Possible Interactions List and Owners is not empty. The array is also output (Interactions List)

Event Begin Play: promote owning actor to a player, then if the owner is locally controlled add the input mapping context IMC_Interact to them. Finally Create and add to the viewport the widget W_Interact

Bind Event Begin Overlap: bound to the owners begin overlap event call Query If Interactable on the overlapped object. If the object is interactable then add it as a unique element to the Possible Interactables array and call Try Find Highest Priority Interactable

Bind Event End Overlap: bound to owners end overlap event find if the actor who we ended overlap with was a member of the Possible Interactables array. If so remove the actor from the array.

Event Tick: Trace for objects in front of the owners camera component. On object hit query if it is interactable. If it is interactable promote that object to a variable Interactable Look At Object and call Try Find Highest Priority Interactable. If no object is hit or if the object hit is not interactable clear the variable Interactable Look At Object and call Try Find Highest Priority Interactable.

Event Try Find Highest Priority Interactable: first set the current highest priority interactable to be the previous highest priority interactable.

  1. Interactable Look At Object IS VALID- set Interactable Look At Object to be the Highest Priority. Then call Display Interact UI. Then do the Active Interaction Check

  2. Interactable Look At Object IS NOT VALID and Possible Interactables Is Not Empty- If the Previous Highest Priority Interactable is valid then use it to set Highest Priority Interactable. If not then set Highest Priority Interactable using the 0 index of the Possible Interactables Array. For each element in the Possible Interactables array check if it is nearer to the owning actor than the Highest Priority Interactable. If it is closer set Highest Priority Interactable using this array element. Finally do the Active Interaction Check.

    1. Interactable Look At Object IS NOT VALID and Possible Interactables Is Empty- remove the W_Interactable widget from its owner. Set Is Interacting and Is In Sub Interact to false. Then if Active Interaction is valid call cancel all interactions on the interactable object.

  3. Active Interaction Check: if Active Interaction variable is valid check on the interactable object if Can Interact is not true or if the Previous Highest Priority Interactable is not equal to Highest Priority Interactable. If either of these are true then on the interactable object call cancel all interactions. Finally set Is Interacting and Is In Sub Interact to false.

Event Input Action Cycle Interact: if Highest Priority Interactable is valid then use it to Query If Interactable. If Highest Priority Interactable is interactable, and Is Interacting is not true, and Is In Sub Interact is not true the input action can go through. Add or decrement to the Selected Interact integer based on the direction of the cycle. If the selected value the bounds of the Interaction List indexes then loop Selected Index to a min or max correlating value from the Interaction List indexes. Finally call Display Interact UI

**Event Display Interact UI:**if Highest Priority Interactable is valid then use it to Query If Interactable. If Highest Priority Interactable is interactable, and Is In Sub Interact is not true the widget can be displayed to viewport else remove from parent. If is interacting is not true then display interact ui can be updated. If the Currently Selected Index exceeds the last value of the Interaction List then set Currently Selected Index to that value (this handles the edge case of an interaction being removed but the selection not being updated until Cycle Interact was called). Finally using the Interaction List and Selected Index call Update Interactable List on W_Interact and display W_Interact to the viewport.

Event Input Action Interact: call Event Try Interact and pass the input value (action value) to that event. Input Action Interact is called when the interact key is pressed and released, so action value is used to give a distinction between which has occurred (true = pressed, false = released)

Event Try Interact: has input value of bool name Action Value.

  1. Highest Priority Interactable Is Valid- on Highest Priority Interactable call Query If Interactable. If it is interactable and the Selected Interact integer is a valid index in the Interaction List use that array element to set Active Interaction. Then call On Interact on the Interact Object/Component from Active Interaction, and use the Interaction Tag for the Interaction Input. Call On Interact on W_Interact. Finally set Is Interacting using the Action Value from the Input bool
  2. Highest Priority Interactable Is Not Valid- set Is Interacting to false

W_Interact Message:

Function Set Show Interact Message: this function takes inputs of Text (action text), visibility, bool (is selected), and input type.

Using input type, action text, and getting the key mapped to the interact action. The display text is set using the format {InteractType} {Key} To {Action}

Finally the display text has its color set to be brighter if it is selected, or duller if it is not.

Function Is Being Interacted With: this function takes a bool input (Is Being Interacted With), and depending on its value if Is Being Interacted With is true then change the display text color to be unique, and when false return the color to its normal version.

W_Interact:

Event Update Interactable List: Takes an input of type array Interaction Item Structure (Interaction List), and integer (selection). Clear all children in the interaction options container, then for each entry in the Interaction List call Get Display Message and Get Interaction Type. Use the values from these to functions to create a WC_Interaction Widget and on that widget call Set Show Interact Message. Finally add the interact message as a child of the interaction option holder. If the array elements index is the same as the selection input then promote that WC_Interaction to a variable Current Interaction Text and set is selected on it to true.

Event On Interact: takes an input of type bool (input value). Using input value if current interaction text is valid then call Is Being Interacted With on it.