# 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`:
```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
```mermaid
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**:
```bash
# 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):
1. Delete in Project window
2. Unity removes both files
3. `git status` shows deletions
4. `git add -A` to stage deletions
5. Commit
**Manual deletion**:
```bash
# 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
```bash
# 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**:
1. **Use Smart Merge** (Unity's tool)
2. **Or choose one version** (theirs or yours)
3. **Or recreate changes manually**
**Unity Smart Merge** (UnityYAMLMerge):
```bash
# 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
```csharp
using UnityEngine;
using UnityEngine.InputSystem; // External
using System.Collections.Generic; // System
namespace MyGame.Player // Namespace (optional but recommended)
{
///
/// Controls player movement and input.
///
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 `public` fields (use `[SerializeField] private` instead)
- Call `GetComponent` in Update
- Use `GameObject.Find` or `GameObject.FindWithTag` in Update
- Leave unused variables (CS0414 warning)
- Mix initialization code in Update
### Type Hints
Always use type hints:
```csharp
// ✓ Good
private Rigidbody rb;
private PlayerController playerController;
private List enemies;
// ❌ Bad (no type hints)
private var rb; // Not valid C#, but illustrates point
```
---
## Performance Best Practices
### General Optimization
**Object Pooling**:
```csharp
// Instead of Instantiate/Destroy in loops
public class BulletPool : MonoBehaviour
{
[SerializeField] private GameObject bulletPrefab;
private Queue pool = new Queue();
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**:
```csharp
// ❌ Bad - GetComponent every frame
void Update()
{
GetComponent().linearVelocity = Vector3.forward;
}
// ✓ Good - Cache in Awake
private Rigidbody rb;
void Awake()
{
rb = GetComponent();
}
void Update()
{
rb.linearVelocity = Vector3.forward;
}
```
**Avoid Find in Update**:
```csharp
// ❌ 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**:
```csharp
using NUnit.Framework;
using UnityEngine;
public class PlayerTests
{
[Test]
public void PlayerTakesDamage()
{
var player = new GameObject().AddComponent();
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
1. **Backup project** (entire folder + git commit)
2. **Read release notes** (API changes, breaking changes)
3. **Test in new version** (create copy first!)
4. **Fix deprecation warnings**
5. **Test thoroughly** (all features, all platforms)
6. **Update packages** (check compatibility)
7. **Commit changes** (separate from feature work)
### When Switching Render Pipeline
1. **Backup project**
2. **Install new pipeline package**
3. **Create pipeline asset**
4. **Upgrade materials** (automatic tool)
5. **Fix pink materials** (manual shader changes)
6. **Reconfigure lighting**
7. **Test performance**
8. **Fix post-processing** (different systems)
---
## Additional Resources
- [Unity Manual - Project Setup](https://docs.unity3d.com/Manual/CreatingProjects.html)
- [Unity Manual - Version Control](https://docs.unity3d.com/Manual/Versioncontrol.html)
- [Git for Unity](https://thoughtbot.com/blog/how-to-git-with-unity)
- [Unity Best Practices](https://unity.com/how-to/programming-unity)
- [Performance Optimization](https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity6.html)