Better Practices

Last modified by Maarten Struijk Wilbrink on 2026/07/21 11:00

Information

Subpages

This page collects practical habits that keep a Unity project maintainable. It is aimed at SOLO researchers building VR / XR experiments in Unity 6 or later.

For XR/VR support: sosxr@fsw.leidenuniv.nl. For general lab support: labsupport@fsw.leidenuniv.nl.

Staying sane in a growing project

Without proper care, a Unity project can become unwieldy within days. The practices below are means to an end: each one should make development easier, not add ceremony for its own sake.

Organising your project

You can organise a project in many ways. There is no single right answer, but a few patterns help later in development.

Smaller projects: organise by type

For prototypes or small studies, group assets by type. Typical folders under the Assets folder include Scripts, Materials, Prefabs, Scenes, Data, and so on:

  • Assets
    • Data
    • Materials
    • Prefabs
    • Scenes
    • Scripts
    • Tests
      • Editor
      • Runtime

This approach is common in tutorials and works well while the project is small. It makes files easy to find: a C# script lives in Scripts, a material in Materials, and so on.

The weakness appears as the project grows, or when you want to reuse parts of one project in another:

  1. Dependencies are not kept in check
  2. Files that together create a specific function are mixed up with all other files, which makes reuse harder.

Larger / multiple projects: organise by function

To counteract these issues, create subfolders for each function or section of your project. Each subfolder can then contain the same type-based folders as above. For example, if your project has areas called Data, Characters, and UI, you might create something like this:

  • Assets
    • Data
      • Prefabs
      • Scripts
      • Tests
        • Editor
        • Runtime
    • Characters
      • Materials
      • Prefabs
      • Scripts
      • Tests
        • Editor
        • Runtime
    • Scenes
    • UI
      • Materials
      • Prefabs
      • ScriptableObjects
      • Scripts
      • Tests
        • Editor
        • Runtime

This practice promotes separation of responsibilities. Combine it with creating separate Assembly Definitions and Namespaces for each function, and use Assembly Definition References (`.asmref`) for the Tests subfolders so they actually compile as test assemblies.

In modern Unity, also consider the Packages folder and the Package Manager for third-party code. Keep the Assets folder for project-specific work, and pin package versions via `manifest.json` / `packages-lock.json` so the project remains reproducible.

Version control and packages

Track the project in Git from the start. Keep the following in mind:

  • Commit both the `Assets` and `Packages` folders, and their `.meta` files.
  • Use Git LFS for large binaries (textures, audio, models, video).
  • Use a `.gitignore` that excludes `Library/`, `Temp/`, `Obj/`, `Logs/`, and user-specific IDE files.
  • Avoid checking in downloaded Asset Store packages as loose files; install them through the Package Manager or as version-controlled packages when possible.
  • Pin package versions and document why a non-default package is needed.
  • Prefer one person editing a scene at a time; scenes and prefabs are text-based YAML, but merge conflicts are still painful.

Avoiding a spaghetti mess

Spaghetti Code is the term used for code that has a lot of interdependencies. Like a bowl of spaghetti, it is hard to see where each piece begins and ends. Small changes often break many other systems.

The goal is code with few dependencies that can be swapped out without breaking other systems. Two good books on this are The Pragmatic Programmer by David Thomas & Andrew Hunt and Clean Code by Robert C. Martin.

A core principle is: Make your code easier to change. Ask yourself whether what you are building right now is making the project easier or harder to change. This applies not only to code, but also to scenes, prefabs, materials, and the folder structure.

Practical habits for Unity:

  • Use namespaces to avoid name clashes and signal ownership.
  • Favour ScriptableObjects and events over global singletons and `FindObjectOfType`.
  • Keep data in plain classes / ScriptableObjects and behaviour in components; separate data from presentation.
  • Minimise cross-scene references; load scenes and data through explicit managers or addressable assets.
  • A few well-placed design patterns can help. Game Programming Patterns is a free online book with clear examples (in C++).

Testing

Unity has built-in support for automated tests through the Test Framework package.

  • Place Edit Mode tests under `Tests/Editor` and Play Mode tests under `Tests/Runtime`.
  • Create an Assembly Definition (`.asmdef`) for each test folder so the tests compile against your production code.
  • Use Play Mode tests for timing-critical XR interactions, and Edit Mode tests for pure logic and asset validation.
  • Run tests locally before committing, and consider adding them to any CI pipeline you use.

VR / XR performance notes

For XR projects, performance is part of correctness. A few guidelines:

  • Target the headset's refresh rate (commonly 72, 90, or 120 Hz) and use OVR Metrics Tool / XR Profiler to confirm.
  • Use Single Pass Instanced rendering where the headset supports it.
  • Keep occlusion culling and LOD groups in mind for complex scenes.
  • Compress textures and audio; use the Texture Compression settings for the target platform.
  • Use the XR Interaction Toolkit or a targeted headset SDK rather than rolling your own input.
  • Choose a render pipeline (URP is a common choice for new projects) and keep the project on it; switching later takes a long time.
  • Profile early and often on the target device, not only in the Editor.

Resources