Gigi Labs

Please follow Gigi Labs for the latest articles. Programmer's Ranch no longer has its domain, so please update your bookmarks and links to programmersranch.blogspot.com.

Friday, May 31, 2013

Unity3D: Space Invaders (Part 4 - Collisions)

Hello again! :)

In the previous article - "Unity3D: Space Invaders (Part 3 - Prefabs)" - we implemented bullet-shooting in our Space Invaders clone. We developed a bullet prefab that we could then create during gameplay when the player shoots at an alien. However, bullets aren't much use if they don't hit something.

In this article, we'll deal with that problem, and allow bullets to destroy aliens upon impact.

First, we need to set up the scene so that the player can actually target an alien when firing. Creating a 2D game in Unity - which uses 3D coordinates - may sometimes be a little bit confusing. To help us get our bearings, let's put create background. From the GameObject menu, select Create Other -> Cube. Rename it to "Background". Resize and move it so that it is very thin, but covers the entire camera view:


Note: we could have used a Plane object here instead of a Cube, but when your game starts to get a little complex, you'll find that a Plane has a number of limitations that a Cube doesn't.

The Alien needs to be directly above the Player, if bullets are to hit it. In the Inspector, set the position of the Player and the Alien so that they have the exact same z-coordinate. Move things around and resize them so that they fit nicely into the camera's view. Give the Background, Player and Alien different coloured materials so that they can be clearly seen from the camera.


Since this looks a bit dull, go to the GameObject menu and select Create Other -> Point Light. Mess with the light's position and intensity (the intensity is in the Inspector when you select the light) until you are happy. In the image below, I also changed the alien's material, since the previous one didn't have very good contrast with the background:


Now, in the Assets panel, double-click the Alien script to open it in MonoDevelop. This is what we had from the article on linear interpolation:

public class Alien : MonoBehaviour
{
    private float startTime;
    
    // Use this for initialization
    void Start ()
    {
        this.startTime = Time.time;
    }
    
    // Update is called once per frame
    void Update ()
    {
        float t = (Time.time - startTime) * 0.4f;
        float x = Mathf.Lerp(-5.0f, 5.0f, t);
        this.transform.position = new Vector3(x, 5, 5);
    }
}

First of all, if you changed position of the Alien, you may need to adjust the code in the Update() method. You can find out the minimum and maximum x coordinates by placing an object at the left and right edges of the Background and taking note of its x-coordinate. A bit of trial and error may also help. For me, this works:

    void Update ()
    {
        float t = (Time.time - startTime) * 0.4f;
        float x = Mathf.Lerp(-2.5f, 2.5f, t);
        this.transform.position = new Vector3(x, this.transform.position.y, this.transform.position.z);
    }

If you didn't do the exercise at the end of the linear interpolation article to make the Alien behave in a proper Space Invaders manner, let's at least make it move back and forth so that the player can hit it at some point. Add this variable to the Alien class:

    private bool movingRight = true;

The Update() method thus becomes:

    void Update ()
    {
        float t = (Time.time - startTime) * 0.4f;
        float x = 0.0f;
        
        if (movingRight)
            x = Mathf.Lerp(-2.5f, 2.5f, t);
        else
            x = Mathf.Lerp(2.5f, -2.5f, t);
        
        if (x >= 2.5f)
        {
            movingRight = false;
            this.startTime = Time.time;
        }
        else if (x <= -2.5f)
        {
            movingRight = true;
            this.startTime = Time.time;
        }
        
        this.transform.position = new Vector3(x, this.transform.position.y, this.transform.position.z);
    }

All this means is that the Alien starts moving right. When it hits the right edge, it starts moving left instead. It keeps moving back and forth between the left and right edges. Try it out in Unity and adjust coordinates if it doesn't feel right:


We are now ready to deal with bullets hitting aliens. Select the Alien, and from the Component menu, select Physics -> Rigidbody, and be sure to turn off "Use Gravity":


This is necessary for the collision to work, as are the box collider on the Alien and the capsule collider on the Bullet. Colliders are just things that help check when an object hits another, and when you create a standard object (such as a cube), colliders are normally added by default.

So now, go double-click the Bullet script to open it in MonoDevelop. Add the following method:

    void OnCollisionEnter(Collision collision)
    {
        GameObject.Destroy(collision.gameObject);
        GameObject.Destroy(this.gameObject);
    }

The OnCollisionEnter() method is called when one collider hits another. The collision parameter gives us information about the collision, including the object was hit (in collision.gameObject). As you can read in the documentation, a rigidbody is required for it to work.

What we are doing here is that when a bullet hits an alien, we use GameObject.Destroy() to remove both the bullet and the alien from the scene. Since the script is running on the bullet, this.gameObject refers to the bullet, and collision.gameObject refers to the alien.

You can now press Play to test this in Unity. Press Space to fire, and when a bullet hits an alien, both should disappear.

There's just one thing that we haven't handled, however: what happens to bullets if they don't hit the alien? As it is, they just keep on accumulating:


Notice the long list of Bullet(Clone)s in the Hierarchy panel. To get rid of bullets that have gone out of view, the easiest way is to just destroy them when they go beyond a certain distance. In the Update() method of the Bullet script, you can add something like this:

        if (this.transform.position.y > 4.8f)
            GameObject.Destroy(this.gameObject);

Awesome! :D In this article, we have handled collisions so that bullets are able to destroy aliens. The game is taking shape.

However, there are still a lot of things we need to do:
  1. Allow the player to move sideways
  2. More aliens
  3. Aliens should move downwards when reaching a side of the game area
  4. Aliens should shoot at the player
  5. There should be a cooldown for firing bullets (e.g. you can only fire one bullet every second)
  6. We need a welcome screen before the game begins
  7. We need a main menu
  8. We need to transition to other levels when all enemies are cleared
  9. We need to show some kind of score
As you can see, creating a game can involve a lot of work, and much of game design is tweaking things until the game feels right and is fun! :)

In tomorrow's last article in the Ranch Invaders series, we will cover the first few points in order to have a single playable level. We will be covering the remaining points as well, but not as part of the Ranch Invaders series!

No comments:

Post a Comment