Component based objects in Unity3d

Hello everyone,
I’m starting this thread to discuss general approaches to design components and communication between components/game-objects. I know that this thread can go deep into off-topic territory. But still I need some help with following questions:

  • How do you design components to be atomic? I’m really struggling with this and most of approaches described online have some kind of issues later in the project’s development.
    Good example is singleton pattern. It’s really easy to start but refactoring becomes a nightmare.
    Same goes for built-in service locator methods. It’s handy, it’s built-in but it assumes hierarchy of gameobjects. So if I use GetComponent I assume that needed component should be on the same GameObject which makes component coupled to hierarchy. Should I create some kind of connector component for specific prefab(e.g. player) to inject dependencies into multiple components using GetComponent/Find().GetComponent?
    -How do you design dependency flow in your project? Is it dynamically spawned objects(players, monsters, etc.) depend on scene objects? Or scene objects search for dynamic objects(managers search for players, monsters, etc)? Or both types(static and dynamic) depend on scriptable objects?
    -How do you test prefabs independently?
    Frankly, I’m frustrated. I’ve been using unity3d for 7 years already and I’m still to find least painful method for dependency management, components design and communication. I know that there’s no such thing as silver bullet but I really hope that this community can help me to find next best thing.

EDIT:
Maybe there’re some simple rules like Godot’s “Call down, signal up” that you’ve come up with?

Citation? I’ve never found singleton or service locator code particularly hard to refactor. It’s just … code.

But the point is that it assumes this in the one single location where you can trade it out. There WILL be some manner of dependency between the code and the hierarchy for any non-trivial game application. A service locator / factory lets you keep it all in one file.

And further, by presenting the service as an interface you can still decouple completely, modulo a change to the interface protocol.

Version 1: put it all in the scene, hook it up, press play. If it looks promising, move onto version 2, else delete.

Version 2: make some factories, managers, break things into separate scenes, hook up some lazy loaders to abstract things that come in varying quantities (enemies, shots, explosions, etc)

You can’t design it in advance: you have no idea what it will be. This is how game software engineering works.

I also like to use LOTS of separate additively-loaded scenes, each handling only one subpart of the game. Once you get the parts all lazy-late-loading and happily waiting for everything before proceeding, it’s awesome. See below.

I’m not sure in the general case they can be. If I have a prefab I am worried about iterating, I make a scene to test and develop it, with enough stuff around it to make it happy.

Unit testing interactive software doesn’t really yield huge results. No matter how much unit testing you plumb in, at the end of the day the typical bug, the bread and butter bug that we spend ALL our time on, is stuff like this:

On iPhoneX, when the user stacks more than 5 activate-able spells without activating them, the top spell enters the stupid notch at the top of the phone and cannot be interacted with.

That’s not a unit-testable problem, not without being perfectly clairvoyant. But this is broadly representative of the types of bugs that appear over a two- to three-year product support lifecycle, especially as team members are added / removed and the game evolves and grows more and more tin whiskers that have a pesky habit of shorting out to chassis ground.

Sorry to hear that… for what Unity does, the complexity that it allows you to trivially embed in your game assets, references and huge trees of ad-hoc combinations and linkages, I’m not sure there ever would be an easy mechanism, short of religious defensive coding, clear error messages, and whatever amount of playtesting you feel is appropriate.

Notes on additive scenes: Additive scene loading is one possible solution:

https://discussions.unity.com/t/820920/2
https://discussions.unity.com/t/820920/4

https://discussions.unity.com/t/824447/2

A multi-scene loader thingy:

https://pastebin.com/Vecczt5Q

My typical Scene Loader:

https://gist.github.com/kurtdekker/862da3bc22ee13aff61a7606ece6fdd3

Other notes on additive scene loading:

https://discussions.unity.com/t/805654/2

Timing of scene loading:

https://discussions.unity.com/t/813922/2

Also, if something exists only in one scene, DO NOT MAKE A PREFAB out of it. It’s a waste of time and needlessly splits your work between two files, the prefab and the scene, leading to many possible errors and edge cases.

Two similar examples of checking if everything is ready to go:

https://discussions.unity.com/t/840487/10

https://discussions.unity.com/t/851480/4

@Kurt-Dekker thank you for your reply.

That’s just my experience. I’ve tried different “flavours” of this pattern(e.g. monostate) and usually end up with spaghetti code. Good example would be when multiple singletons depend on each other. Which is a bad design, I know but it’s really easy to get into that kind of situation with singletons.

I think I need to describe my issue with baking hierarchy in component’s code better. Ok, so, following situation. Component which delays playback of audio clip based on the distance to audio listener to simulate speed of sound propagation. This component needs to know the distance from audio source to audio listener. From my point of view it shouldn’t also be a service locator and should use some external object for this purpose. So decision on how to search for the listener differed to another preferably reusable code. This way this delay audio component will be portable and reusable in any hierarchy. What grinds my gears with this example is that the simplest and probably safest solution is to create search component with search approach exposed as variables in the inspector which leads to drag-n-drop fast for each prefab. So it’s either drag-n-drop fast or badly reusable components.
I tried using factory pattern several times and it all works well until I’d need to reuse some assets which aren’t designed with factory in mind. Example would be Unet/Mirror’s network manager. It doesn’t give a frank about how easy it is to incorporate it into a project. No events. Need to extend it and it usually becomes just another class that I’d need to maintain.

Totally agree that it’s hard to predict everything but I like to be driven by simple rules. For example for Godot it’s “Call down, signal up”. I’d really like to find such rules for unity, simple and just works. Getting back to example with Unet/Mirror. Player is dynamically spawned without any events to listen to. So if something like hit manager or score manager is dependency for player’s components we’re back to square one. Get instances without actually hardcoding search pattern into component or bending unity to our rules. I’ve tried last one way more than I needed. It’s an uphill battle.
Additive scenes is a good approach though. Thank you. Like keeping Map scene and GameType scenes separately and load second one additively when it’s needed.

True this one is hard. I was thinking to have some kind of mocked component as a dependency which will work as a proxy to real component and work as mock when dependency isn’t available. Another idea is to have external dependencies only on ScriptableObjects which implement needed interface. But unity doesn’t work well with interfaces(in editor.)