So far, life has been a little bit too easy on the hero. The marine is impervious to death! Let’s put our Doctor Evil hats on and talk about how we’ll kill our hero (mwuahaha). Death in Bobblehead Wars will work in this fashion:

  • Once the marine gets hurt, the camera will shake and you’ll hear grunting to indicate the marine’s agony.
  • The more hits, the harder the shake.
  • Finally, when the marine suffers that final blow, he’ll emit a blood-curdling cry to signal his inglorious death and his head will pop off. I mean, why wouldn’t it?

You’ll start with the camera shake effect; this script is pre-included with the project for convenience.

Note: This tutorial is part of a collection that teaches Unity development from the ground up. You can read the entire series over here. This series is free and does not require any account creation. All assets are provided. If you find it useful, feel free to buy me a coffee.

Hurting the marine

In the Project window, select the CameraShake script and drag it to Main Camera in the Hierarchy. This script works by providing a shake intensity, then it calls Shake(). Select Main Camera in the Hierarchy and, in the Inspector, change Shake Decay to 0.02 to set the shake’s duration.

This screenshot shows the Camera Shake script with the Shake Decay set 0.02

Next up is providing intensity values. Open PlayerController in your code editor and add the following instance variable:

[SerializeField]
private float[] hitForce;

This provides an array of force values for the camera. Save and switch to Unity. Select SpaceMarine in the Hierarchy and find the Player Controller component in the Inspector. Click the disclosure triangle next to Hit Force and set the Size to 3. Add the following values for the elements: 0.1, 0.2, 0.3:

This screenshot shows the Hit Force set in the Player Controller script

At this point, you could call CameraShake() each time an alien collides with the hero, but it’ll be a little problematic. When the hero is swarmed by aliens, he could die in a matter of seconds, and the game would cease to be fun.

Adding some “buffer”

To avoid this frustration, you’ll add a delay between each hit to give the player time to react and escape a bad situation.

Open PlayerController in your code editor and add the following instance variables

[SerializeField]
private float timeBetweenHits = 2.5f;

private bool isHit = false;
private float timeSinceHit = 0;
private int hitNumber = -1;
  • timeBetweenHits is the grace period after the hero sustains damage.
  • isHit is a flag that indicates the hero took a hit.

The above variables mean the hero won’t accrue damage for a period of time after taking a hit.

  • timeSinceHit tracks of the amount of time in the grace period.
  • hitNumber references the number of times the hero took a hit. It’s also used to get the shake intensity for the camera shake.

Add the following method:

void OnTriggerEnter(Collider other) 
{
  Alien alien = other.gameObject.GetComponent<Alien>();
  if (alien != null) 
  { // 1
    if (!isHit) 
    {
      hitNumber += 1; // 2
      CameraShake cameraShake = Camera.main.GetComponent<CameraShake>();
      if (hitNumber < hitForce.Length) // 3 
      { 
        cameraShake.intensity = hitForce[hitNumber];
        cameraShake.Shake();
      } 
      else 
      {
        // death todo
      }
      isHit = true; // 4
      SoundManager.Instance.PlayOneShot(SoundManager.Instance.hurt);
    }
    alien.Die(); 
  }
}

Here’s the breakdown:

  1. First, you check if the colliding object has an Alien script attached to it. If it’s an alien and the player hasn’t been hit, then the player is officially considered hit.
  2. The hitNumber increases by one, after which you get a reference to CameraShake().
  3. If the current hitNumber is less than the number of force values for the camera shake, then the hero is still alive. From there, you set force for the shaking effect and then shake the camera. (You’ll come back to the death todo in a moment.)
  4. This sets isHit to true, plays the grunt sound and kills the alien.

You’re getting closer to making the hero’s day, but not quite there yet. Once isHit is set to true, there’s no unsetting it and the hero becomes invincible — the opposite of what you want to do here.

Add the following code to Update() to strip away those divine powers:

if (isHit) 
{
  timeSinceHit += Time.deltaTime;
  if (timeSinceHit > timeBetweenHits) 
  {
    isHit = false;
    timeSinceHit = 0;
  }
}

This tabulates time since the last hit to the hero. If that time exceeds timeBetweenHits, the player can take more hits.

Play your game and charge those aliens. Resistance is futile.

