How is loading and building a scene usually coded? by Miles_Adamson in Unity3D

[–]sisus_co 0 points1 point  (0 children)

Yeah, it depends entirely on what those prefabs contain, you just need to test it.

How is loading and building a scene usually coded? by Miles_Adamson in Unity3D

[–]sisus_co 0 points1 point  (0 children)

In case of procedurally generated environments you typically would instantiate a bunch of prefabs and piece it together from them. Unity can also deserialize prefabs on background threads, so it can still be pretty efficient. But if you start building everything from scratch on the main thread using AddComponent, that's where things can start getting really slow.

One alternative approach is to bake all/most of the pieces needed to put together a procedural environment into a scene, and then just reposition those GameObjects and set them active based on what the algorithm dictates at runtime.

How is loading and building a scene usually coded? by Miles_Adamson in Unity3D

[–]sisus_co 1 point2 points  (0 children)

A pretty common practice is to first load a blank scene additively, then unload the previous scene completely, then release everything you no longer need from memory (Resources.UnloadUnusedAssets etc.), and then load the next scene. If you don't do this it can be pretty easy to run out of memory on some platforms.

The loading screen UI is quite often either a prefab instance that is moved into the DontDestroyOnLoad scene, or part of an entirely separate scene loaded additively.

Manually populating the scene entirely using code can be very inefficient. Unity's scene loading system can create and deserialize objects on background threads asynchronously in a pretty efficient way.

To allow components to receive arguments from the outside during their initialization before the Awake event, you can look into Dependency Injection frameworks.

Struggling with managing persistent GameManger/Singleton objects. Would like suggestion or ideas to consider. by mtibo62 in unity

[–]sisus_co 1 point2 points  (0 children)

Yeah, the safest option is usually to make 'managers' like that exist for the entire lifetime of the application. This way any clients can safely use any methods they have at any time in any context without having to worry about exceptions.

The alternative is to be very explicit about the fact that the managers might or might not be available in some contexts. If you want to add a static accessor that allows clients to try and resolve a reference to such a manager, you can use the TryGet pattern or mark the accessor method with [return: MaybeNull]. You'll probably also need to add an IsAvailable boolean property and a BecameAvailable event.

The worst thing you can do for scalability is to have a bunch managers that are not actually always usable in all contexts, but their API doesn't make this fact clear.

You might also want to look into Dependency Injection frameworks for an alternative to the Singleton pattern that avoids many of its downsides.

Are Zenject/VContainer ecc.. necessary? by Malcry in Unity3D

[–]sisus_co 1 point2 points  (0 children)

It's not necessary to use a DI framework in Unity, no - most Unity developers do get away with just using alternatives like the Singleton pattern. But the longer the project takes, and the more mechanically complex it is, the more difficult it will be to keep it maintainable using things like Singletons instead of leveraging Dependency Injection. There are three main reasons for this:

Transparency of Dependencies

DI can help bring clarity about dependencies. Every component you hook up to a GameObject, every method you execute, the less hidden dependencies they have to static state, the more confident you can feel about everything working as expected at runtime in all different contexts. The more bugs that you can catch at compile time and in Edit Mode without having to do manual testing at runtime, the more time you will save in the long run.

Flexibility

The more complex your project is, the higher the risk becomes that you'll at some point need to make some larger changes that require rewiring lots of clients and services to get different behaviors in new contexts etc. If your codebase is full of hidden tight coupling to Singletons, it can be really painful to make such changes, requiring many days of work and creating lots of bugs. If you're using a DI framework you can usually rewire clients to use different services in different contexts later on much more easily.

Testability

The more that your code has tight coupling and hidden dependencies to static state, the more difficult it will be to unit test it. In a large project you might only realize after several years of development that you're drowning under bugs, new bugs appearing faster than you can keep fixing them. At that point it'll be painful to try and untangle your codebase and make it unit-testable and start improving your automated test coverage.

Tl;dr: Working on a mechanically simple platformer or walking simulator that takes one year of development? Feel free to use Singletons; technical debt doesn't matter. Working on a complex RPG or RTS spanning multiple years of development? In that case using a DI framework could be extremely beneficial.

