Best Practices for Unity 3D Project

Some time ago I decided to write a simple project in which I would implement all my accumulated ideas. In this article I want to tell you what came out of it.

In total, 6 projects were written (1 main project and 5 auxiliary packages):

  • Clean Game Example – an example of a simple shooter. The example contains: the main menu and several game levels. In the main menu we can select a level, a character or adjust some parameters. In the game itself we can pick up, throw away or change weapons, shoot and kill enemies, and accordingly pass or lose the level.

  • Clean Architecture Game Framework is a framework that defines the architecture of the entire project and solves some minor problems.

  • Addressables Extensions is a wrapper around Addressables that makes loading and unloading resources more convenient.

  • Addressables Source Generator – a tool for generating source code containing all addresses and labels of addressable assets. Thanks to this tool, you can always be sure that only correct addresses and labels are used in your project.

  • Colorful Project Window – project window extension that highlights: packages, modules, sources and assets. Thanks to this tool, you instantly see all the necessary packages, modules and their sources and assets.

  • UIToolkit Theme Style Sheet – a style library for UIToolkit, written with Stylus.


Clean Game Example Project

Assets

Assets

Packages

Packages

Addressables

Addressables

The project consists of assets (main modules) and packages (additional modules). All main modules are located in the red Project folder. All green folders contain sources and display their namespace. All yellow folders contain assets and display their addresses. Folders with sources also contain *.asmref files, which bind them to specific modules. And the modules themselves are defined in the Assemblies folder. The project also contains an additional Project.Infrastructure module, which is located separately from the main modules and contains everything common or everything that I wanted to hide out of sight.

Project module

Project

Project

The root module contains the entry point and some tools.

Project.UI module

Project.UI

Project.UI

The UI module contains the audio theme, the user interface screen, the router that controls the application state, and all the widgets.

Module Project.UI.Internal

Project.UI.Internal

Project.UI.Internal

The UI.Internal module contains all views. This module has no dependencies on other modules, so the views also do not have any extra dependencies.

Project.App module

Project.App

Project.App

The App module contains all the entities and services of the application level. It is also responsible for starting and stopping the game.

Project.Entities module

Project.Entities

Project.Entities

The Entities module contains basic game entities: game and player.

Module Project.Entities.Actors

Project.Entities.Actors

Project.Entities.Actors

The Entities.Actors module contains entities that can act under the control of the player or independently.

Project.Entities.Things module

Project.Entities.Things

Project.Entities.Things

The Entities.Things module contains entities that actors can own.

Module Project.Entities.Worlds

Project.Entities.Worlds

Project.Entities.Worlds

The Entities.Worlds module contains worlds and various environment objects.

Project.Infrastructure module

Project.Infrastructure

Project.Infrastructure

The Infrastructure module contains, first of all, all the common abstractions, as well as everything that I wanted to hide from view.

It would also be worth adding a module for different types of transport, but in my case it was not necessary.

Clean Architecture Game Framework

The framework defines the architecture of the project and also provides some simple utilities. I was inspired by the ideas of clean architecture, so I chose the appropriate name. The framework itself consists of three modules (layers):

Module Clean.Architecture.Game.Framework.Core

Clean.Architecture.Game.Framework.Core

Clean.Architecture.Game.Framework.Core

The Core module contains all the essentials.

  • UnityEngine.Framework.UI

    • UIThemeBase – audio theme.

    • UIScreenBase – user interface. The user interface consists of a hierarchy of business units (UIWidgetBase) and a hierarchy of visual units (UIViewBase). That is, a hierarchy of widgets is attached to the screen, each widget may contain (or not contain) a view. The root widget is usually responsible for adding a view to the hierarchy of visual units, but other container widgets can also add child views to themselves. In theory, it turns out like in Uber Ribs.

    • UIRouterBase – application state manager. The router is responsible for: loading the main menu, loading the game, reloading the game, unloading the game and closing the entire application.

    • UIWidgetBase – business unit of the user interface.

    • UIViewBase is the visual unit of the user interface.

Module Clean.Architecture.Game.Framework

Clean.Architecture.Game.Framework

Clean.Architecture.Game.Framework

The main module contains various add-ons. The most interesting ones are:

  • UIRootWidgetBase and UIRootWidgetViewBase – the root widget is responsible for placing child widgets on the screen.

  • IDependencyContainer is essentially just a service locator.

