# 6. Worked Example: A Three-Hit Melee String
Part of [[Combat Feel and Hit Feedback]]. Prev: [[5. I-Frames Dodge and Guard]]. Next: [[7. Combat Feel Pitfalls]].
Build this in an empty 3D URP scene. Placeholder capsules are enough. The goal is a string you can *feel*, not a hero mesh.
## 6.1 Scene and layers
1. Project Settings > Tags and Layers: add `PlayerHurtbox`, `PlayerHitbox`, `EnemyHurtbox`, `EnemyHitbox`.
2. Edit > Project Settings > Physics > Layer Collision Matrix: check only the pairs in [[3. Hitboxes and Hurtboxes]]. Uncheck PlayerHitbox vs PlayerHurtbox.
3. Floor plane, a directional light, a `CinemachineCamera` following the player, **Cinemachine Brain** on Main Camera, **CinemachineImpulseListener** on the virtual camera ([[4.Impulse Noise and Extensions]]).
4. Input Actions asset with `Attack` (button) and `Dodge` (button). Pair them as in [[Input System]] / [[5. Worked Example Remappable Player]] if you already have that asset.
## 6.2 Player prefab
```
Player (layer Player)
CharacterController
CombatVictim (team Player; wire hurtbox, knockback, hitStop, impulse, feedback)
KnockbackReceiver
MeleeComboController
DodgeController
AttackHitboxEvents
HitImpulse + CinemachineImpulseSource
HitVfxAudio + child VisualEffect (or a ParticleSystem if you have not opened VFX Graph yet)
Animator (controller described in part 2)
Hurtbox child: capsule trigger, layer PlayerHurtbox, Hurtbox component
Weapon child: box trigger, layer PlayerHitbox, Hitbox component (hurtboxMask = EnemyHurtbox)
```
Create three `AttackDefinition` assets:
| Asset | attackId | damage | hitStopDuration | hitStopScale | knockbackSpeed | impulseForce |
|---|---|---|---|---|---|---|
| Light1 | 0 | 6 | 0.04 | 0.12 | 3 | 0.25 |
| Light2 | 1 | 8 | 0.05 | 0.10 | 3.5 | 0.3 |
| Finisher | 2 | 16 | 0.08 | 0.06 | 6 | 0.7 |
Assign them in order on `MeleeComboController.stringAttacks`. Plant events on each attack clip: `Anim_HitboxOn`, `Anim_HitboxOff`, `Anim_OpenCancel` / `Anim_CloseCancel` (or use the normalized-time fallback), and `Anim_CombatExit` on the finisher's last frame (and on Light1 / Light2 if the string is allowed to end there). `Anim_OpenCancel` can be a one-liner on the combo controller or the facade from [[5.Sub-State Machines Combat Locomotion]]:
```csharp
public void Anim_OpenCancel() => animator.SetBool(CanCancelHash, true);
public void Anim_CloseCancel() => animator.SetBool(CanCancelHash, false);
```
Add a `HitStopService` to a bootstrap object in the scene and reference it on both the player and the dummy.
## 6.3 Dummy
```
Dummy (layer Enemy)
CombatVictim (team Enemy, maxHealth 40)
KnockbackReceiver
Hurtbox child, layer EnemyHurtbox
Animator with Idle / Hit / Die (Hit may be a flinch clip, or a color flash if you have no clip)
```
No hitbox required. The dummy exists to be hit.
A minimal `CombatVictim` already plays Impulse, VFX, hit-stop and knockback. If you want the dummy to flash, hook a material color in a tiny observer:
```csharp
using UnityEngine;
public sealed class HurtFlash : MonoBehaviour
{
[SerializeField] Renderer body;
[SerializeField] Color flash = Color.white;
[SerializeField] float seconds = 0.08f;
Color _base;
float _until;
void Awake()
{
if (body != null) _base = body.material.color; // instanced material; see pitfalls
}
public void Flash()
{
if (body == null) return;
body.material.color = flash;
_until = Time.unscaledTime + seconds;
}
void Update()
{
if (body == null || _until <= 0f) return;
if (Time.unscaledTime >= _until)
{
body.material.color = _base;
_until = 0f;
}
}
}
```
Call `Flash()` from `ApplyDamage`. `Renderer.material` instantiates a material at runtime; acceptable on one dummy, not on a crowd ([[URP Shaders and Shader Graph]] / a MaterialPropertyBlock is the better path when you batch).
## 6.4 Playtest checklist
1. One tap: Attack1 plays, dummy takes 6, short freeze, spark, camera kick, dummy flinches. Exactly one damage tick.
2. Tap-tap inside the cancel window: Attack1 -> Attack2, two hits, two freezes.
3. Tap-tap-tap: the finisher's longer freeze and heavier Impulse are obvious. Dummy dies on or before the third hit at 40 HP (6+8+16=30, so raise dummy HP or add a second dummy).
4. Mash during windup: Attack2 does **not** start until the cancel event.
5. Dodge through a *second* dummy that has a slow swing (or temporarily give the dummy a Hitbox and an Attack clip): you pass through on i-frames; you are hit if you dodge too early.
6. Stand inside the dummy's hurtbox and start a swing: you still get one hit (overlap-on-enable).
7. Pause the Editor mid-freeze and confirm `Time.timeScale` is not left small after you stop Play Mode. (Play Mode exit should restore it; if you ever set timeScale from an Editor script, you can stall the Editor - part 7.)
## 6.5 Optional: mood and UI
- [[Music Mood Mixer]] can listen to a "in combat" bool.
- [[3. Worked Example - Grade Damage and a Cave]] can pulse a hurt Volume when the *player* is the victim.
- [[Quest Systems]] can listen to a `Killed` event for a "defeat the dummy" objective. Do not poll `FindObjectsOfType`.
## Exercise
Swap the finisher for a charged hold (attack action `performed` starts, `canceled` releases). If the charge never reaches a threshold, play Light1; if it does, skip to a heavy that is not part of the string. This is still one brain and one hit pipeline - only the *request* changes.