0
\$\begingroup\$

I am currently considering two possibilities to a centralised interaction system within my game.

Consider that you want to produce different results for different types of objects within the game world - you interact with a signpost and dialogue appears, or interact with an item on the ground and it gets added to your inventory with a notification.

Essentially, I cannot decide whether it would be more efficient to implement such functionality all within one centralised place or to spread each function out over separate events.


Option 1: Centralised

This is what I have started coding. One Interact class which contains several "Types" of possible events in my game.

When the player "interacts" with an object implementing this script, it will implement the functionality that it is set to in the Editor. This means I drag and drop this Event Script onto a prefab and can automatically tweak its functionality within a couple of seconds and have it ready to go in the game world.

I have simplified the general consensus of my code into this block for readability:

public string eventName;

public enum EventType
{
    Movement = 0,
    Dialogue = 1,
    NPC = 2,
    ItemCollect = 3,
    Teleport = 4,
    Cutscene = 5,
    Switch = 6,
    Custom = 7
}

public EventType eventType;

/* 
* #############################
* ##@@## INITIALIZATION ##@@##
* #############################
* 
*/

private void Awake()
{
    SetupEvent(eventType);
}

public void Activate(bool calledFromTrigger = false)
{
    if (autoTrigger && !calledFromTrigger)
        return;

    Debug.Log("Activated " + name + "!");
    StartEvent(eventType);



}

private void StartEvent(EventType type)
{
    switch (type)
    {
        case EventType.Movement:
            MovePlatform();
            break;
        case EventType.Dialogue:
            break;
        case EventType.NPC:
            break;
        case EventType.ItemCollect:
            break;
        case EventType.Cutscene:
            break;
        case EventType.Switch:
            break;
        case EventType.Custom:
            break;
    }
}

Resulting in:

The editor view...


Option 2: Inheritance

It is as if I have a little devil and angel on my shoulder, one tugging me towards the easy and half-finished code of the former example, and another urging me to redesign what I am doing into a much cleaner and abstracted approach.

In the previous example, all "event types" (Movement, Dialogue, Item Collection etc.) were handled by enums that were passed through a Switch statement to check which method should be executed based on the parameters inputted into the Unity Editor for that specific Game Object.

In this system, there would be a base "Interactible" class, which various classes would inherit from to implement functionality. The Activate() method would be overridden in the children for each specific class.

public class NPC : Interactible
{
    public override void Activate()
    {
        //NPC Code...
    }
}

The unfortunate downside to this is that I can't just use one prefab for all my events and change them based on the custom editor that I define.

I would have to create a custom editor for each possible child of the Interactible class, and instead of selecting which type of event I want it to be from a simple drop down, I will have to swap out different scripts for different functionality.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

I'd do neither of these. Instead, I'd apply Composition over Inheritance.

A quick sketch for now since I have to run:

  • IInteractionResponse interface defines the pattern all components that can respond to these interactions will follow, eg. exposing an Activate() method. Then you can implement this on any number of unrelated types:

    • a Dialogue component for conversations and messages
    • a ScriptedMovement component for moving objects & bits of the scene around
    • an ItemTransaction component for adding/removing items in the player's inventory

    • etc...

  • Any script that can start an interaction - say a UsableItem script or a InteractableButton or an InteractionTrigger can expose a public variable for the component it should fire when activated (or search for the first such component attached to its GameObject)

  • If you give these interaction response components a public component triggerNext, then when one interaction component has done its job it can fire the next one in sequence. You can use this to snap-together complex interactions like LEGO bricks, to start a dialogue that then gifts an item then triggers some more dialogue then opens a gate to leave the area....

This lets each class in the setup stay small, simple, and self-contained. And you can remix them modularly, rather than being limited to single directional inheritance.

\$\endgroup\$
0
\$\begingroup\$

This question is quite opinion-based, but personally, I would clearly choose the Inheritance based solution (or I would use an Interface if you don't need any class variables in the Interactible class).

If you put all your logic in a single class, you clearly violate one of the SOLID principle : Single responsibility principle.

The code used to move a platform should not "know" how to start a cutscene. This does not make sense.

The unfortunate downside to this is that I can't just use one prefab for all my events and change them based on the custom editor that I define.

I don't think using a single prefab would be a good idea anyway. Where do you attach the script? Using a specific script according to the event would allow you to attach it to the asset (or an empty parent), making more sense in my opinion.

I would have to create a custom editor for each possible child of the Interactible class, and instead of selecting which type of event I want it to be from a simple drop down, I will have to swap out different scripts for different functionality.

Why would you need to create a custom editor for each child class? The CustomEditor tag has an additionnal parameter so that the custom editor can edit child classes:

public CustomEditor(Type inspectedType, bool editorForChildClasses);
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .