Setting Up: Unity 6, Hub, IDE, Packages & Version Control
Install Unity 6.3 LTS through the Hub, connect a code editor you can debug with, understand what a project folder holds and which packages it uses, and put it under Git from the first day.
- Study time
- 4–6 hours (much of it waiting for installs)
- Reading time
- 94 min read · 11,669 words
- Prerequisites
- T00 · How to Learn Unity with This Handbook
- Courses merged
- 1 course files
- Your progress
- —
- Install Unity Hub, the Unity 6.3 LTS editor and the modules you need, and keep several editor versions side by side
- Create a URP project and say what every top-level folder in it is for, and which ones belong in version control
- Explain what
.metafiles and GUIDs do, and move or rename assets without breaking references - Connect Visual Studio, VS Code or Rider so that autocomplete, double-click-to-open and breakpoints all work
- Add, update and remove packages with the Package Manager, and read
Packages/manifest.json - Put a Unity project under Git with a correct
.gitignore, Git LFS for big files, and Unity's YAML merge tool - Run the chapter's project setup checker and fix what it reports
Before you can learn Unity you need a working setup: the editor, a code editor that understands Unity, and version control so you never lose work. Most beginners rush this and pay for it later, with autocomplete that silently doesn't work, a project they can't open on another computer, or a week of work lost to one bad change. This chapter sets everything up properly once, explains what each piece does, and ends with a script that checks your setup for you.
Expect to spend much of this chapter's time waiting for downloads. Use the waits to read ahead: the sections on project folders and .meta files are the ones you'll come back to most.
The big picture#
A Unity setup is five pieces, and each one has a single job:
- Unity Hub is a launcher and installer. It downloads editor versions, keeps track of your projects, and manages your licence. It doesn't edit anything itself.
- The Unity Editor is the program you build games in. You can have several versions installed side by side, and each project is opened with one specific version.
- A project is just a folder on disk. Your scenes, scripts, art and settings all live in it as ordinary files.
- An IDE (integrated development environment: Visual Studio, VS Code or Rider) is where you write C#. A small Unity package connects the two, so the IDE understands Unity's types and can debug the running game.
- Version control (Git, in this handbook) records every change to the project folder, so you can go back, compare, branch, and share the project with other computers and people.
The order of this chapter follows the order you'll set things up: account and licence, Hub, editor, a first project, what's inside it, editor settings, the IDE, packages, and finally Git. The guided build at the end does all of it again quickly, as a checklist, on a fresh project.
Accounts, plans and licences#
You need a free Unity account (sometimes called a Unity ID) to use the editor, and a licence attached to it. Unity offers several plans; which one you need depends on money, not on features you'll miss while learning.
| Plan | Who it's for | Cost |
|---|---|---|
| Unity Personal | Individuals, hobbyists and small organisations with less than US$200,000 of revenue and funding in the last 12 months | Free |
| Unity Student | Eligible students, verified through Unity's education programme | Free |
| Unity Pro and Enterprise | Companies above the Personal limit, or teams that need their extra services and support | Paid, per seat |
For learning, Personal is all you need. Every feature this handbook teaches works with it.
Activating a licence#
- Create a Unity account at unity.com (top right, sign in, then create one), or sign in with an existing one.
- Install Unity Hub (next section) and sign in to it with the same account.
- In the Hub's settings, open Licenses and choose Add, then the free Personal licence. The Hub may also offer to do this for you the first time you sign in.
A licence is tied to your account and to the machine it's activated on. If you change computers, return the licence from the old one in the Hub's licence settings, or just activate it again on the new one.
Unity Hub#
Download the Hub from unity.com/download and install it. On Windows it installs to C:\Program Files\Unity Hub; on macOS it goes in Applications; on Linux, Unity publishes a package repository for Ubuntu, and the download page gives the commands. The Hub updates itself, so the version doesn't matter much. As of this writing the latest is 3.22 (September 2026).
Check your computer can run the editor before you download several gigabytes of it. These are Unity's official minimums for the Unity 6.3 editor:
| Windows | macOS | Linux | |
|---|---|---|---|
| OS | Windows 10 21H1 (build 19043) or newer on x64; Windows 11 21H2 or newer on Arm64 | macOS 13 Ventura or newer | Ubuntu 22.04 or 24.04 |
| CPU | x64 with SSE2, or Arm64 | Intel x64 with SSE2, or Apple M1 and later | x64 with SSE2 |
| Graphics | DirectX 10, 11, 12 or Vulkan capable GPU | Metal-capable GPU | OpenGL 3.2+ or Vulkan, Nvidia or AMD |
| Other | Official vendor drivers | Rosetta 2 installed, on Apple silicon Macs | GNOME on X11 or Wayland; Nvidia proprietary or AMD Mesa driver |
Those are minimums for the editor to run, not for it to be pleasant. In practice, 16 GB of memory, an SSD, and a GPU from the last few years make a big difference. Imports and compiles are where you'll feel a slow machine most.
What's in the Hub#
- Projects: every project the Hub knows about, with the editor version each one uses. You open projects from here, and add existing ones with Add > Add project from disk.
- Installs: the editor versions installed on this machine, and the modules each one has. Install Editor adds a new version; the gear icon on an installed version lets you add modules or uninstall it.
- Learn: Unity Learn tutorials and sample projects you can download and open directly.
- Settings (the gear at the top): your licences, where editors are installed, and where new projects go by default.
Installing the editor#
In the Hub, go to Installs > Install Editor. You'll see the recommended version at the top and other releases below, grouped by stream. Install Unity 6.3 LTS. The handbook's baseline is 6000.3.27f2, released on 30 October 2026; any later 6000.3 patch is fine too, because patches within an LTS only fix bugs. If the exact version isn't in the list, the Archive tab has every release.
Reading the version number#
Unity releases come in streams. LTS releases (6.3 now) get two years of fixes and no new features: they're what games ship on. Supported Update releases (such as 6.6) add features between LTS releases with a shorter support window. Beta and alpha releases (6.7 beta, 7.0 alpha as of this writing) are previews. T00 has more on why the handbook uses LTS.
Choosing modules#
Next, the Hub asks which modules to add. A module is an optional part of the editor: support for building to a platform, offline documentation, or a language pack. The editor can always build for the platform it runs on (using the Mono scripting backend), so you can skip everything else for now and add modules later from the gear icon on the install. The ones you'll want eventually:
| Module | Add it when | Notes |
|---|---|---|
| Microsoft Visual Studio Community (Windows) | Now, if you'll use Visual Studio | Installs Visual Studio with the Unity workload. Skip it for VS Code or Rider. |
| Documentation | Now, if you often work offline | A local copy of the Manual and Scripting API. |
| Windows / Mac / Linux Build Support (IL2CPP) | When you build for your own desktop with IL2CPP | T50 · Shipping: Build Profiles, Platforms, Web, Steam & Release Checklist explains Mono versus IL2CPP. |
| Web Build Support | When you want to share a game in a browser (T10 · Your First Complete Game Loop: States, Scenes, UI, Audio & Builds) | Called WebGL in older versions. |
| Android Build Support | When you reach mobile (T47 · Mobile: Touch Input, Builds, Optimisation & Store Publishing) | Tick its sub-modules too: Android SDK & NDK Tools and OpenJDK. |
| iOS Build Support | When you reach mobile | The final step of an iOS build needs a Mac with Xcode. |
Plan for something like 15–25 GB of free disk space for one editor with a couple of platform modules (a rough guide; Android is the largest), plus a few gigabytes per project for its Library folder.
Several versions side by side#
You can install as many editor versions as you have space for, and each project opens with the version recorded in its ProjectSettings/ProjectVersion.txt. This is normal: professionals often keep the version a shipped game used, the current LTS, and a beta to try new features. Keep the handbook's sandbox on 6.3 LTS.
If you open a project with a different version, the Hub warns you first. Opening with a newer patch of the same minor version (6000.3.30 instead of 6000.3.27) is safe. Opening with a newer minor or major version upgrades the project: Unity rewrites settings and assets to the new format, and there's no button to undo it. So:
- Commit everything to Git first (or copy the folder, if you're not using Git yet).
- Upgrade on a branch, open the project, and fix any errors and warnings.
- Only merge the upgrade once the project builds and plays correctly.
Creating your first project#
In the Hub, choose Projects > New project. The dialog has three things to decide: the editor version (pick 6.3 LTS), a template, and a name and location.
Templates#
A template is a starting project: settings, packages and sometimes sample content. The ones that matter for this handbook:
- Universal 3D: an empty 3D project with the Universal Render Pipeline (URP) set up. This is the default for every 3D chapter.
- Universal 2D: an empty 2D project with URP set up for its 2D renderer, including 2D lights. Use it for T09 · 2D Development: Sprites, Tilemaps, 2D Physics, 2D Animation & Lights and the 2D course files.
- Universal 3D sample: URP with a set of sample environments. Worth opening once to see what URP can look like, but not as a starting point.
- Templates for the High Definition Render Pipeline (HDRP) and the older Built-in Render Pipeline, plus mobile, XR and multiplayer templates. Later chapters say when you'd use them.
Templates are downloaded the first time you use them, so the first project from each template takes a little longer to create.
Name and location#
Name the project without spaces or special characters (FirstSteps, not My first game!). It becomes a folder name, and later tools and build scripts are happier without spaces. Put it in your short, unsynced projects folder.
The dialog may also offer to connect the project to Unity Cloud and to Unity Version Control. Leave both off for now. Cloud services come in T48 · Monetisation & Services: IAP, Ads, Analytics, Remote Config, and this chapter sets up Git instead, for reasons explained in the version control section. You can connect either later without starting over.
Click Create project. The first import takes a few minutes, while Unity downloads the template's packages and builds the Library folder. Later openings are much faster.
What's inside a Unity project#
Open the project folder in Explorer or Finder (in the Hub, the three-dot menu on a project has Show in Explorer or Reveal in Finder). Knowing what each folder is for answers most "should I commit this?" and "can I delete this?" questions for good.
| Path | What's in it | In Git? |
|---|---|---|
Assets/ | Everything you make or import: scenes, scripts, prefabs, materials, textures, audio, models. Every file and folder has a .meta file next to it. | Yes, with every .meta |
Packages/manifest.json | The packages the project uses, and their versions | Yes |
Packages/packages-lock.json | The exact versions Unity resolved, including dependencies of dependencies | Yes |
Packages/<name>/ | An embedded package: one you've copied into the project to change it | Yes |
ProjectSettings/ | Every Project Settings page (input, physics, tags and layers, quality, graphics, player), and ProjectVersion.txt | Yes |
UserSettings/ | Your personal editor state for this project: window layout, last open scene, search settings | No |
Library/ | Unity's cache: imported versions of your assets, compiled scripts, downloaded packages | No |
Temp/, obj/, Logs/ | Temporary build files and editor logs | No |
*.sln, *.csproj | Project files for your IDE, generated by Unity from your scripts and packages | No |
.vs/, .idea/, .vscode/ | Your IDE's own caches and settings | Usually not |
The package manifest and the version file#
Two small text files say what a project is built with. Packages/manifest.json lists its packages:
{
"dependencies": {
"com.unity.ide.rider": "3.1.0",
"com.unity.ide.visualstudio": "2.0.29",
"com.unity.inputsystem": "1.20.1",
"com.unity.render-pipelines.universal": "17.3.0",
"com.unity.test-framework": "1.6.0",
"com.unity.ugui": "2.0.0",
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.physics": "1.0.0"
}
}Each line is a package name and the version wanted. The com.unity.modules.* entries are built-in modules: parts of the engine itself (physics, audio, UI) that you can switch off to make builds smaller. And ProjectSettings/ProjectVersion.txt records the editor version the project was last saved with:
m_EditorVersion: 6000.3.27f2
m_EditorVersionWithRevision: 6000.3.27f2 (…revision hash…)The Hub reads this file to decide which editor to open the project with.
Meta files and GUIDs#
This is the most important idea in the chapter, because getting it wrong breaks projects in ways that are hard to repair.
When you add a file to Assets/, Unity creates a .meta file beside it with the same name plus .meta: Crate.png gets Crate.png.meta. The meta file holds two things:
- A GUID (globally unique identifier): a random 32-character ID that is the asset's real identity inside Unity.
- The asset's import settings: for a texture, its maximum size, compression and filtering; for a model, its scale and animation settings; for a script, very little.
When anything in Unity refers to an asset (a material using a texture, a scene using a script, a prefab placed in a level) it stores the asset's GUID, not its path. That's what lets you rename and move assets freely inside Unity without breaking anything: the path changes, the GUID doesn't.
Here's what that looks like in the files themselves. A script's meta file is short:
fileFormatVersion: 2
guid: 6a1f0c3e8b2d4e57a9c4b0d2e7f13a58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:And a scene that uses that script refers to it by the same GUID (line 8 below), followed by the values you set in the Inspector:
--- !u!114 &1843029381
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1843029379}
m_Enabled: 1
m_Script: {fileID: 11500000, guid: 6a1f0c3e8b2d4e57a9c4b0d2e7f13a58, type: 3}
m_Name:
greeting: Hello from Unity
repeat: 3
reportAfterFrames: 120(The GUIDs here are examples; yours will differ. Unity writes scenes as text like this only when Asset Serialization is set to Force Text, which is the default and which the next section checks.)
Three rules follow from this, and they cover almost every meta-file disaster:
- Move, rename and delete assets inside Unity's Project window, not in Explorer or Finder. Unity moves the meta file with the asset.
- If you must move files outside Unity (with the editor closed), move each
.metafile with its asset. - Always commit
.metafiles, in the same commit as their assets. A teammate who getsCrate.pngwithoutCrate.png.metagets a new GUID, and every reference to the texture breaks on their machine.
First-run settings worth checking#
A new project from a Universal template is already set up sensibly, so most of this section is about knowing where the settings live and checking a few. There are two settings windows, and the difference matters:
- Preferences (Edit > Preferences on Windows and Linux, Unity > Settings on macOS) belong to you and apply to every project you open on this machine.
- Project Settings (Edit > Project Settings) belong to the project, live in
ProjectSettings/, and are committed, so everyone who opens the project gets them.
Preferences#
- External Tools > External Script Editor: which IDE opens your scripts. The IDE section below sets this.
- Colors > Playmode tint: the colour the editor is tinted while the game is playing. Pick something obvious, like a light red. Changes you make in Play mode are thrown away when you stop, and a strong tint stops you from spending ten minutes tweaking values that are about to vanish.
- General > Editor Theme: dark or light, as you like.
- Asset Pipeline > Auto Refresh: whether Unity notices changed files (for example, a script saved in the IDE) as soon as you switch back to it. Leave it on.
Project Settings#
| Setting | Where | Value for this handbook | Why |
|---|---|---|---|
| Asset Serialization Mode | Editor | Force Text (the default) | Scenes and prefabs saved as readable text, so Git can show and merge changes |
| Version Control Mode | Version Control | Visible Meta Files (the default) | Meta files are ordinary files that Git can see and commit |
| Active Input Handling | Player > Other Settings | Input System Package (New) | The handbook uses the Input System (T07 · Input System, Movement & Cameras (Cinemachine)) |
| Color Space | Player > Other Settings | Linear (the URP template default) | Lighting maths comes out physically correct (T33 · Rendering in Unity 6: URP & HDRP, Cameras, PBR Materials & Post-Processing) |
| Render pipeline asset | Graphics, and per level in Quality | A URP asset | Set by the Universal templates already |
Enter Play Mode settings#
Project Settings > Editor > Enter Play Mode Settings controls what Unity does when you press Play, and it's worth understanding now because it explains some confusing behaviour later. Two things can happen on pressing Play:
- Domain reload: Unity throws away all loaded C# code and loads it again. Every
staticfield goes back to its default value. This is the slow part, and it gets slower as a project grows. - Scene reload: Unity reloads the open scenes from disk, so every object starts fresh.
Unity 6 lets you choose which of these happen, from a drop-down labelled When entering Play Mode: reload both, the scene only, the domain only, or neither. Skipping the domain reload makes pressing Play much faster, but static fields then keep their values from the previous play, which surprises people: a static score counter that "remembers" last game's score is the classic symptom.
Which option a new project starts with has varied between Unity versions, so don't assume: the setup checker in the guided build prints what your project does. While you're learning, reloading both is the safest choice, because statics always behave the way a C# book describes. T05 · Scripting in Unity: Lifecycle, Serialization, Coroutines & Awaitable shows how to write code that works correctly with the faster options too.
Your code editor (IDE)#
You'll write C# in an IDE, not in Unity. Unity supports three, and all of them are good: the choice is mostly about your operating system and taste.
| Visual Studio | Visual Studio Code | JetBrains Rider | |
|---|---|---|---|
| Runs on | Windows | Windows, macOS, Linux | Windows, macOS, Linux |
| Cost | Community edition free for individuals; check Microsoft's licence for company use | Free editor; the C# Dev Kit extension follows Visual Studio Community's licence terms | Free for non-commercial use; paid licence for commercial work |
| Unity package | Visual Studio Editor (com.unity.ide.visualstudio) | Visual Studio Editor, 2.0.20 or later | JetBrains Rider Editor (com.unity.ide.rider) |
| Also needs | The Game development with Unity workload | Three extensions: C#, C# Dev Kit, and Unity | Nothing: Unity support is built in |
| Good at | Mature debugger; Unity's default on Windows | Light and fast; identical on every OS; Unity's default on macOS | The deepest Unity-aware code analysis and refactoring |
If you have no preference: on Windows, use Visual Studio Community; on macOS or Linux, use VS Code; and if you already know and like JetBrains tools, use Rider anywhere. The handbook works the same with all three.
How Unity and the IDE talk to each other#
Your IDE doesn't know what Unity is. It understands C# projects, described by .sln (solution) and .csproj (project) files. One of the IDE packages above writes those files for you: it looks at your scripts, your assembly definitions and your packages, and generates a solution that references every Unity assembly. When you double-click a script in Unity, the package regenerates the files if needed and opens the IDE on that solution.
This explains the most common setup problem: if autocomplete doesn't know what MonoBehaviour is, the IDE has opened your script as a loose file, not as part of the generated solution. The fix is always some version of "make Unity generate the solution, and open it".
Setting up Visual Studio (Windows)#
- Install Visual Studio Community, either as a module from the Hub or from Microsoft's site. In the Visual Studio Installer, tick the Game development with Unity workload. It includes Visual Studio Tools for Unity, which provides the debugger and Unity-aware features.
- In Unity, open Preferences > External Tools and set External Script Editor to your Visual Studio version.
- Double-click any script in the Project window. Visual Studio opens with the whole solution.
Setting up Visual Studio Code#
- Install VS Code from code.visualstudio.com.
- In VS Code's Extensions view, install the Unity extension published by Microsoft (
visualstudiotoolsforunity.vstuc). It installs the C# and C# Dev Kit extensions it depends on. C# Dev Kit may ask you to sign in, and installs a .NET SDK if you don't have one. - In Unity, check that Window > Package Management > Package Manager > In Project lists Visual Studio Editor at version 2.0.20 or later. (The Universal templates include it.)
- In Preferences > External Tools, set External Script Editor to Visual Studio Code.
- Double-click a script. VS Code opens the project folder, and the status bar shows the solution loading. Give it a minute the first time.
Setting up Rider#
- Install Rider from jetbrains.com/rider. On first start, choose the free non-commercial licence if it applies to you, which needs a JetBrains account.
- In Unity, check that JetBrains Rider Editor is in Package Manager > In Project.
- In Preferences > External Tools, set External Script Editor to Rider.
- Double-click a script. Rider opens the solution and connects to the editor (a Unity icon in Rider's status bar shows the connection).
Debugging with breakpoints#
Debug.Log gets you a long way, but a real debugger lets you stop the game on a line, look at every variable, and step through the code one line at a time. Set it up now, while nothing is at stake, so it's ready the first time you need it.
- In your IDE, click in the margin next to a line of a script to set a breakpoint (a red dot).
- Attach the debugger to the running editor. In Visual Studio, click Attach to Unity on the toolbar. In VS Code, open Run and Debug and start Attach to Unity. In Rider, choose the Attach to Unity Editor run configuration and click Debug.
- Unity asks whether to switch to Debug mode. Say yes. (The bug icon at the bottom right of the editor shows the current Code Optimization mode; Release mode runs scripts faster but can't be debugged.)
- Press Play. When the game reaches the breakpoint it freezes and the IDE comes to the front, with the line highlighted. Hover over variables to see their values, then step over to run one line at a time, or continue to run on to the next breakpoint. In Visual Studio and VS Code those are F10 and F5; Rider's keys depend on the keymap you chose when you installed it.
- Stop debugging in the IDE when you're done. The game carries on.
Packages and the Package Manager#
Much of Unity isn't in the editor you installed. It comes as packages: versioned bundles of code and assets that a project chooses to use. URP is a package. So are the Input System, Cinemachine, the UI system, the test framework, and the IDE connectors you just used. This keeps the editor smaller, lets packages be updated on their own schedule, and means two projects on the same editor can use different versions of a package.
Open Window > Package Management > Package Manager. The list on the left switches between sources:
- In Project: the packages this project uses, and their versions.
- Updates: installed packages with a newer version you could move to.
- Unity Registry: every package Unity publishes, which you can install with one click.
- My Assets: things you've bought or claimed on the Asset Store.
- Built-in: the engine modules (physics, audio, terrain…), which you can disable if a project doesn't use them.
- Services: Unity's online services, such as analytics and multiplayer, covered in P6.
The + menu at the top left adds packages by other routes: by name (when a chapter tells you an exact package name such as com.unity.splines), from a Git URL, or from a folder or tarball on disk.
Versions, and what the labels mean#
Packages use semantic versioning: MAJOR.MINOR.PATCH, as in 1.20.1. A patch release only fixes bugs; a minor release adds features without breaking what's there; a major release may change or remove things, so moving across one (Cinemachine 2 to 3, for example) can mean updating your code. Versions with a suffix, such as 3.0.0-pre.2 or 0.4.0-exp.1, are pre-release or experimental: the Package Manager labels them, and they may change in breaking ways before they're finished.
Each editor version comes with a set of package versions Unity has tested with it. Installing a package from the Unity Registry picks the right one for your editor, which is why you rarely need to choose a version yourself.
Dependencies and the lock file#
Packages depend on other packages. URP needs the Core RP Library and Shader Graph; Cinemachine needs Splines. You list only what you want in manifest.json, and Unity works out the rest and records the exact result in packages-lock.json. Commit both: the lock file makes sure every computer that opens the project gets exactly the same versions of everything.
Feature sets, Git packages and scoped registries#
- Feature sets are bundles of packages that go together for one kind of work, such as 2D, Engineering (which includes the IDE packages and the test framework) or 3D Characters and Animation. Installing a feature set installs its packages at versions tested together.
- Git packages come straight from a Git repository. The manifest entry is a URL instead of a version, ideally ending in a tag so it can't change under you (see the example after this list).
- Scoped registries are other package servers, such as OpenUPM for open-source packages. They're added in Project Settings > Package Manager, and packages from them then appear in the Package Manager under My Registries.
"com.example.tool": "https://github.com/example/tool.git#v1.2.0"Asset Store assets aren't the same as packages#
Most Asset Store content is imported: its files are copied into your Assets/ folder, and from then on they're part of your project, committed with everything else. Packages, by contrast, stay in the package cache and are referenced from the manifest. That difference matters for updates (an imported asset has to be re-imported over your copy) and for licences (an imported asset's files are now in your repository, so check its licence before making the repository public).
The handbook's package baseline#
Every chapter is written against these versions, recorded in the handbook's catalogue. The Universal templates include most of them; later chapters tell you when to add the rest.
| Package | Name | Version | First used in |
|---|---|---|---|
| Universal Render Pipeline | com.unity.render-pipelines.universal | 17.3.0 | every chapter |
| Input System | com.unity.inputsystem | 1.20.1 | T07 · Input System, Movement & Cameras (Cinemachine) |
| uGUI (includes TextMesh Pro) | com.unity.ugui | 2.0.0 | T10 · Your First Complete Game Loop: States, Scenes, UI, Audio & Builds |
| Cinemachine | com.unity.cinemachine | 3.1.7 | T07 · Input System, Movement & Cameras (Cinemachine) |
| Visual Studio Editor | com.unity.ide.visualstudio | 2.0.29 | this chapter |
| JetBrains Rider Editor | com.unity.ide.rider | 3.1.0 | this chapter |
| Test Framework | com.unity.test-framework | 1.6.0 | T18 · Debugging, Automated Tests & a Git Workflow for Unity |
| Mathematics | com.unity.mathematics | 1.3.3 | T06 · Game Maths: Vectors, Trigonometry, Matrices, Quaternions & Interpolation |
| Splines | com.unity.splines | 2.9.2 | T06 · Game Maths: Vectors, Trigonometry, Matrices, Quaternions & Interpolation |
| AI Navigation | com.unity.ai.navigation | 2.0.16 | T22 · Game AI: NavMesh, Steering, FSM / BT / GOAP / Utility, Perception |
| Timeline | com.unity.timeline | 1.8.13 | T55 · Animation Rigging, Procedural Animation, Timeline & Cinematics |
| ProBuilder | com.unity.probuilder | 6.1.4 | T29 · Level Design: Grey-Boxing, ProBuilder, Flow & Worldbuilding |
If your project shows a slightly newer patch version (2.0.30 instead of 2.0.29, say), that's fine: patches only fix bugs.
Version control#
Version control records the state of your project every time you commit, with a message saying what changed. It gives you four things you'll need sooner than you think:
- Undo for the whole project. Broke everything an hour ago? Go back to the last commit that worked.
- A record of what changed. "The jump felt better last week" becomes a list of exactly what was changed since.
- Safe experiments. Try a risky idea on a branch; keep it or throw it away.
- Backup and sharing. Push to a remote server and the project survives a dead disk, and other people (or your other computer) can work on it.
Git, Unity Version Control or Perforce?#
| Git (+ Git LFS) | Unity Version Control | Perforce (Helix Core) | |
|---|---|---|---|
| What it is | The most widely used version control system, used across the software industry | Unity's own system (formerly Plastic SCM), built into the editor and the Hub | The long-standing standard at large game studios |
| Strengths | Free, everywhere, huge tooling and hosting choice (GitHub, GitLab, Bitbucket…), a skill every programming job expects | Handles big binary files natively; file locking; a simple mode for artists; integrated in Unity | Scales to enormous repositories; strong locking for binary assets |
| Weaknesses | Big binary files need Git LFS; no locking without extra setup | Less common outside Unity; tied to Unity's cloud for hosting | Complex to set up and run; mostly found in larger teams |
| Best for | Solo developers and small teams, and anyone learning | Teams with many artists who prefer to stay inside Unity | Large studios |
The handbook uses Git, because it's free, it's what most courses and jobs expect, and learning it pays off well beyond Unity. Unity Version Control is a good choice too, especially for art-heavy teams. Unity changed its pricing in March 2026 so that cloud seats no longer cost extra and a monthly storage allowance is free; check Unity's pricing page for the current limits.
How Git works, in one picture#
Git was designed for text files, where it stores changes very efficiently. Large binary files (textures, models, audio, video) are different: every version of a 50 MB texture would be stored in full, forever, in every copy of the repository. Git LFS (Large File Storage) fixes this: for the file types you tell it to handle, the commit holds only a small text pointer, and the actual file is stored on the server and downloaded when needed.
Setting up Git for a Unity project#
1. Install Git and Git LFS. On Windows, install Git for Windows, which includes Git LFS. On macOS, the Xcode command line tools include Git; add Git LFS from git-lfs.com or with Homebrew. On Linux, use your package manager for both. You can use Git from the command line, from your IDE (all three IDEs have Git built in), or from a desktop app such as GitHub Desktop, Fork or Sourcetree. The commands below are the same whatever you click.
2. Tell Git who you are, once per computer:
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git lfs install3. Add a .gitignore to the project folder (next to Assets/). It lists what Git must never commit. This one covers Unity's regenerated folders, the IDE files, and operating system clutter:
# Unity regenerates these folders. The leading / means "at the top of the
# repository", which assumes the Unity project is the repository root.
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/
/[Bb]uild/
/[Bb]uilds/
/[Mm]emoryCaptures/
/[Rr]ecordings/
# IDE project files and caches: Unity and the IDE regenerate them
*.csproj
*.sln
*.slnx
*.suo
*.user
*.userprefs
.vs/
.idea/
.vscode/
# Debug symbols and crash reports
*.pdb
*.pdb.meta
*.mdb
*.mdb.meta
*.pidb
*.pidb.meta
sysinfo.txt
crashlytics-build.properties
# Build outputs and exported packages: share these another way
*.apk
*.aab
*.unitypackage
*.unitypackage.meta
# Operating system clutter
.DS_Store
Thumbs.db
desktop.ini4. Add a .gitattributes beside it. It tells Git how to treat each file type: which are text (and with which line endings), and which go to LFS.
# Let Git detect text files and normalise their line endings.
* text=auto
# Unity's YAML files are always text: keep them LF on every OS so
# Windows and Mac users don't rewrite each other's files.
*.unity text eol=lf
*.prefab text eol=lf
*.mat text eol=lf
*.anim text eol=lf
*.controller text eol=lf
*.overrideController text eol=lf
*.physicMaterial text eol=lf
*.meta text eol=lf
*.cs text diff=csharp
# .asset files can be YAML or binary (LightingData.asset is always binary),
# so they're left to the "text=auto" detection above.
# Large binary files go through Git LFS.
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.tif filter=lfs diff=lfs merge=lfs -text
*.exr filter=lfs diff=lfs merge=lfs -text
*.hdr filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.obj filter=lfs diff=lfs merge=lfs -text
*.blend filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.aif filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text5. Create the repository and make the first commit. Close Unity first, so it isn't writing files while Git reads them, or at least save everything with File > Save and File > Save Project.
git init
git add .gitignore .gitattributes
git commit -m "Add Git ignore and attribute rules"
git add .
git status # check the list before committing (see below)
git commit -m "New Unity 6.3 project from the Universal 3D template"Read the git status output before the second commit. You should see Assets/, Packages/, ProjectSettings/ and their contents, every asset with a matching .meta, and nothing from Library/, Temp/, Logs/ or UserSettings/. If anything from those folders appears, fix the .gitignore before committing: removing a big folder from history later is much harder than never adding it.
6. Push to a remote. Create an empty private repository on GitHub, GitLab or another host (don't let it add a README or .gitignore: you have one). Then connect and push:
git remote add origin https://github.com/your-name/FirstSteps.git
git push -u origin mainHosts limit file sizes and LFS storage. GitHub, for example, refuses files over 100 MB in normal Git history, and each host has its own LFS quota. Check your host's limits once your project has real art in it.
Merging scenes and prefabs: UnityYAMLMerge#
When two people change the same scene or prefab, Git has to merge two versions of a YAML file. Git's normal line-by-line merge doesn't understand Unity's format and can produce a broken scene. Unity ships a merge tool that does understand it, UnityYAMLMerge, inside every editor install:
Windows: C:\Program Files\Unity\Hub\Editor\<version>\Editor\Data\Tools\UnityYAMLMerge.exe
macOS: /Applications/Unity/Hub/Editor/<version>/Unity.app/Contents/Helpers/UnityYAMLMergeUnity's manual gives this configuration for using it as Git's merge tool. Put it in the repository's .git/config, or in your global ~/.gitconfig to use it everywhere, with the real path in place of the placeholder:
[merge]
tool = unityyamlmerge
[mergetool "unityyamlmerge"]
trustExitCode = false
cmd = '<path to UnityYAMLMerge>' merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"When a merge reports a conflict in a .unity or .prefab file, run git mergetool and UnityYAMLMerge resolves what it can on its own. Even so, the best merge conflict is the one you avoid: on a team, agree who is editing which scene, and build levels out of prefabs so that people mostly edit separate files.
Everyday habits#
- Commit at the end of every session, and whenever something works that didn't before. Small commits are easy to understand and easy to undo.
- Save in Unity before you commit. Unity keeps unsaved scene changes in memory, and writes some settings only when the project is saved. A commit made with unsaved changes is missing them.
- Write messages that say what and why: "Make jump height depend on how long the button is held", not "stuff" or "fix".
- Use a branch for anything risky: an editor upgrade, a big refactor, an experiment.
git switch -c try-new-camera, work, then merge it or delete it. - Never commit with errors in the Console if you can help it. A commit that doesn't compile is a trap for future you.
The handbook's sandbox project#
Every Unity code sample in the handbook is copied by its build script into a Unity project in the handbook folder, _sandbox/, so that Unity's own compiler checks it. You don't have to use the sandbox (your own practice projects are better for building things), but it's useful to have open:
- It already has the handbook's package baseline installed, so every sample compiles in it without setup.
- Each page's samples are in
Assets/Handbook/<page id>/, in their own assembly, so samples from different pages never clash. - At each phase review, you'll open it and report any red errors in the Console, so they can be fixed in the pages.
To open it, add _sandbox in the Hub with Add > Add project from disk, then follow _sandbox/README.md: turn on the Input System, create and assign a URP asset, and run Handbook > Check Sandbox from the menu bar. This chapter's two scripts are in Assets/Handbook/T01/, and its setup checker adds a Handbook > T01 menu.
Guided build: a setup you can trust#
Now do it all for real, on a fresh project, and finish with proof that every part works. You'll write two scripts: a tiny one that tests the link between Unity and your IDE, and an editor tool that checks the project's settings and Git setup against this chapter and reports what's wrong. Keep the tool: it's useful at the start of every new project.
Step 1 · Install#
If you haven't already: create a Unity account, install the Hub, sign in, add a Personal licence, install Unity 6.3 LTS (6000.3.27f2 or a later 6000.3 patch), and install your IDE with its Unity support (the Visual Studio workload, the VS Code Unity extension, or Rider). Install Git and Git LFS, and run the four git config / git lfs install commands from the Git section.
Step 2 · Create the project#
Create a project called FirstSteps from the Universal 3D template, in your short, unsynced projects folder. Leave Unity Cloud and Unity Version Control off. When it opens, set the Playmode tint and the External Script Editor in Preferences.
Step 3 · A script that tests the IDE link#
In the Project window, create a folder Assets/Scripts. Right-click it and choose Create > Scripting > MonoBehaviour Script (older versions call it Create > C# Script), and name it SetupProbe, exactly, before pressing Enter. The class name inside the file must match the file name, and Unity uses the name you type to write the class.
Double-click it to open it in your IDE, and replace its contents with this. Type it rather than pasting: you're testing autocomplete as you go.
using UnityEngine;
namespace Handbook.T01
{
// Proves the IDE link works: autocomplete, double-click from the Console, and breakpoints.
public class SetupProbe : MonoBehaviour
{
[SerializeField] string greeting = "Hello from Unity";
[SerializeField, Range(1, 5)] int repeat = 3;
[SerializeField, Min(1)] int reportAfterFrames = 120;
int frames;
void Start()
{
for (int i = 1; i <= repeat; i++)
{
Debug.Log($"{greeting} ({i}/{repeat}) · Unity {Application.unityVersion}");
}
}
void Update()
{
frames++;
if (frames == reportAfterFrames)
{
float seconds = Time.time; // put a breakpoint on this line
Debug.Log($"{frames} frames after Play took {seconds:F2} s: about {frames / Mathf.Max(seconds, 0.001f):F0} frames a second");
}
}
}
}You don't need to understand every line yet; T03 · C# Fundamentals: Types, Logic, Methods & Classes and T05 · Scripting in Unity: Lifecycle, Serialization, Coroutines & Awaitable cover all of it. In short: the [SerializeField] fields show up in the Inspector so you can change them without editing code; Start runs once when the game starts and logs a greeting; Update runs every frame, counts frames, and after 120 of them logs how long that took. The namespace line keeps the handbook's class names from clashing with yours.
Save the file and switch back to Unity. It compiles for a moment (a spinner at the bottom right). Then create an empty GameObject (GameObject > Create Empty), name it Probe, and drag SetupProbe from the Project window onto it in the Inspector. Save the scene with Ctrl + S.
Step 4 · Test the three links#
- Autocomplete. In the IDE, on a new line inside
Start, typetransform.and check that a list of members appears (position,rotation…), each with a description. Delete the line again. If no list appears, see the first item in Common mistakes. - Console to code. Press Play. Three greetings appear in the Console (Window > General > Console), then the frame report. Double-click the frame report: the IDE should open
SetupProbe.csat theDebug.Logline. - Breakpoints. Stop Play mode. In the IDE, set a breakpoint on the highlighted line (
float seconds = Time.time;), attach the debugger, accept Debug mode in Unity, and press Play. After 120 frames (about two seconds at 60 frames a second) the game freezes on that line. Hover overframes(it shows 120) and overgreeting, then step once and hover overseconds. Continue, stop debugging, and stop Play mode.
If all three work, your IDE is set up properly, and that's worth more than it sounds: many learners go months with autocomplete half-working without realising it.
Step 5 · An editor tool that checks the setup#
Now a script that runs in the editor rather than in the game. Create a folder Assets/Scripts/Editor and, inside it, a script called ProjectSetupChecker. A folder named Editor is special in Unity: scripts in it can use the UnityEditor namespace and are left out of game builds. (Later you'll do the same thing with assembly definitions, as the handbook's sandbox does.)
Replace its contents with the file below. It's longer than anything you've written so far, but it's made of ten small, similar methods. Read it through once before typing it, using the walk-through after it.
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace Handbook.T01.EditorTools
{
// Handbook > T01 > Check Project Setup: checks the settings this chapter recommends
// and logs one line per check. Fix Project Settings applies the safe fixes.
public static class ProjectSetupChecker
{
const string Menu = "Handbook/T01/";
enum Status { Pass, Info, Warn, Fail }
readonly struct Check
{
public readonly string Name;
public readonly Status Status;
public readonly string Detail;
public Check(string name, Status status, string detail)
{
Name = name;
Status = status;
Detail = detail;
}
}
[MenuItem(Menu + "Check Project Setup")]
static void RunChecks()
{
string repo = FindRepositoryRoot();
var checks = new List<Check>
{
CheckEditorVersion(),
CheckSerialization(),
CheckMetaFiles(),
CheckRenderPipeline(),
CheckColorSpace(),
CheckInputHandling(),
DescribeEnterPlayMode(),
CheckRepository(repo),
CheckGitIgnore(repo),
CheckGitAttributes(repo),
};
Report(checks);
}
static Check CheckEditorVersion()
{
string version = Application.unityVersion; // for example "6000.3.27f2"
return version.StartsWith("6000.3.")
? new Check("Editor", Status.Pass, version + " (Unity 6.3 LTS)")
: new Check("Editor", Status.Warn, version + ": the handbook is written for 6000.3.x (Unity 6.3 LTS)");
}
static Check CheckSerialization()
{
SerializationMode mode = EditorSettings.serializationMode;
return mode == SerializationMode.ForceText
? new Check("Asset serialization", Status.Pass, "Force Text")
: new Check("Asset serialization", Status.Fail, mode + ": use Force Text so Git can diff and merge scenes");
}
static Check CheckMetaFiles()
{
string mode = VersionControlSettings.mode;
return mode == "Visible Meta Files"
? new Check("Version control mode", Status.Pass, mode)
: new Check("Version control mode", Status.Warn, mode + ": with Git, use Visible Meta Files");
}
static Check CheckRenderPipeline()
{
// currentRenderPipeline includes a per-quality-level override, if there is one.
RenderPipelineAsset asset = GraphicsSettings.currentRenderPipeline;
if (asset is UniversalRenderPipelineAsset)
return new Check("Render pipeline", Status.Pass, "URP (" + asset.name + ")");
return asset == null
? new Check("Render pipeline", Status.Fail, "Built-in: assign a URP asset in Project Settings > Graphics")
: new Check("Render pipeline", Status.Warn, asset.GetType().Name + ": the handbook's samples assume URP");
}
static Check CheckColorSpace()
{
return PlayerSettings.colorSpace == ColorSpace.Linear
? new Check("Colour space", Status.Pass, "Linear")
: new Check("Colour space", Status.Warn, "Gamma: URP's lighting is designed for Linear");
}
static Check CheckInputHandling()
{
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
return new Check("Active input handling", Status.Pass, "Input System Package (New)");
#elif ENABLE_INPUT_SYSTEM
return new Check("Active input handling", Status.Warn, "Both: fine while you translate old tutorials, but the handbook uses the Input System");
#else
return new Check("Active input handling", Status.Fail, "Input Manager (Old): switch to Input System Package (New) in Player settings");
#endif
}
static Check DescribeEnterPlayMode()
{
if (!EditorSettings.enterPlayModeOptionsEnabled)
return new Check("Enter Play Mode", Status.Info, "reloads domain and scene (slower, but static fields always reset)");
EnterPlayModeOptions options = EditorSettings.enterPlayModeOptions;
bool domain = (options & EnterPlayModeOptions.DisableDomainReload) == 0;
bool scene = (options & EnterPlayModeOptions.DisableSceneReload) == 0;
string what = domain && scene ? "reloads domain and scene"
: domain ? "reloads the domain only"
: scene ? "reloads the scene only: static fields keep their values between plays"
: "reloads nothing: static fields and scene changes carry over";
return new Check("Enter Play Mode", Status.Info, what);
}
// The repository can be the project folder or any folder above it.
static string FindRepositoryRoot()
{
string project = Directory.GetParent(Application.dataPath).FullName;
for (var dir = new DirectoryInfo(project); dir != null; dir = dir.Parent)
{
string git = Path.Combine(dir.FullName, ".git");
if (Directory.Exists(git) || File.Exists(git)) // .git is a file in worktrees and submodules
return dir.FullName;
}
return null;
}
static Check CheckRepository(string repo)
{
return repo != null
? new Check("Git repository", Status.Pass, repo)
: new Check("Git repository", Status.Fail, "none found: run git init in the project folder");
}
static Check CheckGitIgnore(string repo)
{
if (repo == null)
return new Check(".gitignore", Status.Fail, "no repository yet");
string path = Path.Combine(repo, ".gitignore");
if (!File.Exists(path))
return new Check(".gitignore", Status.Fail, "missing: Library/ and Temp/ would be committed");
string rules = File.ReadAllText(path);
return rules.Contains("[Ll]ibrary") || rules.Contains("Library/")
? new Check(".gitignore", Status.Pass, "ignores Library/")
: new Check(".gitignore", Status.Fail, "doesn't ignore Library/");
}
static Check CheckGitAttributes(string repo)
{
if (repo == null)
return new Check(".gitattributes", Status.Warn, "no repository yet");
string path = Path.Combine(repo, ".gitattributes");
if (!File.Exists(path))
return new Check(".gitattributes", Status.Warn, "missing: binary files won't go through Git LFS");
return File.ReadAllText(path).Contains("filter=lfs")
? new Check(".gitattributes", Status.Pass, "sends binary files through Git LFS")
: new Check(".gitattributes", Status.Warn, "no LFS rules: fine until you add art or audio");
}
static void Report(List<Check> checks)
{
var text = new StringBuilder("Project setup check (T01)\n");
int fails = 0, warns = 0;
foreach (Check check in checks)
{
string mark = check.Status switch
{
Status.Pass => " OK ",
Status.Info => "INFO",
Status.Warn => "WARN",
_ => "FAIL",
};
text.AppendLine($"[{mark}] {check.Name}: {check.Detail}");
if (check.Status == Status.Fail) fails++;
if (check.Status == Status.Warn) warns++;
}
text.Append($"{fails} to fix, {warns} to look at.");
if (fails > 0) Debug.LogError(text.ToString());
else if (warns > 0) Debug.LogWarning(text.ToString());
else Debug.Log(text.ToString());
}
[MenuItem(Menu + "Fix Project Settings")]
static void FixSettings()
{
bool confirmed = EditorUtility.DisplayDialog(
"Fix project settings",
"Set Asset Serialization to Force Text, Version Control to Visible Meta Files and Colour Space to Linear?\n\n" +
"Commit your work first: these can rewrite or reimport many assets.",
"Fix", "Cancel");
if (!confirmed)
return;
EditorSettings.serializationMode = SerializationMode.ForceText;
VersionControlSettings.mode = "Visible Meta Files";
PlayerSettings.colorSpace = ColorSpace.Linear;
AssetDatabase.SaveAssets();
RunChecks();
}
}
}How it works, from the top:
- The
Checkstruct holds one result: a name, a status and a sentence of detail. Every check method returns one, so they can all go in one list and be reported the same way.readonly structmeans aCheckcan't be changed after it's made, which suits a result. [MenuItem(…)]is an attribute: a label Unity reads to add a menu command (here Handbook > T01 > Check Project Setup) that calls the method under it. That's all it takes to add your own menu to the editor. T17 · Editor Scripting & Custom Tools (IMGUI and UI Toolkit) goes much further.- The settings checks each read one setting through Unity's editor API (
EditorSettings,VersionControlSettings,GraphicsSettings,PlayerSettings) and compare it with this chapter's recommendation, using thecondition ? a : boperator to pick a passing or failing result. CheckInputHandlinguses#if, which is decided when the code compiles, not when it runs. Unity definesENABLE_INPUT_SYSTEMandENABLE_LEGACY_INPUT_MANAGERaccording to Active Input Handling, so only one of the threereturnlines is ever compiled.FindRepositoryRootwalks up from the project folder looking for a.gitfolder, because the repository can start above the project (as the handbook's own does). The Git checks then just look for files and text in them.Reportbuilds one message and logs it as an error, a warning or plain information depending on the worst result, so the Console's colour tells you the outcome at a glance.FixSettingsasks before changing anything, withEditorUtility.DisplayDialog, then sets the three settings that are always safe to fix automatically and runs the checks again. It deliberately doesn't touch the render pipeline or input handling: changing those has knock-on effects you should choose yourself.
Step 6 · Run it#
Save, switch to Unity, wait for the compile, then choose Handbook > T01 > Check Project Setup. Since there's no Git repository yet, you should see a red error message something like this (your editor patch, URP asset name and input line may differ):
Project setup check (T01)
[ OK ] Editor: 6000.3.27f2 (Unity 6.3 LTS)
[ OK ] Asset serialization: Force Text
[ OK ] Version control mode: Visible Meta Files
[ OK ] Render pipeline: URP (PC_RPAsset)
[ OK ] Colour space: Linear
[ OK ] Active input handling: Input System Package (New)
[INFO] Enter Play Mode: reloads domain and scene (slower, but static fields always reset)
[FAIL] Git repository: none found: run git init in the project folder
[FAIL] .gitignore: no repository yet
[WARN] .gitattributes: no repository yet
2 to fix, 1 to look at.Fix anything other than the Git lines now. If Active input handling says Both, change it in Player > Other Settings (the editor restarts). Note what the Enter Play Mode line says about your project.
Step 7 · Put it under Git#
Close Unity. In the FirstSteps folder, create .gitignore and .gitattributes with the contents from the Git section, then run the commands from steps 5 and 6 of Setting up Git: git init, commit the two rule files, add everything else, read git status, commit, and push to a new private remote.
Reopen the project and run the checker again. Every line should now be OK or INFO, and the message is white instead of red. Commit once more if Unity changed any files on opening (git status tells you).
Step 8 · Prove it works on a clean machine#
The real test of a setup is whether the project works from the repository alone, with no Library folder and nothing that isn't committed. Clone it to a different folder:
cd C:\Dev
git clone https://github.com/your-name/FirstSteps.git FirstSteps-checkAdd FirstSteps-check in the Hub and open it. It takes as long as a first import, because Unity rebuilds Library. Then open your scene, press Play, and run the checker. If the probe logs its greetings and the checker is all OK, your repository contains everything the project needs. If a script shows as Missing or a material is magenta, something wasn't committed: compare git status in the original folder. Delete the check folder when you're done.
That's the build. You have an editor, an IDE with working autocomplete, Console links and breakpoints, a project whose settings you've checked with your own tool, and a repository that can recreate the project anywhere.
Going further#
File locking for binary assets#
Two people can't meaningfully merge two edits of the same texture or model: one of them loses their work. Git LFS can lock files to prevent this. Mark the file types as lockable in .gitattributes by adding lockable to their LFS line, then lock a file before editing it and unlock it after pushing:
# in .gitattributes
*.psd filter=lfs diff=lfs merge=lfs -text lockable
git lfs lock Assets/Art/Hero.psd
# … edit, commit, push …
git lfs unlock Assets/Art/Hero.psdLockable files are checked out read-only until you lock them, which reminds everyone to ask first. Solo, you don't need this; on a team with artists, you will.
Unity from the command line#
The editor can run without a window, which is how studios build games automatically on a server. The core flags are -batchmode (no window), -projectPath (which project), -executeMethod (call a static method in an editor script, such as a build script) and -quit. Unity Hub 3.18 also added a standalone command-line interface for installing editors and modules from scripts. T50 · Shipping: Build Profiles, Platforms, Web, Steam & Release Checklist uses both to set up automatic builds.
Setting up a team#
- Commit the
.gitignore,.gitattributesand an.editorconfig(code style, T11 · Clean Code, SOLID & a C# Style Guide for Unity) on day one, before anyone commits assets. - Pin everyone to the same editor patch.
ProjectVersion.txtrecords it, and the Hub warns anyone who opens the project with a different one. - Split work so people edit different files: prefabs for anything placed in more than one scene, and several scenes loaded together (T10 · Your First Complete Game Loop: States, Scenes, UI, Audio & Builds) rather than one giant one.
- Write down where things go in
Assets/(one folder per feature, or per asset type) and stick to it. T11 · Clean Code, SOLID & a C# Style Guide for Unity discusses the options.
Faster iteration#
As projects grow, the wait after every script change gets longer, because Unity recompiles and reloads the domain. Two tools keep it short, both covered later: assembly definitions, which split your code into separate assemblies so a change recompiles only the part that changed (T05 · Scripting in Unity: Lifecycle, Serialization, Coroutines & Awaitable), and the Enter Play Mode options from earlier in this chapter, once your code handles static state correctly.
Editor layouts#
Arrange the editor's windows the way you like, then save the arrangement from the Layout drop-down at the top right (or Window > Layouts). Many people keep one layout for building scenes and one for scripting and debugging, with the Console large. It's a small thing that saves a lot of dragging. T02 · The Unity Editor: Scenes, GameObjects, Components & Prefabs tours every window.
Common mistakes & debugging#
Each problem below comes with its symptom, the usual cause, and the fix. Most setup problems are one of these.
IDE problems#
- Autocomplete doesn't know Unity types (
MonoBehaviour,Vector3underlined in red, no suggestions). Cause: the IDE opened the script as a loose file instead of the generated solution. Fix: set External Script Editor to your IDE (not "Open by file extension"), click Regenerate project files, close the IDE, and open a script by double-clicking it in Unity. For VS Code, check that the C# Dev Kit and Unity extensions are installed and that the solution has finished loading. - Double-clicking a script opens Notepad or TextEdit. Cause: no External Script Editor set, or the IDE package is missing. Fix: set the editor in Preferences; check Visual Studio Editor or JetBrains Rider Editor is in Package Manager > In Project.
- Breakpoints are hollow circles and never hit. Cause: the debugger isn't attached, or Unity is in Release mode. Fix: attach from the IDE and accept Debug mode; check the bug icon at the bottom right of the editor.
- An old project has
com.unity.ide.vscodeand VS Code misbehaves. Fix: remove that package and use Visual Studio Editor.
Project problems#
- Magenta materials, "Missing (Mono Script)", or fields that say Missing. Cause: broken GUID references: assets moved or renamed outside Unity, or
.metafiles not committed. Fix: undo the move (Git makes this easy), or move the.metafiles to match. Then re-read the three meta-file rules. (Magenta can also mean a material uses a shader for a different render pipeline; T33 · Rendering in Unity 6: URP & HDRP, Cameras, PBR Materials & Post-Processing covers that case.) - Unity opens in Safe Mode. Cause: the project has compile errors, and Unity won't import assets until they're fixed. Fix: read the errors in Safe Mode's Console, fix the scripts in your IDE, and Unity leaves Safe Mode by itself once everything compiles.
- Imports are very slow, or stuck. Causes: the project is in a cloud-synced folder (OneDrive, Dropbox, iCloud Drive, Google Drive), whose sync client fights Unity over thousands of
Libraryfiles; or antivirus software scanning every file Unity writes. Fix: move the project to an unsynced folder; ask whoever manages your computer about the antivirus. As a last resort, close Unity and deleteLibrary/. - Errors about paths being too long (on Windows). Cause: Windows has a 260-character path limit by default, and paths inside
Library/and packages are long. Fix: keep projects in a short folder near the drive root, such asC:\Dev\FirstSteps. - The Hub says the project was made with a different version. Fix: open it with the version in
ProjectVersion.txtif it's installed. Only upgrade deliberately, on a branch, after committing. - "No valid Unity licence" when opening. Fix: open the Hub, sign in, check Licenses, then reopen.
- Changes made in Play mode vanish when you stop. That's by design: Play mode changes are temporary. Set a Playmode tint so you notice. To keep a value, right-click the component's header and choose Copy Component, stop playing, then Paste Component Values.
Git problems#
- The repository is huge, or pushing takes forever. Cause:
Library/or big binaries were committed without LFS. Fix: if it hasn't been pushed, it's easiest to delete the.gitfolder and start the repository again with the right.gitignoreand.gitattributes. If it has been pushed and shared, look upgit rm -r --cached Library(stops tracking) andgit lfs migrate(moves binaries into LFS, rewriting history), and agree it with everyone first. - Git warns "LF will be replaced by CRLF" on hundreds of files. Cause: line-ending conversion without rules. Fix: the
.gitattributesin this chapter. Commit it, then rungit add --renormalize .once and commit the result. - A merge conflict in a
.unityor.prefabfile. Fix: rungit mergetoolwith UnityYAMLMerge set up. If it can't resolve it, choose one whole side (git checkout --oursor--theirson that file) and redo the other person's change by hand. Never hand-edit conflict markers in a scene file unless you really know the format. - A teammate's scene has missing scripts after they pull. Cause: you committed a script but not its
.meta, so their Unity made a new GUID. Fix: commit the.meta; they pull again.
Exercises#
1 · Find every version
Find and write down: your Hub version; your editor version (two different places); and the versions of URP, the Input System and your IDE package in your project. Compare them with the handbook's baseline table.
Hint
Hub: its settings have an About page. Editor: the Hub's Installs list, and Help > About Unity in the editor. Packages: Package Manager > In Project, or open Packages/manifest.json in a text editor.
Solution
The editor version appears in the Hub's Installs and in Help > About Unity, and also in ProjectSettings/ProjectVersion.txt and in your setup checker's first line. Package versions are in the Package Manager and in manifest.json; the exact resolved ones are in packages-lock.json. A newer patch than the baseline (17.3.1 instead of 17.3.0) is fine. A different minor or major version means you're on a different editor version than the handbook, and you should note it in your journal.
2 · Break the IDE link, then fix it
Set External Script Editor to Open by file extension, close your IDE, and double-click SetupProbe. What happens to autocomplete? Then put everything back and confirm autocomplete works again.
Solution
The script opens without the generated solution (or in a plain text editor), so MonoBehaviour and Debug aren't recognised and there's no Unity autocomplete. To fix: set External Script Editor back to your IDE, click Regenerate project files, close the IDE, and open a script from Unity. Now you know what the broken state looks like, so you'll recognise it instantly when it happens by accident.
3 · Break a reference on purpose
Create a material in Assets/Materials, put it on a cube, and save the scene. Close Unity. In Explorer or Finder, move only the .mat file (not its .mat.meta) into a new folder Assets/Moved. Reopen Unity and look at the cube. Explain what happened, then repair it two ways.
Solution
Unity found a .mat without a meta file in Moved/ and gave it a new GUID, and found an orphaned .meta in Materials/ and deleted it. The scene still refers to the old GUID, so the cube's material slot is now Missing and the cube renders magenta (or with no material).
Repair 1: with Git, git status shows the deleted meta and the new files; restore with git checkout -- . and git clean -fd Assets/Moved (careful: clean deletes untracked files), then move the material inside Unity. Repair 2, without Git: drag the material onto the cube again. The first repair restores everything that referred to it; the second only fixes the references you know about, which is why Git matters.
4 · Follow a GUID
Open SetupProbe.cs.meta in a text editor and copy its GUID. Then open your scene file (.unity) in a text editor, search for that GUID, and find the values you set on the component in the Inspector. Change repeat in the Inspector, save the scene, and see the file change.
Solution
The scene has a MonoBehaviour block whose m_Script line contains the script's GUID, followed by greeting, repeat and reportAfterFrames with their saved values. After changing repeat and saving, that one line changes; git diff shows exactly that. This is why Force Text matters: you can read, compare and merge scene changes like code.
5 · Add and remove a package two ways
Add the Splines package (com.unity.splines) to FirstSteps using + > Install package by name. Look at what changed in manifest.json and packages-lock.json with git diff. Remove it with the Package Manager, then add it again by editing manifest.json by hand. Finally, remove it and check that git status is clean.
Solution
Installing adds one line to manifest.json ("com.unity.splines": "2.9.2" or similar) and a block to packages-lock.json that records the exact version, its source and its dependencies. Editing the manifest by hand and switching back to Unity has the same effect: Unity notices, resolves, and updates the lock file. Removing it either way reverts both files. The lesson: the manifest is the source of truth, and the Package Manager window is just a friendly editor for it.
6 · Clean up a bad first commit
A learner ran git add . without a .gitignore, and git status shows this. List everything wrong, and the commands to fix it before committing.
Changes to be committed:
new file: Assets/Art/hero.png
new file: Assets/Scenes/Level1.unity
new file: Assets/Scenes/Level1.unity.meta
new file: Assets/Scripts/Player.cs
new file: FirstSteps.sln
new file: Library/ArtifactDB
new file: Logs/AssetImportWorker0.log
new file: Packages/manifest.json
new file: ProjectSettings/ProjectVersion.txt
new file: UserSettings/EditorUserSettings.assetSolution
Wrong: FirstSteps.sln, Library/, Logs/ and UserSettings/ should be ignored. hero.png and Player.cs have no .meta files staged (they were probably added outside Unity while it was closed, so Unity hasn't made them yet). packages-lock.json is missing, and there's no .gitattributes, so hero.png would go into normal history instead of LFS.
Fix: unstage everything with git reset; add the .gitignore and .gitattributes from this chapter; open the project in Unity once so it creates the missing .meta files and the lock file; close it; then git add .gitignore .gitattributes, commit, git add ., and check git status again before the second commit.
7 · Teach the checker a new check
Add a check to ProjectSetupChecker that warns if the project folder is inside a cloud-synced folder (its path contains OneDrive, Dropbox, CloudDocs or Google Drive) or if the path is longer than 80 characters, and passes otherwise.
Hint
Copy the pattern of the other checks: a static method that returns a Check. Get the path the same way FindRepositoryRoot does, and add your method to the list in RunChecks.
Solution
static Check CheckProjectPath()
{
string project = Directory.GetParent(Application.dataPath).FullName;
string[] synced = { "OneDrive", "Dropbox", "CloudDocs", "Google Drive" };
foreach (string name in synced)
{
if (project.IndexOf(name, System.StringComparison.OrdinalIgnoreCase) >= 0)
return new Check("Project path", Status.Warn, $"inside {name}: cloud sync and Unity's Library folder don't mix");
}
return project.Length > 80
? new Check("Project path", Status.Warn, $"{project.Length} characters: keep it short to stay clear of path-length limits")
: new Check("Project path", Status.Pass, project);
}Then add CheckProjectPath(), to the list in RunChecks. OrdinalIgnoreCase makes the match ignore capital letters, since folder names on Windows and macOS do too. (iCloud Drive's folder on disk is called com~apple~CloudDocs, hence CloudDocs.)
8 · Survive a scene merge conflict
On a branch, move the Probe object and change its greeting; commit. Switch back to main, change the same object's greeting to something else and add a cube; commit. Merge the branch into main, resolve the conflict with UnityYAMLMerge, and check the result in Unity. Write down in your journal what UnityYAMLMerge resolved by itself and what it asked you about.
Notes on an approach
Use git switch -c probe-edit for the branch, and git merge probe-edit on main. The merge reports a conflict in the scene. With the mergetool configured, git mergetool runs UnityYAMLMerge. It should merge the move and the added cube automatically, because they touch different properties and objects, and leave only the two greeting values in conflict, which it resolves by picking one or by asking, depending on its settings. Open the scene in Unity: the object is moved, the cube exists, and greeting has one of the two values. Commit the merge. If anything looks wrong, git merge --abort before committing takes you back to where you started, which is exactly why you try this on a practice project first.
Check yourself#
Which folder can you delete (with Unity closed) without losing any work?
Assets/Library/ProjectSettings/Packages/
Library/ is a cache that Unity rebuilds from the other three. Deleting it costs a full reimport, nothing more.
What's the most important thing a .meta file holds?
- A thumbnail of the asset
- The asset's file size
- The asset's GUID, which other assets use to refer to it
- The date the asset was imported
References between assets use GUIDs, not paths. The meta file also holds the import settings.
You rename a texture in Explorer, leaving its .meta file with the old name. What happens?
- Unity treats it as a new asset with a new GUID, so everything that used the texture loses it
- Unity notices the rename and keeps every reference
- Nothing until you make a build
- The texture is deleted
Without its meta file, the renamed texture gets a new GUID, and the old meta is removed as orphaned. Rename inside Unity, which renames both.
Which Unity package connects Visual Studio Code to Unity 6?
com.unity.ide.vscodecom.unity.ide.rider- No package is needed
com.unity.ide.visualstudio(Visual Studio Editor), 2.0.20 or later
The old com.unity.ide.vscode package is unsupported. The Visual Studio Editor package serves both Visual Studio and VS Code.
Why should Asset Serialization be set to Force Text?
- It makes builds smaller
- Scenes and prefabs are saved as readable text, so Git can show and merge changes to them
- It's required for URP
- It makes the editor faster
Binary scenes can't be compared or merged. Force Text is the default for new projects.
You open a 6.3 project in Unity 6.6. What should you have done first?
- Nothing: minor versions are compatible both ways
- Deleted
ProjectSettings/ - Committed everything, ideally on a branch, because the upgrade rewrites files and can't be undone from inside Unity
- Uninstalled 6.3
Opening with a newer minor or major version upgrades the project. Commit first so you can go back, and upgrade on a branch.
What is packages-lock.json for?
- It records the exact versions Unity resolved, including dependencies, so every machine gets the same ones
- It stops packages being updated
- It's a cache you should never commit
- It lists packages you've bought on the Asset Store
The manifest says what you asked for; the lock file says exactly what you got. Commit both, and don't edit the lock file by hand.
What does Git LFS do?
- Compresses the whole repository
- Locks every file automatically
- Makes Git merge binary files
- Stores large binary files apart from the history, with small pointers in the commits
Without LFS, every version of every big file stays in the history forever, in every clone. LFS can also lock files, but only ones you mark as lockable.
You switch Enter Play Mode to skip the domain reload. What changes?
- Scenes stop reloading
- Entering Play mode is faster, but static fields keep their values from the previous play
- Scripts stop compiling
- Debugging stops working
Domain reload is what resets static state. Without it, code has to reset its own statics, which T05 · Scripting in Unity: Lifecycle, Serialization, Coroutines & Awaitable shows how to do.
Which of these is stored in the project and shared through version control?
- Your Preferences, such as the Playmode tint
- Your window layout
- Project Settings, such as Active Input Handling
- The External Script Editor setting
Project Settings live in ProjectSettings/ and are committed. Preferences and layouts belong to you and your machine.
Cheat sheet#
Setup checklist
- Unity account + Personal licence
- Hub → Unity 6.3 LTS (6000.3.x)
- IDE + its Unity support
- Universal 3D project, short unsynced path
- Playmode tint, External Script Editor
- Git + LFS, .gitignore, .gitattributes
- Commit, push, clone to test
Commit / don't commit
- Yes:
Assets/(+ every .meta),Packages/manifest.json,packages-lock.json,ProjectSettings/ - No:
Library/,Temp/,obj/,Logs/,UserSettings/,*.sln,*.csproj, IDE folders, builds
Meta-file rules
- Move, rename, delete inside Unity
- Outside Unity: move the .meta too
- Commit .meta with its asset
- Duplicate with Ctrl/Cmd + D
- Magenta / Missing = broken GUID (usually)
Settings
- Editor › Asset Serialization: Force Text
- Version Control › Visible Meta Files
- Player › Active Input Handling: Input System
- Player › Color Space: Linear
- Editor › Enter Play Mode: know what yours does
IDE first aid
- External Tools › External Script Editor
- Regenerate project files
- Close IDE, open a script from Unity
- VS Code: C#, C# Dev Kit, Unity extensions
- Breakpoints: attach + Debug mode
Git every day
- Save in Unity first
git status·git add .·git commit -m "…"git push·git pullgit switch -c ideafor experimentsgit mergetool→ UnityYAMLMerge
Courses that cover this#
Glossary#
- Unity Hub
- The launcher that installs editor versions and modules, lists your projects and manages your licence.
- Module
- An optional part of an editor install: platform build support, offline documentation, or a language pack.
- LTS
- Long-term support: an editor release that gets two years of fixes and no new features. Unity 6.3 is the current one.
- Template
- A starting project with settings and packages already set up, such as Universal 3D.
- Render pipeline
- The system that draws the scene. Unity 6 has URP, HDRP and the legacy Built-in pipeline; the handbook uses URP.
- Asset
- Any file in
Assets/that Unity imports: a scene, script, texture, model, sound, material or prefab. - .meta file
- The file Unity keeps next to every asset, holding its GUID and import settings. Always committed with the asset.
- GUID
- The 32-character ID in a meta file. Assets refer to each other by GUID, not by path, so they can be moved and renamed inside Unity safely.
- Library folder
- Unity's cache of imported assets and compiled code. Rebuilt from the project's other folders; never committed.
- Force Text
- The Asset Serialization mode that saves scenes and prefabs as readable YAML text, so version control can compare and merge them.
- Package
- A versioned bundle of code and assets a project chooses to use, listed in
Packages/manifest.json. - Lock file
packages-lock.json: the exact package versions Unity resolved, including dependencies.- IDE
- Integrated development environment: the program you write code in (Visual Studio, VS Code, Rider).
- Solution and project files
- The
.slnand.csprojfiles Unity generates so your IDE understands the project's code. Never committed. - Breakpoint
- A marker on a line of code where an attached debugger pauses the running game, so you can inspect variables.
- Domain reload
- Unity unloading and reloading all C# code, which resets static fields. Happens after compiling and, depending on settings, when entering Play mode.
- Commit
- A saved snapshot of the project in version control, with a message saying what changed.
- Remote
- A copy of the repository on a server (GitHub, GitLab…), used for backup and sharing.
- .gitignore
- The file listing paths Git must never track, such as
Library/. - .gitattributes
- The file telling Git how to handle each file type: text and line endings, or Git LFS.
- Git LFS
- Large File Storage: keeps big binary files out of Git's history, storing pointers in commits and the files on the server.
- UnityYAMLMerge
- Unity's merge tool for scene and prefab files, shipped with every editor and usable as Git's mergetool.