Unity project conventions — folder layout, asmdef structure, naming, source control (.gitignore + LFS + force text serialization), prefab variants, ScriptableObject-based architecture. USE WHEN: starting a new Unity project, refactoring an unhealthy one, setting up source control, organising assets, deciding on naming. DO NOT USE FOR: specific systems (use feature-specific skills).
Scanned 9/8/2026
Install to Claude Code
npx -y skills add claude-dev-suite/claude-dev-suite --skill unity-best-practices --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Unity Best Practices?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/claude-dev-suite-unity-best-practices)More formats (shields.io, HTML) on the badges page.
---
name: unity-best-practices
description: |
Unity project conventions — folder layout, asmdef structure, naming, source
control (.gitignore + LFS + force text serialization), prefab variants,
ScriptableObject-based architecture.
USE WHEN: starting a new Unity project, refactoring an unhealthy one,
setting up source control, organising assets, deciding on naming.
DO NOT USE FOR: specific systems (use feature-specific skills).
allowed-tools: Read, Grep, Glob, Write, Edit
---
# Unity Best Practices
## Folder layout (recommended)
```
Assets/
_Project/ ← all our content (lead underscore sorts first)
Art/
Models/ Sprites/ Materials/ Shaders/ Animations/
Audio/
Music/ SFX/
Prefabs/
Characters/ Enemies/ UI/ VFX/
ScriptableObjects/
WeaponData/ EnemyConfig/ EventChannels/
Scenes/
Boot.unity Main.unity ...
Scripts/
Runtime.asmdef (game logic)
Editor.asmdef (editor tools)
Tests.asmdef (tests, Test Assemblies)
Settings/ (Render Pipeline asset, Volume profiles, Input Actions)
Plugins/ ← third-party packages (DOTween, etc.)
StreamingAssets/ ← runtime-readable raw files
ProjectSettings/ ← committed
Packages/ ← committed (manifest.json + lock)
```
`_Project/` keeps your content separated from imported packages.
## Asmdef structure
Three asmdefs minimum:
1. **Runtime** — game code, no `UnityEditor` references.
2. **Editor** — depends on Runtime + UnityEditor, Include Platforms = Editor only.
3. **Tests** — depends on Runtime (and Editor for editor tests), Test Assemblies flag enabled.
Bigger projects: feature-level asmdefs (`Combat.Runtime`, `Inventory.Runtime`) — speeds up incremental compile dramatically.
## Naming
| Element | Convention |
|---|---|
| Type / class | PascalCase |
| Method / property | PascalCase |
| Public field (rare) | PascalCase |
| Private field | `_camelCase` |
| Const | UPPER_SNAKE |
| Asset (prefab, SO) | PascalCase, no spaces |
| Folder | PascalCase |
## Inspector exposure
```csharp
// Wrong
public WeaponData weapon;
// Right
[SerializeField] private WeaponData weapon;
public WeaponData Weapon => weapon;
```
`[SerializeField] private` keeps API surface minimal while still letting designers wire references.
## Source control
`.gitignore`:
```
Library/
Temp/
Logs/
Build/
UserSettings/
.vs/
.vscode/
*.csproj
*.sln
sysinfo.txt
[Mm]emoryCaptures/
[Bb]uilds/
```
`.gitattributes` (Git LFS):
```
*.psd filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.unity -text merge=unityyamlmerge
*.prefab -text merge=unityyamlmerge
*.asset -text merge=unityyamlmerge
*.mat -text merge=unityyamlmerge
*.anim -text merge=unityyamlmerge
```
Editor settings: **Force Text** asset serialization, **Visible Meta Files** mode (default in Unity 6).
## Prefab Variants
When you need variants of a base (Enemy → Enemy_Boss with bigger HP and a unique mesh): right-click prefab → **Create > Prefab Variant**. Override only the differing fields; everything else cascades from the base.
## ScriptableObject-driven architecture
Decouple via SO event channels:
```csharp
[CreateAssetMenu(menuName = "Events/Int Event")]
public class IntEventChannel : ScriptableObject {
public event Action<int> OnRaised;
public void Raise(int value) => OnRaised?.Invoke(value);
}
```
Enemy raises a `playerDamaged` channel; HUD listens to it. No direct reference between systems → trivial to test, easy to swap.
## Anti-patterns
| Anti-pattern | Fix |
|---|---|
| Singleton tangle (every system has `Instance`) | SO event channels + dependency injection in Awake |
| Massive Manager classes | Split by responsibility; thin orchestrator + dedicated systems |
| Unstructured `Assets/` (everything in root) | Adopt the layout above |
| Relying on Build Settings scene order at runtime | Load via Addressables or explicit `SceneManager.LoadScene(name)` with constants |
| Public fields everywhere | `[SerializeField] private` + properties |
## Production checklist
- [ ] `_Project/` content separated from packages
- [ ] Three asmdefs minimum (Runtime / Editor / Tests)
- [ ] `.gitignore` + `.gitattributes` (LFS) committed
- [ ] Force Text serialization on
- [ ] Naming conventions enforced (lint via Roslyn analyzer if possible)
- [ ] No singletons in new code; SO event channels for cross-system events
- [ ] Boot scene loads required systems explicitly (no auto-Find)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!