Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[SOLVED] Logic button system : Composition VS Heritance/Interface

Discussion in 'Scripting' started by Kamalen, Jan 28, 2015.

  1. Kamalen

    Kamalen

    Joined:
    Dec 27, 2012
    Posts:
    29
    Hi,

    I am quite new to Unity and encounter some troubles with the good practice regarding code organisation. I am using C#.

    I want to make some kind of logical trigger scene, with for example one button able to activate a door, a trap or whatever. I want this system to be complex, with the use of And/Or functions.

    Coming from the OOP world, my first clue would be to use some ITriggerable with a Trigger() method - the interface way. However, it's not possible to use interfaces in the Unity Inspector.

    Plus, I've read in some topics it's better in Unity to use composition ; making a Trigger component with a boolean "Active", and making the other components check for the bool state to react. That seems slowed, as the object will react only on the next Update.

    I can't make my mind on the topic, and I need more pro and cons from the more experienced.

    Thanks by advance for your help !
     
  2. Kamalen

    Kamalen

    Joined:
    Dec 27, 2012
    Posts:
    29
    No one has a clue ? A resource on the topic ?
     
  3. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,670
    Hi, and welcome to Unity! Use SendMessage. SendMessage uses reflection. For efficiency, you shouldn't call it multiple times per frame (e.g., multiple calls in the Update method). But it's ideal for general triggering, and it's a great way to keep your scripts decoupled and to use composition.

    First add a script that handles the button click, something like this:
    Code (csharp):
    1. public class TriggerOnClick : MonoBehaviour {
    2.  
    3.     void OnClick() {
    4.         SendMessage("HandleTrigger"); // Send message when button is clicked.
    5.     }
    6. }
    Then add one or more scripts that handle the message:
    Code (csharp):
    1. public class DoorTrigger : MonoBehaviour {
    2.  
    3.     void HandleTrigger() {
    4.         // (open/close the door)
    5.     }
    6. }
    Code (csharp):
    1. public class TrapTrigger : MonoBehaviour {
    2.  
    3.     void HandleTrigger() {
    4.         // (spring the trap)
    5.     }
    6. }
     
  4. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    If you want to use the Inspector (and don't want to write/use an editor plugin) then make an abstract class instead and serialize a field of that.

    Code (csharp):
    1.  
    2. public inteface ITriggerHandler
    3. {
    4.     void HandleTrigger();
    5. }
    6.  
    7. public abstract class TriggerHandler : MonoBehaviour, ITriggerHandler
    8. {
    9.     virtual void HandleTrigger() { }
    10. }
    11.  
    12. public class SomeClass : MonoBehaviour
    13. {
    14.     [SerializeField]
    15.     TriggerHandler handler;
    16. }
    17.  
     
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,670
    I prefer using SendMessage because you don't have to do any explicit hookups in the inspector. It's one less thing to do. Just my opinion, though. If you need explicit hookups, the abstract class is the way to go.
     
  6. Kamalen

    Kamalen

    Joined:
    Dec 27, 2012
    Posts:
    29
    Abstract classes are not possible for me, because later I may need a Door that can be either Triggered, Destructible or both.

    I am orienting towards the TonyLi solution, but instead of SendMessage who is vulnerable to typo errors, I prefer to use a delegate.

    Code (CSharp):
    1. public class Trigger : MonoBehaviour
    2. {
    3.     public delegate void TriggerDelegate (bool value);
    4.  
    5.     public TriggerDelegate OnTrigger;
    6. }
    7.  
    The component using the triggers will have to subscribe, but that's better than hard to find string error IMO.

    Thanks for your help !
     
    angrypenguin likes this.
  7. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,670
    Good solution!

    If you make it an event, multiple handlers can subscribe to the trigger.
     
  8. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    Delegates support that too.

    Another option is GetComponent("InterfaceName") and an appropriate interface. Then you can call the appropriate method on any Component on the GameObject which implements the interface. This doesn't require subscriptions, and it won't be called often so the GetComponent isn't going to cause issues. (You could cache it anyway if you wanted.)

    Yeah, I typically don't use SendMessage myself (personal preference, not a suggestion), and this is one of the reasons. Others are the possibility of name conflicts, violation of encapsulation (it'll call private stuff) and lack of intellisense help.
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    If you are already reasonably familiar with C# you should check out EventSystems.ExecuteEvents. Its designed as a replacement for SendMessage from 4.6 onwards. It allows many of the benefits of SendMessage, with fewer of the shortcomings. Biggest downside is the syntax is far from intuitive.
     
    TonyLi likes this.
  10. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,670
    Thanks for mentioning EventSystem. I still support a lot of 4.3 - 4.5, so I forget to mention newer features, but this is definitely better than SendMessage if you're using 4.6+.