Like a lot of people I grew up with video games. But these were quit different from what we have today. Space invaders, Lunar lander, Missile Command and Asteroids look like cave drawings when compared to what is available today.  I have experimented with tools like LightWave and Maya but their costs are prohibitive and they are not really suited for amateur game developers. Unity 3D, on the other hand, is ideally suited for those just getting started with game development. In addition, it can easily support more complex professional games. Their recent announcement for free support for mobile applications means its time for me to make the leap.

A modern game typically requires a lot of people, mainly artists, to create scenes and characters. I can  use tools such as Blender but I am not nearly proficient enough to build the images as well as create the game. I need a game where I can leverage existing art work and just focus on the mechanics of  the game and learning Unity.

What I need is a  2D side scrolling space game. I decided on trying to replicate the Lunar Lander game.

old-lunar-lander

It won’t be an exact match but instead more updated and something that fits with the Unity model. Look around in the Apple and Google app stores and you can find a number of these games. Some are 2D while others are 3D and much more realistic. I am not trying to be the next “Flappy Bird” so I don’t expect to compete with other games. Its all about the learning.

Unity 3D

A lot can bee done with Unity right out of the box. Anything that requires reacting to a user(player) in going to bring up the need to add custom coding. There are two choices for doing this, C# and Javascript.  A lot of the tutorials and examples are in Javascript so I’ll stick with this.

The Game

The point of game is to land the ship on the surface before you run out fuel and crash. In the earlier games the ship would rotate as well as translate. Correcting the rotation makes the game much more difficult to play. For this version I’ll stick with simple translation left, right, up and down. Of course there needs to be a surface to land on. A simple flat surface is boring. Adding some sort of obstacles will make it a bit more challenging.

Things to consider:

  • The ship
  • Obstacles
  • Landing
  • Movement
  • Gravity
  • Fuel
  • Crashing
  • Player controls
  • Scoring
  • Sound

The Ship

Unity can import models from many tools such as Blender and Max 3D. For a mobile game the model can not be too complex. The more detailed the poorer the game performance will be. I found a reasonably sized lunar lander model from NASA that is free to use.

nasa-model

Obstacles

In the original game the surface changed from flat to mountains. I decided to add rocks to a flat surface. In order to make things a bit more complex I added the rocks at random locations and sizes.

rocks rocks2

Landing

The rocks provide obstacles to avoid but there needs to be a ‘safe’ landing place. These are marked ‘green’ so the player can be seen. Since the rocks are randomly placed the landing places need to be adjusted as well. The process is to place a landing spot and then place the rocks. The code has to make sure the rocks are not covering the landing place and that there is enough room for the lander.

Startup code to build the scene:

Declare the rocks and landing pads

var rocks: Transform[];
var landingPads: Transform[];

Find the game object tagged GUI so that we can determine the player’s level. The landing pads are adjusted differently once the player is beyond level one.

Create the landing pads by varying the “x” value.

GUI = GameObject.FindGameObjectWithTag("GUI").GetComponent(InGameGUI);
 if(GUI.playerLevel > 1)
 startx = (GUI.playerLevel * 1.1)* 4895.0;
 else
 startx = 4895.0;
 currentXoffset =startx + 1200*Random.Range(3,10);
 for(i =1; i < numberOfLandingPads; i++) {
 lp = Instantiate(landingPads[0], Vector3 (currentXoffset,-69.0, 514.6719), Quaternion.identity);
 lp.transform.localScale.x = 160;
 lp.transform.localScale.y = 1.1;
 lp.transform.localScale.z = 160;
 lp_locations[lp_locations_index,0] = currentXoffset;
 lp_locations[lp_locations_index,1] = (lp.transform.localScale.x*5);
 
 currentXoffset += (lp.GetComponent.().bounds.size.x*Random.Range(3,6));
 lp_locations_index++; 
 }

Create a 1000 rocks. Each rock is generated in a random x location. The height of each rock is also random( y direction). The game is 2D but I am using Unity in 3D mode. For creating the rocks I am creating a 3D field. At some point I may change the game to be more 3D.  Each rock is check to make sure that it doesn’t  overlap with a landing pad. I didn’t want the code to get stuck in the overlap process so after 10 tries I give up.

for (var x = 0; x < 1000; x++) {  var breakOut=0;  do {  var index = Random.Range(0,4);  var locX = Random.Range(-50000,50000);  var locZ = Random.Range(-3000,2000);  var scaleX = 200;//Random.Range(Random.Range(5,50),Random.Range(150,200));  var scaleY = Random.Range(Random.Range(5,50),Random.Range(70,500));  if(GUI.playerLevel >2)
 {
 scaleY = Random.Range(Random.Range(5,50),Random.Range(70,GUI.playerLevel*500));
 }
 var scaleZ = 400; //Random.Range(Random.Range(5,50),Random.Range(50,100));
 // Debug.Log( " Creating rocks locX "+locX + " locZ " +locZ +" scaleX " +scaleX+ " scaleY " +scaleY); 
 breakOut++;
 if(breakOut > 10)
 {
 // Debug.Log("==============breakOut++++++++++++");
 return;
 }
 } while (checkOverlap(locX) );
 
 rock = Instantiate(rocks[index], Vector3 (locX, 0, locZ), Quaternion.identity);
 rock.transform.localScale.x = scaleX;
 rock.transform.localScale.y = scaleY;
 rock.transform.localScale.z = scaleZ; 
 rock.tag = "rock";
 
}

A lot of values are hardcoded simply for expedience.  Good software practice would be to use variables or contestants

