Destructing 2D sprites in Unity

Joseph Youngquist
3 min readAug 21, 2023

--

https://en.wikipedia.org/wiki/File:SpaceInvaders-Gameplay.gif

When I started working in Unity, I’ve wanted to work on a clone for Space Invaders. The overall game play is pretty simple, the part that I was struggling to figure out is how to make the bunkers destruct on weapon impacts from the player and from the invaders.

The way the bunkers work is when an impact hits, part of the bunker gets destroyed, weakening its ability to block future hits. First we need to know that there’s been an impact. I’ve used Unity’s tagging ability and set up two tags, one for the player’s laser and the other for the enemy.

private void OnTriggerEnter2D(Collider2D other) {
bool projectileHit = other.gameObject.tag == "PlayerLaser" || other.gameObject.tag == "EnemyProjectile";
if (projectileHit)
{
RegisterHit(other);
}
}

From there the order of operations are

  1. Who hit the bunker? Player or Enemy?
  2. Where did the impact hit on the bunker?
  3. Are there bunker parts left that can block the hit?
  4. Damage the Bunker or let the projectile pass through the bunker

On old 8-bit systems such as the C64 (my first computer) there were hardware registers (on the vic-2) that managed the collision notifications. In Unity we have Colliders, and for 2D games, we’ll use a 2D Box Collider:

In the image above, we have the outline of the sprite (the orange line around the green bunker sprite, a texture) and the Box Collider (the green square around the entire sprite). In old systems the registered hit is on the sprite, in Unity the hit is registered on the collider which presents our first challenge, we need to convert the hit from world coordinates to where on the sprite (texture). We use transform.InverseTransformPoint() to convert the world space position to local position. However, the local point is for the collider, not the bunker. We add 0.5f to normalize the point to the sprite texture and not the collider.

private Vector2 WorldToPixel(Vector3 worldPos)
{
Vector3 localPos = transform.InverseTransformPoint(worldPos);
return new Vector2((localPos.x + 0.5f) * _bunkerTexture.width, (localPos.y + 0.5f) * _bunkerTexture.height);

}

Next, since we don’t know if the bunker has parts left to block a hit, we loop through the column of pixels centered at the hit.x and either loop from the top of the bunker for an enemy hit, or from the bottom from a player hit. If we find an “active” pixel — aka a pixel who’s alpha value is 1 since when we “destroy” the bunker, we just set the alpha at that point to 0.

Color pixelColor = _bunkerTexture.GetPixel(x, y);
if (pixelColor.a == 1f)
{
return (new Vector2(x, y), true);
}

We short circuit the loop with the return on where the impact was and a bool for an impact. If we didn’t find a bunker part then we’d return false.

For the destruction, rather than using GetPixel() we use SetPixel(x,y, Color.Clear)

Player Laser punches hole through bunker

I’ll update this basic logic so that we use an explosive pattern to destroy the bunker and better represent the original.

Up next, I’ll get back to my 2D shooter, but this was an itch that I wanted to scratch for a while now.

--

--

Joseph Youngquist

Veteran to Digital Media publishing, Software Engineering and Architecture starting on a pivot to Unity Development