Unity 4.3 2D Tutorial: Scrolling, Scenes and Sounds

In this final long awaited part of the Unity 2D series, you will create the conga line as well as the win and lose states for the game. By Chris LaPollo.

Leave a rating/review
Save for later
Share
You are currently viewing page 5 of 8 of this article. Click here to view the first page.

Handling Enemy Contact

In Zombie Conga, the player's goal is to gather a certain number of cats into its conga line before colliding with some number of enemies. Or, that will be the goal once you've finished this tutorial.

To make it harder to build the conga line, you'll remove some cats from the line every time an enemy touches the zombie.

To do so, first open CatController.cs in MonoDevelop and add the following method to the class:

public void ExitConga()
{
  Vector3 cameraPos = Camera.main.transform.position;
  targetPosition = new Vector3(cameraPos.x + Random.Range(-1.5f,1.5f),
                               cameraPos.y + Random.Range(-1.5f,1.5f),
                               followTarget.position.z);

  Transform cat = transform.GetChild(0);
  cat.GetComponent<Animator>().SetBool("InConga", false);
}

The first two lines above assign targetPosition a random position in the vicinity of the camera's position, which is the center of the screen. The code you already added to Update will automatically move the cat toward this new position.

The next two lines get the cat from inside the Cat Carrier and disable its Animator's InConga flag. Remember from Unity 4.3 2D Tutorial: Animation Controllers, that you need to set InConga to false in the Animator in order to move the animation out of the CatConga state. Doing so will trigger the cat to play the CatDisappear animation clip.

Save the file (File\Save).

You maintain the conga line in ZombieController, so that's where you'll add a call to ExitConga. Open ZombieController.cs in MonoDevelop now.

Inside the class, find the following line in OnTriggerEnter2D:

Debug.Log ("Pardon me, ma'am.");

And replace it with this code:

for( int i = 0; i < 2 && congaLine.Count > 0; i++ )
{
  int lastIdx = congaLine.Count-1;
  Transform cat = congaLine[ lastIdx ];
  congaLine.RemoveAt(lastIdx);
  cat.parent.GetComponent<CatController>().ExitConga();
}

This for loop may look a little strange, but it's really not doing much. If there are any cats in the conga line, this loop removes the last two of them, or the last one if there is only one cat in the line.

After removing the cat's Transform from congaLine, it calls ExitConga, which you just added to CatController.

Save the file (File\Save) and switch back to Unity.

Run the scene and get some cats in your conga line, then crash into an old lady and see what happens!

bad_enemy_collisions

Unfortunately, when you crashed into the old lady, you crashed right into two more problems.

First, if the conga line had more than two cats when the zombie collided with the enemy, you probably saw every cat spin out of the line. You can see that in the previous animation.

The second problem is yet another exception in the Console:

anim_event_error_2

No receiver, eh? Before fixing the first problem, try debugging the exception yourself. You've already solved an identical problem earlier in this tutorial. If you get stuck, check out the following spoiler.

[spoiler title="Cat not receiving your (function) calls?"]You can fix this in either of two ways.

Option 1: You could add a method like the following to CatUpdater.cs:

void GrantCatTheSweetReleaseOfDeath()
{
  catController.GrantCatTheSweetReleaseOfDeath();
}

However, for that to work, you need to change the declaration of GrantCatTheSweetReleaseOfDeath in CatController.cs so it is public, like this:

public void GrantCatTheSweetReleaseOfDeath()

Option 2: The easier way to handle this situation is to add a method like the following to CatUpdater.cs:

void GrantCatTheSweetReleaseOfDeath()
{
  Destroy( transform.parent.gameObject );
}

This simply tells the cat's parent to remove itself, which in turn removes the cat.
[/spoiler]

With the exception fixed, it's time to figure out how to keep the enemy from destroying your entire conga line with just one hit.

First, what's going on? As you saw in Unity 4.3 2D Tutorial: Physics and Screen Sizes, Unity is reporting quite a few collisions as the zombie walks through the enemy.

For the cats, you solved this problem by disabling the cat's collider when handling the first event. To eliminate redundant enemy collisions, you'll do something a bit fancier.

You're going to add a period of immunity after the initial collision. This is common in many games, where contacting an enemy reduces health or points and then blinks the player's sprite for a second or two, during which time the player can take no damage. And yes, you're going to make the zombie blink, too!

Open ZombieController.cs in MonoDevelop and add the following variables to the class:

private bool isInvincible = false;
private float timeSpentInvincible;

As their names imply, you'll use isInvincible to indicate when the zombie is invincible, and timeSpentInvincible to keep track of for how long the zombie has been invincible.

Inside OnTriggerEnter2D, find the following line:

else if(other.CompareTag("enemy")) {

and replace it with this code:

else if(!isInvincible && other.CompareTag("enemy")) {
  isInvincible = true;
  timeSpentInvincible = 0;

This change to the if condition causes the zombie to ignore enemy collisions while the zombie is invincible. If a collision occurs while the zombie is not invincible, it sets isInvincible to true and resets timeSpentInvincible to zero.

To let the player know they have a moment of invincibility, as well as to indicate that they touched an enemy, you'll blink the zombie sprite.

Add the following code to the end of Update:

//1
if (isInvincible)
{
  //2
  timeSpentInvincible += Time.deltaTime;

  //3
  if (timeSpentInvincible < 3f) {
    float remainder = timeSpentInvincible % .3f;
    renderer.enabled = remainder > .15f; 
  }
  //4
  else {
    renderer.enabled = true;
    isInvincible = false;
  }
}

Here's what this code does:

  1. The first if check verifies that the zombie is currently invincible, because that's the only time you want to execute the rest of this logic.
  2. If so, it adds Time.deltaTime to timeSpentInvincible to keep track of the total time the zombie has been invincible. Remember that you reset timeSpentInvincible to zero when the collision first occurs.
  3. It then checks if the collision occurred less than three seconds ago. If so, it enables or disables the zombie's renderer based on the value of timeSpentInvincible. This bit of math will blink the zombie on and off about three times per second.
  4. Finally, if it's been at least three seconds since the collision, the code enables the zombie's renderer and sets isInvincible to false. You enable the renderer here to ensure the zombie doesn't accidentally stay invisible.

Save the file (File\Save) and switch back to Unity.

Run now and the conga line grows and shrinks as it should.

good_enemy_collisions

Ok, the conga line works, but without a way to win or lose, it's still not a game. (That's right, I said it. If you can't win or lose, it's not a game!) Time to fix that.

Contributors

Over 300 content creators. Join our team.