Difference between revisions of "Flappy Tim - Part Two"

From Contraptionmaker Wiki
Jump to: navigation, search
Line 175: Line 175:
 
</code>
 
</code>
  
And one final bug fix. You may have noticed that you get more than one point when hitting a billiard ball. Sometimes the engine detects more than one collision before the part is deleted. You can fix this by adding a check to see if the part is inactive and *not* adding to the score if it is.
+
Another thing you may have noticed that you get more than one point when hitting a billiard ball. Sometimes the engine detects more than one collision before the part is deleted. You can fix this by adding a check to see if the part is inactive and *not* adding to the score if it is.
  
 
The FINAL collision function now looks like this:
 
The FINAL collision function now looks like this:

Revision as of 23:45, 5 September 2014

Flappy Tim - Part Two

If you are just getting started with mods, make sure to first check out Contraption Maker Modding Guide , and Flappy Tim - Part One.

Recapping Part One

Picking up where Flappy Tim - Part one left off, this is what our level looks like right now:

Timpipes.png

And this is the entire Flappy Tim script:

// onStart called when play is hit 
function onStart()
{
}

// onUpdate called every frame
function onUpdate()
{
  if (FlappyTim.getVelX() < 50)
  {
     FlappyTim.addForce(CM.DIR_RIGHT,1);
  }
}

function onKeyDown(keyCode)
{
  if (keyCode == CM.KEY_SPACEBAR)
  {
     FlappyTim.addForce(CM.DIR_UP,5);
  }
}
 
FlappyTim.onCollision = function(otherPart)
{
   partType = otherPart.getType();

   if (partType==CM.PART_BIG_PIPE)
   {
       CM.showGameOver();
   }
}

Camera Control

The biggest problem with this version of Flappy Tim right now is that once you flap your way off the screen - you cannot see Flappy Tim any more. In Flappy Bird, the camera follows the bird throughout the level so the level can be as large as you want it to be. Let’s do the same for Flappy Tim.

Fortunately, we have a global CM function that can do just this. It allows you to center the camera on a point and set the zoom level. We need to make sure this happens every frame, so we’ll put this into the onUpdate function:

function onUpdate()
{
   var x = FlappyTim.getPosX();
   var y = FlappyTim.getPosY();
   CM.zoomAtPoint(x, y, 2);

  if (FlappyTim.getVelX() < 50)
  {
     FlappyTim.addForce(CM.DIR_RIGHT,1);
  }
}

Now when you hit play, the camera will follow right alongside FlappyTim. Now we can make our level a little bigger by adding some more pipes. We’ll also add a pipe at the top so they can’t just fly over the whole level.

Biggerlevel.png

Scoring

That’s more like it! It’s starting to feel like a real game now. One thing that is missing is a score. In Flappy Bird, you get points by making it through each gap. Internally in Contraption Maker we can add invisible collision zones but we have not yet exposed this through the Make Mode tools yet. Instead, we will take advantage of the property of one of the existing Contraption Maker parts, the billiard ball. It is not affected by gravity so we can put them wherever we want. Instead of getting points for passing through the gaps, we’ll let the player get points for hitting one of these billiard balls. Add a ball in each gap between the pipes so it looks like this:

Levelwithballs.png

Now for the script changes. The first thing we’ll do is add a global score variable up at the top of the script:

var score = 0;

Now in the collision function, we’ll check for collisions with the billiard balls and increase the score. For now we’ll just print the score out.

FlappyTim.onCollision = function(otherPart)
{
   partType = otherPart.getType();
	
   if (partType==CM.PART_BILLIARDBALL)
   {
       score++;
       print(“Score:”+score);
   }
   if (partType==CM.PART_BIG_PIPE)
   {
       CM.showGameOver();
   }
}

There is only one problem with this function - when FlappyTim hits a Billiard Ball - the ball is still there and he keeps bumping into it. We need a way to remove the ball from the level. Contraption Maker provides a function to do just that on every part: inactivate. The collision function looks like this now:

FlappyTim.onCollision = function(otherPart)
{
   partType = otherPart.getType();
	
   if (partType==CM.PART_BILLIARDBALL)
   {
       otherPart.inactivate();
       score++;
       print(“Score:”+score);
   }
   if (partType==CM.PART_BIG_PIPE)
   {
       CM.showGameOver();
   }
}

Now let’s display the score so the player can see how they are doing while they play. In the onStart method, you can create a label that displays while the game is running like so:

