Demystifying Unity for Large Projects – 6 rules

Hi, I’m Nick Varchavsky, lead developer at Indelve Studios.

I’ve been using Unity since it’s version 2.x (around 2008 or so), and I’ve seen it grow year after year. Along with the engine, the projects done with it grew as well.

One thing I’ve noticed is the general mis-understanding (from my point of view) on how capable Unity is handling large projects, and how to do so. I see a huge misconception on what Unity is, and how one should use it.

If I can explain these tips, you’ll never fall back on using Unity like before.

Throughout years of experience, research, and forehead slaps, I’ve come up with a set of rules I implement here at Indelve. I’m sure this is not the only way to handle large projects on Unity, but it has helped us greatly and worked wonderfully in small teams like in our mobile division, and in large teams like in our current large game Initiative: Red Daw.

You’ll notice some of them are not exclusive to Unity and more general to programming, but since they are part of my mantra, I’ll share them as well.


1- Keep it sharp
Use C#. Plain and simple. There’s no reason to use UnityScript (or what many confuse with “Javascript”). As I’ve explained on another article, there are a series of reasons why I use C# for everything, and completely ban UnityScript from any project I work on.

First, most libraries you’ll find are written in C# (such as Facebook’s or other major SDKs).

Second, UnityScript and C# in Unity have different compiling time. This means they are not compiled together. Hence, if you want a UnityScript to access a C# script, you have to follow specific folder guidelines to achieve that. I’ve found myself in a hybrid nightmare of C# and UnityScript scripts that didn’t see each other, so I had to arrange some on the Assets folder, some on Standard Assets, some on Plugins, etc. to be able to achieve what I wanted. Since then, I chosen to use C# for everything.

Also, it’s not Javascript. Unity’s Javascript is not really Javascript. In fact, it’s actually UnityScript and it lacks a few of the most powerful things Javascript has (for my taste), which is JSON. You can see a more detailed list of the differences here as well, but I’ve found several that make UnityScript a turndown for me. I develop a lot in JS (NodeJS and AngularJS for example), and I absolutely love JS. But when it comes to Unity, I go with C#.


2- The point of no return
When working on large projects, there’s a point of no return (usually pretty early) where you must commit and stick to an engine version (strictly speaking, to a specific version of every library you use). This is true not only to Unity. Although Unity still lacks a good version management like Unreal Engine has.

Also, under no circumstance you should depend on a future feature Unity says they will provide (i.e.: Nested Prefabs have been in the roadmap as the next feature since 2013 and still at the end of 2016, no nested prefabs). If you need a feature, either code it yourself or get a plugin from the Asset store. If you plan the latter, check below on #5.


3- Make your own DLLs
When working with multiple programmers and designers it becomes imperative to protect the code from accidental edits. And if you are working with freelancers, you’re also protecting your source code.

You can create one or multiple DLLs if you want. Here are a couple of uses we have for DLLs:

– Prevents accidental edits
– Protects source code
– Easier version tracking (easier to know all developers are working on the same library version)
– Easier update: you simply send the new DLL instead of the source code script or worse, the full Unity project. This is specially useful if you work with programmers off site
– Easier propagation: you can setup your environment to automatically copy linked DLLs to a master server from which others will update automatically

You can make .NET DLLs in Windows and in Mac as well using Mono. Unity uses Mono so you’re not loosing any special features. Send me a buzz if you need some guidelines as to how to create such DLLs.


4- Unity for Unity
One of the biggest mistakes I find online is wanting to have Unity solve every single need we may have.

As a general rule, I put no game logic on Unity. I use Unity simply as a visual front-end of the game. For example, on Initiative: Red Dawn can be played fully from a linux console. Unity is just a visual representation of that.

Initiative: Red Dawn console view (early version)

Remember,  just because Unity has certain functionality does not mean you need to adapt your code to use it.

Use Unity stuff (i.e.: MonoBehaviours) only for Unity-specific visual stuff. If a class has no visual representation on the game world, then it makes no sense to make it a Monobehaviour or even have it inside Unity.

Don’t use Monobehaviours just for the Start/Update/etc. Use Dirty updates instead.

We have a single MonoBehaviour performing the update to the DLL, and everything else is handled through a dirty update from there, even Unity stuff.

TL;DR
If it has a visual representation, use Unity. If not, goes into the DLL.


5- Asset Store plugins
If you want/need to use asset store plugins, there are a few caveats we always check:

– Make sure the plugin is actively maintained by the developer: you can check the forums, their webpage and comments on the Asset Store itself.

– Always have an empty project to import the plugin, run the demo scenes, and make sure it works. Then, delete all unwanted content (demo content mostly), and create your own UnityPackage to use. Never have developers download the asset from the store, always share your version of it (also check the licensing to see how many seats you need when sharing)

– Just like Unity, you need to stick to a version of the plugin and be very cautious when upgrading

