From unity-plugin
Provides Unity C# fundamental patterns like TryGetComponent, SerializeField, RequireComponent, null safety, and lifecycle management for safe, maintainable code. Use for any Unity C# development.
npx claudepluginhub creator-hian/claude-code-plugins --plugin unity-pluginThis skill uses the workspace's default tool permissions.
Core Unity C# patterns for safe, maintainable code. Not optimizations but **fundamental practices**.
Provides Unity C# scripting patterns covering MonoBehaviour lifecycle, coroutines, async/await, physics APIs, raycasts, collisions, animations, NavMesh, pooling, singletons, ECS, Jobs, and Burst.
Validates Unity C# scripts for best practices, performance patterns like component caching and StringBuilder use, and Unity conventions. Use when reviewing or checking code quality.
Unity 6 C# scripting guide. Use when writing MonoBehaviour scripts, handling lifecycle events (Awake, Start, Update, FixedUpdate), using coroutines or async/await (Awaitable), working with ScriptableObjects, events, delegates, or core APIs like Vector3, Quaternion, Time, Debug. Based on Unity 6.3 LTS documentation.
Share bugs, ideas, or general feedback.
Core Unity C# patterns for safe, maintainable code. Not optimizations but fundamental practices.
Foundation Required: C# basics, Unity MonoBehaviour lifecycle
Core Topics: TryGetComponent, SerializeField, RequireComponent, Null-safe patterns, Lifecycle management
Always use TryGetComponent instead of GetComponent:
// ❌ WRONG
Rigidbody rb = GetComponent<Rigidbody>();
rb.velocity = Vector3.zero; // NullReferenceException!
// ✅ CORRECT
Rigidbody rb;
if (TryGetComponent(out rb))
{
rb.velocity = Vector3.zero;
}
// ✅ Cache in Awake with validation
private Rigidbody mRb;
void Awake()
{
if (!TryGetComponent(out mRb))
{
Debug.LogError($"Missing Rigidbody on {gameObject.name}", this);
}
}
// ❌ OBSOLETE - DON'T USE
GameManager manager = FindObjectOfType<GameManager>();
// ✅ CORRECT - Fastest (unordered)
GameManager manager = FindAnyObjectByType<GameManager>();
// ✅ CORRECT - Ordered
GameManager manager = FindFirstObjectByType<GameManager>();
// ✅ Multiple objects
Enemy[] enemies = FindObjectsByType<Enemy>(FindObjectsSortMode.None);
// ❌ WRONG: Public field
public float speed;
// ✅ CORRECT: SerializeField + private
[SerializeField] private float mSpeed = 5f;
// ✅ With Inspector helpers
[SerializeField, Tooltip("Units/second"), Range(0f, 100f)]
private float mMoveSpeed = 5f;
public float Speed => mSpeed; // Read-only access
[RequireComponent(typeof(Rigidbody))]
[DisallowMultipleComponent]
public class PhysicsObject : MonoBehaviour
{
private Rigidbody mRb;
void Awake()
{
TryGetComponent(out mRb); // Guaranteed to exist
}
}
// ❌ WRONG: C# null operators don't work with Unity Objects
Transform target = mCached ?? FindTarget(); // Broken!
mEnemy?.TakeDamage(10); // May fail after Destroy
// ✅ CORRECT: Explicit null check
Transform target = mCached != null ? mCached : FindTarget();
if (mEnemy != null)
{
mEnemy.TakeDamage(10);
}
void Awake() { /* 1. Self-init, cache components */ }
void OnEnable() { /* 2. Subscribe events */ }
void Start() { /* 3. Cross-object init */ }
void OnDisable() { /* 4. Unsubscribe events */ }
void OnDestroy() { /* 5. Final cleanup */ }
Important: Unity's Mono/IL2CPP runtime lacks
IsExternalInit.initaccessor causes compile error CS0518.
// ❌ COMPILE ERROR in Unity
public string Name { get; private init; }
// ✅ Use private set
public string Name { get; private set; }
// ✅ Or readonly field + property
private readonly string mName;
public string Name => mName;
Available: Pattern matching, switch expressions, covariant returns
NOT Available: init, required (C# 11)
| Pattern | Rule |
|---|---|
| Component access | Always TryGetComponent, never bare GetComponent |
| Serialization | [SerializeField] private, not public |
| Dependencies | Use [RequireComponent] for guaranteed components |
| Null checks | Explicit != null, not ?? or ?. |
| Caching | Get in Awake, reuse everywhere |
| Events | Subscribe in OnEnable, unsubscribe in OnDisable |
| Global search | FindAnyObjectByType (fastest), not FindObjectOfType |
init accessor alternatives with code examplesrequired modifier alternatives