Module Clean.Architecture.Game.Framework.Internal

Clean.Architecture.Game.Framework.Internal

Clean.Architecture.Game.Framework.Internal

The Internal module contains various utilities and other internals. The most interesting ones are:

  • Assert – allows you to perform checks in a more convenient form than the standard Debug.Assert.

  • Option is a type that can be very useful sometimes.

Colorful Project Window Pack

As you have already noticed, my project window is very conveniently colored. I highlight: packages (blue), modules (red), sources (green) and assets (yellow). This has significantly simplified navigation through the project, reduces cognitive load and saves a lot of nerves.

Results

I think I managed to write a project with a convenient structure and architecture. But even such a simple project quickly grew and became difficult to maintain. And this is despite the fact that in recent years Unity has gained support for modularity (Assembly Definition) and dependencies (Package Manager). And not so long ago, this was not there ((

I'm sure there are many things that could be done better if Unity allowed it. Below I'd like to point out some of my complaints about Unity:

  • Probably the most basic problem of Unity is MonoBehaviour. In classes inherited from MonoBehaviour you can't use a constructor, constructor arguments and accordingly constant fields and properties. This turns programming into real idiocy. By the way, to my great surprise, in Unreal you can't pass arguments to the actor constructor either. This is very strange and inconvenient. Maybe I don't understand something.

  • Third-party objects cannot listen to GameObject events. Only MonoBehaviour can listen and handle Unity events. In theory, we could completely abandon MonoBehaviour and use regular POCO classes, but then we would not be able to handle many events.

  • There are few options for customizing the project window. I would really like to set the order of displaying elements myself, change their names and generally hide unnecessary ones. But you can only draw the elements a little.

  • There is no way to temporarily unload all unnecessary things from the project. For example, in Visual Studio you can unload any projects from the solution. This allows the developer to focus on a specific project and throw everything else out of his head. This is especially useful when the solution becomes large enough and changing one project leads to problems in other projects. Unfortunately, this is not provided in Unity. And we have to constantly keep all the code and all the assets in our heads. I don’t know how it is in other engines, but I think it’s the same.

  • UIToolkit is hell. I deeply regretted using UIToolkit in this project. The idea with style sheets turned out to be very unsuccessful. Firstly, it takes a lot of time to understand all the properties of styles. Secondly, it takes even more time to understand how the elements are arranged in order to write styles for them. I spent a lot of time and nerves writing styles for several elements. And all this can break in any next update. Worse, Unity integrated this monster into the editor itself. And this means that it is now with us forever. And it would be wiser to simplify the editor and runtime as much as possible.

  • The company's attitude to the quality of its product is simply amazing. Probably many people know that in Unity the constants of some colors have always been a little wrong. After many, many years, the developers finally fixed the Color.green constant. I already thought that Unity had finally gotten their heads around it, but then I noticed that Color.yellow still remains not yellow, but a shade of yellow (Yellow. RGBA is (1, 0.92, 0.016, 1), but the color is nice to look at!). I remember some Unity developer writing that they would never fix Color.green because backward compatibility is very important to them. As we can see, backward compatibility is not very important to them. And the quality of their product is apparently not very important either. Can you imagine that some PAID graphic editor would have not black, but almost black? And can you imagine that this would continue for 20 years? The whole Unity is a solid legacy. Of course, in some cases, backward compatibility is very important, unfortunately, but definitely not in this case. I recently opened a bug report (a fairly serious problem in UIToolkit), but Unity simply closed it with the words “There are no fixes planned for this Bug”. Probably there were reasons for that, but still… As usual, a lot could have been better, but as they say, the people will swallow it.

Without unnecessary details, but with pictures, I told you about my project. If you were interested, you can take a closer look at my project on GitHub. If you have ideas on how to do something better, I will be glad to hear about them!

Links

  • https://github.com/Denis535/CleanGameExample/tree/v1.0.7

  • https://openupm.com/packages/com.denis535.clean-architecture-game-framework

  • https://openupm.com/packages/com.denis535.addressables-extensions

  • https://openupm.com/packages/com.denis535.addressables-source-generator

  • https://openupm.com/packages/com.denis535.colorful-project-window

  • https://assetstore.unity.com/packages/tools/gui/uitoolkit-theme-style-sheet-273463

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *