Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Feedback Decupling of components through UnityEvents

Discussion in 'Scripting' started by matzomat, Sep 26, 2021.

?

Good idea?

  1. Maybe...

    0 vote(s)
    0.0%
  2. Yes

    1 vote(s)
    50.0%
  3. No

    1 vote(s)
    50.0%
  1. matzomat

    matzomat

    Joined:
    Mar 4, 2018
    Posts:
    63
    Hey,

    I would like to get some ideas or feedback about my concept for decupling of components.

    My Example:
    I have an Agent (a guy in a game, Player or AI controlled) and he's repairing stuff. He has an AgentController, that pretty much controls what he's doing, no matter, where the order came from. The Agent has abilities (skills) that he needs to do his work and that he also trains on his job. My goal is now to decuple everything I can so I can write the AgentController or the AbilitySystem and whatever comes next (Equipment,...) in peace and alone. I want no hardlinks between them.

    My solution: Whenever something is happening, all Controllers and Managers expose UnityEvents. I added a few that can pass parameters, like when something is targeted, all listeners get the info which GameObject was targeted. The AbilitySystem for example waits for this event and then calculates how good the Agent would be at repairing something the moment the agent targets something. In case the target has no suitable Monobehavior attached (RequiredAbilities), it simple does nothing. Otherwise it does his calculation about how good the Agent will be at it's job and exposes an event for feedback with a float, that the AgentController grabs back.

    That way the finished Agent with plenty of systems will later inform all systems that he has a new target and get feedback from all systems about how good he is, how good his equipment is, how good the surroundings are or if it's a good idea to repair an antenna in a thunderstorm and so on. But he doesn't really care, where it's coming from, he simply uses whatever he gets. Maybe he targets an enemy later on and then he still gets feedback but from other systems as there is nothing to repair (yet).

    When such an agent is ready, plenty of UnityEvents need to be linked between all the systems and I'm sure this is prone to error. But I don't know how else to make sure that all systems are totally independent.

    ScriptableObjects are not an option, because I might have many Agents in the scene doing their jobs.

    Am I on the right track or am I totally wrong and there is a far better solution for this?

    Thank you in advance for feedback.
     
  2. Cameron_SM

    Cameron_SM

    Joined:
    Jun 1, 2009
    Posts:
    915
    You're talking about dependency management.

    Try not to focus on severing links between systems. Encapsulation and separation of concerns is a great goal, but nothing works in a vacuum and over encapsulation or over abstraction can make things really hard to debug. Instead, think you your systems in terms of what dependencies they have in order to function - what inputs are needed and what works best for getting those inputs. Also, how you validate those inputs as correct is where a good % of bugs occur in general development.

    Sometimes a hard dependency is fine, like if a CharacterMovement component assumes there will be a CharacterController component on the same game object where it can read input or a movement goal in order to move the agent around. No need to decouple that dependency because you're never likely to use CharacterMovement on something without a CharacterController so using GetComponent() and having it throw an error if it can't be found is actually a good - the sooner you know that relationship isn't setup correctly the less time you spend debugging wondering why something isn't working.

    For other stuff like a lot of UI components or prefabs exposing fields in the inspector and referencing that way works fine if the dependency is compositional and fairly obvious.

    For spatial dependencies, triggers and colliders are obviously an ideal way to handle dependencies as the code you write for the collisions acts as a kind of input validation - great place to catch errors!

    For instances where you have a system that needs to notify other systems without a direct relationship raising events is a fairly common method - the observer pattern is often used here - could be Unity Events or Plain C# events or a custom system like an injected signal bus - 1000 ways to skin that cat.

    Using events also tends to foster Inversion of Control principals (IoC) where you pass in dependencies when objects are constructed so they can subscribe to events they want to respond to later on. It also pairs nicely with Dependency Injection - few different ways to do that in Unity but the most common one is Zenject which I use a lot.

    Singletons are another option for but they're a kind of a poor-mans dependency injection. I wouldn't recommend using that pattern and it makes testing and a lot of other stuff harder.

    As for wiring everything up using Unity Events in the editor it really only works for compositional dependencies like prefabs. I wouldn't go hard on them for everything, there's no one size fits all solution which is why Unity has different ways to connect things together.
     
    Last edited: Sep 27, 2021
    matzomat likes this.