@musashishao/agent-kit 1.9.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/.agent/agents/ai-asset-factory.md +700 -0
  2. package/.agent/agents/ai-audio-factory.md +503 -0
  3. package/.agent/agents/game-developer.md +4 -4
  4. package/.agent/agents/orchestrator.md +113 -3
  5. package/.agent/agents/project-planner.md +67 -0
  6. package/.agent/agents/unity-mobile-master.md +949 -0
  7. package/.agent/mcp/config/registry.json +65 -51
  8. package/.agent/mcp/servers/notebooklm/README.md +114 -0
  9. package/.agent/mcp/servers/notebooklm/package.json +35 -0
  10. package/.agent/mcp/servers/notebooklm/src/auth/chrome.ts +225 -0
  11. package/.agent/mcp/servers/notebooklm/src/auth/index.ts +1 -0
  12. package/.agent/mcp/servers/notebooklm/src/index.ts +516 -0
  13. package/.agent/mcp/servers/notebooklm/src/services/index.ts +3 -0
  14. package/.agent/mcp/servers/notebooklm/src/services/library.ts +217 -0
  15. package/.agent/mcp/servers/notebooklm/src/services/notebooklm.ts +380 -0
  16. package/.agent/mcp/servers/notebooklm/tsconfig.json +15 -0
  17. package/.agent/mcp-gateway/README.md +169 -20
  18. package/.agent/mcp-gateway/package.json +22 -7
  19. package/.agent/mcp-gateway/src/auth/index.ts +55 -0
  20. package/.agent/mcp-gateway/src/auth/middleware.ts +242 -0
  21. package/.agent/mcp-gateway/src/auth/oauth.ts +462 -0
  22. package/.agent/mcp-gateway/src/auth/scopes.ts +227 -0
  23. package/.agent/mcp-gateway/src/index.ts +252 -105
  24. package/.agent/mcp-gateway/src/observability/index.ts +5 -0
  25. package/.agent/mcp-gateway/src/observability/otel.ts +405 -0
  26. package/.agent/mcp-gateway/src/transports/index.ts +5 -0
  27. package/.agent/mcp-gateway/src/transports/streamableHttp.ts +235 -0
  28. package/.agent/rules/CODEX.md +89 -0
  29. package/.agent/rules/CODE_RULES.md +73 -0
  30. package/.agent/rules/GEMINI.md +25 -0
  31. package/.agent/rules/MEMORY_STATE.md +110 -0
  32. package/.agent/rules/REFERENCE.md +33 -141
  33. package/.agent/rules/REF_SKILLS.md +116 -0
  34. package/.agent/rules/REF_WORKFLOWS.md +81 -0
  35. package/.agent/scripts/ak_cli.py +106 -5
  36. package/.agent/scripts/memory_manager.py +48 -9
  37. package/.agent/skills/anti-hallucination/SKILL.md +295 -0
  38. package/.agent/skills/anti-hallucination/scripts/check_hallucination.py +299 -0
  39. package/.agent/skills/bifurcation-analysis/SKILL.md +56 -0
  40. package/.agent/skills/brainstorming/SKILL.md +80 -6
  41. package/.agent/skills/decision-memory/SKILL.md +317 -0
  42. package/.agent/skills/emergence-detector/SKILL.md +230 -0
  43. package/.agent/skills/emergence-detector/scripts/check_emergence.py +265 -0
  44. package/.agent/skills/explained-qa/SKILL.md +142 -0
  45. package/.agent/skills/explained-qa/game-terminology.md +214 -0
  46. package/.agent/skills/game-development/ai-dialogue-engine/SKILL.md +442 -0
  47. package/.agent/skills/game-development/ai-graphics-generator/SKILL.md +463 -0
  48. package/.agent/skills/game-development/ai-playtest-framework/SKILL.md +570 -0
  49. package/.agent/skills/game-development/camera-systems/SKILL.md +607 -0
  50. package/.agent/skills/game-development/card-battle-engine/SKILL.md +618 -0
  51. package/.agent/skills/game-development/character-controller-3d/SKILL.md +908 -0
  52. package/.agent/skills/game-development/cloud-save-sync/SKILL.md +527 -0
  53. package/.agent/skills/game-development/combat-system/SKILL.md +748 -0
  54. package/.agent/skills/game-development/compliance-rating/SKILL.md +277 -0
  55. package/.agent/skills/game-development/crossplatform-build/SKILL.md +386 -0
  56. package/.agent/skills/game-development/cultivation-progression/SKILL.md +520 -0
  57. package/.agent/skills/game-development/data-driven-balance/SKILL.md +535 -0
  58. package/.agent/skills/game-development/game-analytics-integrator/SKILL.md +410 -0
  59. package/.agent/skills/game-development/game-audio-advanced/SKILL.md +646 -0
  60. package/.agent/skills/game-development/game-economy-designer/SKILL.md +375 -0
  61. package/.agent/skills/game-development/game-marketing/SKILL.md +85 -0
  62. package/.agent/skills/game-development/game-state-manager/SKILL.md +883 -0
  63. package/.agent/skills/game-development/hybrid-game-spec/SKILL.md +220 -0
  64. package/.agent/skills/game-development/inventory-quest/SKILL.md +747 -0
  65. package/.agent/skills/game-development/liveops/SKILL.md +308 -0
  66. package/.agent/skills/game-development/localization/SKILL.md +286 -0
  67. package/.agent/skills/game-development/mobile-input-patterns/SKILL.md +343 -0
  68. package/.agent/skills/game-development/monetization-strategy/SKILL.md +94 -0
  69. package/.agent/skills/game-development/multiplayer-master/SKILL.md +727 -0
  70. package/.agent/skills/game-development/narrative-branching/SKILL.md +593 -0
  71. package/.agent/skills/game-development/procedural-level-ai/SKILL.md +367 -0
  72. package/.agent/skills/game-development/prototyping-rapid/SKILL.md +205 -0
  73. package/.agent/skills/game-development/spec-ecosystem/SKILL.md +155 -0
  74. package/.agent/skills/game-development/spec-ecosystem/decision-log-format.md +129 -0
  75. package/.agent/skills/game-development/spec-ecosystem/templates/PLAN-template.md +178 -0
  76. package/.agent/skills/game-development/spec-ecosystem/templates/SPEC-template.md +110 -0
  77. package/.agent/skills/game-development/spec-ecosystem/templates/TASKS-template.md +156 -0
  78. package/.agent/skills/game-development/survival-systems/SKILL.md +493 -0
  79. package/.agent/skills/game-development/testing-qa/SKILL.md +270 -0
  80. package/.agent/skills/game-development/unity-mobile-optimization/SKILL.md +271 -0
  81. package/.agent/skills/intent-capture/SKILL.md +65 -0
  82. package/.agent/skills/mcp-composition/SKILL.md +362 -0
  83. package/.agent/skills/mcp-observability/SKILL.md +323 -0
  84. package/.agent/skills/mcp-security/SKILL.md +314 -0
  85. package/.agent/skills/trust-spectrum/SKILL.md +291 -0
  86. package/.agent/skills/vibe-coding-guard/SKILL.md +328 -0
  87. package/.agent/templates/AGENTS.game.md +63 -0
  88. package/.agent/templates/docs/WORKFLOW_GUIDE.en.md +100 -0
  89. package/.agent/templates/docs/WORKFLOW_GUIDE.vi.md +100 -0
  90. package/.agent/workflows/ai-agent.md +2 -0
  91. package/.agent/workflows/autofix.md +1 -0
  92. package/.agent/workflows/brainstorm.md +1 -0
  93. package/.agent/workflows/context.md +1 -0
  94. package/.agent/workflows/create.md +39 -8
  95. package/.agent/workflows/dashboard.md +1 -0
  96. package/.agent/workflows/debug.md +14 -0
  97. package/.agent/workflows/deploy.md +14 -0
  98. package/.agent/workflows/enhance.md +44 -0
  99. package/.agent/workflows/gamekit-init.md +177 -0
  100. package/.agent/workflows/gamekit-launch.md +338 -0
  101. package/.agent/workflows/gamekit-plan.md +204 -0
  102. package/.agent/workflows/gamekit-qa.md +153 -0
  103. package/.agent/workflows/gamekit-spec.md +243 -0
  104. package/.agent/workflows/gamekit-tasks.md +208 -0
  105. package/.agent/workflows/marketing.md +2 -0
  106. package/.agent/workflows/next.md +1 -0
  107. package/.agent/workflows/orchestrate.md +12 -0
  108. package/.agent/workflows/pentest.md +2 -0
  109. package/.agent/workflows/plan.md +42 -0
  110. package/.agent/workflows/preview.md +1 -0
  111. package/.agent/workflows/quality.md +1 -0
  112. package/.agent/workflows/saas.md +2 -0
  113. package/.agent/workflows/spec.md +42 -0
  114. package/.agent/workflows/status.md +1 -0
  115. package/.agent/workflows/test.md +14 -0
  116. package/.agent/workflows/ui-ux-pro-max.md +1 -0
  117. package/bin/cli.js +411 -111
  118. package/package.json +1 -2
  119. package/.agent/agents/game-asset-curator.md +0 -317
  120. package/.agent/agents/game-narrative-designer.md +0 -310
  121. package/.agent/agents/game-qa-agent.md +0 -441
  122. package/.agent/workflows/game-prototype.md +0 -154
  123. package/docs/AI_DATA_INFRASTRUCTURE.md +0 -288
  124. package/docs/CHANGELOG_AI_INFRA.md +0 -141
  125. package/docs/MIGRATION_GUIDE_V1.9.md +0 -55
