Loading...
Loading...
C# scripting in Unity for gameplay, behavior, and engine integration. PROACTIVELY activate for: (1) writing Unity C# scripts, (2) MonoBehaviour lifecycle (Awake/OnEnable/Start/Update/FixedUpdate/LateUpdate), (3) coroutines and async/await in Unity, (4) delegates, events, Action/Func patterns, (5) ScriptableObject creation and serialization, (6) GetComponent / TryGetComponent and component caching, (7) physics scripting (Rigidbody, raycast, collision/trigger callbacks), (8) animation scripting (Animator parameters, state machines, IK), (9) NavMesh and NavMeshAgent scripting, (10) input handling (Input System package), (11) custom serialization and SerializeField. Provides: lifecycle reference, coroutine vs async patterns, ScriptableObject templates, Rigidbody/collision recipes, NavMesh examples, and Input System setup.
npx skill4agent add josiahsiegel/claude-plugin-marketplace unity-csharp-scriptingAwake() -> OnEnable() -> Start() -> FixedUpdate() -> Update() -> LateUpdate() -> OnDisable() -> OnDestroy()| Method | When Called | Use For |
|---|---|---|
| Once, when object instantiates (before Start) | Self-initialization, caching references |
| Each time object becomes active | Subscribe to events, reset state |
| Once, before first Update (after all Awake) | Cross-object initialization |
| Fixed timestep (default 0.02s) | Physics, Rigidbody movement |
| Every frame | Input, non-physics logic |
| After all Update calls | Camera follow, post-movement adjustments |
| When object deactivates | Unsubscribe events, save state |
| When object is destroyed | Final cleanup, resource release |
[DefaultExecutionOrder(N)]OnValidate()IEnumerator SpawnWaves(int count, float delay)
{
for (int i = 0; i < count; i++)
{
SpawnEnemy();
yield return new WaitForSeconds(delay);
}
}
// Start: Coroutine handle = StartCoroutine(SpawnWaves(5, 1f));
// Stop: StopCoroutine(handle); or StopAllCoroutines();| Yield Instruction | Behavior |
|---|---|
| Wait one frame |
| Wait t seconds (affected by timeScale) |
| Unscaled time |
| Wait for next FixedUpdate |
| After rendering |
| Wait until predicate is true |
| Wait for nested coroutine |
Awaitableasync Awaitable LoadLevelAsync(string sceneName)
{
await Awaitable.WaitForSecondsAsync(1f);
var op = SceneManager.LoadSceneAsync(sceneName);
while (!op.isDone)
{
progressBar.value = op.progress;
await Awaitable.NextFrameAsync();
}
}// Publisher
public class Health : MonoBehaviour
{
public event System.Action<float> OnDamaged; // event keyword prevents external invocation
public event System.Action OnDeath;
public void TakeDamage(float amount)
{
currentHealth -= amount;
OnDamaged?.Invoke(amount);
if (currentHealth <= 0) OnDeath?.Invoke();
}
}
// Subscriber
void OnEnable() => health.OnDamaged += HandleDamage;
void OnDisable() => health.OnDamaged -= HandleDamage;
void HandleDamage(float amount) => /* react */;GameEventGameEventListenerreferences/design-patterns.md| Task | Method | Where |
|---|---|---|
| Continuous force | | FixedUpdate |
| Instant impulse | | FixedUpdate |
| Direct velocity | | FixedUpdate |
| Kinematic move | | FixedUpdate |
| Rotation | | FixedUpdate |
Rigidbody.velocityRigidbody.linearVelocityif (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask))
{
Debug.Log($"Hit {hit.collider.name} at {hit.point}");
}
// Use Physics.RaycastAll or Physics.RaycastNonAlloc for multiple hits
// 2D: Physics2D.Raycast, Physics2D.OverlapCircle, etc.| Callback | Requires | Use For |
|---|---|---|
| Both have colliders, at least one Rigidbody, isTrigger=false | Physical impacts |
| One collider has isTrigger=true, at least one Rigidbody | Zones, pickups, detection |
CompareTag("Enemy")other.tag == "Enemy"[RequireComponent(typeof(Animator))]
public class CharacterAnimation : MonoBehaviour
{
static readonly int SpeedHash = Animator.StringToHash("Speed");
static readonly int JumpTrigger = Animator.StringToHash("Jump");
Animator _anim;
void Awake() => _anim = GetComponent<Animator>();
void Update()
{
_anim.SetFloat(SpeedHash, moveSpeed);
if (jumped) _anim.SetTrigger(JumpTrigger);
}
}Animator.StringToHashOnAnimatorIK(int layerIndex)SetIKPosition/Rotation/Weight[RequireComponent(typeof(AudioSource))]
public class SFXPlayer : MonoBehaviour
{
[SerializeField] AudioClip[] clips;
AudioSource _source;
void Awake() => _source = GetComponent<AudioSource>();
public void PlayRandom() => _source.PlayOneShot(clips[Random.Range(0, clips.Length)]);
}AudioSource.PlayOneShot()AudioMixer| Type | Serialized | Notes |
|---|---|---|
| Yes | Visible in Inspector |
| Yes | Preferred -- maintains encapsulation |
| Yes, hidden | Serialized but not shown |
| No | Opt-out of serialization |
| Properties | No | Never serialized by Unity |
| Dictionaries | No | Use serialized list + rebuild, or Odin/SerializedDictionary |
| Interfaces | No | Use abstract ScriptableObject or SerializeReference |
[SerializeReference]| Pattern | When to Use | Approach |
|---|---|---|
| Singleton | Global managers (Audio, GameState) | ScriptableObject-based or lazy MonoBehaviour |
| Observer | Decoupled communication | C# events or SO event channels |
| Command | Input/undo systems | Command interface + history stack |
| State Machine | AI, player states | Enum + switch, or class-based states |
| Object Pool | Bullets, particles, enemies | Queue<T> with pre-instantiation |
| Service Locator | Testable global access | Static registry with interface keys |
references/design-patterns.md| Concept | Role |
|---|---|
| Entity | Lightweight ID (no MonoBehaviour) |
| IComponentData | Pure data struct on entities |
| SystemBase / ISystem | Logic operating on component queries |
| EntityQuery | Filter entities by component sets |
| Jobs (IJobEntity) | Multithreaded systems |
| Burst Compiler | SIMD-optimized native code |
references/design-patterns.mdreferences/design-patterns.mdreferences/physics-animation-audio.md