Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon

Building Mobile Games with Crafty.js and PhoneGap, Part 3

Save for later
  • 9 min read
  • 13 Jul 2015

article-image

In this post, we will build upon what we learned in our previous series on using Crafty.js, HTML5, JavaScript, and PhoneGap to make a mobile game. In this post we will add a trigger to call back our monster AI, letting the monsters know it’s their turn to move, so each time the player moves the monsters will also move.

Structuring our code with components

Before we begin updating our game, let’s clean up our code a little bit. First let’s abstract out some of the code into separate files so it’s easier to work, read, edit, and develop our project. Let’s make a couple of components. The first one will be called PlayerControls.js and will tell the system what direction to move an entity when we touch on the screen. To do this, first create a new directory under your project WWW directory called src. Then create a new directory in src called com . In the folder create a new file called PlayerControls.js. Now open the file and make it look like the following:

// create a simple object that describes player movement
Crafty.c("PlayerControls", {
                           init: function() {
                   //lets now make the hero move where ever we touch
                       Crafty.addEvent(this, Crafty.stage.elem, 'mousedown', function(e) {
                       // lets simulate a 8 way controller or old school joystick
                       //build out the direction of the mouse point. Remember that y increases as it goes 'downward'
 
                       if (e.clientX < (player.x+Crafty.viewport.x) && (e.clientX - (player.x+Crafty.viewport.x))< 32)
                           {
             myx = -1;
                           }
               else if (e.clientX > (player.x+Crafty.viewport.x) && 
(e.clientX - (player.x+Crafty.viewport.x)) > 32){
       myx = 1;
               }
               else {
       myx = 0;
           }
 
 
               if (e.clientY < (player.y+Crafty.viewport.y) && 
(e.clientY - (player.y+Crafty.viewport.y))< 32)
                           {
             myy= -1;
                           }
               else if (e.clientY > (player.y+Crafty.viewport.y) && 
(e.clientY - (player.y+Crafty.viewport.y)) > 32){
       myy= 1;
               }
               else {
       myy = 0;}
               // let the game know we moved and where too
               var direction = [myx,myy];
               this.trigger('Slide',direction);
                Crafty.trigger('Turn');
                       lastclientY = e.clientY;
                       lastclientX = e.clientX;
                       console.log("my x direction is " + myx + " my y direction is " + myy)
                console.log('mousedown at (' + e.clientX + ', ' + e.clientY + ')');
                });
                }
});

You will note that this is very similar to the PlayerControls  component in our current index.html. One of the major differences is now we are decoupling the actual movement of our player from the mouse/touch controls. So if you look at the new PlayerControls component you will notice that all it does is set the X and Y direction, relative to a player object, and pass those directions off to a new component we are going to make called Slide. You will also see that we are using crafty.trigger to trigger an event called turn. Later in our code we are going to detect that trigger to active a callback to our monster AI letting the monsters know it’s their turn to move, so each time the player moves the monsters will also move.

So let’s create a new component called Slide.js and it will go in your com directory with PlayerControls.js. Now open the file and make it look like this:

Crafty.c("Slide", {
   init: function() {
     this._stepFrames = 5;
     this._tileSize = 32;
     this._moving = false;
     this._vx = 0; this._destX = 0; this._sourceX = 0;
     this._vy = 0; this._destY = 0; this._sourceY = 0;
     this._frames = 0;
 
     this.bind("Slide", function(direction) {
       // Don't continue to slide if we're already moving
       if(this._moving) return false;
       this._moving = true;
 
       // Let's keep our pre-movement location
       this._sourceX = this.x;
       this._sourceY = this.y;
 
       // Figure out our destination
       this._destX = this.x + direction[0] * 32;
       this._destY = this.y + direction[1] * 32;
 
       // Get our x and y velocity
       this._vx = direction[0] * this._tileSize / this._stepFrames;
       this._vy = direction[1] * this._tileSize / this._stepFrames;
 
       this._frames = this._stepFrames;
     }).bind("EnterFrame",function(e) {
       if(!this._moving) return false;
 
       // If we'removing, update our position by our per-frame velocity
       this.x += this._vx;
       this.y += this._vy;
      this._frames--;
 
       if(this._frames == 0) {
         // If we've run out of frames,
         // move us to our destination to avoid rounding errors.
         this._moving = false;
         this.x = this._destX;
         this.y = this._destY;
       }
       this.trigger('Moved', {x: this.x, y: this.y});
     });
 
   },
   slideFrames: function(frames) {
       this._stepFrames = frames;
   },
 
   // A function we'll use later to
   // cancel our movement and send us back to where we started
   cancelSlide: function() {
     this.x = this._sourceX;
     this.y = this._sourceY;
     this._moving = false;
   }
}); 

As you can see, it is pretty straightforward. Basically, it handles movement by accepting a direction as a 0 or 1 within X and Y axis’. It then moves any entity that inherits its behavior some number of pixels; in this case 32, which is the height and width of our floor tiles.

Now let’s do a little more housekeeping. Let’s pull out the sprite code in to a Sprites.js file and the asset loading code into a Loading.js  file. So create to new files, Sprites.js and Loading.js respectively, in your com directory and edit them to looking like the following two listings.