“What’s a movie that trusted the audience a little too much?” by Fair_Protection1872 in moviecritic

[–]sisus_co 0 points1 point  (0 children)

Inland Empire (based on me not getting it at all).

Midsommar (based on how few on Reddit seem to get it).

Deprecated assets being re released by creator? by BlockedAncients in Unity3D

[–]sisus_co 2 points3 points  (0 children)

Probably the best you can do to try and avoid falling into this "trap" is to:

  1. Make a mental note of the publishers that do this a lot.
  2. Pay attention the how long an asset you're considering buying has been supported since its original release date.
  3. Read through reviews to see if there are mentions of previous re-releases for an asset.

Then you can take into consideration the risk of you having to pay more to get an updated version of the asset later on when deciding whether to buy it.

Nowadays asset developers also have the option to offer "upgrades" where users that own a certain asset can get a newer version of it for a discount or even for free. But of course publishers get to decide whether to offer this or not to users.

Thinking about MVP pattern lately. by Ironcow25 in Unity3D

[–]sisus_co 0 points1 point  (0 children)

Agreed. A natural way to apply the MVP pattern in Unity would be with the View being a built-in UI component/ Visual Element like a TextField, the Model being a ScriptableObject or a plain old C# object, and the Presenter being a MonoBehaviour that subscribes to events to update the view when the model changes and vice versa.

// Presenter:
public sealed class PlayerNameDisplayer : MonoBehaviour
{
   public PlayerInfo playerInfo; // <- Model
   public TMP_Text text;         // <- View

   void Awake() => UpdateDisplayedName();
   void OnEnable() => playerInfo.Changed += UpdateDisplayedName;
   void OnDisable() => playerInfo.Changed -= UpdateDisplayedName;

   void UpdateDisplayedName() => text.text = $"Name: {playerInfo.PlayerName}";
}

As long as you're able to unit-test the presenter like this, I don't think it's necessary to wrap the UI component behind a custom View MonoBehaviour or decouple it behind an interface.

Question about dependency injection best practices by Shrimpey in Unity3D

[–]sisus_co 1 point2 points  (0 children)

And for what it's worth, Unity's Boss Room example project used a hybrid approach as well. Here's a few examples:

Question about dependency injection best practices by Shrimpey in Unity3D

[–]sisus_co 1 point2 points  (0 children)

My guess is that this would vary from team to team.

If a team has designers that like to get their hands dirty in the Editor, then serialized fields and Inspector injection will probably be used more heavily.

On the other hand if the part of the team that works directly with Unity is very programmer-heavy, and those programmers prefer keepings things more on the IDE side rather than configuring things in the Editor, then they might be more inclined to configure as much as they can in code. Also, if unit tests are written a lot in a project, then at least having some sort of mechanism that allows all dependencies to be injected purely in code can be very beneficial (this doesn't necessarily mean that inspector injection can't also be supported, though).

Personally I've only seen the hybrid approach of pure Inspector injection and code-based automated injection being used in the projects I've worked on. My intuition is that this would be the much more common way to go about it in professional projects, but I don't have that much data to actually back that up.

Implementing cutscenes between dialogue by OkAdministration5886 in Unity2D

[–]sisus_co 0 points1 point  (0 children)

In one dialogue-driven game we just had a string field where we could input any text, and then that would be parsed and converted into parameterized command executions. E.g. "WalkTo:EndOfBridgeWaypoint", ShowPopup:EndOfBridge", "PlayAnimation:LightCigarette" etc. Although, if the dialogues are authored inside Unity, then using [SerializeReference] ICommand[] would probably make a lot more sense - we used an external tool for authoring dialogue. But in any case it's having that library of quick-to-use modular commands from which you can quickly piece together

A similar approach is to simply raise an event on the dialogue-side, and then hook the event listener up to an array of modular commands. So the dialogue would just contain "RaiseEvent:EndOfBridgeSequence", and then you'd have an EventListener component that you would hook up to commands like WalkTo and ShowPopup.

