Best way for a objects interaction system?

Hello everyone,
I am here to talk about a project of mine that i am making to improve my Unity Skills and learn new things.

I am working with Unity since 7 months, I have seen a lot of different aspect of Unity: Canvas, Physics, 2D, 3D and so on, but never specialized in any of this.

###What I would like to do###

I am trying to replicate Toca Life: World and its clones (Fiete World, Sago Mini World and so on) because i think that it’s a complete environment where to train 2D skills,: it has drag & drop, interaction between different objects and a free playable world.

What intrigue me more it’s the interaction system beyond its gameplay, almost any object can interact each other and have a different result based on the combination of the interaction.

My problem / The difficult part

I don’t have a great idea where to start to build a good architecture to manage all those interactions. I have thought that when an object is dragged, as you release it, it check if it is near another interactable object and if so, it check if they can interact each other and if also this is verified the resulting action/animation will happen. In particular my problem is how to store those interaction and check which one execute, because exist the possibility that an object A can interact with object B in a way and interact with object C in another way an so on. So, i need a system that every time i catch an interaction between 2 object, understand which is the right action to do as result of this interaction. I thought about a manager script that has a database (let say a Dictionary, method ptr>) where i store all the possible interaction based on the type of the object but i don’t think its a very good idea, but i couldn’t find anything better. An alternative is that every object store their possible action based on which object it will interact with it, but i think is a waste of memory because every object in the scene should have a Dictionary and use memory.

I have also followed tutorial on Scriptable Object and I find them interesting and that could help, but at the moment i didn’t find a useful application for them at the moment.

I would like help from you if possible, any suggestion will be appreciated.

What I have done

Until now i developed the drag & drop system and the “proximity check”. In particular:

  • I use a orthographic camera to develop the game in 2D
  • i create on game startup a BoxCollider at cam.FarClippingPlane+Cam.pos.z to be able to drag the camera with OnMouseDrag
  • the objects are sprites with a boxCollider and a script that manage the drag and check if it is released on another object ( I am not using boxCollider2D because when I drag it, the OnMouseDrag is caught also from the camera boxCollider, as if you click on the boxCollider2D the mouse input goes through it and hit also the boxColliders behind it)
  • to check if an object is released on another object i use Physics.OverlapBox
  • to render an object behind another in a 2D space, i use the layer sorting order of the sprite render and i change the z position of the dragged object in function of its y position (only the sorting order is not enough, because it helps only on the render order to see one object in front of another but when you go to click on the object, if they are on the same z, unity will drag always the same object also if for the layer sorting order is behind, so i made a proportion to calculate at which z needs to be located the object based on the y). i used this tutorial for the sorting layer.

I have provided those information to let you understand the situation of my project so you can help me on the interaction system realization. Feel free to give your opinion about my choice and to suggest correction if you think i made something wrong or it can be improved.

What you’ve described sounds reasonable enough design wise - in terms of implementation, this is where object-oriented programming shines. You can create a base interface for interactions and simply create scripts inheriting from that interface - implementing its functionality per-instance and handling all cases directly in the scripts.

For this interface, let’s call it IInteractible (interface convention is to prefix with a capital ‘I’, as much as it might look weird in this context) and give it two functions - one for deciding whether this interactible can interact with another interactible, and one for actually handling interactions:

public interface IInteractible
{
    public bool CanInteract (IInteractible a_OtherInteractible);
    public void Interact (IInteractible a_OtherInteractible);
}

From there, you can create scripts as you need for each interaction and add them to each object. For an example, let’s create two objects that will interact with each other: a generator and a lamp.

public class Lamp : MonoBehaviour, IInteractible
{
    private Light m_Light;

    private void Start ()
    {
        m_Light = GetComponent<Light>();
        m_Light.enabled = false;
    }

    public bool CanInteract (IInteractible a_OtherInteractible)
    {
        // If the other object is a Generator we can interact with it
        return (a_OtherInteractible is Generator);
    }

    public void Interact (IInteractible a_OtherInteractible)
    {
        // If the other object is a Generator
        if (a_OtherInteractible is Generator)
        {
            // Cast it to a type of Generator and enable our light if the generator is powered on
            m_Light.enabled = ((Generator)a_OtherInteractible).poweredOn;
        }
    }
}

...

public class Generator : MonoBehaviour, IInteractible
{
    public bool poweredOn = true;

    public bool CanInteract (IInteractible a_OtherInteractible)
    {
        // No interactions generator-side
        return false;
    }

    public void Interact (IInteractible a_OtherInteractible)
    {
        // No interactions generator-side
    }
}

In this case, if a generator is powered on and interacts with a lamp it will turn the lamp on. You could even create another interactible ‘Switch’ that the generator interacts with, turning on if a Switch connects to it. To make the interaction happen, you can check for IInteractibles like you mentioned via placement and OverlapBox, then just fetch any interactible components:

// Assuming you are checking for other interactibles from within the placed object; fetch this interactible
IInteractible myInteractible = GetComponent<IInteractible>();
Colider2D[] overlappingColliders = Physics2D.OverlapBoxAll (...);
for (int i = 0; i < overlappingColliders.Length; i++)
{
    // Try to get an interactible attached to the other object
    IInteractible otherInteractible = overlappingColliders*.GetComponent<IInteractible>();*

if (otherInteractible != null)
{
// If the other object has an interactible, send interaction events; passing in our interactible
otherInteractible.Interact (myInteractible);
// Also send interaction back to our interactible, passing in the other
myInteractible.Interact (otherInteractible);
}
}
For consistency, even though an interaction could go both ways, I would try to keep the interaction functionality on the side that will be affected (i.e. the light turning on goes in the Lamp class rather than the Generator class) - this way the code will be cleaner and classes won’t need to specialise too much for every interaction.