Awesome Accessories - Introduction & Core Concepts
For almost 15 years now I have been professionally involved in software development. For some years I have also been trying my hand as a game developer. During this time I have found many solutions for common and also less common problems. Especially in game development you basically have to deal with similar questions over and over again.
Where do I store the user settings? How do I handle translations? How can I easily play sounds without having to write my own solution every time? Of course, there are already solutions for all these problems in the Asset Store. But in my experience, they are often either relatively expensive, too complex for simpler projects, or too difficult to figure out.
That's why I usually just wrote solutions myself. Over time, a considerable collection has come together. And now I want to share this collection with you. On the one hand to get feedback for my work and to possibly develop this project further with your support. On the other hand to help you to realize your projects.
But before you download Awesome Accessories, let me introduce you to the core concepts of this collection. You will also find explanations of all the different modules in other articles.
Services
Services are the core of this collection of development tools. They are basically designed to be self-contained. Each service has exactly one defined task. All external dependencies must be passed via the respective constructor. In most cases there are useful defaults for these dependencies. There can be any number of instances of one service in the same scene.
Managers
Managers are used to connect services and behaviours of this collection in Unity. All managers act as singletons. This means that you can have only one instance of a particular manager in your scene. Most managers have settings that can be influenced via the Unity editor. There is also a prefab for each of the managers in the package with useful presets. I will write more about the use of singletons later.
Behaviours
All scripts that can be used on any GameObject in Unity are called Behaviours here. They are typically used to take advantage of a single feature of a particular service. For example, they are used to synchronize the volume defined by the AudioService with other AudioSources. Most Behaviours depend on the existence of certain managers.
Communication & Events
With a large number of Services, Managers and Behaviours, it is necessary that some form of communication between the individual classes is possible. I have chosen the following design here:
All services use Delegates to communicate changes or other service-related events to the immediate outside world. Managers listen to these delegates and publish events (if necessary) using the so-called PubSubManager. Behaviours always use this central PubSubManager to respond to relevant events.
This means that all communication between the individual methods runs exclusively via a central PubSub service. This approach has advantages and disadvantages, which I will discuss in detail in the article about the PubSubManager.
Why Singletons?
A wise man once said, "Friends don't let friends use singletons." You see, then, some view the use of singletons as an anti-pattern. And there is some truth to that. Singletons have certain advantages, but unfortunately they also have some disadvantages.
Among the advantages are the following, for example:
Global access: singletons provide easy global access to a single instance of a class. In Unity, where multiple scripts and objects need to interact with the same instance, Singleton can provide a convenient way to access and edit that instance.
Centralized control: By encapsulating an object in a Singleton, you can control its creation and lifecycle from a single point. This can be useful in managing resources or maintaining a single state across different components.
Avoid duplicates: Singletons ensure that only one instance of a class exists in the entire application. This can prevent accidental duplication of objects and ensure consistency of data or functions.
Simplified communication: with a singleton, different objects can easily communicate with each other through the common instance. This can simplify communication between components, especially when multiple scripts need to access the same data or trigger actions.
However, as mentioned, there are also disadvantages:
Violation of the Single Responsibility Principle (SRP): The Singleton pattern can lead to a class taking on multiple responsibilities instead of distributing them cleanly among different classes. This can make the code harder to understand, maintain, and extend.
Difficult testability: due to their global nature, singletons are often difficult to test in isolation. Because they are accessible from anywhere in the code, they can cause unexpected side effects in tests. This makes it difficult to test the code in isolated units and perform automated tests.
Strong coupling: the use of singletons can make components highly dependent on each other. This makes the code harder to understand, maintain and extend. Changes to a singleton can have a far-reaching impact on other parts of the system, which can affect flexibility and scalability.
More difficult debugging and error handling: because singletons provide global access, errors in a singleton can be difficult to find and debug. It can be difficult to track the flow of data and the state of the system, especially when multiple entities can access and modify the singleton.
I still decided to use singletons for several reasons and tried to allow other approaches in the design at the same time. Probably the most important reason is Unity itself. Many elements of the UnityEngine can only be changed globally. These are for example: EventSystem (Unity UI), AudioListener, only one scene operation at a time (Loading & Unloading), targetedFrameRate, TimeScale and many other elements.
However, why didn't I just turn these managers into singletons? Three words: Consistency above Correctness. This collection of services, managers and behaviours should be consistent and therefore accessible. Each manager should basically work the same and in an expectable way, so that working with them is easier.
And the best part: All services are designed so that you can use them without the managers. So if you don't like singletons and/or want to use a different software architecture, you can do that. The managers are only necessary if you also want to use the behaviours.
How to use Awesome Accessories
So how should you best use this collection of services? First of all, it should be said that this is a very broad selection of modules. You will almost certainly not need every service in every game. That's why the services are divided into different modules and namespaces, so you only have to reference the namespaces you really need in your scripts.
From my point of view, you have several ways to use Awesome Accessories. First of all, you can install the package completely via Unity's Package Manager. You can then decide if you want to use managers and behaviors or just integrate the services into your application. You can also copy only single managers, services and behaviours and use only those and extend them for your purposes. Or you can get inspired for your own solutions and just copy the code of single functions or behaviours.
Feedback
I really hope that Awesome Accessories will help others you realize your projects. If you run into any problems using this package or have ideas for new features, please create an issue in the GitHub repository. I'm also eager to hear your thoughts on this project and your feedback. Let me know how you like Awesome Accessories. Feel free to do so here in the comments section, on Instagram, or by email.
There are no comments yet.