Difference between revisions of "Flappy Tim - Part Two"

From Contraptionmaker Wiki
Jump to: navigation, search
 
(15 intermediate revisions by the same user not shown)
Line 1: Line 1:
 +
== 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]].
 
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:
 
Picking up where Flappy Tim - Part one left off, this is what our level looks like right now:
Line 7: Line 11:
 
And this is the entire Flappy Tim script:
 
And this is the entire Flappy Tim script:
  
<code>
+
<syntaxhighlight lang="javascript">
 
  // onStart called when play is hit  
 
  // onStart called when play is hit  
 
  function onStart()
 
  function onStart()
Line 18: Line 22:
 
   if (FlappyTim.getVelX() < 50)
 
   if (FlappyTim.getVelX() < 50)
 
   {
 
   {
       FlappyTim.addForce(CM.DIR_RIGHT,1);
+
       FlappyTim.addForce(CM.DIR_RIGHT,1000);
 
   }
 
   }
 
  }
 
  }
Line 26: Line 30:
 
   if (keyCode == CM.KEY_SPACEBAR)
 
   if (keyCode == CM.KEY_SPACEBAR)
 
   {
 
   {
       FlappyTim.addForce(CM.DIR_UP,5);
+
       FlappyTim.addForce(CM.DIR_UP,5000);
 
   }
 
   }
 
  }
 
  }
Line 39: Line 43:
 
     }
 
     }
 
  }
 
  }
</code>
+
</syntaxhighlight>
 +
 
 +
=== 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.
 
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.
Line 45: Line 51:
 
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:
 
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:
  
<code>
+
<syntaxhighlight lang="javascript">
 
  function onUpdate()
 
  function onUpdate()
 
  {
 
  {
Line 54: Line 60:
 
   if (FlappyTim.getVelX() < 50)
 
   if (FlappyTim.getVelX() < 50)
 
   {
 
   {
       FlappyTim.addForce(CM.DIR_RIGHT,1);
+
       FlappyTim.addForce(CM.DIR_RIGHT,1000);
 
   }
 
   }
 
  }
 
  }
</code>
+
</syntaxhighlight>
  
 
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.
 
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.
  
 
[[File:Biggerlevel.png|400px|center]]
 
[[File:Biggerlevel.png|400px|center]]
 +
 +
=== 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:
 
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:
Line 69: Line 77:
 
Now for the script changes. The first thing we’ll do is add a global score variable up at the top of the script:
 
Now for the script changes. The first thing we’ll do is add a global score variable up at the top of the script:
  
<code>
+
<syntaxhighlight lang="javascript">
 
  var score = 0;
 
  var score = 0;
</code>
+
</syntaxhighlight>
  
 
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.
 
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.
  
<code>
+
<syntaxhighlight lang="javascript">
 
  FlappyTim.onCollision = function(otherPart)
 
  FlappyTim.onCollision = function(otherPart)
 
  {
 
  {
Line 90: Line 98:
 
     }
 
     }
 
  }
 
  }
</code>
+
</syntaxhighlight>
  
 
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:
 
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:
  
<code>
+
<syntaxhighlight lang="javascript">
 
  FlappyTim.onCollision = function(otherPart)
 
  FlappyTim.onCollision = function(otherPart)
 
  {
 
  {
Line 110: Line 118:
 
     }
 
     }
 
  }
 
  }
</code>
+
</syntaxhighlight>
  
 
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:
 
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:
  
<code>
+
<syntaxhighlight lang="javascript">
 
  function onStart()
 
  function onStart()
 
  {
 
  {
Line 121: Line 129:
 
     GameText.setPosition(“Ready!”);
 
     GameText.setPosition(“Ready!”);
 
  }
 
  }
</code>
+
</syntaxhighlight>
  
 
Now in our collision function where we increase the score, we can just update that text by calling:
 
Now in our collision function where we increase the score, we can just update that text by calling:
  
<code>
+
<syntaxhighlight lang="javascript">
 
  GameText.setText(“Score: “+score);
 
  GameText.setText(“Score: “+score);
</code>
+
</syntaxhighlight>
  
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.
+
=== Ending the Level ===
 +
 
 +
Finally - let’s give Tim something to shoot for to end the game successfully. 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.
  
 
[[File:Finallevel.png|500px|center]]
 
[[File:Finallevel.png|500px|center]]
Line 135: Line 145:
 
And let’s add a check for a collision with the house - and set the game text to a congratulatory message.
 
And let’s add a check for a collision with the house - and set the game text to a congratulatory message.
  