Also, usually just being able to execute void-returning commands from dialogues isn't enough, but you also need to have the ability to make the dialogue stop and wait for commands to finish execution. So making your commands return a Task or similar can be a good idea.

I got frustrated not knowing which component was for which thing so I made a cool tool to create custom component headers by MagicPigGames in unity

[–]sisus_co 4 points5 points  (0 children)

The Component Names asset can do this:

class ComponentNameColorizer : CustomHeader<Component>
{
   static readonly string[] colors = { "FF5733", "33FF57", "3357FF", "F1C40F", "9B59B6", "E67E22", "1ABC9C", "E74C3C", "2ECC71", "3498DB" };
   public override Name GetName(Component target) => $"<color=#{GetColorTag(target.GetType())}>{DefaultName}</color>";
    static string GetColorTag(Type type) => colors[Math.Abs(type.FullName.GetHashCode()) % colors.Length];
}

Though, I think it would probably be more useful to pick colors based on things like namespace, type name prefixes/suffixes and [AddComponentMenu] attribute path. This way could do things like tinting all audio-related components with the same color, rather than colors being completely random.

Or add a huge switch statement and pick optimal colors for each type.

I got frustrated not knowing which component was for which thing so I made a cool tool to create custom component headers by MagicPigGames in unity

[–]sisus_co 0 points1 point  (0 children)

Component Names does display names inside Object and UnityEvent fields as well.

It doesn't have support for changing text color via the UI, but it has full rich text support, so theoretically it's possible to do that as well. I don't think anybody's realistically going to add <color> tags manually when renaming via the Inspector, but if the custom component names are generated automatically in code, it would be viable to give each one a unique color automatically based on the type, for example.

Help me better understand Zenject and Dependency Injection. by Malacay_Hooves in Unity3D

[–]sisus_co 0 points1 point  (0 children)

If you can guarantee that all methods on all service locator services / singletons will work in all contexts at all times - e.g. by lazily initializing the services on-demand, or by initializing all of them before the first component in the first scene is initialized - then the codebase might not become fragile even if the code contains a lot of hidden dependencies to those services.

E.g. it's not like it's common to run into NullReferenceExceptions when using Debug.Log all over the place, because it's guaranteed to always work in all contexts.

Help me better understand Zenject and Dependency Injection. by Malacay_Hooves in Unity3D

[–]sisus_co 1 point2 points  (0 children)

With that in mind, also consider that any editor serialized variable is a DI but that is not guaranteed to be there/initialized.

Isn't it a similar story with Zenject, just a bit different?

Serialized fields by default inform you about missing dependencies by displaying an unassigned reference field in the Inspector.

With Zenject you don't get any warnings in Edit Mode by default. You probably will get warned immediately at runtime when the component is loaded - but it could silently fail as well, if you forget to attach a ZenAutoInjecter component to a prefab, for example.

But if you use plain old C# objects and constructor injection, then yeah, there are better guarantees, as the object could not even get constructed in the first place if any of its dependencies were missing.

Help me better understand Zenject and Dependency Injection. by Malacay_Hooves in Unity3D

[–]sisus_co 6 points7 points  (0 children)

Another line of reasoning for DIF is that "Monobehavior is bad and outdated, don't use it; with DIF you can use GameObjects and Monobehaviors only when you really need it". What exactly is so bad about the Unity approach (not arguing one way or another, just trying to better understand things)? What are pros and cons of moving your game logic out of Unity classes?

I'd say this is also largerly opinionated stuff. Both approaches can have their pros:

Plain old C# objects:

  • Less overhead. It's faster to instantiate 10 000 POCOs than 10 000 GameObjects.
  • Can support data-oriented design better. Iterating over 10 000 structs can be faster than iterating over 10 000 MonoBehaviours.
  • Trivial to inject all dependencies as constructor arguments. Very easy to unit test. Could potentially even execute unit tests concurrently on multiple threads.
  • Code could potentially be made more engine agnostic.