Sprites.js:

Crafty.sprite(32,"assets/dungeon.png", {
floor: [0,1],
wall1: [18,0],
stairs: [3,1]
});
 
// This will create entities called hero1 and blob1
Crafty.sprite(32,"assets/characters.png", {
hero: [11,4],
goblin1: [8,14]
});

Loading.js:

Crafty.scene("loading", function() {
                     //console.log("pants")
   Crafty.load(["assets/dungeon.png","assets/characters.png"], function() {
         Crafty.scene("main"); // Run the main scene
         console.log("Done loading");
     },
   function(e) { //progress
     },
     function(e) { //somethig is wrong, error loading
                      console.log("Error,failed to load", e)
 
   });
});

Okay, now that is done let’s redo our index.html to make it cleaner:

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at R$50/month. Cancel anytime
<!DOCTYPE html>
<html>
<head></head>
<body>
   <div id="game"></div>
   <script type="text/javascript" src="lib/crafty.js"></script>
   <script type="text/javascript" src="src/com/loading.js"></script>
   <script type="text/javascript" src="src/com/sprites.js"></script>
   <script type="text/javascript" src="src/com/Slide.js"></script>
   <script type="text/javascript" src="src/com/PlayerControls.js"></script>
 
   <script>
                                                // Initialize Crafty
                        Crafty.init(500, 320);
                                                // Background
   Crafty.background('green');
 
                        Crafty.scene("main",function() {
 
                           Crafty.background("#FFF");
                           player = Crafty.e("2D, Canvas,PlayerControls, Slide, hero")
                                 .attr({x:0, y:0})
 
                           goblin = Crafty.e("2D, Canvas, goblin1")
                                 .attr({x:50, y:50});
 
                        });
                        Crafty.scene("loading");
   </script>
</body>
</html>

Go ahead save the file and load it in your browser. Everything should work as expected but now our index file and directory is a lot cleaner and easier to work with. Now that this is done, let’s get to giving the monster the ability to move on its own.

Monster fun – moving game agents

We are up to the point that we are able to move the hero of our game around the game screen with mouse clicks/touches. Now we need to make things difficult for our hero and make the monster move as well. To do this we need to add a very simple component that will move the monster around after our hero moves. To do this create a file called AI.js in the com directory. Now open it and edit it to look like this:

 
              Crafty.c("AI",{
                        _directions: [[0,-1], [0,1], [1,0], [-1,0]],
                        init: function() {
                           this._moveChance = 0.5;
                           this.requires('Slide');
 
                           this.bind("Turn",function() {
                             if(Math.random() < this._moveChance) {
                               this.trigger("Slide", this._randomDirection());
                             }
                           });
                        },
                        moveChance: function(val) {
                           this._moveChance = val;
                        },
                        _randomDirection: function() {
                           return this._directions[Math.floor(Math.random()*4)];
                        }
                        });

As you can see all AI.js does, when called, is feed random directions to slide. Now we will add the AI component to the goblin entity. To do this editing your
index.html to look like the following:

<!DOCTYPE html>
<html>
<head></head>
<body>
   <div id="game"></div>
   <script type="text/javascript" src="lib/crafty.js"></script>
   <script type="text/javascript" src="src/com/loading.js"></script>
   <script type="text/javascript" src="src/com/sprites.js"></script>
   <script type="text/javascript" src="src/com/Slide.js"></script>
   <script type="text/javascript" src="src/com/AI.js"></script>
   <script type="text/javascript" src="src/com/PlayerControls.js"></script>
   <script>
                        Crafty.init(500, 320);
   Crafty.background('green');
 
                        Crafty.scene("main",function() {
 
                           Crafty.background("#FFF");
                           player = Crafty.e("2D, Canvas,PlayerControls, Slide, hero")
                                 .attr({x:0, y:0})
 
                           goblin = Crafty.e("2D, Canvas, AI, Slide, goblin1")
                                 .attr({x:50, y:50});
                        });
                        Crafty.scene("loading");
   </script>
</body>
</html>

Here you will note we added a new entity called goblin and added the components Slide and AI. Now save the file and load it. When you move your hero you should see the goblin move as well like in this screenshot:

building-mobile-games-craftyjs-and-phonegap-part-3-img-0

Summary

While this was a long post, you have learned a lot. Now that we have the hero and goblin moving in our game, we will build a dungeon in part 4, enable our hero to fight goblins, and create a PhoneGap build for our game.

About the author

Robi Sen, CSO at Department 13, is an experienced inventor, serial entrepreneur, and futurist whose dynamic twenty-plus year career in technology, engineering, and research has led him to work on cutting edge projects for DARPA, TSWG, SOCOM, RRTO, NASA, DOE, and the DOD. Robi also has extensive experience in the commercial space, including the co-creation of several successful start-up companies. He has worked with companies such as UnderArmour, Sony, CISCO, IBM, and many others to help build out new products and services. Robi specializes in bringing his unique vision and thought process to difficult and complex problems, allowing companies and organizations to find innovative solutions that they can rapidly operationalize or go to market with.