Game Feel Integrator
Apply juice to gameplay events using the GFD Feedback Matrix.
Before You Start
- Read
docs/ProjectConfig.yaml for:
feel_tools.tweening — which tween library ("dotween", "primetween", or "none").
feel_tools.feedback_system — feedback framework if any.
feel_tools.audio — audio middleware.
feel_tools.camera — camera system (Cinemachine, custom, etc.).
mcp.unity_mcp — if true, call refresh_unity after creating files.
- Read
docs/GFD.md for the Feedback Matrix — it defines which events need which feedback channels.
- Read
docs/CODING_STANDARDS.md for async patterns (Awaitable + CancellationToken) used in middleware-agnostic patterns.
Rule of Three
Every meaningful action needs feedback in at least 3 channels: Visual, Audio, Kinesthetic.
Middleware Reference
- DOTween: See references/dotween.md
- PrimeTween: See references/primetween.md
- No middleware: Use
async Awaitable coroutines
- Shader effects as feel: See references/shaders-as-feel.md — outline flash, dissolve, chromatic aberration, UV scroll, distortion
Theory & Inspiration
- Game feel foundations + Loic Jacob methodology: See references/gamefeel-theory.md
Universal Patterns (middleware-agnostic)
Hitstop
private async Awaitable Hitstop(float duration = 0.05f)
{
Time.timeScale = 0f;
await Awaitable.WaitForSecondsAsync(duration);
Time.timeScale = 1f;
}
Shake Profile
[System.Serializable]
public struct ShakeProfile
{
public float duration;
public float magnitude;
public static ShakeProfile Light => new() { duration = 0.1f, magnitude = 0.1f };
public static ShakeProfile Medium => new() { duration = 0.2f, magnitude = 0.3f };
public static ShakeProfile Heavy => new() { duration = 0.35f, magnitude = 0.6f };
}
Object Pooling
Frequently spawned FX (particles, floating text, projectiles) must be pooled to avoid GC spikes:
public class FXPool<T> where T : MonoBehaviour
{
private readonly Queue<T> _pool = new();
private readonly T _prefab;
private readonly Transform _parent;
public T Get() {
var item = _pool.Count > 0 ? _pool.Dequeue() : Object.Instantiate(_prefab, _parent);
item.gameObject.SetActive(true);
return item;
}
public void Return(T item) {
item.gameObject.SetActive(false);
_pool.Enqueue(item);
}
}
Rules: Pool all particles, floating text, projectiles. Return to pool on OnParticleSystemStopped or after tween completes. Pre-warm pools during scene load.
Performance Tips
- Camera separation: Use separate world + UI cameras so post-processing doesn't affect UI
- Shaders over CPU animations: For simple repetitive motion (scrolling, pulsing), prefer shader-based animation — runs parallel on GPU, cheaper than DOTween
- Shared materials enable batching: Use material property blocks to vary parameters without breaking draw call batching
- Legacy Animation for simple UI: For simple UI animations (fade, slide), Legacy Animation clips are more performant than Animator controllers
Tween Cleanup
Tweens must be killed when their target is destroyed or disabled, otherwise they cause null reference exceptions or operate on stale objects.
DOTween:
private void OnDestroy()
{
transform.DOKill(); // Kill all tweens on this transform
}
PrimeTween:
private void OnDestroy()
{
Tween.StopAll(this); // Kill all tweens targeting this object
}
No middleware (Awaitable): Use CancellationToken linked to destroyCancellationToken:
private async Awaitable FlashAsync()
{
var ct = destroyCancellationToken;
// Awaitable work — auto-cancels when MonoBehaviour is destroyed
await Awaitable.WaitForSecondsAsync(0.1f, ct);
}
Tuning
- Start exaggerated, then dial back.
- Always check the GFD Feedback Matrix before implementing.
- Ensure tweens are killed on object destruction (see Tween Cleanup above).
- Sync ADSR across channels: Attack and Release timings must match across Visual, Audio, and Kinesthetic. If the SFX fades over 0.5s, particles and shake damping must also fade over 0.5s.
- Profile feel code: Use Unity Profiler (Timeline view) to check that feel effects don't cause frame drops. Particle bursts, tween cascades, and audio one-shots in the same frame can spike.
Sourcing & Communication
- Cite your sources: When suggesting a feel pattern, name the reference game. Example: "A swap feel inspired by Royal Match's snappy 0.15s tween" or "Celeste's coyote time approach." This helps the user visualize and verify.
- Recommend assets: When VFX, SFX, or art assets are needed, reference
docs/ASSET_RESOURCES.md for curated free/paid sources.
After Setup
- Write tests: Use
uw-unity-test-runner — test feel parameters (shake profile values, hitstop duration) in EditMode tests.
- Code review: Use
uw-code-review to verify Rule of Three and tween cleanup before committing.
- UI animation: Use
uw-ui-toolkit-binder for UI Toolkit USS transitions. Use game feel patterns here for effects that go beyond USS (complex sequences, screen flash).
- Debug feel issues: Use
uw-unity-debugging if effects aren't triggering or timing feels off.
Rules
- Rule of Three: every meaningful action needs feedback in at least 3 channels (Visual, Audio, Kinesthetic).
- Pool all particles, floating text, and projectiles — never
Instantiate in hot paths.
- Kill/stop tweens in
OnDestroy or OnDisable to prevent null reference exceptions.
- Sync ADSR across channels — mismatched timing breaks immersion.
[SerializeField] private for shake profiles, tween durations, and other tuning values.
- All game feel code must live inside an
.asmdef.
- If
ProjectConfig.yaml -> mcp.unity_mcp is true, call refresh_unity after creating files.
1---2name: uw-game-feel-integrator3description: Inject juice and game feel into gameplay code using the project's chosen middleware. Use when adding screen shake, tweens, particles, audio feedback, haptics, hitstop, or any form of "juice" to gameplay events. Triggers on requests like "add juice", "make this feel better", "add screen shake", "polish this feature", "add feedback to this action", "make the hit feel impactful", "add particles when", "screen shake on damage", "tween this", "add camera shake", "make this snappy", or any game feel, polish, or feedback work. Always reads ProjectConfig.yaml -> feel_tools and docs/GFD.md Feedback Matrix before generating code.4---56# Game Feel Integrator78Apply juice to gameplay events using the GFD Feedback Matrix.910## Before You Start11121. Read `docs/ProjectConfig.yaml` for:13 - `feel_tools.tweening` — which tween library (`"dotween"`, `"primetween"`, or `"none"`).14 - `feel_tools.feedback_system` — feedback framework if any.15 - `feel_tools.audio` — audio middleware.16 - `feel_tools.camera` — camera system (Cinemachine, custom, etc.).17 - `mcp.unity_mcp` — if `true`, call `refresh_unity` after creating files.182. Read `docs/GFD.md` for the Feedback Matrix — it defines which events need which feedback channels.193. Read `docs/CODING_STANDARDS.md` for async patterns (`Awaitable` + `CancellationToken`) used in middleware-agnostic patterns.2021## Rule of Three22Every meaningful action needs feedback in **at least 3 channels**: Visual, Audio, Kinesthetic.2324## Middleware Reference25- **DOTween**: See [references/dotween.md](references/dotween.md)26- **PrimeTween**: See [references/primetween.md](references/primetween.md)27- **No middleware**: Use `async Awaitable` coroutines28- **Shader effects as feel**: See [references/shaders-as-feel.md](references/shaders-as-feel.md) — outline flash, dissolve, chromatic aberration, UV scroll, distortion2930## Theory & Inspiration31- **Game feel foundations + Loic Jacob methodology**: See [references/gamefeel-theory.md](references/gamefeel-theory.md)3233## Universal Patterns (middleware-agnostic)3435### Hitstop36```csharp37private async Awaitable Hitstop(float duration = 0.05f)38{39 Time.timeScale = 0f;40 await Awaitable.WaitForSecondsAsync(duration);41 Time.timeScale = 1f;42}43```4445### Shake Profile46```csharp47[System.Serializable]48public struct ShakeProfile49{50 public float duration;51 public float magnitude;52 public static ShakeProfile Light => new() { duration = 0.1f, magnitude = 0.1f };53 public static ShakeProfile Medium => new() { duration = 0.2f, magnitude = 0.3f };54 public static ShakeProfile Heavy => new() { duration = 0.35f, magnitude = 0.6f };55}56```5758## Object Pooling5960Frequently spawned FX (particles, floating text, projectiles) must be pooled to avoid GC spikes:6162```csharp63public class FXPool<T> where T : MonoBehaviour64{65 private readonly Queue<T> _pool = new();66 private readonly T _prefab;67 private readonly Transform _parent;6869 public T Get() {70 var item = _pool.Count > 0 ? _pool.Dequeue() : Object.Instantiate(_prefab, _parent);71 item.gameObject.SetActive(true);72 return item;73 }7475 public void Return(T item) {76 item.gameObject.SetActive(false);77 _pool.Enqueue(item);78 }79}80```8182**Rules:** Pool all particles, floating text, projectiles. Return to pool on `OnParticleSystemStopped` or after tween completes. Pre-warm pools during scene load.8384## Performance Tips85- **Camera separation**: Use separate world + UI cameras so post-processing doesn't affect UI86- **Shaders over CPU animations**: For simple repetitive motion (scrolling, pulsing), prefer shader-based animation — runs parallel on GPU, cheaper than DOTween87- **Shared materials enable batching**: Use material property blocks to vary parameters without breaking draw call batching88- **Legacy Animation for simple UI**: For simple UI animations (fade, slide), Legacy Animation clips are more performant than Animator controllers8990## Tween Cleanup9192Tweens must be killed when their target is destroyed or disabled, otherwise they cause null reference exceptions or operate on stale objects.9394**DOTween:**95```csharp96private void OnDestroy()97{98 transform.DOKill(); // Kill all tweens on this transform99}100```101102**PrimeTween:**103```csharp104private void OnDestroy()105{106 Tween.StopAll(this); // Kill all tweens targeting this object107}108```109110**No middleware (Awaitable):** Use `CancellationToken` linked to `destroyCancellationToken`:111```csharp112private async Awaitable FlashAsync()113{114 var ct = destroyCancellationToken;115 // Awaitable work — auto-cancels when MonoBehaviour is destroyed116 await Awaitable.WaitForSecondsAsync(0.1f, ct);117}118```119120## Tuning121- Start **exaggerated**, then dial back.122- Always check the GFD Feedback Matrix before implementing.123- Ensure tweens are killed on object destruction (see Tween Cleanup above).124- **Sync ADSR across channels**: Attack and Release timings must match across Visual, Audio, and Kinesthetic. If the SFX fades over 0.5s, particles and shake damping must also fade over 0.5s.125- **Profile feel code**: Use Unity Profiler (Timeline view) to check that feel effects don't cause frame drops. Particle bursts, tween cascades, and audio one-shots in the same frame can spike.126127## Sourcing & Communication128- **Cite your sources**: When suggesting a feel pattern, name the reference game. Example: *"A swap feel inspired by Royal Match's snappy 0.15s tween"* or *"Celeste's coyote time approach."* This helps the user visualize and verify.129- **Recommend assets**: When VFX, SFX, or art assets are needed, reference `docs/ASSET_RESOURCES.md` for curated free/paid sources.130131## After Setup132133- **Write tests:** Use `uw-unity-test-runner` — test feel parameters (shake profile values, hitstop duration) in EditMode tests.134- **Code review:** Use `uw-code-review` to verify Rule of Three and tween cleanup before committing.135- **UI animation:** Use `uw-ui-toolkit-binder` for UI Toolkit USS transitions. Use game feel patterns here for effects that go beyond USS (complex sequences, screen flash).136- **Debug feel issues:** Use `uw-unity-debugging` if effects aren't triggering or timing feels off.137138## Rules139140- Rule of Three: every meaningful action needs feedback in at least 3 channels (Visual, Audio, Kinesthetic).141- Pool all particles, floating text, and projectiles — never `Instantiate` in hot paths.142- Kill/stop tweens in `OnDestroy` or `OnDisable` to prevent null reference exceptions.143- Sync ADSR across channels — mismatched timing breaks immersion.144- `[SerializeField] private` for shake profiles, tween durations, and other tuning values.145- All game feel code must live inside an `.asmdef`.146- If `ProjectConfig.yaml -> mcp.unity_mcp` is `true`, call `refresh_unity` after creating files.