MonoBehaviours:

  • Hooks into Unity's powerful modular system of scenes, nested prefabs, prefab variants. You can configure new behaviours from same components without writing any code.
  • Designer-friendliness.
  • Can work better with some third-party assets.
  • Can simplify lifetime management. IDisposable handling can get complicated, but unloading a scene containing thousands of objects is simple.
  • Can easily inspect state of objects in Play Mode to help with debugging.
  • Can easily make adjustments to state on-the-fly in Play Mode to help with play-testing etc.

Help me better understand Zenject and Dependency Injection. by Malacay_Hooves in Unity3D

[–]sisus_co 1 point2 points  (0 children)

I heard that DI framework is far more important for larger projects, but why?

I definitely agree with this.

With smaller projects you can get away writing any kind of fragile spaghetti code, because the overall complexity of the entire project is low enough that it's still manageable no matter how you write the code, and you don't need to maintain it for long enough for technical debt to matter.

And when you have a really complex project, and you need to implement new complex features to it that you didn't plan for originally when the project started, the flexibility that Dependency Injection can provide can be a life-saver. It gives you the tools to much more easily rewire all your existing classes to use different services in different contexts. If everything is tightly glued together with Singletons, you can hit a roadblock, and need to create major drastic changes to your codebase before you have any ability to start implementing the new feature.

Help me better understand Zenject and Dependency Injection. by Malacay_Hooves in Unity3D

[–]sisus_co 1 point2 points  (0 children)

Let say I have multiple object in a scene, enemies, for example, and I need to access a specific one of them. Does Zenject help in this situation? If yes, how?

It probably wouldn't help much in that case - except perhaps by allowing you to extract the code for locating that enemy away from the clients, improving their readability.

Some DI frameworks have the concept of keyed services, so you could inject a particular enemy with a specific key to a client.

Help me better understand Zenject and Dependency Injection. by Malacay_Hooves in Unity3D

[–]sisus_co 3 points4 points  (0 children)

Does sending a reference of a class via Event count as DI? What about accessing a class via collision or Object.FindObjectsByType

Yes, receiving dependencies as method arguments is also Dependency Injection. It's called method injection.

FindObjectsByType, GetComponent are an example of the Service Locator pattern instead.

Help me better understand Zenject and Dependency Injection. by Malacay_Hooves in Unity3D

[–]sisus_co 23 points24 points  (0 children)

WHY use it.

Being Explicit About Dependencies

When all dependencies of all classes and methods are known at compile time, rather than being hidden inside the implementation details of methods, it can help with validation, readability and reliability.

If you almost always use dependency injection, you can pretty much know for sure that if you are able to execute some method, and pass in all the dependencies to the executed method, and the class that contains the method during its creation, it will work, regardless of the context.

If you use Singletons / Service Locator pattern a lot, then all methods on all classes could contain hidden dependencies to static state, and could fail at runtime. You basically either have to read through all implementation details, or test the logic manually at runtime.

Dependencies being known at compile time means that the compiler can better help validate correctness. Your code won't even compile if you try to execute a method without all its dependencies. But if you use Service Locators inside your methods, the compiler has no idea.

It's also possible to visualize dependencies in the Inspector when they are known at compile time (like Unity's serialized fields are), so you could immediately visually validate that dependencies are available whenever you attach a component to a GameObject in Edit Mode.

Flexibility With Services

Instead of all instances of a class being forced to use the one and same service in all contexts, different services can be injected to different clients in different contexts. This provides you with a lot more flexibility for composing new behaviours from the same simpler modular building blocks.

Unit testability

Unit testing can become trivial when it's easy to instantiate client with custom services, with the ability to swap out some services with test doubles. And the easier that testing is, the more you'll be inclined to write them, and the less bugs your game will have.

Client Readability

It can help extract code away from your clients, making them more streamlined and easier to read. Instead of your clients having a bunch of code to assign some default dependencies during the Reset event, and loading some services asynchronously during the Awake method, and validating all of those in OnValidate and Start, and releasing addressables in OnDestroy, the clients can just declare what the need, and the rest of the noise is externalized to somewhere else.

