Unity Mastery Handbook
Topic chapterT01P0 · Orientation

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
—
After this chapter you can…
  • 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 .meta files 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.
Unity Hub installs, opens, licences Unity Editor 6000.3.x + modules (Android, Web…) installs IDE Visual Studio · VS Code · Rider .sln / .csproj debugger attaches Project folder Assets/ Packages/ ProjectSettings/ Library/ Temp/ Logs/ UserSettings/ (rebuilt) opens one version Unity registry packages Git repository history of the blue folders Remote (GitHub…) backup, sharing · LFS store push blue: committed grey: rebuilt by Unity, not committed
How the pieces fit. Only the blue folders go into version control; Unity rebuilds everything else from them.

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.

PlanWho it's forCost
Unity PersonalIndividuals, hobbyists and small organisations with less than US$200,000 of revenue and funding in the last 12 monthsFree
Unity StudentEligible students, verified through Unity's education programmeFree
Unity Pro and EnterpriseCompanies above the Personal limit, or teams that need their extra services and supportPaid, per seat

For learning, Personal is all you need. Every feature this handbook teaches works with it.

Activating a licence#

  1. Create a Unity account at unity.com (top right, sign in, then create one), or sign in with an existing one.
  2. Install Unity Hub (next section) and sign in to it with the same account.
  3. 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:

WindowsmacOSLinux
OSWindows 10 21H1 (build 19043) or newer on x64; Windows 11 21H2 or newer on Arm64macOS 13 Ventura or newerUbuntu 22.04 or 24.04
CPUx64 with SSE2, or Arm64Intel x64 with SSE2, or Apple M1 and laterx64 with SSE2
GraphicsDirectX 10, 11, 12 or Vulkan capable GPUMetal-capable GPUOpenGL 3.2+ or Vulkan, Nvidia or AMD
OtherOfficial vendor driversRosetta 2 installed, on Apple silicon MacsGNOME 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#

6000 . 3 . 27 f 2 Unity 6 major 6.3 minor patch 27 bug fixes final a = alpha b = beta build 2 of that patch
Unity 6 versions start with 6000. Within one minor version (6.3), a higher patch number is the same features with more bugs fixed.

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:

ModuleAdd it whenNotes
Microsoft Visual Studio Community (Windows)Now, if you'll use Visual StudioInstalls Visual Studio with the Unity workload. Skip it for VS Code or Rider.
DocumentationNow, if you often work offlineA local copy of the Manual and Scripting API.
Windows / Mac / Linux Build Support (IL2CPP)When you build for your own desktop with IL2CPPT50 · Shipping: Build Profiles, Platforms, Web, Steam & Release Checklist explains Mono versus IL2CPP.
Web Build SupportWhen 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 SupportWhen you reach mobile (T47 · Mobile: Touch Input, Builds, Optimisation & Store Publishing)Tick its sub-modules too: Android SDK & NDK Tools and OpenJDK.
iOS Build SupportWhen you reach mobileThe 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:

  1. Commit everything to Git first (or copy the folder, if you're not using Git yet).
  2. Upgrade on a branch, open the project, and fix any errors and warnings.
  3. 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.

PathWhat's in itIn 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.jsonThe packages the project uses, and their versionsYes
Packages/packages-lock.jsonThe exact versions Unity resolved, including dependencies of dependenciesYes
Packages/<name>/An embedded package: one you've copied into the project to change itYes
ProjectSettings/Every Project Settings page (input, physics, tags and layers, quality, graphics, player), and ProjectVersion.txtYes
UserSettings/Your personal editor state for this project: window layout, last open scene, search settingsNo
Library/Unity's cache: imported versions of your assets, compiled scripts, downloaded packagesNo
Temp/, obj/, Logs/Temporary build files and editor logsNo
*.sln, *.csprojProject files for your IDE, generated by Unity from your scripts and packagesNo
.vs/, .idea/, .vscode/Your IDE's own caches and settingsUsually 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:

Packages/manifest.json (excerpt)JSON
{
  "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:

ProjectSettings/ProjectVersion.txtYAML
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:

  1. A GUID (globally unique identifier): a random 32-character ID that is the asset's real identity inside Unity.
  2. 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.

Crate.png Crate.png.meta guid: a1b2… max size, compression… Importer reads the file and its import settings Library/ GPU-ready texture for the current platform (a cache) Crate.mat _BaseMap: {guid: a1b2…} Warehouse.unity m_Materials: [{guid: c3d4…}] refers to it by GUID, not by path
References between assets use GUIDs from the meta files. The texture can move or be renamed in Unity and the material still finds it. Lose the meta file and the GUID is gone with it.

Here's what that looks like in the files themselves. A script's meta file is short:

Assets/Scripts/SetupProbe.cs.metaYAML
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:

Assets/Scenes/Sandbox.unity (excerpt)YAML
--- !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:

  1. Move, rename and delete assets inside Unity's Project window, not in Explorer or Finder. Unity moves the meta file with the asset.
  2. If you must move files outside Unity (with the editor closed), move each .meta file with its asset.
  3. Always commit .meta files, in the same commit as their assets. A teammate who gets Crate.png without Crate.png.meta gets 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#

SettingWhereValue for this handbookWhy
Asset Serialization ModeEditorForce Text (the default)Scenes and prefabs saved as readable text, so Git can show and merge changes
Version Control ModeVersion ControlVisible Meta Files (the default)Meta files are ordinary files that Git can see and commit
Active Input HandlingPlayer > Other SettingsInput System Package (New)The handbook uses the Input System (T07 · Input System, Movement & Cameras (Cinemachine))
Color SpacePlayer > Other SettingsLinear (the URP template default)Lighting maths comes out physically correct (T33 · Rendering in Unity 6: URP & HDRP, Cameras, PBR Materials & Post-Processing)
Render pipeline assetGraphics, and per level in QualityA URP assetSet 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 static field 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 StudioVisual Studio CodeJetBrains Rider
Runs onWindowsWindows, macOS, LinuxWindows, macOS, Linux
CostCommunity edition free for individuals; check Microsoft's licence for company useFree editor; the C# Dev Kit extension follows Visual Studio Community's licence termsFree for non-commercial use; paid licence for commercial work
Unity packageVisual Studio Editor (com.unity.ide.visualstudio)Visual Studio Editor, 2.0.20 or laterJetBrains Rider Editor (com.unity.ide.rider)
Also needsThe Game development with Unity workloadThree extensions: C#, C# Dev Kit, and UnityNothing: Unity support is built in
Good atMature debugger; Unity's default on WindowsLight and fast; identical on every OS; Unity's default on macOSThe 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)#

  1. 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.
  2. In Unity, open Preferences > External Tools and set External Script Editor to your Visual Studio version.
  3. Double-click any script in the Project window. Visual Studio opens with the whole solution.

Setting up Visual Studio Code#

  1. Install VS Code from code.visualstudio.com.
  2. 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.
  3. 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.)
  4. In Preferences > External Tools, set External Script Editor to Visual Studio Code.
  5. 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#

  1. 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.
  2. In Unity, check that JetBrains Rider Editor is in Package Manager > In Project.
  3. In Preferences > External Tools, set External Script Editor to Rider.
  4. 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.

  1. In your IDE, click in the margin next to a line of a script to set a breakpoint (a red dot).
  2. 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.
  3. 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.)
  4. 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.
  5. 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.
