A Pure Code Approach to Unity App Code Design
There are many ways to design your Unity app from a coding perspective. Today’s article talks about just one path you might take. Like any path, it’ll have its own advantages and disadvantages. The choice of app code design will make a huge impact, so it’s good to think about it before you start down any path. Perhaps this is the right one for you…
The “pure code” path— as mentioned briefly last week—minimizes the MonoBehaviour
class as much as possible. This has the effect of minimizing the Unity GUI editor too. Minimizing the editor maximizes the code, hence the name.
The MonoBehaviour
class is minimized by creating as few derivative classes of them as possible. Usually this entails just one or two in the whole app. The first runs almost the entire app’s logic and is attached to a single empty GameObject
in a single Unity scene. The Start
or Awake
function of this MonoBehaviour
is treated like the constructor of a Flash app’s “document class” or “root Sprite
” or a command line app’s main
or Main
function. It’s just like the application’s entry point. Here’s how one might look:
using UnityEngine; public class MyApp : MonoBehaviour { public void Start() { // {all app startup code} } }
Likewise, the Update
, FixedUpdate
, LateUpdate
, OnApplicationQuit
, and various other functions are treated like app-wide events rather than events regarding a particular actor in the scene. The MonoBehaviour
now looks like this:
using UnityEngine; public class MyApp : MonoBehaviour { public void Start() { // {all app startup code} } public void Update() { // {all app logic} } public void OnApplicationQuit() { // {all app shutdown code} } }
Other game objects may be added to the scene, but they never have a MonoBehaviour
added to them for the purposes of controlling that GameObject
. Instead, an auxiliary MonoBehaviour
is used simply for for the purposes of forwarding events related to the GameObject
. Without this MonoBehaviour
, it’d be quite difficult in this scheme to use the GameObject
in many ways. Here’s how that event-forwarding class might look with just a couple of events:
using UnityEngine; public class EventForwarder : MonoBehaviour { public delegate void EventHandler<T>(T e); public event EventHandler<Collision> CollisionEnterEvent = c => {}; public event EventHandler<float> JointBreakEvent = f => {}; public void OnCollisionEnter(Collision collisionInfo) { CollisionEnterEvent(collisionInfo); } public void OnJointBreak(float force) { JointBreakEvent(force); } }
The EventForwarder
class performs no logic of its own. Instead, it receives events using Unity’s “magic event function” (for lack of a better term) system and forwards them along via the standard C# event system using the delegate
and event
keywords.
With this event-forwarding MonoBehaviour
in place, every GameObject
except the “main” one is treated simply as input and output, not as an independent object that handles its own logic. All other GameObject
instances are really just tools of the “main” GameObject
.
At this point it may sound like the MonoBehaviour
on the “main” GameObject
will become absolutely huge and unmaintainable. This is certainly not the case though any more than it is the case for any given command-line application like a compiler. The key is to minimize the size and complexity of the “main” MonoBehaviour
by simply forwarding along the Start
, Update
, and other events to other classes. These classes are not derived from MonoBehaviour
and they therefore are not attached to any GameObject
.
Of course there are tons of different ways to structure your non-MonoBehaviour
classes. One common approach is to use some form of a finite-state machine to represent the app’s major states like UI screens or gameplay modes. In turn, these state classes delegate much of their logic to the components of those states like UI widgets/components or game elements.
If the above all sounds very straightforward, that’s because it is. The preceding paragraph simply describes a form of object-oriented programming (OOP), which is ubiquitous these days. Everything else in this article only exists to shoehorn “standard” programming like OOP into Unity. That’s not to say that OOP is required. You could certainly use imperative or functional programming (e.g. with F#) if you’d rather.
The “pure code” path is great for programmers coming from other environments—including Flash—because it ignores a lot of Unity-specific quirks and details. This is also its main drawback as some of those quirks and details are quite powerful and ignoring them negates their advantages. For example, this approach doesn’t use the Unity editor’s ability to show public variables in the Inspector panel. The “normal” approach to Unity programming might have a public int Health
field on an Enemy
MonoBehaviour
attached to the GameObject
with that enemy’s graphical representation. Selecting the enemy GameObject
in the Hierarchy panel of the Unity editor would show the Enemy
component including a “Health” field with the current health. That might be interesting to see or change for debugging purposes.
Overall, losing this capability is mainly a drawback. However, it can also be an advantage when it comes to the design of many classes. For access protection, you probably don’t want to make every field of every class public
. However, in the above example it was necessary to make the Health
field a public
variable in order for it to show up in the Inspector panel of the Unity editor. A private
variable or a public
property won’t work unless the SerializeField workaround property is used.
Another advantage is that typical MonoBehaviour
derivatives don’t really get to use constructors:
Note to experienced programmers: you may be surprised that initialization of an object is not done using a constructor function. This is because the construction of objects is handled by the editor and does not take place at the start of gameplay as you might expect. If you attempt to define a constructor for a script component, it will interfere with the normal operation of Unity and can cause major problems with the project.
But constructors are essential to core OOP techniques like RAII. Losing them does serious damage to code maintainability, quality, and ease of use. Workarounds like creating an Init
function and remembering to call it every time provide only weak workarounds.
Again though, this is just another example of the “pure code” approach’s philosophy of code purity over all else. It’d rather give up some Unity editor usefulness to get access to techniques that many would argue make code “cleaner”. In the end, as always, the decision will be up to the individual development team and must be made with project-specific factors—skill sets, timelines, etc.— in mind. This is definitely not the “normal” approach to programming Unity projects, but it’s probably worth considering when you plan out a new project.
What do you think of the “pure code” approach? Post a comment and let me know!
#1 by marcum on January 5th, 2015 ·
I think this post is key for flash/actionscript/adobe air (Starling) developers. If they can get over the hump of Unity’s reliance on the Editor and the conceptual issues of Monobehavior everything falls into place (with the added bonus of 3 dimensions!!). Thanks
#2 by Creatify on March 3rd, 2015 ·
I’m just getting my feet we with Unity coming from many years of development using ActionScript and solely ActionScript outside of the Flash IDE. My question is, are there third party MVC type conventions for Unity? The way I’m interpreting Unity in this early stage is that it would be difficult to develop or rather invent a pseudo-MVC structure for Unity. I’m leaning towards Unity for a fairly advanced UI that will really be the orchestrator for the entire middleware stack — and, all of the content will need to be dynamic. Why choose Unity, well, their frame rate and magical gpu handled rendering at the lower levels really allows for some impressive motion etc. when running at 1920×1080.
#3 by jackson on March 3rd, 2015 ·
I doubt you’ll find much in the way of MVC frameworks for the new (to 4.6) Unity GUI, but there is some discussion for the NGUI extension that was popular before 4.6 came out.
In general, there doesn’t seem to be much in the way of MVC frameworks at all. I did a quick search and found only this project which seems to be abandoned since all 8 commits were over a year ago and within three days.
However, you can certainly apply MVC principles to Unity. In fact, following the “pure code” design discussed in this article will help a lot with that. The game objects are essentially your view rather than the “normal” Unity approach where they have
MonoBehaviour
scripts on them that also do the model (public fields) and controller (various functions likeStart
andUpdate
).If you end up making an MVC framework and releasing it for the public, please let me know. I’d certainly be interested in it as a design pattern for Unity app development.
#4 by Creatify on March 3rd, 2015 ·
Thank you for the quick reply with great input! My team is pressed for time on the UI development, but it does seem like starting down an MVC road does make sense. We’ll consider starting this and maybe we can get the community to build on what we start – would be fun!
#5 by flexwiz on April 22nd, 2015 ·
I was looking for more info on the pure code approach, and found the Futile framework – http://struct.ca/futile/ .
Not sure how popular that is, but it looks like a solid solution.
Most Unity samples and tutorials are relying on the IDE, which remind me of writing actionscript code on the Flash timeline. Personally, I would like to eliminate any IDE dependency to make Unity actually usable.
#6 by jackson on April 22nd, 2015 ·
Thanks for the link to Futile. I hadn’t heard about it before. It reminds me a lot of Starling for Flash in that it basically ignores Flash and implements its own platform on top of it. The advantage in the case of Futile is that it allows more of a “pure code” approach to be used. It seems to have some traction too, judging by their Reddit.
Let me know if you find any other “pure code”-style libraries or projects. I just made up the term, so perhaps there’s a whole bunch of people using it with just a different name.
#7 by Myrtanim on May 5th, 2015 ·
You should also have a look at StrangeIoC (http://strangeioc.github.io/strangeioc/), which not only brings MVCS but also dependency injection. Its inspired by Robotlegs, so it should be familiar to some ;) We are using it quite happily and could reduce our amount of MonoBehaviours a lot.
#8 by jackson on May 5th, 2015 ·
Thanks for the link and info. I saw mention of StrangeIOC in the Unity 4.6.5 patch notes but hadn’t gotten around to checking it out. I’ll definitely be taking a look soon.
#9 by kjempff on May 8th, 2015 ·
I am also thinking about a way to stay sane as a programmer while working in Unity. What I hear from people who have worked with Unity projects is that it always turn into a bowl of spaghetti, and I can easily see how that would happen if you code like Unity wants us to (Component based).
In other games using various frameworks I have found MVC approach is what makes a solid codebase. Strong typing and standard OOP is another thing I will need to force feed into Unity somehow (theory in progress), but some of that will come with MVC.. I want errors to be caught by the compiler, not by the user (runtime) – The flexibility and no doubt smart workflow for designers, you get from component based scripting, only comes back to bite you in the back later on.
Anyways.. This “pure code” approach seem like a great start, so I can reclaim what component based took away, and have MVC-Controller(s) to manage game control, flow, states etc.
I think I want to centralize the event dispatcher so there is only one place to register listeners, while also being able to register listeners for specific GameObjects (for example an event for only the player collider), hmm but how? Manually editing the centralized EventDispatcher for every type of event is an option (ex: http://www.bendangelo.me/unity3d/2014/12/24/unity3d-event-manager.html), but if there is a smoother way..
Another thing I am debating with myself on.. what is really a MVC-View in a Unity context ?
A concrete example, I have a parallax script that move parallax layers and load/unload prefabs as they come in/out of camera view.
Is this a View or a Controller ?
This is half thinking out loud typing to manage my thoughts, but comments are welcome :)
Still having doubts about it all, but I find myself cursing so much at Unity, that I feel I need to come up with something that I can accept from a code perspective (brittle code makes me sleep poorly).
Cheers.
#10 by jackson on May 8th, 2015 ·
I’d really recommend taking a look at StrangeIOC if you’re interested in an MVC framework for Unity. I spent a good chunk of yesterday reading through the documentation and prototyping a little Space Invaders-style game with it and can say that it may very well be up your alley if you’re looking to formalize on MVC. It’s actually described as an MVCS (service) system and I’d go one further and describe it as an MMVCS system since StrangeIOC uses mediators for its views. At a minimum it should be a good read to inspire your own system as it’s clear that the authors (and the authors of Robotlegs that it’s based on) have put a lot of thought into “clean” application design.
I should mention that the “pure code” approach can take many forms. StrangeIOC is more-or-less a “pure code” framework with a formalized MVC system. You can just as easily use “pure code” like in the article- an ad hoc system of your own that doesn’t formalize around MVC or any other pattern. Which way you choose to go is, as always, up to you, your team, your project, and the rest of your particular circumstances. In any case, let me know if you end up using StrangeIOC and how it works out for you.
#11 by kjempff on May 11th, 2015 ·
Thanks for the reply,
I already looked at StrangeIOC, but it is too strange for me.
No seriously, StrangeIOC lost me on the whole Inversion of Control and Injection thing – I fail to see why that is smart (or I just disagree hehe). I might take another read into the MVCS part of it and see if I can make sense of it (the mediator part is a bit foggy) and use it without Injection/IOC.
I will start out with propagating events from MonoBehaviour (Views) to a centralized dispatcher, and have Controller(s) register as listeners. See how that works on two small projects I am working on now.
Cheers.
#12 by Vijay Kumar R on October 15th, 2015 ·
Hi, I am using the pure code approach in all my Unity projects (2d board games). Only for layouting the UI elements, I am using the Unity editor. GameApplicatiom.cs on a GameObject manages the entire game.
I was wondering, whether this approach defeats the purpose of Unity.
Thanks
Vijay
#13 by jackson on October 15th, 2015 ·
That’s a valid concern with the “pure code” approach. By design, it shifts the focus away from the editor and toward IDEs like Microsoft Visual Studio and MonoDevelop. It still allows for editor integration though, as discussed in the next article. Personally, I don’t think it defeats the purpose of Unity because you’re still using it for its rendering, sound, scripting, asset management, and so forth. That said, you’ve been using it on multiple projects so I’m curious to hear from you. Do you feel like it’s defeating the purpose of Unity? If so, why?
#14 by Migs on December 4th, 2017 ·
I am resuscitating this old topic because think it’s key and I haven’t really found any other blog/post debating it.
I have been using Unity for about 2 years, not at a professional level but rather for spare time projects. I am a developer by training and was immediately confused when I started using Unity by the feeling of “mess” as opposed to well structured OOP applications. After trying different approaches I settled for a code only strategy and I must say that I am quite happy with it so far.
For me the biggest issue when using the editor/composant based programming paradigm is that it gets really difficult to follow the “story” of your code. If you are joining a project and try to understand how it is built, or even if you open your project again after a long break, it can be really difficult understand/remember the game logic, which component manages what, where are the messages/events coming from etc.
With a code only approach, there is one script that starts everything and it is usually quite easy to follow it in order to understand what is going on. In opposite, when using component programming, if there are 50 components in your scene and each of them start on their own and have their own logic, you need to go through each of them one by one in order to understand what is going on otherwise you may miss something. It is also a lot harder to make mental representation of the overall game structure when all your code is fragmented so many small pieces.
I guess this can be solved by pushing some very strong guidelines and conventions when using the editor to avoid the mess but conventions are often a pain to enforce and control and it forces new developers to learn the convention which may be different from a team/company to another.
Another big issue when using the editor instead of the code, is that many settings are stored in a .unity file which is unreadable from source control tools such as Git. Changing a setting such as the speed of a car can be really hard to see in the history within a .unity file but in the code you should see a constant with a clear name being assigned a new value.
The only drawback I have found with the code only approach is that it is a bit harder to use plugins that were designed to work with the editor. But if everyone was using the code only approach, this would not be an issue.
To come back on a few tips given in this article, I used a similar technic as for the EventForwarder. I created a centralized EventHandler that can take event subscriptions and dispatch them. Regarding the dependency injection, I use Zenject which is awsome and quite easy to use.
As a conclusion I would say that I am quite surprised that the Unity documentation and tutorials do not really mention or teach the 2 ways of using Unity (please let me know if I’m wrong). And I am a bit puzzled to think that large development teams working on large projects would be able to use the editor/component approach at an industrial level without creating a big mess and waste a lot of efforts in maintenance and refactoring… I am curious to know what their tricks are because as far as I’m concern I see close to no advantage in this approach, unless you are a non programmers wanting to build a mini game for fun…
#15 by jackson on December 4th, 2017 ·
Well said!
While reading this it occurred to me that programmers might look at component-based design as more of a graph and more traditional design as more of a tree. Alternatively, programmers might view the components’ data as a bunch of global variables, not “scoped” to a function, class, namespace, assembly, etc.
The component-based design certainly has its advantages, such as extreme flexibility via what’s essentially runtime multiple inheritance. The disadvantages include the extreme complexity of managing all those potential combinations.
I suspect that some developers’ mental model is more amenable to component-based design while others prefer the more rigid, controllable approach of a conventional design. So in some ways it’s a matter of preference, but in other ways (e.g. diffs for .unity files) it’s pretty objectively clearer with the “pure code” approach.
#16 by Migs on December 5th, 2017 ·
I agree, I think it is a matter of personal preference that can be driven by the type of project being done.
I am curious though to know which approach big companies use and if they use the editor approach, how do they address the challenges it inflicts.
#17 by Franz on December 6th, 2017 ·
Hello Migs,
could you please explain this:
“I created a centralized EventHandler that can take event subscriptions and dispatch them. Regarding the dependency injection, I use Zenject which is awsome and quite easy to use.”
I’m developing with the Pure Code approach that Jackson describes here, using his EventForwarder pattern too. As of now I’m building a sort of library in order to keep the main script more readable and I was looking for a way to managing event subscription and unsubscription, but I had problems.
thank you
#18 by Migs on December 18th, 2017 ·
Hello Franz,
Sorry for the delayed response, somehow I don’t get the notifications.
The EventHandler is basically a class containing a Dictionary of events mapped to a event handler function.
I call myGameObject.addEventListener(MouseEvent.CLICK, myMouseEventHandlerFunction) to add an event listener on mouse click. The EventHandler class then automatically adds a MonoBehavior script to a myGameObject and this script contains a OnMouseDown method which dispatches the event using a EventHandler.dispatchEvent(myMouseEvent)
I’ve created multiple methods to add and remove event listeners and dispatch events to make my life easier but that’s basically the principle.
I’m not sure that you need this to have a pure code approach but in case, I think there is also a Messaging system provided by Unity out of the box that has more or less the same purpose. When I had a look at it a while ago, it didn’t satisfy me so I wrote this EventHandler instead.
#19 by piter on June 14th, 2018 ·
Hi Migs,
Could you share your code?
#20 by Tadas on May 3rd, 2020 ·
I look at my Pickup object and it has a bunch of scripts like Rotator.cs, ObjectScaler.cs, AutoDestroy.cs and so on. All of them as Monobehaviours. :D If anybody still here, can you you suggest where to search for education to learn to avoid this or something? Would be incredible.