Stage 7: Arrows That Hit
arrows that damage slimes, and slimes that die at zero health
2D trigger collisions and a health variable
real combat — towers kill the wave
The big idea
Your arrows fly right through slimes like ghosts. This stage makes them connect. For two objects to notice each other touching, Unity needs to detect overlap, and that's the job of a Collider2D — an invisible shape around an object. When we tick a collider's Is Trigger box, it stops blocking movement and instead just reports overlaps. Perfect for an arrow: we don't want it to bump a slime aside, we want it to register a hit.
Detecting overlap also needs motion that the physics system can see. So one of the two objects also needs a Rigidbody2D — the component that hands an object to Unity's physics. We'll set it to Kinematic, which means "I move it by script, not by gravity," so nothing falls or bounces. The rule to remember: a 2D trigger only fires if at least one of the two objects has a Rigidbody2D, and both have a Collider2D with Is Trigger on.
Finally, the arrow has to tell a slime from a tile from another arrow. We label slimes with a Tag — the exact text "Enemy" — and the arrow checks that tag with CompareTag before doing damage. When the overlap happens, Unity calls a special method, OnTriggerEnter2D, and hands us the other collider.
- Collider2D
- An invisible 2D shape that lets Unity detect when objects touch or overlap.
- Is Trigger
- A collider setting that makes it report overlaps instead of physically blocking — used for hits and pickups.
- Rigidbody2D
- The component that hands an object to the physics system. Required on at least one object for triggers to fire.
- Kinematic
- A Rigidbody2D mode meaning 'moved by script, not by gravity' — no falling, no bouncing.
- Tag
- A short text label on a GameObject. Here we tag slimes 'Enemy' so arrows know what they hit.
- OnTriggerEnter2D
- A method Unity calls automatically the moment two trigger colliders start overlapping.
- CompareTag
- Checks whether a GameObject has a given tag — safer than comparing the tag text by hand.
Finish Stage 6: Towers That Fire — you need towers that auto-fire Arrow projectiles, and the slimes from Stage 1.
Build it
Step 1 — Give the slime health
Open Enemy.cs. We add health in two passes; press Play after each.
Pass 1 — track health and react to a hit. Add a health field and a TakeDamage method that just logs for now:
public int health = 3;
public void TakeDamage(int amount)
{
health -= amount;
Debug.Log("Slime hit! Health now " + health);
}
Add those inside the existing Enemy class, alongside speed and gateX. You can't trigger a hit yet, but the code compiles and the slime now carries health. Press Play to confirm no red errors. Stop.
Pass 2 — die at zero. A slime with no health should disappear. Add the check:
The full script, line by line
The Stage 7 additions to Enemy.cs: the slime carries health and removes itself when that health runs out.
public int health = 3;
public void TakeDamage(int amount)
{
health -= amount;
if (health <= 0)
{
Destroy(gameObject);
}
}
Line 1Health is public and tunable
health starts at 3, so it takes three arrows of 1 damage to fall. Being public, you can make tougher slimes by raising it in the Inspector.
Lines 3–5TakeDamage is called from outside
It's public so the arrow can call it. Each call subtracts amount from health — the arrow decides how much damage that is.
Lines 6–9Remove the slime at zero
Once health drops to zero or below, Destroy(gameObject) removes the whole slime from the Scene. We check <= 0, not == 0, in case a big hit overshoots past zero.
These lines go inside Enemy, which still has its Stage 1 Update that walks the slime left toward gateX. We're adding to the class, not replacing it.
Step 2 — Set up the colliders and tag
This is the physics plumbing that makes hits possible. Do all three pieces, then test.
Build this GameObjectSlime (combat setup)
2D Sprite (existing)Open recipe
Slime (combat setup)
2D Sprite (existing)- Sprite
- the green slime from your sliced tilemap
- Tag
- "Enemy" (set in the Tag dropdown at the top of the Inspector)
- Collider2D → Is Trigger
- checked
- Rigidbody2D → Body Type
- Kinematic
- Rigidbody2D → Gravity Scale
- 0 (so it never falls)
- Sprite Renderer
- Enemy.cs
- Collider2D (Is Trigger ON)
- Rigidbody2D (Body Type = Kinematic)
The Rigidbody2D is what makes triggers actually fire. Kinematic means the slime keeps moving by your Update script, with no gravity or bouncing. Set the Tag to Enemy on the Slime prefab so every spawned slime is tagged.
Build this GameObjectArrow (combat setup)
2D Sprite (existing)Open recipe
Arrow (combat setup)
2D Sprite (existing)- Sprite
- an arrow sprite from your sliced tilemap
- Collider2D → Is Trigger
- checked
- Sprite Renderer
- Projectile.cs
- Collider2D (Is Trigger ON)
The arrow needs a trigger collider too. It doesn't need its own Rigidbody2D — the slime's covers the pair. Add the collider to the Arrow prefab so every fired arrow has it.
Step 3 — Make the arrow deal damage
Now the payoff. Add OnTriggerEnter2D to Projectile.cs. This is the third pass that ties towers to slimes.
The full script, line by line
The Stage 7 addition to Projectile.cs: when an arrow overlaps something tagged Enemy, it damages that slime and removes itself.
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Enemy"))
{
Enemy enemy = other.GetComponent<Enemy>();
if (enemy != null)
{
enemy.TakeDamage(damage);
}
Destroy(gameObject);
}
}
Line 1Unity hands us the other collider
OnTriggerEnter2D runs automatically the instant the arrow's trigger overlaps another trigger. 'other' is whatever the arrow touched.
Line 3Only react to slimes
CompareTag('Enemy') checks the other object's tag. If the arrow grazes a tile or another arrow, the tag won't match and we ignore it.
Lines 5–9Find the Enemy script and hurt it
GetComponent<Enemy>() reaches into the slime for its Enemy script, then calls TakeDamage with the arrow's damage value. The null check guards against an object tagged Enemy that somehow lacks the script.
Line 10The arrow is spent
An arrow hits once. Destroy(gameObject) removes it so a single arrow can't damage two slimes.
This method goes inside Projectile, next to the Stage 6 Update that flies the arrow right and destroys it off-screen.
Press Play, place a tower, and let a slime walk into the lane. Three arrows should drop one slime.
Understand it
Triggers are the right tool here, not solid collisions. A solid collision would make the arrow shove the slime or stop dead against it — wrong for a projectile. A trigger lets the arrow pass into the slime's space and simply report the touch, so we decide what a "hit" means in code: subtract health, destroy the arrow. That separation — physics detects, your script decides — is the whole point of triggers.
The single most confusing rule in 2D combat is the Rigidbody2D requirement. It feels like two colliders should be enough, but Unity's physics only watches objects that have a Rigidbody2D. We put a Kinematic one on the slime so it's "in the physics world" without falling, and that one body covers every arrow that hits it. Tagging is the other half: CompareTag("Enemy") keeps arrows from reacting to tiles, towers, or each other, and it's safer than typing the tag string into an if by hand, where a typo would silently never match.
Try this
Try this
Three short experiments. Predict before you run, then test your guess.
Set a slime's health to 1 in the Inspector. Before you press Play, decide how many arrows it takes to drop it now, and whether the rest of the arrows in flight still matter. Run it and watch.
Uncheck Is Trigger on the Arrow's collider and run it. Arrows now bump or pass slimes without ever dealing damage, because OnTriggerEnter2D only fires for triggers. Re-check it. Triggers, not solid colliders, are what make hits work here.
Right now your only income is the slow gold trickle from Stage 4. In Stage 8 you'll add a Gold Mine that pays out over time and a second kind of tower — with almost no new code. Look at how your Archer Tower is built from a few small scripts. Which of those scripts could a different tower reuse with just different numbers?
Test your stage
- An arrow that overlaps a slime makes the slime lose health (watch the Console or the
healthfield). - A slime disappears after taking enough hits (three arrows of 1 damage by default).
- Each arrow is destroyed on its first hit and never damages two slimes.
- Arrows ignore tiles, towers, and other arrows — only slimes take damage.
- Design check. Watch a tower fight a slime. Do three hits feel like a fair trade for the gold the tower cost, or does the slime die too easily / shrug off arrows? Tune
healthanddamageuntil a kill feels earned but achievable.
If it breaks
- Arrows pass straight through slimes, no damage. This is the classic 2D-trigger trap. Check all three: the slime has a Rigidbody2D (Kinematic), and both the slime and the arrow have a Collider2D with Is Trigger checked. Without a Rigidbody2D on at least one of them, triggers never fire.
OnTriggerEnter2Dnever runs. Same cause as above, plus make sure the method name is spelled exactlyOnTriggerEnter2D(notOnTriggerEnter, which is the 3D version).- Arrows hit, but slimes never lose health. The slime's Tag isn't exactly
"Enemy", soCompareTagreturns false. The tag is case-sensitive and must match character for character. - A red
NullReferenceExceptionwhen an arrow hits. Something tagged"Enemy"doesn't actually have theEnemyscript. The null check guards the damage call, so confirm your Slime prefab hasEnemy.csattached. - The slime falls or drifts. Its Rigidbody2D isn't Kinematic, or Gravity Scale isn't
0. Set Body Type to Kinematic so yourUpdateis the only thing moving it.