manifest.json: a Git package pinned to a tagJSON
"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.

PackageNameVersionFirst used in
Universal Render Pipelinecom.unity.render-pipelines.universal17.3.0every chapter
Input Systemcom.unity.inputsystem1.20.1T07 · Input System, Movement & Cameras (Cinemachine)
uGUI (includes TextMesh Pro)com.unity.ugui2.0.0T10 · Your First Complete Game Loop: States, Scenes, UI, Audio & Builds
Cinemachinecom.unity.cinemachine3.1.7T07 · Input System, Movement & Cameras (Cinemachine)
Visual Studio Editorcom.unity.ide.visualstudio2.0.29this chapter
JetBrains Rider Editorcom.unity.ide.rider3.1.0this chapter
Test Frameworkcom.unity.test-framework1.6.0T18 · Debugging, Automated Tests & a Git Workflow for Unity
Mathematicscom.unity.mathematics1.3.3T06 · Game Maths: Vectors, Trigonometry, Matrices, Quaternions & Interpolation
Splinescom.unity.splines2.9.2T06 · Game Maths: Vectors, Trigonometry, Matrices, Quaternions & Interpolation
AI Navigationcom.unity.ai.navigation2.0.16T22 · Game AI: NavMesh, Steering, FSM / BT / GOAP / Utility, Perception
Timelinecom.unity.timeline1.8.13T55 · Animation Rigging, Procedural Animation, Timeline & Cinematics
ProBuildercom.unity.probuilder6.1.4T29 · 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 ControlPerforce (Helix Core)
What it isThe most widely used version control system, used across the software industryUnity's own system (formerly Plastic SCM), built into the editor and the HubThe long-standing standard at large game studios
StrengthsFree, everywhere, huge tooling and hosting choice (GitHub, GitLab, Bitbucket…), a skill every programming job expectsHandles big binary files natively; file locking; a simple mode for artists; integrated in UnityScales to enormous repositories; strong locking for binary assets
WeaknessesBig binary files need Git LFS; no locking without extra setupLess common outside Unity; tied to Unity's cloud for hostingComplex to set up and run; mostly found in larger teams
Best forSolo developers and small teams, and anyone learningTeams with many artists who prefer to stay inside UnityLarge 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#

