13 KiB
Unity Project Setup & Best Practices
Overview
Proper project setup prevents most common Unity errors. This guide covers initial configuration, workflows, and best practices for Unity projects with version control.
Project Setup Checklist
Before Starting Development
1. Choose Render Pipeline
- Built-in: Legacy, simple projects
- URP: Modern, cross-platform (recommended)
- HDRP: High-end PC/console only
If using URP:
- Install URP package
- Create URP asset
- Assign in Graphics settings
- Verify renderer configured
→ See reference/urp-setup.md for details
2. Configure Input System
- Legacy (0): Old projects only
- New (1): New projects with new system
- Both (2): Migration or compatibility (recommended)
Location: Edit → Project Settings → Player → Active Input Handling
Restart Unity after changing!
→ See reference/input-systems.md for details
3. Set Up Version Control
Git Configuration:
Create .gitignore:
# Unity generated
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/
# Asset meta files MUST be tracked
# (Do NOT ignore *.meta files!)
# Visual Studio / VSCode
.vs/
.vscode/
*.csproj
*.unityproj
*.sln
*.suo
*.user
*.userprefs
*.pidb
*.booproj
# OS
.DS_Store
Thumbs.db
Critical: Keep .meta files in version control!
→ See reference/guid-system.md for details
4. Configure Editor Settings
Edit → Preferences:
- External Script Editor: Set to VSCode/Rider/Visual Studio
- Error Pause: Enable for debugging
- Auto Refresh: Enable
Edit → Project Settings → Editor:
- Asset Serialization Mode: Force Text (better for git)
- Version Control Mode: Visible Meta Files
5. Set Up Quality Settings
Edit → Project Settings → Quality:
- Configure quality levels for platforms
- Set shadow settings
- Configure anti-aliasing
- Assign URP asset (if using URP)
6. Configure Physics
Edit → Project Settings → Physics:
- Set up Layer Collision Matrix
- Configure default solver iterations
- Set Fixed Timestep (default: 0.02 = 50Hz)
7. Set Up Layers
Edit → Project Settings → Tags and Layers:
Common layer structure:
Layer 0: Default
Layer 6: Player
Layer 7: Enemy
Layer 8: Bullet
Layer 9: Environment
Layer 10: UI
Layer 11: Ignore Raycast
Configure collision matrix: Physics → Layer Collision Matrix
Folder Structure
Recommended Organization
Assets/
├── Scenes/
│ ├── MainMenu.unity
│ └── Gameplay.unity
├── Scripts/
│ ├── Player/
│ ├── Enemy/
│ ├── UI/
│ └── Utilities/
├── Prefabs/
│ ├── Characters/
│ ├── Props/
│ └── UI/
├── Materials/
├── Textures/
├── Models/
├── Audio/
│ ├── Music/
│ └── SFX/
├── Animations/
├── Resources/ # For Resources.Load()
│ └── Data/
└── Settings/
└── URP/ (or HDRP/)
Best Practices
✅ Do:
- Organize by feature or content type
- Keep folder names clear and consistent
- Use Resources folder sparingly
- Separate art assets from code
❌ Don't:
- Mix unrelated assets in one folder
- Create deep nested hierarchies (>5 levels)
- Put everything in root Assets folder
Git Workflows
Adding New Assets
graph TD
A[Create Asset in Unity] --> B[Unity generates .meta]
B --> C[git status - verify both files]
C --> D[git add Asset + Asset.meta]
D --> E[git commit]
Commands:
# Check what's new
git status
# Stage both asset and meta
git add Assets/NewScript.cs
git add Assets/NewScript.cs.meta
# Or stage all new files
git add Assets/
# Commit
git commit -m "Add NewScript"
Deleting Assets
Via Unity Editor (Recommended):
- Delete in Project window
- Unity removes both files
git statusshows deletionsgit add -Ato stage deletions- Commit
Manual deletion:
# Delete both files
rm Assets/Scripts/OldScript.cs
rm Assets/Scripts/OldScript.cs.meta
# Stage deletions
git add -A
# Or explicitly
git rm Assets/Scripts/OldScript.cs
git rm Assets/Scripts/OldScript.cs.meta
# Commit
git commit -m "Remove OldScript"
Checking for Issues
# Find orphaned .meta files
for meta in Assets/**/*.meta; do
[ -f "${meta%.meta}" ] || echo "Orphaned: $meta"
done
# Find assets without .meta files
find Assets -type f ! -name "*.meta" ! -path "*/.*" | while read file; do
[ -f "$file.meta" ] || echo "Missing .meta: $file"
done
# Check for untracked .meta files
git status | grep "\.meta$"
Merge Conflicts
Scene/Prefab Conflicts
Unity scenes/prefabs are YAML but complex. Merge conflicts can break them.
Strategy:
- Use Smart Merge (Unity's tool)
- Or choose one version (theirs or yours)
- Or recreate changes manually
Unity Smart Merge (UnityYAMLMerge):
# Configure git to use Unity's merge tool
# In .git/config or ~/.gitconfig:
[merge]
tool = unityyamlmerge
[mergetool "unityyamlmerge"]
trustExitCode = false
cmd = 'C:\\Program Files\\Unity\\Editor\\Data\\Tools\\UnityYAMLMerge.exe' merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"
Alternative: Use a staging/prefab variant workflow to avoid conflicts
Script Conflicts
Standard git merge works fine for .cs files. Resolve conflicts manually.
Asset Naming Conventions
Files and Assets
| Asset Type | Convention | Example |
|---|---|---|
| Scripts | PascalCase | PlayerController.cs |
| Prefabs | PascalCase | EnemyGoblin.prefab |
| Scenes | PascalCase | MainMenu.unity |
| Materials | PascalCase_Mat | Metal_Mat.mat |
| Textures | PascalCase_Tex | Brick_Diffuse_Tex.png |
| Audio | PascalCase | Explosion_01.wav |
| Animations | PascalCase_Anim | Walk_Anim.anim |
GameObjects in Scene
- Descriptive names:
Player,MainCamera,EnemySpawner - Hierarchy markers: Use empty GameObjects as folders
- Consistent naming: Follow team conventions
Code Quality Practices
Script Organization
using UnityEngine;
using UnityEngine.InputSystem; // External
using System.Collections.Generic; // System
namespace MyGame.Player // Namespace (optional but recommended)
{
/// <summary>
/// Controls player movement and input.
/// </summary>
public class PlayerController : MonoBehaviour
{
#region Serialized Fields
[Header("Movement")]
[SerializeField] private float speed = 5f;
[SerializeField] private float jumpForce = 10f;
[Header("References")]
[SerializeField] private Rigidbody rb;
#endregion
#region Private Fields
private bool isGrounded;
private Vector3 moveDirection;
#endregion
#region Unity Callbacks
private void Awake()
{
// Component caching
}
private void Start()
{
// Initialization
}
private void Update()
{
// Input and per-frame logic
}
private void FixedUpdate()
{
// Physics logic
}
#endregion
#region Public Methods
public void TakeDamage(int damage)
{
// Public interface
}
#endregion
#region Private Methods
private void HandleMovement()
{
// Internal logic
}
#endregion
}
}
Best Practices
✅ Do:
- Use
[SerializeField]instead of public fields - Add
[Header]for organization in Inspector - Cache component references in Awake/Start
- Use descriptive variable names
- Add XML comments for public APIs
- Use regions to organize code (optional)
❌ Don't:
- Use
publicfields (use[SerializeField] privateinstead) - Call
GetComponentin Update - Use
GameObject.FindorGameObject.FindWithTagin Update - Leave unused variables (CS0414 warning)
- Mix initialization code in Update
Type Hints
Always use type hints:
// ✓ Good
private Rigidbody rb;
private PlayerController playerController;
private List<Enemy> enemies;
// ❌ Bad (no type hints)
private var rb; // Not valid C#, but illustrates point
Performance Best Practices
General Optimization
Object Pooling:
// Instead of Instantiate/Destroy in loops
public class BulletPool : MonoBehaviour
{
[SerializeField] private GameObject bulletPrefab;
private Queue<GameObject> pool = new Queue<GameObject>();
public GameObject GetBullet()
{
if (pool.Count > 0)
{
GameObject bullet = pool.Dequeue();
bullet.SetActive(true);
return bullet;
}
return Instantiate(bulletPrefab);
}
public void ReturnBullet(GameObject bullet)
{
bullet.SetActive(false);
pool.Enqueue(bullet);
}
}
Cache References:
// ❌ Bad - GetComponent every frame
void Update()
{
GetComponent<Rigidbody>().linearVelocity = Vector3.forward;
}
// ✓ Good - Cache in Awake
private Rigidbody rb;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
rb.linearVelocity = Vector3.forward;
}
Avoid Find in Update:
// ❌ Bad - Find every frame
void Update()
{
GameObject player = GameObject.FindWithTag("Player");
}
// ✓ Good - Find once
private GameObject player;
void Start()
{
player = GameObject.FindWithTag("Player");
}
Physics Optimization
- Use simple colliders (box, sphere, capsule)
- Use triggers instead of collisions when possible
- Configure layer collision matrix
- Set objects to sleep when stationary
- Use continuous collision detection only when needed
→ See reference/physics-system.md for details
Rendering Optimization
- Enable Static Batching for static objects
- Use GPU Instancing on materials
- Use LOD (Level of Detail) for complex models
- Limit real-time lights
- Use texture compression
- Optimize shadow settings
→ See reference/urp-setup.md for details
Testing and Debugging
Unity Test Framework
Install Test Framework:
- Window → Package Manager → Test Framework → Install
Create Tests:
using NUnit.Framework;
using UnityEngine;
public class PlayerTests
{
[Test]
public void PlayerTakesDamage()
{
var player = new GameObject().AddComponent<Player>();
player.TakeDamage(10);
Assert.AreEqual(90, player.Health);
}
}
Profiling
Window → Analysis → Profiler
Monitor:
- CPU usage
- Rendering time
- Physics time
- Memory allocations
- GC (Garbage Collection)
Build Settings
Platform-Specific Settings
Android:
- API Level: 24+ (Android 7.0+)
- Scripting Backend: IL2CPP (better performance)
- Target Architectures: ARMv7 + ARM64
iOS:
- Target SDK: Latest
- Architecture: ARM64
- Camera Usage Description: Required for camera access
PC (Windows/Mac/Linux):
- API Compatibility: .NET Standard 2.1
- Scripting Backend: Mono (faster builds) or IL2CPP (better runtime)
Development vs Release
Development Build:
- Enable for testing
- Shows profiler stats
- Allows debugger attachment
Release Build:
- Disable development build
- Enable optimization
- Strip debug symbols
Project Maintenance
Regular Tasks
Weekly:
- Run tests
- Check for Unity updates
- Review Console warnings
- Update packages (carefully!)
Monthly:
- Clean unused assets
- Optimize texture sizes
- Review performance in Profiler
- Backup project
Before Release:
- Fix ALL warnings
- Remove debug code
- Test on target devices
- Verify build size
- Test loading times
- Profile performance
Migration Checklist
When Upgrading Unity Version
- Backup project (entire folder + git commit)
- Read release notes (API changes, breaking changes)
- Test in new version (create copy first!)
- Fix deprecation warnings
- Test thoroughly (all features, all platforms)
- Update packages (check compatibility)
- Commit changes (separate from feature work)
When Switching Render Pipeline
- Backup project
- Install new pipeline package
- Create pipeline asset
- Upgrade materials (automatic tool)
- Fix pink materials (manual shader changes)
- Reconfigure lighting
- Test performance
- Fix post-processing (different systems)