Saturday, September 3, 2011

A Journey into Unity

I'm back after far to long to talk about my adventures with Unity, a cross-platform gaming dev tool from Unity Technologies.

I actually heard about Unity when I attended the first meeting of the IDGA Newfoundland chapter (they have a Facebook group...if you live in good ol' Newfoundland and love games, join the group. We need as many people as we can get!). I was talking to some of the local game devs and mentioned that I'm looking to improve my game programming skills. One of them suggested Unity, and gave it high praise. After using it, I can see why...without knowing the first thing about the tool, I had a fully functional (albeit simple) Space Invaders clone whipped up in 6 hours. What did I do to make that happen? Glad you asked! Buckle up!


The above image is what my unity set up looked like after the game was ready to play. Quite simple looking, wouldn't you say? To make the game, I used geometric shapes that come ready to use with Unity (I wasn't worried about making it fancy, just functional). The entire game is made up of seven components, used one or more times:

  • The player's ship object (the square)
  • An enemy (the circles)
  • The bullet (a cylinder)
  • The camera
  • A light source (the little "sun")
  • Sound objects
  • The bullet boundary (large rectangle above all the other objects)
Now, there are some really great resources that give details on how to place the objects, adjust the lighting, e.t.c., so I don't plan on getting into all that. What I do want to talk about is the coding that went behind the objects to transform them from geometric shapes to elements in a game. For this learning exercise, I stuck with good old JavaScript. I'm quite familiar with it thanks to my day job, so I didn't need to learn a new tool AND a new scripting language.

The Player's Ship Object

There are two scripts attached to the player's ship object. One that controls the player's movement, and the other object activates a "Game Over" condition if an enemy should drop low enough to collide with the player.

PlayerMovement.js
var speed = 10.0;

function Update () {
    var translation : float = Input.GetAxis ("Horizontal") * speed;   

    // Make it move 10 meters per second instead of 10 meters per frame...
    translation *= Time.deltaTime;   

    // Move translation along the object's x-axis
    transform.Translate (translation, 0, 0);
}

The above script seems pretty self-explanatory. It sets the player's ship to move at a desirable speed and sets the access along which the movement can occur. One thing worth mentioning is the Input.GetAxis call. You'll note that it's accepting a string, namely "Horizontal". This tells the script to capture the input keys if it is one of the buttons mapped for horizontal control (by default the left and right arrow keys. These keys can be changed via the editor). Finally, the Update function itself is a construct of Unity, and defines the game loop. In programming with Unity, the Update function is your bread and butter...you'll be calling it A LOT.

DestroyPlayer.js
function OnTriggerEnter (other : Collider) {       
    Application.LoadLevel ("GameOver");
}

Simple as it gets with this script. If the player object collides with ANYTHING, load the Game Over level. The Game Over level is simply a screen with the text...you guessed it..."Game Over."

The Bullet Object

PewPew.js
 var shootSound : AudioSource;
var bullet : Rigidbody;
var speed = 10.0;

function FireBullet () {
    var bulletClone : Rigidbody = Instantiate(bullet, transform.position, transform.rotation);
    bulletClone.velocity =   transform.TransformDirection (Vector3.up * speed);
    shootSound.Play();   
}


function Update () {
      if (Input.GetButtonDown("Fire1")) {
        FireBullet();
    }
}

PewPew.js spawns the bullet object, sends it in a vertical direction at a constant speed, and plays the "pew" sound effect. This script was basically taken straight from the api on the Unity site.

The Enemy Object

The two scripts attached to all the enemy objects are DestroyEnemy.js and EnemyMovement.js.

DestroyEnemy.js
var deathSound : AudioSource;
static var enemyCount = 21;

function OnTriggerEnter (other : Collider) {   
    Destroy(other.gameObject);
    Destroy(gameObject);
    deathSound.Play();
    enemyCount = enemyCount - 1;
    print(enemyCount);
    checkGameOver();
}

function checkGameOver () {
    if(enemyCount == 0){
        Application.LoadLevel ("GameOver");
    }

}

So, this script is actually doing a fair bit of work. When an object collides with the enemy, it both destroys the object that collided with it as well as the object itself. It plays the enemy "pop" sound effect as well. Finally, it decrements the enemyCount variable. This was the way I decided to handle the "player win" condition. Simply, there are a total of 21 enemies on the play field. When the enemyCount has hit 0, it takes the player to the game over screen. Destroying the bullet and enemy object were simple calls to the Destroy function (another native Unity function) since I know that the only object that will hit the enemy will be a bullet object. The player may eventually hit an enemy if they drop low enough, but at that point calling this function wouldn't matter, since the game over script attached to the player object would be called and the game would end.

EnemyMovement.js
function Start () {   
    for(var i = 0; i < 6; i++){
            gameObject.transform.Translate (1, 0, 0);
            yield WaitForSeconds (1);       
        }
   
    while(true){
        for(var j = 0; j < 11; j++){
            gameObject.transform.Translate (-1, 0, 0);
            yield WaitForSeconds (1);       
        }
       
        gameObject.transform.Translate (0, -1, 0);
       
        for(var k = 0; k < 11; k++){
            gameObject.transform.Translate (1, 0, 0);
            yield WaitForSeconds (1);       
        }
       
    }
    yield;
}

I think that writing this script was the biggest challenge in getting the game to function similar to Space Invaders. I'll admit that I kind of copped out a bit, as you can see from the script. The movement of the enemies in Space Invaders seems pretty darn simple (move right, drop down, move left, drop down, repeat) but coding it can actually be a bit of a challenge. For instance, if an end row of enemies is destroyed, the enemies do not drop down until the next row hits the edge of the screen. This is a feature that is NOT accounted for in this script...hence saying I copped out. Also, the enemies do not speed up as they're numbers are depleted. The script simply moves each and every enemy object to the right, drops them down, and them moves them to the left at a constant speed. I'd like to improve this script later on to give the game that true Space Invaders feel.

The Bullet Boundary

DestroyBulletAtBoundary.js
function OnTriggerEnter (other : Collider) {
    Destroy(other.gameObject);
}


Another nice, simple script. This is attached to a large, invisible plane hovering above the play field. This was to make sure the bullets do not pile up at the top of the scene and cause eventual slow-down. Plus they could bounce back down and get in the way. Keeps things nice and clean.


And that's pretty much it! The muscle behind my little Space Invaders clone using Unity. Like I said before, I hope to improve on some of these scripts (especially the enemy movement). I'll post those scripts whenever I should write them.

Until next time,

gl hf!