Get the latest Education e-news
  • Top 3 Tips For Starting A New Unity Project

    - Charles Hinshaw

  • #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:

    • Runtime code that is not game specific (utilities and core systems)

    • Actual gameplay code

    • Code that extends the Unity Editor (tools, etc.)

    • Tests (unit and integration)

    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:

    • You cut down on compile time since you won't have to recompile all scripts when you make changes to an isolated area.

    • You can enforce the "no references to Prototypes" and "no Prototypes in builds" rules by having a Prototypes assembly that nobody references (and marking it to be only included in the Editor).

    • You can make your tests be proper tests by marking their assembly as a Test Assembly in the Inspector. This adds references to unit.framework.dll and UnityEngine.TestRunner.dll in the Assembly Definition file so that you can use Unity Test Runner with them.


comments powered by Disqus