This screenshot shows the space marine between attacked by a horde of aliens.

The hero will now react to each hit until the maximum number of hits. At that point, nothing will happen — but you’ll fix that next, in a dramatic way!

Removing the head in code

There’s nothing more terrifying to a bobblehead than losing its precious head. Your space marine is no different. It’s only fitting that the head rolls off the body to make the game’s end spectacular and undeniable.

Time for a little more scripting. Open PlayerController in your editor and add the following instance variables:

[SerializeField]
private Rigidbody marineBody;

private bool isDead = false;

marineBody is, well, the marine’s body. You’re adding this because you’ll make some physics alterations when the hero dies. And, of course, isDead keeps track of the player’s current death state. Together, they let the script know if the space marine is dead.

Now you need a method that manages the death, so add the following:

public void Die() 
{
  bodyAnimator.SetBool("IsMoving", false);
  marineBody.transform.parent = null;
  marineBody.isKinematic = false;
  marineBody.useGravity = true;
  marineBody.gameObject.GetComponent<CapsuleCollider>().enabled = true;
  marineBody.gameObject.GetComponent<Gun>().enabled = false;
}

You set IsMoving to false since the marine is dead and you don’t want a zombie running around. Next, you set the parent to null to remove the current GameObject from its parent. Then, by enabling Use Gravity and disabling IsKinematic, the body will drop and roll, and you enable a collider to make this all work. Disabling the gun prevents the player from firing after death.

At this point, the head and body still share a joint. You need to destroy the hinge joint to enable the hero’s inevitable disembodiment. Add the following code beneath the previous block:

Destroy(head.gameObject.GetComponent<HingeJoint>());
head.transform.parent = null;
head.useGravity = true;
SoundManager.Instance.PlayOneShot(SoundManager.Instance.marineDeath);
Destroy(gameObject);

First, this destroys the joint to release the head from the body. Then, like the body, you remove the parent and enable gravity. Now that head can roll! Finally, you destroy the current GameObject while playing the death sound.

Now to trigger the death. In OnTriggerEnter() in the else block of the condition hitNumber < hitForce.Length, replace the comment with the following:

Die();

Save the file and go back to Unity.

Adjusting the head in Unity

Select SpaceMarine in the Hierarchy. In the Inspector, drag the BobbleMarine-Body to the new Marine Body property.

This screenshot shows the BobbleMarine-Body being dragged to the Marine Body.

The space marine’s body can fall now, but you need to attach a collider to it. Select BobbleMarine-Body and click the Add Component button. Select the Physics category and choose Capsule Collider.

Set Center to (-1.61, 3.58, 0), Radius to 2.08 and Height to 0.24. Make sure the Direction is set to Y-axis. Finally, disable the collider by unchecking the checkbox in the upper left of the component.

This screenshot shows the capsule collider disabled.

Currently, the GameManager may spawn more aliens after the space marine dies. There’s no need to subject the hero to an inevitable feeding frenzy!

Open GameManager in your code editor, and add the following before any of the existing code in Update():

if (player == null) 
{
  return;
}

If there’s no player, the GameManager won’t spawn enemies. Save and switch to Unity. Play your game and charge the aliens. After a bit of time, the marine successfully loses that head. Why is the marine’s body still standing around?

This shows the space marine's head missing rolling off his body

Adjusting gravity

This strange behavior is due to gravity and scaling. The typical gravity level is set to -9.81. If you scaled the models to one meter per unit, then they’d play well with gravity. However, your models are somewhat larger. You can fix this by scaling down the models or scaling up the gravity — you’ll scale up the gravity in this book.

Click Edit ▸ Project Settings ▸ Physics and set Gravity to (0, -150, 0).

Now the player’s death makes both the head and body fall like you’d see in a good episode of Game of Thrones.

Setting up the alien head

If the space marine gets his head knocked off, it seems only fair for the alien to suffer the same fate. In the Project window, open the Prefabs folder and double-click the Alien prefab. This will open your Prefab editor.

From the models folder, drag a BobbleEnemy-Head. You’ll discover the material is not configured correctly.

This screenshot shows the alien's head without any texture.

You already have a material configured for the alien. Open the materials folder, and drag the Main Texture.001 onto the alien head.