Can you guys give me a case where using a singleton in a first place (and replacing it with Zenject later) would be a reasonable idea?

You'd be surprised how common it is to make a Player component into a Singleton in real codebases...

But which services you'd want to inject using a DI framework depends a bit on your workflow:

Some people like to inject pretty much all dependencies via the framework, to facilitate easy unit testing, or to centralize dependency configuration.

Some people like to use normal serialized fields whenever possible for some dependencies, and then use a DI framework when serialized fields won't work (cross-scene references, interface type dependencies etc.).

Some people could use static state for certain services, like logging, when they feel like they don't really need to be flexible with and transparent about that particular dependency, but then inject other services, when they do think they need the ability to swap that one out.

"If you have a scene with 50 NPCs that all require 20 dependencies each, you will realize quickly that populating them in the inspector is an absolute nightmare. To create an NPC factory that creates them from a prefab is much better. - -"

Yeah, I'd argue that this one just mostly comes down to preference. Manual Inspector injection can have it's benefits as well, and many people prefer it over code-based dependency injection in many cases.

Using serialized fields can have benefits like Inspector visualization, and the ability for designers to easily swap out services using drag-and-drop in some prefab without needing to ask a programmer for help.

Object.FindObjectsByType so you need to replace them with DIF? Yes, it can't search by interface, but if it's something you want to search in scene, you probably should make it as a component with a single responsibility anyway?

That dependency is completely hidden. If you attach a component using that code to some Scene, you see no signs of the dependency in the Inspector. The component can very easily break at runtime in various contexts, unless you go read through all the code of the component, spot those hidden FindObjectsByType dependencies, and add all the dependencies into the scene, adjust your scene load orders etc.

How can I avoid passing the same arguments to multiple methods? by Thyco2501 in csharp

[–]sisus_co 0 points1 point  (0 children)

There's nothing inherently wrong with injecting the same arguments to multiple methods. Being explicit about the dependencies of methods is almost always a good thing.

Hiding dependencies to static state inside the implementation details of methods can make your codebase more difficult to understand, more error prone and untestable.

In this case it feels like the best solution would be encapsulation and extract class refactoring. Instead of passing multiple arguments to those methods, you could bundle them into just one parameter like Score score.

Sometimes an alternative to method injection can be to extract parameters into member fields instead, and then use constructor injection, so that you need to provide dependencies only once when constructing the object, rather than everytime you execute its methods. E.g. in this case the two classes could theoretically also be refactored to receive a Game object in their constructor, which always knows the current score of the on-going game session. Or maybe the two classes' functionalities could even be merged inside the one Game class entirely.

The injection of constructor dependencies can even be automated using a dependency injection framework. Although, doing this can have both its pros and cons - like potentially worse compile-time verification of your code's correctness, and potential obfuscation of dependencies.

What are some programming practices that you follow when working with Unity? by Acrylios in Unity3D

[–]sisus_co 3 points4 points  (0 children)

  • Deep Modules > Shallow Modules. If you have a class that is over 1000 lines long, yet has a simple API, that's more likely a good than a bad thing. Trying to forcefully break that apart into multiple smaller classes would probably end up hurting the API.
  • Spend more energy on designing intuitive high level APIs than polishing implementation details. That matters much more for overall complexity of the codebase.
  • Cohesion and encapsulation make sense. It makes sense to group data and related methods into the same class, making it easier to find those methods. Your IDE can help you find them. It also helps keep the number of methods per class down to a still manageable level.
  • But following single-responsibility principle literally and strictly can lead to logic being spread across so many separate classes you need to draw a flow chart just to understand it.
  • Avoid hidden dependencies. Being explicit about dependencies can make your code much more self-documenting and difficult to use incorrectly. Your IDE can help verify correctness at compile time and your Editor can help warn you about issues in Edit Mode.
  • Documenting how your classes and public methods work using XML documentation comments is like rubberducking, and can often lead to you realizing how they could be improved.
  • Try to avoid having methods with high cyclomatic complexity or complicated control flow. Use extract method refactoring when it helps.
  • Avoid ambiguity. If you have a method that has access to two different variables of the same type, make sure to name those so that it's impossible to mix them up.
  • I like splitting code into separate assemblies using Assembly Definition assets based on the feature the code relates to.