<code>
+
<syntaxhighlight lang="javascript">
 
  if (partType==CM.PART_TIM_HOUSE)
 
  if (partType==CM.PART_TIM_HOUSE)
 
  {
 
  {
 
     GameText.setText(“Congratulations! Final Score: “+score);
 
     GameText.setText(“Congratulations! Final Score: “+score);
 
+
 
     CM.showGameOver();
 
     CM.showGameOver();
 
  }
 
  }
</code>
+
</syntaxhighlight>
 +
 
 +
=== 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();
 
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();
  
<code>
+
<syntaxhighlight lang="javascript">
 
  CM.playSound("Explosion");  
 
  CM.playSound("Explosion");  
 
  CM.playEffect("explosion", FlappyTim.getPosX(), FlappyTim.getPosY());
 
  CM.playEffect("explosion", FlappyTim.getPosX(), FlappyTim.getPosY());
</code>
+
</syntaxhighlight>
  
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 by inactivating FlappyTim when the game ends.
+
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:
 
Just before CM.showGameOver, add this line:
  
<code>
+
<syntaxhighlight lang="javascript">
 
  FlappyTim.inactivate();
 
  FlappyTim.inactivate();
</code>
+
</syntaxhighlight>
  
The FINAL collision function now looks like this:
+
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.
  
<code>
+
The final collision function now looks like this:
 +
 
 +
<syntaxhighlight lang="javascript">
 
  FlappyTim.onCollision = function(otherPart)
 
  FlappyTim.onCollision = function(otherPart)
 
  {
 
  {
 
     partType = otherPart.getType();
 
     partType = otherPart.getType();
+
 
     if (partType==CM.PART_BILLIARDBALL)
 
     if (partType==CM.PART_BILLIARDBALL)
 
     {
 
     {
 +
        if (otherPart.inactive())
 +
          return;
 
         otherPart.inactivate();
 
         otherPart.inactivate();
 
         score++;
 
         score++;
Line 185: Line 205:
 
     }
 
     }
 
  }
 
  }
</code>
+
</syntaxhighlight>
  
 
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:
 
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:
  
<code>
+
<syntaxhighlight lang="javascript">
 
  if (!FlappyTim)  
 
  if (!FlappyTim)  
 
     return;
 
     return;
</code>
+
</syntaxhighlight>
 +
 
 +
And here is the entire final script:
 +
 
 +
<syntaxhighlight lang="javascript">
 +
// 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,1000);
 +
      }
 +
}
 +
 
 +
function onKeyDown(keyCode)
 +
{
 +
  if (!FlappyTim)
 +
      return;
 +
 
 +
  if (keyCode == CM.KEY_SPACEBAR)
 +
  {
 +
      FlappyTim.addForce(CM.DIR_UP,5000);
 +
  }
 +
}
 +
 
 +
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)
 +
  {
 +
    GameText.setText(“Congratulations! Final Score: “+score);
 +
    CM.showGameOver();
 +
  }
 +
}
 +
</syntaxhighlight>
 +
 
 +
=== Next Steps ===
 +
 
 +
* Read the more detailed Javascript API docs: [http://jsdocs.contraptionmaker.net/index.html Documentation]
 +
* Study other mods people have made by finding them in the workshop, flipping the card over, and clicking the edit button (pencil icon).
 +
* Follow Spotkin on Twitter and we will tweet when we post new tutorials on the wiki: [https://twitter.com/intent/user?screen_name=Spotkin Follow Spotkin]

Latest revision as of 23:43, 16 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,1000);
   }
 }
 
 function onKeyDown(keyCode)
 {
   if (keyCode == CM.KEY_SPACEBAR)
   {
      FlappyTim.addForce(CM.DIR_UP,5000);
   }
 }
 
 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,1000);
   }
 }

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 successfully. 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,1000);
      }
 }
 
 function onKeyDown(keyCode)
 {	
   if (!FlappyTim)
      return;
 
   if (keyCode == CM.KEY_SPACEBAR)
   {
      FlappyTim.addForce(CM.DIR_UP,5000);
   }
 }
 
 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)
  {
    GameText.setText(“Congratulations! Final Score:+score);
    CM.showGameOver();
  }
 }

Next Steps

  • Read the more detailed Javascript API docs: Documentation
  • Study other mods people have made by finding them in the workshop, flipping the card over, and clicking the edit button (pencil icon).
  • Follow Spotkin on Twitter and we will tweet when we post new tutorials on the wiki: Follow Spotkin