This screenshot shows the Main Texture.001 being dragged on the model.

Set the head to the following transform, rotation, and scale:

  • Position: (0, 2.442, 0.470)
  • Rotation: (-90, 0, 0)
  • Scale: (64.123, 64.123, 64.123)

The second head should match the dimensions of the first head. It doesn’t need to perfect. Feel free to eyeball it in the case it doesn’t match.

This shows the alien head added to the Alien GameObject.

Click the Add Component button and select Rigidbody from the physics category. Uncheck the Use Gravity option and check the Is Kinematic option.

Click Add Component once again, and select Sphere Collider from the physics category. Set Center to (0, 0, 0.02) and Radius to 0.03. Disable the component by unchecking the component checkbox.

Finally, assign it to the layer Alien Head and select Yes, change children when prompted. Finally, click Edit ▸ Project Settings ▸ Physics. Disable the Alien Head on the Player column.

This shows the collision matrix of update for the alien head.

Writing the decapitation code

Open the Alien script and add the following instance variables:

[SerializeField]
private Rigidbody head;

[SerializeField]
private bool isAlive = true;

head will help you launch the head, and isAlive will track the alien’s state.

Save and switch back to Unity. Drag the BobbleEnemy-Head to the Head property on the Alien script. Switch back to code. Replace Die() with the following:

public void Die() 
{
  isAlive = false;
  head.GetComponent<Animator>().enabled = false;
  head.isKinematic = false;
  head.useGravity = true;
  head.GetComponent<SphereCollider>().enabled = true;
  head.gameObject.SetActive(true);
  head.gameObject.transform.parent = null;
  head.linearVelocity = new Vector3(0, 26.0f, 3.0f);
}

This code works similarly to the marine’s death, but you disable the Animator for the alien. In addition, this code launches the head off the body. Add this code before the closing brace:

OnDestroy.Invoke();
OnDestroy.RemoveAllListeners();
SoundManager.Instance.PlayOneShot(SoundManager.Instance.alienDeath);
Destroy(gameObject);

You want to make sure that you don’t call Die() more than once, so modify OnTriggerEnter() as follows:

void OnTriggerEnter(Collider other) 
{
  if (isAlive) 
  {
    Die();
    SoundManager.Instance.PlayOneShot(SoundManager.Instance.alienDeath);
  }
}

Here you check isAlive first. Finally, in Update(), surround the existing navigation code with a similar check to see if the alien is alive.

if (isAlive) 
{
    agent.destination = target.position;
}

Save and switch to Unity. Play the game. You’ll quickly discover lots of heads.

This screenshot shows lots of alien heads.

Head clean-up on aisle 2

It won’t take long to accumulate an excess of disembodied alien heads. After a while, the space marine will struggle to differentiate the dead aliens from the living. Stop your game. Open the Alien Prefab and select the BobbleEnemy-Head. In the Inspector, click the Add Component button and select New Script.

Name it SelfDestruct and click the Create and Add button. Open it in your editor. Inside the class, replace the contents of the class with the following:

[SerializeField]
private float destructTime = 3.0f;

public void Initiate() 
{
    Invoke("selfDestruct", destructTime);
}

private void selfDestruct()   
{
    Destroy(gameObject);
}

This deletes the attached GameObject after a given amount of time. You could have named it AlienHead, but its general name makes it available throughout the game.

Open Alien and add the following to Die() before the Destroy(gameObject); line:

head.GetComponent<SelfDestruct>().Initiate();

Now play the game and watch those heads disappear after three seconds. Apologies to all the head hunters out there.

Where to go from here

That was a lot of decapitation! Hopefully, underneath all the rolling heads is a fun game to play. Removing the heads after a period of time is a minor optimization and it declutters the arena. This is something you don’t need to implement it in your own game.

There’s still more to be done. For instance, it would be nice to see lots of blood spray out of the decapitated head. I never said this was a PG game. You’ll employ some particle systems for this and you’ll do this in the next tutorial.


Discover more from Jezner Books

Subscribe to get the latest posts sent to your email.

By Brian Moakley

Brian Moakley is a writer and editor who lives amongst the quiet hills in New England. When not reading tales of high adventure, he is often telling such stories to all who will listen.

Related Post

Leave a Reply