- Home /
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.
I don't think there's the "best" way, but you could google and read about Multiple Dispatch
I agree with you, every application needs different modifications, and cannot exist a single "best" way to every need, I wanted to know which is the "most powerful" way of thinking about this kind of interaction with a lot of combinations. As @Namey5 said, is a design "problem". Do you think that changing the title could be a good option? Something like Object interaction system pattern design? Or similar, i don't know... I know Multiple Dispatch, but thinking about it didn't help me with ideas, I think i will read again about it now that i have an answer that satisfies me to see how it could help reach the same design wise. Thank you.
EDIT: I studied multiple dispatch long time ago with C++ and only now I have seen that things are different with C#, so i think i need to study in deep this argument as you suggested, thank you again.
Answer by Namey5 · Sep 14, 2021 at 09:21 AM
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[i].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.
Thank you very much @Namey5 . This morning i was thinking about something very similar but it didn't take a good shape in my $$anonymous$$d yet, you helped me a lot to figure out my ideas that were so confused. I agree with you to keep the interaction functionality on the side that will be affected, also because there can be some interaction that in the opposite way could be nonsense (a simple example, if you drag a scissor on the head of a person, you cut his hair, but if you drag this person on the scissor it shouldn't do anything or utmost take the scissors in his hands). I will try to apply this design, thank you. I think that for something I need I will modify something, if so, i will update the post.
Hello @Namey5. i was using your suggestion but only now i noticed hat you use Collider2D while i described that i didn't used boxCollider2D. I made this choice, because while for the rendering visualization i followed the instruction described in the video linked above and resolved the depth problem, those solution didn't work on the drag&Drop side. I use onMouseDown and onMouseDrag to develop the drag&drop functionalities, and those works with boxCollider and boxCollider2D, but if i use boxCollider2D i am not able to drag only one collider but each one that is behind the input mouse position... so if there are more than one object in a point and rendered with the right order (thanks to the tutorial linked above) i will drag all the objects. Instead, with boxCollider, if the object are all on the same z, i will drag always the same object (let's say objA objB and objC are one over the other in this order and i want to drag objC that is at the top of them, i will always drag B, for example, because unity decided so.) Have you any further suggestions to avoid those problem and use Collider2D?
Your answer
Follow this Question
Related Questions
OnTriggerEnter2D Running twice when destroying game object. 1 Answer
Moving A 2D Object Forward 1 Answer
moving player top down,movement in unity 2d 0 Answers
Blurry game build 0 Answers