@@ -0,0 +1,646 @@
1
+ ---
2
+ name: game-audio-advanced
3
+ description: Advanced game audio systems. Spatial audio, music systems, sound pooling, FMOD/Wwise basics, footstep systems.
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, WebSearch
5
+ ---
6
+
7
+ # Advanced Game Audio Skill
8
+
9
+ > Create immersive audio experiences for games.
10
+
11
+ ---
12
+
13
+ ## When to Use
14
+
15
+ - Building audio manager systems
16
+ - Implementing spatial 3D audio
17
+ - Creating adaptive music
18
+ - Setting up sound pooling
19
+ - Designing footstep systems
20
+
21
+ ---
22
+
23
+ ## Audio Manager
24
+
25
+ ### Singleton Pattern
26
+
27
+ ```csharp
28
+ public class AudioManager : MonoBehaviour
29
+ {
30
+ public static AudioManager Instance { get; private set; }
31
+
32
+ [Header("Mixers")]
33
+ [SerializeField] private AudioMixerGroup masterGroup;
34
+ [SerializeField] private AudioMixerGroup musicGroup;
35
+ [SerializeField] private AudioMixerGroup sfxGroup;
36
+ [SerializeField] private AudioMixerGroup voiceGroup;
37
+
38
+ [Header("Sources")]
39
+ [SerializeField] private AudioSource musicSource;
40
+ [SerializeField] private AudioSource ambienceSource;
41
+ [SerializeField] private int sfxPoolSize = 20;
42
+
43
+ private List<AudioSource> _sfxPool = new();
44
+ private Dictionary<string, AudioClip> _clipCache = new();
45
+
46
+ void Awake()
47
+ {
48
+ if (Instance != null)
49
+ {
50
+ Destroy(gameObject);
51
+ return;
52
+ }
53
+ Instance = this;
54
+ DontDestroyOnLoad(gameObject);
55
+
56
+ InitializeSFXPool();
57
+ }
58
+
59
+ private void InitializeSFXPool()
60
+ {
61
+ for (int i = 0; i < sfxPoolSize; i++)
62
+ {
63
+ var go = new GameObject($"SFX_Source_{i}");
64
+ go.transform.SetParent(transform);
65
+
66
+ var source = go.AddComponent<AudioSource>();
67
+ source.outputAudioMixerGroup = sfxGroup;
68
+ source.playOnAwake = false;
69
+
70
+ _sfxPool.Add(source);
71
+ }
72
+ }
73
+
74
+ #region SFX
75
+
76
+ public void PlaySFX(AudioClip clip, float volume = 1f)
77
+ {
78
+ if (clip == null) return;
79
+
80
+ var source = GetAvailableSource();
81
+ if (source != null)
82
+ {
83
+ source.clip = clip;
84
+ source.volume = volume;
85
+ source.spatialBlend = 0f; // 2D
86
+ source.Play();
87
+ }
88
+ }
89
+
90
+ public void PlaySFX(string clipPath, float volume = 1f)
91
+ {
92
+ var clip = LoadClip(clipPath);
93
+ PlaySFX(clip, volume);
94
+ }
95
+
96
+ public void PlaySFXAtPosition(AudioClip clip, Vector3 position, float volume = 1f)
97
+ {
98
+ if (clip == null) return;
99
+
100
+ var source = GetAvailableSource();
101
+ if (source != null)
102
+ {
103
+ source.transform.position = position;
104
+ source.clip = clip;
105
+ source.volume = volume;
106
+ source.spatialBlend = 1f; // 3D
107
+ source.Play();
108
+ }
109
+ }
110
+
111
+ public AudioSource PlayLoopingAtPosition(AudioClip clip, Vector3 position, float volume = 1f)
112
+ {
113
+ var source = GetAvailableSource();
114
+ if (source != null)
115
+ {
116
+ source.transform.position = position;
117
+ source.clip = clip;
118
+ source.volume = volume;
119
+ source.spatialBlend = 1f;
120
+ source.loop = true;
121
+ source.Play();
122
+ }
123
+ return source;
124
+ }
125
+
126
+ private AudioSource GetAvailableSource()
127
+ {
128
+ foreach (var source in _sfxPool)
129
+ {
130
+ if (!source.isPlaying)
131
+ {
132
+ source.loop = false;
133
+ return source;
134
+ }
135
+ }
136
+
137
+ // All busy - return oldest
138
+ Debug.LogWarning("SFX pool exhausted!");
139
+ return _sfxPool[0];
140
+ }
141
+
142
+ #endregion
143
+
144
+ #region Music
145
+
146
+ public void PlayMusic(AudioClip clip, float fadeDuration = 1f)
147
+ {
148
+ StartCoroutine(CrossfadeMusic(clip, fadeDuration));
149
+ }
150
+
151
+ private IEnumerator CrossfadeMusic(AudioClip newClip, float duration)
152
+ {
153
+ float startVolume = musicSource.volume;
154
+
155
+ // Fade out
156
+ float elapsed = 0f;
157
+ while (elapsed < duration / 2f)
158
+ {
159
+ elapsed += Time.deltaTime;
160
+ musicSource.volume = Mathf.Lerp(startVolume, 0f, elapsed / (duration / 2f));
161
+ yield return null;
162
+ }
163
+
164
+ // Switch clip
165
+ musicSource.clip = newClip;
166
+ musicSource.Play();
167
+
168
+ // Fade in
169
+ elapsed = 0f;
170
+ while (elapsed < duration / 2f)
171
+ {
172
+ elapsed += Time.deltaTime;
173
+ musicSource.volume = Mathf.Lerp(0f, startVolume, elapsed / (duration / 2f));
174
+ yield return null;
175
+ }
176
+ }
177
+
178
+ public void StopMusic(float fadeDuration = 1f)
179
+ {
180
+ StartCoroutine(FadeOutMusic(fadeDuration));
181
+ }
182
+
183
+ private IEnumerator FadeOutMusic(float duration)
184
+ {
185
+ float startVolume = musicSource.volume;
186
+ float elapsed = 0f;
187
+
188
+ while (elapsed < duration)
189
+ {
190
+ elapsed += Time.deltaTime;
191
+ musicSource.volume = Mathf.Lerp(startVolume, 0f, elapsed / duration);
192
+ yield return null;
193
+ }
194
+
195
+ musicSource.Stop();
196
+ }
197
+
198
+ #endregion
199
+
200
+ #region Volume Control
201
+
202
+ public void SetMasterVolume(float volume)
203
+ {
204
+ SetMixerVolume("MasterVolume", volume);
205
+ }
206
+
207
+ public void SetMusicVolume(float volume)
208
+ {
209
+ SetMixerVolume("MusicVolume", volume);
210
+ }
211
+
212
+ public void SetSFXVolume(float volume)
213
+ {
214
+ SetMixerVolume("SFXVolume", volume);
215
+ }
216
+
217
+ private void SetMixerVolume(string parameter, float linearVolume)
218
+ {
219
+ // Convert linear (0-1) to dB
220
+ float dB = linearVolume > 0.001f
221
+ ? Mathf.Log10(linearVolume) * 20f
222
+ : -80f;
223
+
224
+ masterGroup.audioMixer.SetFloat(parameter, dB);
225
+ }
226
+
227
+ #endregion
228
+
229
+ private AudioClip LoadClip(string path)
230
+ {
231
+ if (_clipCache.TryGetValue(path, out var cached))
232
+ {
233
+ return cached;
234
+ }
235
+
236
+ var clip = Resources.Load<AudioClip>(path);
237
+ if (clip != null)
238
+ {
239
+ _clipCache[path] = clip;
240
+ }
241
+ return clip;
242
+ }
243
+ }
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Adaptive Music System
249
+
250
+ ```csharp
251
+ public class AdaptiveMusicManager : MonoBehaviour
252
+ {
253
+ [System.Serializable]
254
+ public class MusicLayer
255
+ {
256
+ public string Name;
257
+ public AudioClip Clip;
258
+ public AudioSource Source;
259
+ [Range(0, 1)] public float TargetVolume = 1f;
260
+ public float FadeSpeed = 1f;
261
+ }
262
+
263
+ [SerializeField] private MusicLayer[] layers;
264
+ [SerializeField] private float bpm = 120f;
265
+
266
+ private float _beatDuration;
267
+ private float _nextBeatTime;
268
+
269
+ // Music states
270
+ public enum MusicState { Exploration, Combat, Danger, Boss }
271
+ private MusicState _currentState = MusicState.Exploration;
272
+
273
+ void Start()
274
+ {
275
+ _beatDuration = 60f / bpm;
276
+ InitializeLayers();
277
+ SyncAllLayers();
278
+ }
279
+
280
+ private void InitializeLayers()
281
+ {
282
+ foreach (var layer in layers)
283
+ {
284
+ layer.Source.clip = layer.Clip;
285
+ layer.Source.loop = true;
286
+ layer.Source.volume = 0f;
287
+ layer.Source.Play();
288
+ }
289
+
290
+ // Start with exploration layers
291
+ SetState(MusicState.Exploration);
292
+ }
293
+
294
+ void Update()
295
+ {
296
+ // Smooth volume transitions
297
+ foreach (var layer in layers)
298
+ {
299
+ layer.Source.volume = Mathf.MoveTowards(
300
+ layer.Source.volume,
301
+ layer.TargetVolume,
302
+ layer.FadeSpeed * Time.deltaTime
303
+ );
304
+ }
305
+
306
+ // Keep layers in sync
307
+ if (Time.time > _nextBeatTime)
308
+ {
309
+ _nextBeatTime = Time.time + _beatDuration;
310
+ OnBeat();
311
+ }
312
+ }
313
+
314
+ private void OnBeat()
315
+ {
316
+ // Could trigger beat-synced events
317
+ }
318
+
319
+ public void SetState(MusicState newState)
320
+ {
321
+ _currentState = newState;
322
+ UpdateLayerVolumes();
323
+ }
324
+
325
+ private void UpdateLayerVolumes()
326
+ {
327
+ switch (_currentState)
328
+ {
329
+ case MusicState.Exploration:
330
+ SetLayerVolume("Base", 1f);
331
+ SetLayerVolume("Melody", 0.5f);
332
+ SetLayerVolume("Drums", 0f);
333
+ SetLayerVolume("Intensity", 0f);
334
+ break;
335
+
336
+ case MusicState.Danger:
337
+ SetLayerVolume("Base", 1f);
338
+ SetLayerVolume("Melody", 0.3f);
339
+ SetLayerVolume("Drums", 0.5f);
340
+ SetLayerVolume("Intensity", 0.3f);
341
+ break;
342
+
343
+ case MusicState.Combat:
344
+ SetLayerVolume("Base", 1f);
345
+ SetLayerVolume("Melody", 0f);
346
+ SetLayerVolume("Drums", 1f);
347
+ SetLayerVolume("Intensity", 0.7f);
348
+ break;
349
+
350
+ case MusicState.Boss:
351
+ SetLayerVolume("Base", 1f);
352
+ SetLayerVolume("Melody", 0.5f);
353
+ SetLayerVolume("Drums", 1f);
354
+ SetLayerVolume("Intensity", 1f);
355
+ break;
356
+ }
357
+ }
358
+
359
+ private void SetLayerVolume(string layerName, float volume)
360
+ {
361
+ var layer = layers.FirstOrDefault(l => l.Name == layerName);
362
+ if (layer != null)
363
+ {
364
+ layer.TargetVolume = volume;
365
+ }
366
+ }
367
+
368
+ private void SyncAllLayers()
369
+ {
370
+ float syncTime = layers[0].Source.time;
371
+ foreach (var layer in layers)
372
+ {
373
+ layer.Source.time = syncTime;
374
+ }
375
+ }
376
+ }
377
+ ```
378
+
379
+ ---
380
+
381
+ ## Footstep System
382
+
383
+ ```csharp
384
+ public class FootstepSystem : MonoBehaviour
385
+ {
386
+ [System.Serializable]
387
+ public class SurfaceAudio
388
+ {
389
+ public string SurfaceTag;
390
+ public PhysicMaterial PhysicsMaterial;
391
+ public AudioClip[] FootstepClips;
392
+ public AudioClip[] LandingClips;
393
+ [Range(0, 1)] public float Volume = 1f;
394
+ }
395
+
396
+ [SerializeField] private SurfaceAudio[] surfaces;
397
+ [SerializeField] private SurfaceAudio defaultSurface;
398
+
399
+ [Header("Settings")]
400
+ [SerializeField] private float footstepInterval = 0.5f;
401
+ [SerializeField] private float runMultiplier = 0.7f;
402
+ [SerializeField] private LayerMask groundMask;
403
+
404
+ private AudioSource _audioSource;
405
+ private float _lastFootstepTime;
406
+ private CharacterController _controller;
407
+
408
+ void Awake()
409
+ {
410
+ _audioSource = gameObject.AddComponent<AudioSource>();
411
+ _audioSource.spatialBlend = 1f;
412
+ _controller = GetComponent<CharacterController>();
413
+ }
414
+
415
+ public void PlayFootstep(bool isRunning = false)
416
+ {
417
+ float interval = isRunning ? footstepInterval * runMultiplier : footstepInterval;
418
+
419
+ if (Time.time - _lastFootstepTime < interval) return;
420
+ _lastFootstepTime = Time.time;
421
+
422
+ var surface = GetCurrentSurface();
423
+ if (surface != null && surface.FootstepClips.Length > 0)
424
+ {
425
+ var clip = surface.FootstepClips[Random.Range(0, surface.FootstepClips.Length)];
426
+ _audioSource.PlayOneShot(clip, surface.Volume);
427
+ }
428
+ }
429
+
430
+ public void PlayLanding()
431
+ {
432
+ var surface = GetCurrentSurface();
433
+ if (surface != null && surface.LandingClips.Length > 0)
434
+ {
435
+ var clip = surface.LandingClips[Random.Range(0, surface.LandingClips.Length)];
436
+ _audioSource.PlayOneShot(clip, surface.Volume * 1.2f);
437
+ }
438
+ }
439
+
440
+ private SurfaceAudio GetCurrentSurface()
441
+ {
442
+ if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 2f, groundMask))
443
+ {
444
+ // Check by tag
445
+ foreach (var surface in surfaces)
446
+ {
447
+ if (hit.collider.CompareTag(surface.SurfaceTag))
448
+ {
449
+ return surface;
450
+ }
451
+ }
452
+
453
+ // Check by physics material
454
+ if (hit.collider.material != null)
455
+ {
456
+ foreach (var surface in surfaces)
457
+ {
458
+ if (surface.PhysicsMaterial == hit.collider.material)
459
+ {
460
+ return surface;
461
+ }
462
+ }
463
+ }
464
+ }
465
+
466
+ return defaultSurface;
467
+ }
468
+
469
+ // Called from animation events
470
+ public void OnFootstepLeft() => PlayFootstep();
471
+ public void OnFootstepRight() => PlayFootstep();
472
+ }
473
+ ```
474
+
475
+ ---
476
+
477
+ ## Spatial Audio Setup
478
+
479
+ ```csharp
480
+ public class SpatialAudioSource : MonoBehaviour
481
+ {
482
+ [Header("3D Settings")]
483
+ [SerializeField] private float minDistance = 1f;
484
+ [SerializeField] private float maxDistance = 50f;
485
+ [SerializeField] private AudioRolloffMode rolloffMode = AudioRolloffMode.Logarithmic;
486
+
487
+ [Header("Occlusion")]
488
+ [SerializeField] private bool useOcclusion = true;
489
+ [SerializeField] private LayerMask occlusionMask;
490
+ [SerializeField] private float occlusionDamping = 0.5f;
491
+
492
+ private AudioSource _source;
493
+ private AudioLowPassFilter _lowPass;
494
+ private Transform _listener;
495
+
496
+ void Awake()
497
+ {
498
+ _source = GetComponent<AudioSource>();
499
+ _source.spatialBlend = 1f;
500
+ _source.minDistance = minDistance;
501
+ _source.maxDistance = maxDistance;
502
+ _source.rolloffMode = rolloffMode;
503
+
504
+ if (useOcclusion)
505
+ {
506
+ _lowPass = gameObject.AddComponent<AudioLowPassFilter>();
507
+ _lowPass.cutoffFrequency = 22000f;
508
+ }
509
+ }
510
+
511
+ void Start()
512
+ {
513
+ _listener = Camera.main.transform;
514
+ }
515
+
516
+ void Update()
517
+ {
518
+ if (!useOcclusion || _listener == null) return;
519
+
520
+ Vector3 direction = _listener.position - transform.position;
521
+
522
+ if (Physics.Raycast(transform.position, direction.normalized, direction.magnitude, occlusionMask))
523
+ {
524
+ // Occluded - lower frequencies
525
+ _lowPass.cutoffFrequency = Mathf.Lerp(
526
+ _lowPass.cutoffFrequency,
527
+ 1000f,
528
+ occlusionDamping * Time.deltaTime * 10f
529
+ );
530
+ _source.volume = Mathf.Lerp(_source.volume, 0.5f, occlusionDamping * Time.deltaTime * 10f);
531
+ }
532
+ else
533
+ {
534
+ // Clear path
535
+ _lowPass.cutoffFrequency = Mathf.Lerp(
536
+ _lowPass.cutoffFrequency,
537
+ 22000f,
538
+ occlusionDamping * Time.deltaTime * 10f
539
+ );
540
+ _source.volume = Mathf.Lerp(_source.volume, 1f, occlusionDamping * Time.deltaTime * 10f);
541
+ }
542
+ }
543
+ }
544
+ ```
545
+
546
+ ---
547
+
548
+ ## Audio Cue Variations
549
+
550
+ ```csharp
551
+ [CreateAssetMenu(fileName = "New Audio Cue", menuName = "Audio/Cue")]
552
+ public class AudioCue : ScriptableObject
553
+ {
554
+ [System.Serializable]
555
+ public class ClipVariant
556
+ {
557
+ public AudioClip Clip;
558
+ [Range(0, 1)] public float Weight = 1f;
559
+ }
560
+
561
+ public ClipVariant[] Variants;
562
+
563
+ [Header("Pitch")]
564
+ public float PitchMin = 0.95f;
565
+ public float PitchMax = 1.05f;
566
+
567
+ [Header("Volume")]
568
+ public float VolumeMin = 0.9f;
569
+ public float VolumeMax = 1f;
570
+
571
+ private int _lastPlayedIndex = -1;
572
+
573
+ public void Play(AudioSource source)
574
+ {
575
+ if (Variants.Length == 0) return;
576
+
577
+ source.clip = GetRandomClip();
578
+ source.pitch = Random.Range(PitchMin, PitchMax);
579
+ source.volume = Random.Range(VolumeMin, VolumeMax);
580
+ source.Play();
581
+ }
582
+
583
+ public void PlayOneShot(AudioSource source)
584
+ {
585
+ if (Variants.Length == 0) return;
586
+
587
+ var clip = GetRandomClip();
588
+ float volume = Random.Range(VolumeMin, VolumeMax);
589
+
590
+ // Temporarily adjust pitch
591
+ float originalPitch = source.pitch;
592
+ source.pitch = Random.Range(PitchMin, PitchMax);
593
+ source.PlayOneShot(clip, volume);
594
+ source.pitch = originalPitch;
595
+ }
596
+
597
+ private AudioClip GetRandomClip()
598
+ {
599
+ // Weighted random, avoiding immediate repeat
600
+ float totalWeight = Variants.Sum(v => v.Weight);
601
+ float random = Random.Range(0f, totalWeight);
602
+
603
+ float cumulative = 0f;
604
+ for (int i = 0; i < Variants.Length; i++)
605
+ {
606
+ cumulative += Variants[i].Weight;
607
+ if (random <= cumulative && i != _lastPlayedIndex)
608
+ {
609
+ _lastPlayedIndex = i;
610
+ return Variants[i].Clip;
611
+ }
612
+ }
613
+
614
+ _lastPlayedIndex = 0;
615
+ return Variants[0].Clip;
616
+ }
617
+ }
618
+ ```
619
+
620
+ ---
621
+
622
+ ## Quick Reference
623
+
624
+ | System | Key Approach |
625
+ |--------|--------------|
626
+ | **SFX** | Pool audio sources, reuse |
627
+ | **Music** | Crossfade, layer-based |
628
+ | **Spatial** | Set spatialBlend = 1 |
629
+ | **Footsteps** | Surface detection + variations |
630
+ | **Ambience** | Looping, zone-based |
631
+
632
+ ---
633
+
634
+ ## Anti-Patterns
635
+
636
+ | ❌ Don't | ✅ Do |
637
+ |----------|-------|
638
+ | Create new AudioSource each time | Use pooling |
639
+ | Same clip every time | Add variations |
640
+ | Hard transitions | Crossfade |
641
+ | Ignore occlusion | Use low-pass for walls |
642
+ | Max volume everything | Dynamic mixing |
643
+
644
+ ---
645
+
646
+ > **Remember:** Audio is 50% of immersion. Invest in it.