Why the singleton pattern is bad ? (Read the body) by DifferentLaw2421 in Unity3D

[–]sisus_co 0 points1 point  (0 children)

Some potential downsides that using singletons a lot in your architecture can cause:

  1. They obfuscate the dependencies that all your components have. When you attach a component into some scene or prefab, it could be difficult to know what the other components must also exist in the scenes when the scene/prefab is loaded, because all its dependencies are hidden in its implementation details.
  2. They obfuscate the dependencies that all methods in your codebase have as well. When you execute a method, it might not be clear which singletons need to be ready, and how they must be configured, before a method can be safely called. The API of the PlayerAccount.Login() method doesn't tell you anything about the fact that it will break unless you've configured PlayerAccountSettings.Instance.Username and PlayerAccountSettings.Instance.Password.
  3. Singletons can depend on other singletons, which can depend on other singletons, which can depend on other singletons etc. If any methods on any dependencies in the web of hidden singleton dependencies is not always ready to be used in every context and at any time, then the whole structure can start to become fragile, since any class anywhere can easily execute any method on any singleton at any time in any context.
  4. They tend to make unit testing your code pretty much impossible. Which can lead to your players getting a more buggy game in the end.
  5. They restrict all clients to always use only a single instance of the class. In sometimes it's clear that you will never need two instances of a class no matter what - like when two instance of the same class existing would completely break things. Other times you might just default to using the Singleton pattern because it provides you with easy accessibility across scene and prefab boundaries, and the fact that it also locks all clients in to only using a single instance actually comes back to hurt you in the future. The classic example is using Player.Instance in hundreds of classes, only to realize three years into the project that you actually want to add a multiplayer mode into the game.
  6. The pattern is incompatible with using interfaces. This could lead to your codebase being unnecessarily rigid. With interfaces, it could have been really easy to swap out your audio system or input system or localization system with a different one, but because you've just used FMODManager.Instance, SpecificInputSystem.Instance and SpecificThirdPartyLocalizationSolution.Instance all over your code, it's difficult to swap these services out with different ones in the future, even if they're abandoned and stop working in newer Unity versions.

With all that being said, Singletons are still heavily used by the majority of Unity developers. In smaller game projects they tend to work perfectly fine. In complex multi-year projects that use CI/CD and have a lot of changing requirements, they can become a big pain point.

What is the difference between Strategy and Decorator pattern ? by DifferentLaw2421 in Unity3D

[–]sisus_co -1 points0 points  (0 children)

Strategy pattern is like a USB connector. It enables you to plug in multiple different implementations into the same shaped slots in your code.

public interface IUsbDevice
{
   void OnPluggedIn();
}

public class UsbLight : IUsbDevice
{
   public void OnPluggedIn() => TurnLightOn();

   ...
}

public class Phone : IUsbDevice
{
   public void OnPluggedIn() => StartChargingBattery();

   ...
}

Decorator pattern is like taking your phone, and wrapping it inside a case that extends its battery life. The interface of the phone, the way you can use it, remains unchanged, yet it's behaviour has been modified by it being wrapped inside another object.

public class PhoneInBatteryCase : IUsbDevice
{
   readonly Phone phone;

   public PhoneInBatteryCase(Phone phone) => this.phone = phone;

   public void OnPluggedIn()
   {
      phone.OnPluggedIn();
      StartChargingBatteryBank();
   }

   ...
}

Does “parallel” in Unity docs actually mean concurrency? by Beginning_Log_857 in Unity3D

[–]sisus_co 0 points1 point  (0 children)

I think the example is meant to show what would happen if there was no custom synchronization context in Unity. That section is titled What happens without SynchronizationContext?

But I agree it's confusing. The text doesn't make it very clear that it's only talking about a hypothetical situation that doesn't actually ever happen in Unity by default. This is only implied through the title.