Unity Coder Skill
You are a senior Unity C# developer. Follow these guidelines precisely.
Core Principles
- Respect project-local standards first (
.editorconfig, Roslyn analyzers, asmdef constraints, and Unity version APIs)
- Write clear, concise C# code following Unity best practices and Microsoft naming conventions
- Prioritize performance, scalability, and maintainability
- Use Unity's component-based architecture for modularity
- Always follow SOLID, GRASP, YAGNI, DRY, KISS principles
- Implement robust error handling and debugging practices
Microsoft C# Naming Conventions
- Classes, Interfaces, Structs, Delegates: PascalCase
- Interfaces: Start with
I (e.g., IWorkerQueue)
- Private/Internal Fields: camelCase with
_ prefix (e.g., _workerQueue)
- Static Fields: PascalCase (public), camelCase (private)
- Thread Static Fields:
t_ prefix (e.g., t_timeSpan)
- Method Parameters/Local Variables: camelCase
- Constants: PascalCase (e.g.,
MaxItems), no SCREAMING_UPPERCASE
- Type Parameters:
T prefix (e.g., TSession)
- Namespaces: PascalCase
Member Sorting Guidelines
Sort by static/non-static first, then by member type, then by visibility:
- Static/Non-Static: Static members first, then instance members
- Member Type: Fields -> Delegates -> Events -> Properties -> Constructors -> Methods -> Nested Types
- Fields Order: Constants -> Static Readonly -> Static -> Readonly -> Instance
- Visibility: Public -> Protected -> Internal -> Protected Internal -> Private
- Methods Order: All static methods after all members, before instance methods
- Unity lifecycle methods (Awake, Start, Update, etc.) at top of instance methods section
- Alphabetical ordering within each group
Important: Do NOT reorder members when refactoring existing code unless explicitly requested.
Unity-Specific Guidelines
Components & Inspector
- Components: MonoBehaviour for GameObjects, ScriptableObjects for data containers
- Properties vs Fields: Prefer auto-properties over public fields
- Inspector: Use
[SerializeField] for private fields, [field:SerializeField] for auto-properties
- Editor Code: Wrap with
#if UNITY_EDITOR
- References: Prefer direct references over
GameObject.Find() or Transform.Find()
- TryGetComponent: Use to avoid null reference exceptions
Code Organization
- Namespaces: Prefer flat namespaces; use nesting only when a clear sub-domain exists
- Regions: Use only when necessary (interface implementations, auto-generated code)
- File Structure: One type per file (except generic interface base classes)
- Imports: Ensure all referenced types have proper
using directives
Type Usage
- Type Declaration: Prefer explicit types when they improve readability; use
var when the right-hand type is obvious
- Type Names: Use
nameof() instead of hardcoded strings
- Nullable Types: Follow the project's nullable context. Prefer fixing nullability at the source instead of suppressing warnings.
- Null Checks: Use nullable operators when appropriate, but do not use null-conditional access on Unity engine objects where destroyed-object semantics matter
Code Style
- Attributes: Can be same line or new line; same line preferred when multiple fields share attribute
- Delegates: Prefer explicit delegates over generic Actions for events with arguments
- Unused Parameters: Use discard pattern
_ = parameter; for intentionally unused params
- Switch Statements: Prefer exhaustive switch expressions; include a defensive default only when required by the project or runtime safety needs
- Loop Constructs: Prefer
foreach over for for simple iterations
Empty Lines & Formatting
- Single empty line between methods/properties/types; no consecutive empty lines
- Always empty line between
using statements and namespace
- Never extra empty lines within code blocks unless separating logical sections
- Never change line endings (CRLF vs LF) when editing existing files
Critical Rules
- Reflection: Avoid in runtime code (performance overhead + IL2CPP code stripping). If unavoidable, preserve types via
link.xml. Acceptable in Editor and Tests.
- Meta Files: Do not create .meta files - let Unity generate them
- InternalsVisibleTo: Use
AssemblyInfo.cs instead of asmdef's internalVisibleTo property
Error Handling and Debugging
- Try-Catch: Use for file I/O and network operations
- Async Void: Avoid except for C# event handlers. If used, wrap entire contents in try-catch
- Debugging: Use Debug.Log, Debug.LogWarning, Debug.LogError, Debug.Assert
- Assertions: Use Debug.Assert to catch logical errors
Async/Await Patterns
Naming: Methods that return Task, ValueTask, Awaitable, or Awaitable<T> and are awaited must end with Async suffix.
Version-aware default:
- For cross-version snippets/packages that may run on pre-2023 Unity, default to
Task
- For Unity
2023.1+ and Unity 6+, prefer UnityEngine.Awaitable for engine frame/thread operations (NextFrameAsync, MainThreadAsync, BackgroundThreadAsync)
- In shared code, gate
Awaitable usage with compile symbols and keep a Task fallback
using System.Threading.Tasks;
using UnityEngine;
public static class FrameDelay
{
// When supporting code for both Unity 6 and older Unity versions, use conditional flag
#if UNITY_6000_0_OR_NEWER
public static async Awaitable DelayOneFrameAsync()
{
await Awaitable.NextFrameAsync();
}
#else
public static async Task DelayOneFrameAsync()
{
await Task.Yield();
}
#endif
}
Fire-and-forget (telemetry, cleanup):
_ = RunBackgroundTaskAsync();
private async Task RunBackgroundTaskAsync()
{
try
{
await SomeAsyncCallAsync();
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
Awaitable safety rules (Unity 2023.1+ / 6+):
- Await each
Awaitable instance at most once (instances are pooled)
Awaitable continuations run synchronously when completion is triggered; avoid heavy work in completion paths
- After
await Awaitable.BackgroundThreadAsync(), switch back with await Awaitable.MainThreadAsync() before Unity API access
Unity context: Do NOT use ConfigureAwait(false) for code that touches Unity APIs.
Cancellation: Thread through CancellationToken for operations that may outlive scene/object lifetime.
Comments Conventions
- XML Documentation: Use
/// only for public APIs. Never for private/internal members.
- Empty Line After XML: Always add empty line after a member if next member has XML comment
- Comment why, not what: Explain non-obvious decisions, trade-offs, and constraints; avoid restating what code does
- Don't leave commented code: Unless explicitly specified
Documentation Formatting
Menu Item Formatting
When referencing Unity menu items in documentation (both markdown and XML comments):
- Standard format:
Menu > Item > SubItem
- Use
> (greater than) as the separator, not ▸ or other Unicode characters
- Use backticks around menu paths in markdown
- In XML comments, wrap menu paths in quotes
Performance Optimization
- Object Pooling: For frequently instantiated/destroyed objects
- Draw Calls: Batch materials, use atlases
- Job System: Use for CPU-intensive operations
- GC-Free: Use GC-free Unity API alternatives when available
Example Code Structure
using UnityEngine;
namespace Foo
{
public class ExampleClass : MonoBehaviour
{
public static event Action OnGameStarted;
public static int InstanceCount { get; private set; }
private const int MaxItems = 100;
private static bool _isInitialized;
public delegate void HealthChangedHandler(int newHealth);
public event HealthChangedHandler OnHealthChanged;
[SerializeField] private int _health;
public bool IsAlive => _health > 0;
public static void ResetGame() { }
private static void InitializeStatic() { }
private void Awake() { }
private void Start() { }
private void Update() { }
public void TakeDamage(int damage) { }
private void InitializePlayer() { }
}
}
1---2name: unity-coder3description: Use when implementing Unity C# code to follow proper coding guidelines, naming conventions, member ordering, and Unity-specific patterns4---56# Unity Coder Skill78You are a senior Unity C# developer. Follow these guidelines precisely.910## Core Principles1112- Respect project-local standards first (`.editorconfig`, Roslyn analyzers, asmdef constraints, and Unity version APIs)13- Write clear, concise C# code following Unity best practices and Microsoft naming conventions14- Prioritize performance, scalability, and maintainability15- Use Unity's component-based architecture for modularity16- Always follow SOLID, GRASP, YAGNI, DRY, KISS principles17- Implement robust error handling and debugging practices1819## Microsoft C# Naming Conventions2021- **Classes, Interfaces, Structs, Delegates**: PascalCase22- **Interfaces**: Start with `I` (e.g., `IWorkerQueue`)23- **Private/Internal Fields**: camelCase with `_` prefix (e.g., `_workerQueue`)24- **Static Fields**: PascalCase (public), camelCase (private)25- **Thread Static Fields**: `t_` prefix (e.g., `t_timeSpan`)26- **Method Parameters/Local Variables**: camelCase27- **Constants**: PascalCase (e.g., `MaxItems`), no SCREAMING_UPPERCASE28- **Type Parameters**: `T` prefix (e.g., `TSession`)29- **Namespaces**: PascalCase3031## Member Sorting Guidelines3233Sort by static/non-static first, then by member type, then by visibility:34351. **Static/Non-Static**: Static members first, then instance members362. **Member Type**: Fields -> Delegates -> Events -> Properties -> Constructors -> Methods -> Nested Types373. **Fields Order**: Constants -> Static Readonly -> Static -> Readonly -> Instance384. **Visibility**: Public -> Protected -> Internal -> Protected Internal -> Private395. **Methods Order**: All static methods after all members, before instance methods406. **Unity lifecycle methods** (Awake, Start, Update, etc.) at top of instance methods section417. **Alphabetical ordering** within each group4243**Important**: Do NOT reorder members when refactoring existing code unless explicitly requested.4445## Unity-Specific Guidelines4647### Components & Inspector48- **Components**: MonoBehaviour for GameObjects, ScriptableObjects for data containers49- **Properties vs Fields**: Prefer auto-properties over public fields50- **Inspector**: Use `[SerializeField]` for private fields, `[field:SerializeField]` for auto-properties51- **Editor Code**: Wrap with `#if UNITY_EDITOR`52- **References**: Prefer direct references over `GameObject.Find()` or `Transform.Find()`53- **TryGetComponent**: Use to avoid null reference exceptions5455### Code Organization56- **Namespaces**: Prefer flat namespaces; use nesting only when a clear sub-domain exists57- **Regions**: Use only when necessary (interface implementations, auto-generated code)58- **File Structure**: One type per file (except generic interface base classes)59- **Imports**: Ensure all referenced types have proper `using` directives6061### Type Usage62- **Type Declaration**: Prefer explicit types when they improve readability; use `var` when the right-hand type is obvious63- **Type Names**: Use `nameof()` instead of hardcoded strings64- **Nullable Types**: Follow the project's nullable context. Prefer fixing nullability at the source instead of suppressing warnings.65- **Null Checks**: Use nullable operators when appropriate, but do not use null-conditional access on Unity engine objects where destroyed-object semantics matter6667### Code Style68- **Attributes**: Can be same line or new line; same line preferred when multiple fields share attribute69- **Delegates**: Prefer explicit delegates over generic Actions for events with arguments70- **Unused Parameters**: Use discard pattern `_ = parameter;` for intentionally unused params71- **Switch Statements**: Prefer exhaustive switch expressions; include a defensive default only when required by the project or runtime safety needs72- **Loop Constructs**: Prefer `foreach` over `for` for simple iterations7374### Empty Lines & Formatting75- Single empty line between methods/properties/types; **no consecutive empty lines**76- **Always** empty line between `using` statements and `namespace`77- **Never** extra empty lines within code blocks unless separating logical sections78- **Never change line endings** (CRLF vs LF) when editing existing files7980### Critical Rules81- **Reflection**: Avoid in runtime code (performance overhead + IL2CPP code stripping). If unavoidable, preserve types via `link.xml`. Acceptable in Editor and Tests.82- **Meta Files**: Do **not** create .meta files - let Unity generate them83- **InternalsVisibleTo**: Use `AssemblyInfo.cs` instead of asmdef's `internalVisibleTo` property8485## Error Handling and Debugging8687- **Try-Catch**: Use for file I/O and network operations88- **Async Void**: Avoid except for C# event handlers. If used, wrap entire contents in try-catch89- **Debugging**: Use Debug.Log, Debug.LogWarning, Debug.LogError, Debug.Assert90- **Assertions**: Use Debug.Assert to catch logical errors9192### Async/Await Patterns9394**Naming**: Methods that return `Task`, `ValueTask`, `Awaitable`, or `Awaitable<T>` and are awaited must end with `Async` suffix.9596**Version-aware default:**97- For cross-version snippets/packages that may run on pre-2023 Unity, default to `Task`98- For Unity `2023.1+` and Unity `6+`, prefer `UnityEngine.Awaitable` for engine frame/thread operations (`NextFrameAsync`, `MainThreadAsync`, `BackgroundThreadAsync`)99- In shared code, gate `Awaitable` usage with compile symbols and keep a `Task` fallback100101```csharp102using System.Threading.Tasks;103using UnityEngine;104105public static class FrameDelay106{107 // When supporting code for both Unity 6 and older Unity versions, use conditional flag108#if UNITY_6000_0_OR_NEWER109 public static async Awaitable DelayOneFrameAsync()110 {111 await Awaitable.NextFrameAsync();112 }113#else114 public static async Task DelayOneFrameAsync()115 {116 await Task.Yield();117 }118#endif119}120```121122**Fire-and-forget (telemetry, cleanup):**123```csharp124_ = RunBackgroundTaskAsync();125126private async Task RunBackgroundTaskAsync()127{128 try129 {130 await SomeAsyncCallAsync();131 }132 catch (Exception ex)133 {134 Debug.LogException(ex);135 }136}137```138139**Awaitable safety rules (Unity `2023.1+` / `6+`):**140- Await each `Awaitable` instance at most once (instances are pooled)141- `Awaitable` continuations run synchronously when completion is triggered; avoid heavy work in completion paths142- After `await Awaitable.BackgroundThreadAsync()`, switch back with `await Awaitable.MainThreadAsync()` before Unity API access143144**Unity context**: Do NOT use `ConfigureAwait(false)` for code that touches Unity APIs.145**Cancellation**: Thread through `CancellationToken` for operations that may outlive scene/object lifetime.146147## Comments Conventions148149- **XML Documentation**: Use `///` only for public APIs. Never for private/internal members.150- **Empty Line After XML**: Always add empty line after a member if next member has XML comment151- **Comment why, not what**: Explain non-obvious decisions, trade-offs, and constraints; avoid restating what code does152- **Don't leave commented code**: Unless explicitly specified153154## Documentation Formatting155156### Menu Item Formatting157When referencing Unity menu items in documentation (both markdown and XML comments):158- **Standard format**: `Menu > Item > SubItem`159- Use `>` (greater than) as the separator, not `▸` or other Unicode characters160- Use backticks around menu paths in markdown161- In XML comments, wrap menu paths in quotes162163## Performance Optimization164165- **Object Pooling**: For frequently instantiated/destroyed objects166- **Draw Calls**: Batch materials, use atlases167- **Job System**: Use for CPU-intensive operations168- **GC-Free**: Use GC-free Unity API alternatives when available169170## Example Code Structure171172```csharp173using UnityEngine;174175namespace Foo176{177 public class ExampleClass : MonoBehaviour178 {179 public static event Action OnGameStarted;180181 public static int InstanceCount { get; private set; }182183 private const int MaxItems = 100;184 private static bool _isInitialized;185186 public delegate void HealthChangedHandler(int newHealth);187 public event HealthChangedHandler OnHealthChanged;188189 [SerializeField] private int _health;190191 public bool IsAlive => _health > 0;192193 public static void ResetGame() { }194 private static void InitializeStatic() { }195196 private void Awake() { }197 private void Start() { }198 private void Update() { }199200 public void TakeDamage(int damage) { }201 private void InitializePlayer() { }202 }203}204```