function onStart()
{
   GameText = UI.createLabel();
   GameText.setPosition(20,20);
   GameText.setPosition(“Ready!”);
}

Now in our collision function where we increase the score, we can just update that text by calling:

GameText.setText(“Score: “+score);

Ending the Level

Finally - let’s give Tim something to shoot for to end the game succesfully. Let’s put a house at the end of the level that he can enter. This is the final version of the level for this tutorial.

Finallevel.png

And let’s add a check for a collision with the house - and set the game text to a congratulatory message.

if (partType==CM.PART_TIM_HOUSE)
{
    GameText.setText(“Congratulations! Final Score: “+score);

    CM.showGameOver();
}

Explosions

I said we’d be adding explosions, so let’s get that in there now as well. When Tim hits a pipe we’ll set off a big explosion and play an explosion sound. The global CM object provides methods for that. You can just add these two lines right before CM.showGameOver();

CM.playSound("Explosion"); 
CM.playEffect("explosion", FlappyTim.getPosX(), FlappyTim.getPosY());

After adding this code you will notice that when Tim hits a pipe, a whole bunch of explosions are created and the sound plays constantly. What is happening is that Tim is still colliding with the pipe, over and over again, creating an infinite number of explosions. Let's fix that.

A few Final Cleanups

Right now the explosion you added is out of control. Tim is colliding with the pipe over and over, spawning explosions and sounds indefinitely. Let’s fix that by inactivating FlappyTim when the game ends.

Just before CM.showGameOver, add this line:

FlappyTim.inactivate();

Another thing you may have noticed that you get more than one point when hitting a billiard ball. Sometimes the engine detects more than one collision before the part is deleted. You can fix this by adding a check to see if the part is inactive and *not* adding to the score if it is.

The FINAL collision function now looks like this:

FlappyTim.onCollision = function(otherPart)
{
   partType = otherPart.getType();
	
   if (partType==CM.PART_BILLIARDBALL)
   {
       if (otherPart.inactive())
          return;
       otherPart.inactivate();
       score++;
       GameText.setText(“Score: “+score);
   }
   if (partType==CM.PART_BIG_PIPE)
   {
       CM.playSound("Explosion");
       CM.playEffect("explosion", FlappyTim.getPosX(), FlappyTim.getPosY());
       FlappyTim.inactivate();
       CM.showGameOver();
   }
   if (partType==CM.PART_TIM_HOUSE)
   {
       GameText.setText(“Congratulations! Final Score: “+score);
       CM.showGameOver();
   }
}

There is just one more thing we need to add to finish the game. Inactivating a part means that you can no longer call methods on it. The object will actually be deleted. So we need to add checks in our onUpdate() and onKeyDown() methods to see if FlappyTim still exists. Otherwise the script will have an error and the program will stop running. At the beginning of each function add these two lines:

if (!FlappyTim) 
   return;

And here is the entire final script:

// ContraptionMaker Javascript
var score = 0;
var gameOver = false;
  
// onStart called when play is hit
function onStart()
{
   GameText = UI.createLabel();
   GameText.setPosition(20,20);
   GameText.setText("FlappyTim!");
}
 
// onUpdate called every frame
function onUpdate()
{	
     if (!FlappyTim)
        return;
 
     var x = FlappyTim.getPosX();
     var y = FlappyTim.getPosY();
     CM.zoomAtPoint(x,y,2);
 
     if (FlappyTim.getVelX() < 50)
     {
        FlappyTim.addForce(CM.DIR_RIGHT,1);
     }
}
  
function onKeyDown(keyCode)
{	
  if (!FlappyTim)
     return;
 
  if (keyCode == CM.KEY_SPACEBAR)
  {
     FlappyTim.addForce(CM.DIR_UP,5);
  }
}
  
FlappyTim.onCollision = function(otherPart)
{
  partType = otherPart.getType();
 
  if (partType==CM.PART_BILLIARDBALL)
  {
     // Do not double count
     if (otherPart.inactive())
        return;
 		
     score ++;
     otherPart.inactivate();
     print("Score: "+score);
     GameText.setText("Score: "+score);
  }
  
 if (partType==CM.PART_BIG_PIPE)
 { 
  CM.showGameOver();
  FlappyTim.inactivate();
  	
  CM.playSound("Explosion");
  CM.playEffect("explosion",FlappyTim.getPosX(),FlappyTim.getPosY());
 }	
 
 if (partType==CM.PART_TIM_HOUSE)
 {
   CM.showGameOver();
 }
}