– If the plugin comes with source code and the license allows it, embed such code in your DLL and use it from there. If the plugin does not come with source code, I simply don’t use it. We already have to live with Unity’s lack of source code to add yet another black box middleware.


6- Scene usage

Don’t. Just don’t use or relay on scenes for your end-game. Use prefabs for everything (I mean, everything). If its a GameObject, then it is a prefab, no matter how simple it is.

You can use scenes during development to test specific elements of the game if you must. If you do, plan ahead and make sure each scene is fully playable without the need to go through several other scenes to test a single feature.

In our case, we use different Unity projects for each section of the game: the base on Earth is one project (not a scene, a separate project), the technology tree is another project, etc.

All projects have a shared library containing the DLLs and all shared assets (icons, logos, images, sounds).

Also, since we are not using scenes, we can have multiple programmers working on the same part of the game and it’s just like a script file edit.

If they are changing visual elements (models, animations, etc.), it’s just updating a prefab.

 


Well! I hope I did a fair job explaining our method. Remember, I’m not stating this is by far the best method there is, but it sure worked for me in the past and is working wonderfully now.

Game developing consumes an immense amount of time, which unless you’re doing it fully as a hobby, translates into money which, for most of us, it’s very limited and heavily guarded by our CFOs and alike. So, ensuring you use a cleaner and smoother workflow saves plenty of time in the end.

As always, let me know if you find any issues, typos, or if you want me to go deeper into any topic explained here.

Thanks for reading!

You can follow me on Twitter at

 

4 Replies to “Demystifying Unity for Large Projects – 6 rules”

  1. Hello Nick,

    I want to ask about “Unity independent” game logic code approach. How much independent it is ?

    I mean for example do you use “using UnityEngine” in your non MonoBehaviour scripts ? if not, is that mean you have your own Vector3 , Quaternion etc. implementations ?

    Another question is how do you handle UI and Game rendering , are you updating them on every frame or using event handlers?

    Thanks

    1. Hi Omer,

      I’m on mobile but I’ll try to reply a bit.

      Everything that is Unity dependent I place it in Unity. If you need to use Vector3, then that should be on the Unity side.

      I use Unity for all those operations.

      With independent, I mean all core classes that do not need or use Unity. Those are plain simple .NET classes.

      For example, in an RTS game, the structures have a non-unity implementation (name, level, production, player who owns it). And a Unity implementation (transform, renderer). I keep those apart. I hope this makes sense.

      What type of game are you working on? I could give a much deeper reply with real example using your game’s genre. I’m currently on mobile but I didn’t want to delay a response.

      Nick-

      1. Thank you for the quick answer. The genre is likely a roguelike game, which has randomly generated dungeons and with RPG elements. It is pretty common genre i think. But my concern is how to render that simulation in an efficient way. I am working for mobile platforms by the way

        For instance

        Simulation side created a projectile
        How Render manager will behave to spawn a renderer object and match it to simulation object?
        -Is it going to check it in every update cycle ?
        -Or Is it going to listen an event ?

        Witch one is less risky and more efficient.

        Another issue is asset dependent data. Like, bullet spawn position of a weapon. All data should be in simulation so the solution in my mind is an artist tool that exports those datas to a simulation database. What do you think about that.

        Thank you again, you may answer when you have your time. Don’t rush 🙂

        1. Omer,

          Regarding projectile simulation, I think that is something the engine should definitely take care of if physics are involved.

          The main idea I talked about was to use pure c# classes to everything that is not purely visual (I count physics as visual in this case). If your projectile can hit a moving target, then that’s something you should use the engine for. However, you could have your pure c# classes handle what to do in the event of a collision. But instantiation could be handled outside of the engine, and just call a method on the engine to instantiate a projectile passing arguments such as origin, direction, speed, etc. Once a collision is found, the engine can call back the pure c# class to notify of the collision.

          This would be an event-driven approach, instead of polling every frame which is inefficient.

          On my case, all asset dependent data (what we could call database), comes from an XML file that is read at the beginning. Keep in mind my game is moddable so we need that file editable and easy updatable. But, it’s all handled outside of the engine.

          For example, in our game, the overall functionality of all NPCs you can hire (called Talents), is handled outside the engine. All the engine does is renders what it needs to show, and receives the input it needs to receive. And, of course, performs all animations, physics calculations, sound effects, etc.

          It’s an old concept of separation of concerns: the visual layer should be independent from the business layer, which should be independent from the data layer.

          I want the Unity designers to manipulate only the things I want them to (which I expose in the Editor), and not be concerned about underlying logic. Do you need to get a list of your roster? Call Player.GetRoster(). Do you want to hire a Talent? Call Client.HireTalent (int talentId). Etc.

          I find it much easier to decouple, and therefore unit-test, by working outside of Unity for all the underlying logic.

          But, also, keep in mind it heavily depends on the game. If you’re making an FPS, it will be much more engine dependent than a simulation.

Leave a Reply

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

*