Working folder your project files Staging area the next commit Local repository .git/ · every commit Remote GitHub, GitLab… add commit push pull / clone Git LFS commits hold small pointers; big files are kept apart
You choose what goes into each commit by staging it. Commits live on your machine until you push them. Git LFS keeps large binary files out of the commit history itself.

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:

Terminal (once per computer)Shell
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git lfs install

3. 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:

.gitignoreText
# 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.ini

4. 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.

.gitattributesText
# 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 -text

5. 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.

Terminal (in the project folder)Shell
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:

TerminalShell
git remote add origin https://github.com/your-name/FirstSteps.git
git push -u origin main

Hosts 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:

Where UnityYAMLMerge isText
Windows: C:\Program Files\Unity\Hub\Editor\<version>\Editor\Data\Tools\UnityYAMLMerge.exe
macOS:   /Applications/Unity/Hub/Editor/<version>/Unity.app/Contents/Helpers/UnityYAMLMerge

Unity'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:

.gitconfigText
[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.

SetupProbe.csUnity sandboxC#
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#

  1. Autocomplete. In the IDE, on a new line inside Start, type transform. 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.
  2. 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.cs at the Debug.Log line.
  3. 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 over frames (it shows 120) and over greeting, then step once and hover over seconds. 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.

Editor/ProjectSetupChecker.csUnity sandboxC#
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 Check struct 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 struct means a Check can'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 the condition ? a : b operator to pick a passing or failing result.
  • CheckInputHandling uses #if, which is decided when the code compiles, not when it runs. Unity defines ENABLE_INPUT_SYSTEM and ENABLE_LEGACY_INPUT_MANAGER according to Active Input Handling, so only one of the three return lines is ever compiled.
  • FindRepositoryRoot walks up from the project folder looking for a .git folder, 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.
  • Report builds 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.
  • FixSettings asks before changing anything, with EditorUtility.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):

ConsoleText
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:

TerminalShell
cd C:\Dev
git clone https://github.com/your-name/FirstSteps.git FirstSteps-check

Add 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:

.gitattributes line, then the terminalShell
# 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.psd

Lockable 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, .gitattributes and 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.txt records 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, Vector3 underlined 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.vscode and 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 .meta files not committed. Fix: undo the move (Git makes this easy), or move the .meta files 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 Library files; 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 delete Library/.
  • 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 as C:\Dev\FirstSteps.
  • The Hub says the project was made with a different version. Fix: open it with the version in ProjectVersion.txt if 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 .git folder and start the repository again with the right .gitignore and .gitattributes. If it has been pushed and shared, look up git rm -r --cached Library (stops tracking) and git 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 .gitattributes in this chapter. Commit it, then run git add --renormalize . once and commit the result.
  • A merge conflict in a .unity or .prefab file. Fix: run git mergetool with UnityYAMLMerge set up. If it can't resolve it, choose one whole side (git checkout --ours or --theirs on 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.

git statusText
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.asset
Solution

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
Add to ProjectSetupCheckerC#
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?

  1. Assets/
  2. Library/
  3. ProjectSettings/
  4. 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?

  1. A thumbnail of the asset
  2. The asset's file size
  3. The asset's GUID, which other assets use to refer to it
  4. 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?

  1. Unity treats it as a new asset with a new GUID, so everything that used the texture loses it
  2. Unity notices the rename and keeps every reference
  3. Nothing until you make a build
  4. 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?

  1. com.unity.ide.vscode
  2. com.unity.ide.rider
  3. No package is needed
  4. 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?

  1. It makes builds smaller
  2. Scenes and prefabs are saved as readable text, so Git can show and merge changes to them
  3. It's required for URP
  4. 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?

  1. Nothing: minor versions are compatible both ways
  2. Deleted ProjectSettings/
  3. Committed everything, ideally on a branch, because the upgrade rewrites files and can't be undone from inside Unity
  4. 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?

  1. It records the exact versions Unity resolved, including dependencies, so every machine gets the same ones
  2. It stops packages being updated
  3. It's a cache you should never commit
  4. 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?

  1. Compresses the whole repository
  2. Locks every file automatically
  3. Makes Git merge binary files
  4. 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?

  1. Scenes stop reloading
  2. Entering Play mode is faster, but static fields keep their values from the previous play
  3. Scripts stop compiling
  4. 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?

  1. Your Preferences, such as the Playmode tint
  2. Your window layout
  3. Project Settings, such as Active Input Handling
  4. 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

  1. Unity account + Personal licence
  2. Hub → Unity 6.3 LTS (6000.3.x)
  3. IDE + its Unity support
  4. Universal 3D project, short unsynced path
  5. Playmode tint, External Script Editor
  6. Git + LFS, .gitignore, .gitattributes
  7. 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 pull
  • git switch -c idea for experiments
  • git mergetool → UnityYAMLMerge

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 .sln and .csproj files 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.