Movement

Since the game has more than one or two controls it requires the addition of buttons. Keyboard controls are not an options and  multi-touch is complicated. I need to control the main engine(up), left and right thrusters and a pause button.

A ParticleEmitter is used to indicate engine or thruster action.

var engineThruster : ParticleEmitter;
var LeftThruster : ParticleEmitter;
var RightThruster : ParticleEmitter;

An audio file is played when the engine is on. While the engine button is pressed the emitter is set too true

// if the Emitter is not running then fire it
// and play the sound
// then move ship up
 if(engineThruster.emit == false)
 {
 GetComponent.().PlayOneShot(engineSound);
 engineThruster.emit = true; 
 }
 moveShip_up();
function moveShip_up(){
 
 var dir:Vector3;
 // if we are out of fuel then do not move the ship
 if(fuelMeterCurentValue == 0)
    return;
 
 // update the fuel status
 updateFuel();
 
// get the local pos
 pos = Camera.main.WorldToScreenPoint(transform.position);
 
 // if the ship is higher than the screen 
 // set the velocity to 0
 if( pos.y >= Screen.height)
 {
     transform.GetComponent.().velocity.y=0.0;
 }
 else
 {
 // yMovement is either 1 or 0 depending on the button pushed
 // it limits movement to X or Y movement only
 // adjust the upward velocity the further away from the ground.
 // the value '200' should be replaced with ratio of the screen 
 // height
 if( (pos.y < ceiling) && (pos.y > Screen.height-200))
 {
     dir = Vector3(0,yMovement*upwardThrust/2.0,0);
 }
 else 
 {
    if( (pos.y < Screen.height-200) && (pos.y > Screen.height/2))
    {
      dir = Vector3(0,yMovement*upwardThrust/1.5,0);
    }
    else
   {
     dir = Vector3(0,yMovement*upwardThrust,0);
   } 
 } 
   // add force to the ship
   GetComponent.().AddForce(dir);

  }
}

Gravity

The assumption is that the planet has gravity. I have left the gravity setting standard as Unity sets it.

Fuel

Fuel usage is adjusted when ever the engine is running. In the FixedUpdate() Unity function the fuel is adjusted:

 fuelMeterCurentValue -=fuelLossRate*Time.deltaTime;

The term  Time.deltaTime increments the fuel usage according to the FixedUpdate() rate. It is standard in Unity to do this when doing something in the fixed update call.

Crashing

There are two ways to fail a landing. One is to land on rocks. The other is to land too fast. A vertical velocity indicator turns red when the ship is landing too fast. When the  ship touches the landing pad the velocity is checked. The function OnCollisionEnter() is called when two objects touch. In this case it will be the ship and either a landing pad or a rock. setting Time.timeScale to zero stops the game play. the GUI.guiMode is set to either win or lose. This will cause the correct screen to be displayed and the score to be adjusted.

 if( theCollision.gameObject.tag == "landingpad" )
 {
   if( (theCollision.relativeVelocity.magnitude > 50.0) )
   {
    Explode(); 
    GUI.guiMode ="Lose";
    Time.timeScale = 0;
  } 
  else
 { 
   Time.timeScale = 0;
   GUI.guiMode ="Win";
 }  
}

PlayerControls

Since this is a mobile game there needs to be buttons for the player. A single touch would work if it was to run the lander engine. Left and right translations are harder. Touch to the left of the lander could go left and the same for right.  Since the lander moves it could move under the touch point and cause the movement to change. Buttons just seem easier.

Unfortunately Unity’s UI is not straight forward.The placement and operation of a button is pretty simple. Buttons are  GUITexture components. Getting the position and sizing correct for different size devices is a challenge. There is talk that future versions of Unity will have better UI tools.

In the FixedUpDate function I test each button.

for (var touch : Touch in Input.touches)
 {
    if (engineButton.HitTest (touch.position))
    {
      // handle engine event
    }
    if (leftThrusterButton.HitTest (touch.position))
    {
      // handle left thruster event
    }
.
.
.
.
}

Scoring

Scoring is pretty straight forward. Land successfully and you get a point and proceed  to the next level. Crash and you have to repeat the level. At each level the landing spots get harder to find. As the level increases I need to increase the fuel(or lower the rate at which its is used).

Sound

Sound is handled from an AudioSource component.

 GetComponent.().PlayOneShot(engineSound);

This plays the sound once. As long as the button is held down the sound will be played over and over. Playing the sound in a loop is possible for something like background music. For sounds like the engine or  thrusters I need short burst of sound.

Screen Shots

The ship approaching a landing pad. The vertical velocity is in white and positive. This indicates that the ship is moving up at rate within the range for landing.

landing-pad

Since the landing pads are randomly placed I found it hard to locate them and no run out of fuel. I added a overhead view in the upper right corner to guide the player towards a landing pad.

over-head

The left corner shows the fuel and velocity levels.

fuel-speed-menu

The ship over the rocks. The vertical velocity is in red and negative. This indicates that the ship is moving down at rate too large to land.

paused

Goggle Play

I decided to put the game on Goggle Play just to see how this process works.

Update: I see one person has complained that at a high level you just crash into the rocks. It could be that this is a fuel issue. The landing pads are too far away for the fuel usage rate.

https://play.google.com/store/apps/details?id=com.punkinsoft&hl=en

Once I get the iOS version to work I’ll put it on the Apple Store as well.

About gricker

Living and learning
This entry was posted in Uncategorized. Bookmark the permalink.