Top 3 Tips For Starting A New Unity Project

By Charles Hinshaw [07.03.18]

I've been using the Unity game engine nearly daily for around a dozen years (with five of those years as an early employee of Unity Technologies.) With all this time spent using Unity, I've seen a lot of projects in various states of chaos and have been responsible for a making a mess or two.

After a recent "best practices" consulting gig at a local game studio, I've been thinking about what advice I would include in my top three tips for anyone starting a new project in Unity.

#1: Plan a Sane Project Structure

The battle against entropy begins the moment you select File/New Project...and the choices you make now will directly impact the amount of joy or pain you experience every day while working on your game. Since I firmly believe that creating should be a joyful experience, my top piece of advice for anyone starting a new project is to spend time planning a sane project structure.

I have personal preferences when it comes to "the best way" to organize a project, but regardless of how you choose to organize things, I think all good structures are built around a principle like this:

All of this starts with you really thinking things through - you can't hope for other people to understand how things are organized and maintain a project if you don't understand it yourself.

Ok, that is pretty high-level/big picture. Let's consider some concrete advice:

Communicate Your Structure

Your structure isn't as "obvious" as you believe and you need to actively consider how it is communicated to new team members. Part of this is about carefully considering how you name your folders, but I like going one step further and having the communication right there in the Project Window in the form of notes on folders that say exactly what is inside (like having "Plugins - Extensions to Unity functionality. Special folder offering phase-one compilation" right there in the inspector when the Plugins folder is selected.)

How do I do this? In Unity, the type DefaultAsset is used for assets that do not have any specific type... like folders. Like all Assets, DefaultAssets have an AssetImporter and can have a custom Editor to get a custom inspector. I like to store a description of a folder in the AssetImporter's userData and use the custom Editor for DefaultAssets to display userData strings.

Enforce Your Structure

Planning and communicating will only get you so far. At some point, you need to think about how you are going to enforce the discipline necessary to maintain your project's organization. As a first line of defense, I recommend ensuring that only files of certain types are allowed into specific folders.

Again, use the userData in a folder's AssetImporter. But in addition to descriptive text, include the allowed file extensions. Technically, I recommend storing the GUID to a ScriptableObject that contains that list so that when you realize that "Code Only" folders need to store not only .cs files but also .asmdef files, you can update that globally.

With that in place, you can use AssetPostProcessors to ensure that an asset is allowed to be where it is on import and warn the user if they place a file someplace where it doesn't belong. You could even take this a step further and hook it up to your VCS to reject commits until the user gets that prefab out out the scripts folder.

Firewall What You Won't Ship

Your project contains some stuff that isn't really part of the game: things like reference implementations and prototype environments. Invariably, whether through mis-clicking or misunderstanding, these things creep into production scenes or are referenced in ways that pull them along into your build.

I like to keep these things separated at the top level of the project in a "Prototypes" folder and to have a hard rule that says that nothing from outside this folder should ever reference anything inside this folder.

Fortunately, it is pretty easy to detect when this rule is violated-we can make a simple editor tool that walks through the SerializedObjects in our project and ensures that none of them have a SerializedProperty that is a PPtr to an asset in /Prototypes. As your project grows, this can start to take a little time to process everything, so this step is perhaps best done as part of your Continuous Integration setup.


#2: Structure Your Code

With a sane project structure in place, it is time to turn our attention to how we structure our code.

People have written entire books that barely scratch the surface of this topic, and I'm not under the delusion that I'll make much of an impact with a few words. My hope here is only to offer some practical and Unity-specific advice and to mostly look at this in the context of the previous point, because unsurprisingly, the same principles that applied to our project as a whole also apply here - we want to think through a structure that removes ambiguity and makes things easy to find.

I usually start by making a broad distinction between four types of code at the highest level:

For sanity and maintainability, each of these things should be kept separate from the others and each should only be allowed to reference the things that came before it.

Communicate Your Structure

We communicate code structure on two levels: through project hierarchy and through API. This is the reason that I prefer parity between namespaces and folder structure in Unity.

Fortunately, most IDEs (I'll plug JetBrains Rider here because I strongly recommend it if you are working on a Mac) can auto-enforce this. By default, Rider assumes that the namespace each class matches its location in the project and lets you know with "Code Inspection: Namespace does not correspond to file location" if you aren't following the convention.

Enforce Your Structure

I mentioned that we don't want, for example, core systems code referencing gameplay code. We can enforce this by using Assembly Definition files to keep those high-level distinctions between kinds of code in our project. Assembly definition files define your own managed assemblies based upon your project's structure and since references can't be cyclic, we can be assured that nothing in our core systems references gameplay-specific code.

This comes with a few added bonuses:


#3: Don't Let Broken Things Build Up

The little inconveniences and time wasters that you experience each day will build up and suck the joy out of development. My advice is to aggressively remove these things are they are encountered.

Conclusion

When writing this, I tried to prioritize simple things that make a big difference when done early in a project's life. There are, of course, a million other things where I would say "oh, that is super important!", but I don't think any of them would bump these three points right when you start a new project.

One last thing that I'd like to point out - all of this advice applies even if you are a solo developer working on a hobby project. Just read statements like " you can't hope for other people to understand how things are organized and maintain a project it if you don't understand it yourself" as "you can't hope for future you to understand how things are organized and maintain a project if present you doesn't understand it."

I hope you enjoyed these tips and found them useful. If you have any other advice to share with someone creating a new project, let me know!

Return to the web version of this article
Copyright © UBM TechWeb