© Frank Zammetti 2018
Frank ZammettiPractical Webixhttps://doi.org/10.1007/978-1-4842-3384-9_10

10. The Fun Side of Webix: A Game!

Frank Zammetti
(1)
Pottstown, Pennsylvania, USA
 
All work and no play makes Jack a dull boy (and a murderer at a snowy resort lodge, as we learned in The Shining). With luck, you’ll avoid that fate, but the point stands: if you don’t stop to smell the roses every now and again, you tend to not have as good a life as you should. This is true for web development and Webix too! (You didn’t think I would be able to pull this back to relevance, did you?)
Throughout this book, you’ve seen Webix through the prism of writing actual useful code and applications. But, nothing says that’s all you can do with Webix. No, you can do something more frivolous, something more fun, something like, say, write a game!
Games are a great project for any developer to undertake because they touch on so many different disciplines in programming, from graphics and sound to AI, data structures, algorithmic efficiency, and so on. In my position as a lead architect, I’m sometimes asked by developers how they can sharpen their skills. My answer is always the same: write a game! I don’t believe any other project allows the diversity and creativity and, therefore, opportunity for learning.
Plus, by their very nature, games are fun to write!
In this chapter, you’ll use Webix to write a game. The benefit in terms of this book is that you’ll get to see a few new Webix facilities and see others in ways you maybe haven’t before. In the end, you’ll learn while ideally having fun.
Let’s kick things off by figuring out what kind of game to make and coming up with what every great game needs: a story!

The Story So Far

The inhabitants of Gorgona 6 are a cosmic contradiction: a technologically advanced civilization that is simultaneously technologically backward! For example, they visited their own moon before figuring out that they should put wheels on luggage and, more importantly for the purposes here, they can build fast, sleek spaceships, but they are pretty wimpy ships that can’t survive much of anything! Just a bump into a space rock is enough to do them in (and being a peaceful people, the Gorgonians never develop weapons of any sort).
This is problematic because their star system has a vermin problem: it’s lousy with space-born critters and dangers! They have the gargantuan space fish of the third moon of Valtrax; the naturally occurring sentient machine beings of protoplanet 10101110; space zombies (but who doesn’t have space zombies, amiright?), and your basic rogue asteroids tumbling about. These things gum up the works of the shipping lanes and pleasure cruise trails (though how anyone can derive pleasure from a cruise where your piece of garbage ship could be destroyed at any moment by the slightest impact is yet another contradiction embodied by the Gorgonians).
Fortunately, there is a solution to these problems: on the outskirts of the solar system is an alien crystal that emits an unknown type of energy that kills the space vermin, at least for a little while. The Gorgonians have figured out how to collect this energy, little by little. So, they send out ships that are essentially space tankers (but being Gorgonian ships, they at least look cool!) to collect the energy and return it to the home world.
Your job, as one of the brave pilots of the “crystal tanker fleet,” is to make your way through the space vermin to extract energy from the crystal and then bring it home. When you collect enough energy, the vermin are destroyed, and you’re a Gorgonian hero!
At least for a little while.
You get some points or something for doing this, of course! Let’s call ’em space credits with which you can maintain your Gorgonian lizard-licking habit.
And that, friends, is how you conceive a simple game that you can code up with web technologies, obviously including Webix. I mean, I’ll say up front that if you’re expecting Call of Duty, Halo, or Destiny levels of gameplay, then you’re going to be sorely disappointed. This ain’t gonna be no AAA title, and it’s not a game you’ll want to be playing over and over again (probably—hey, you could wind up loving it I suppose!). But it’ll be a good learning experience, which of course is the goal here.
Let’s get to it; the vermin need destroying!

The Basic Layout

So, what does this game look like? Well, if you’ve ever played a game with, say, a frog that hops across lanes of traffic of various types to get to a goal on the other side…well, this game may or may not be conceptually similar to that. To be more precise, Figure 10-1 shows what it looks like.
A455420_1_En_10_Fig1_HTML.jpg
Figure 10-1
Maybe we should call it Spacershipper?
Your ship starts at the bottom of the screen , near the home world. You use the cursor keys to move through the lanes of vermin (asteroids, zombies, sentient machines, and space fish, starting from the bottom). When you reach the top, you touch the crystal, and the energy bar at the top fills. Then, you return through the vermin, touch your home world, and the energy is transferred, at which point all the vermin explode, you get some points, and the vermin come back so you can do it all over again. Of course, you explode if you touch anything but the crystal or your home world.
Like I said, it’s not exactly a complex, top-tier game, but it is a game!

Basic Application Architecture

In many respects, this is a simple application to architect. As with most web apps, it starts with an index.html file that loads all the resources, including a single CSS file and a number of JavaScript source files. There is a main class, WXGAME, which contains the webix.ready() call in its constructor, as you’ve come to expect from a Webix app.
Games usually have a loop that executes for the duration of the game’s run. This loop is responsible for moving game objects (which can be anything that makes up the game, like the crystal, planet, player’s ship, and vermin in this game), checking for collisions, updating scores, and so on. WXGAME is no exception, as you’ll see when you get to main.js. But, in terms of architecture, the key is the main game loop, so keep that in mind for later.
You also, of course, need some graphics, and there are a number of image files in an img directory. The files are named consistently: XX-Y, where XX is the type of graphic (enemy, explosion, planet, player) and Y is the frame number because these objects are animated. So, for example, to show an explosion on the screen, you’ll need to show images explosion-0.png, explosion-1.png, explosion-2.png, explosion-3.png, and explosion-4.png in rapid succession. The one exception are the enemy files, which are actually named XXZ-Y where Z is just a number from 1 to 4 (because there’s four types of enemies: space fish, sentient machines, zombies, and asteroids). There’s also a static background image, named, unsurprisingly enough, background.png.
I’ll go through each file, in an order that I think will make sense, but just to give you a bird’s-eye view, Figure 10-2 shows the files you’ll be looking at.
A455420_1_En_10_Fig2_HTML.jpg
Figure 10-2
The directory structure and files for this project
In terms of the architecture of what’s on the screen, it’s a simple setup: a toolbar of content at the top that contains the current score and the energy bar that fills and unfills as the player contacts the crystal and the planet, and below that is the playfield area where the action happens. That’s all there is to it!
Now, let’s start exploring some code, starting with index.html.

index.html

The index.html file, shown in Listing 10-1, is really the same as any you’ve seen before, but for the sake of completeness, I’m showing it here.
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0,minimum-scale=1.0,user-scalable=yes">
    <link rel="stylesheet" href="../webix/codebase/webix.css" type="text/css" media="screen" charset="utf-8">
    <link rel="stylesheet" href="main.css" type="text/css" media="screen" charset="utf-8">
    <script src="../webix/codebase/webix_debug.js" type="text/javascript" charset="utf-8"></script>
    <script src="GameObject.js" type="text/javascript" charset="utf-8"></script>
    <script src="Player.js" type="text/javascript" charset="utf-8"></script>
    <script src="Enemy.js" type="text/javascript" charset="utf-8"></script>
    <script src="Crystal.js" type="text/javascript" charset="utf-8"></script>
    <script src="Planet.js" type="text/javascript" charset="utf-8"></script>
    <script src="Explosion.js" type="text/javascript" charset="utf-8"></script>
    <script src="main.js" type="text/javascript" charset="utf-8"></script>
    <title>wxGame</title>
  </head>
  <body></body>
</html>
Listing 10-1
The index.html File
Really, except for all the JS files aside from main.js being imported, it’s exactly the same as before. All the interesting stuff is in the JavaScript files. Before you get to those, though, let’s see what’s in main.css (hint: not much!).

main.css

The main.css file contains a grand total of one class, as you can see in Listing 10-2.
.cssPlayfield {
  background-image : url("img/background.png");
  overflow : hidden;
  position : relative;
}
Listing 10-2
It Doesn’t Get Much Simpler Than main.css!
As you can probably guess, this class will be applied to the main playfield area to provide the space background. This class also tells you that any overflowing content will be hidden, which is important so that your vermin will smoothly move off one edge of the playfield and can then be brought back from the other side. To put it another way, content will be “clipped” at the edges of the playfield area. Also, as you’ll see, all the game objects are positioned absolutely, which means they need to be children of a positioned element. That’s the purpose of setting position:relative here.

main.js

The main.js file is where the core code is, including that main game loop I mentioned. I’ll walk you through this code in sections to make it more easily digestible, beginning with this bit:
"use strict";
class WXGAME {
}
const wxGame = new WXGAME();
I’m cheating a little here and showing you the high-level structure of the code. There’s obviously a lot more code in this file, but it’s all in that WXGAME class. Showing it this way shows that it’s a single class and a single instance of that class that you’re dealing with.
Now, in terms of the code inside the class, it begins with the constructor .
constructor() {
  this.score = 0;
  this.enemies = [ ];
  this.player = null;
  this.crystal = null;
  this.planet = null;
  this.KEY_UP = 38;
  this.KEY_DOWN = 40;
  this.KEY_LEFT = 37;
  this.KEY_RIGHT = 39;
  this.explosionCount = 0;
  this.explosions = { };
  for (let i = 0; i < 360; i = i + 45) {
    this[`CSS_ROTATE_${i}`] = webix.html.createCss({
      "transform" : `rotate(${i}deg)`,
      "-webkit-transform" : `rotate(${i}deg)`,
      "-moz-transform" : `rotate(${i}deg)`,
      "-o-transform" : `rotate(${i}deg)`,
      "-ms-transform" : `rotate(${i}deg)`
    });
  }
  webix.ready(this.start.bind(this));
}
First up, you have a member to record the player’s score. After that are four members that are references to the game objects: all the enemies on the screen, the player’s ship, the crystal, and the planet. Whenever you write a game, two things you need to keep in mind are optimization and performance. One thing this means, especially in JavaScript, is to cache references to things you’ll frequently be accessing, especially in the main game loop, so you don’t need to incur the overhead of a lookup every time. That’s the reason for these members.
After those are four pseudoconstants (since there’s no real constants in JavaScript) that store the key codes associated with the up, down, left, and right arrow keys. You’ll need them when you handle keypress events to determine what to do.
Next up are two variables for dealing with explosions . At any point in time, there could be multiple explosions on the screen (when you return all the energy to the planet, all the vermin explode, for example), and that means you’ll need to keep track of how many there are, which is why you need explosionCount. The explosions object will store a reference to each of those explosions as they occur. (Remember, an explosion is a sequence of animation frames, and it takes time to cycle through those frames, and then there is work to do when each completes, as you’ll see.)
Next up, you see something new from Webix land: the webix.html.createCss() method. This method allows you to create a new CSS class, which will be inserted into the DOM and which you can then apply to DOM nodes. In this case, the image of the player’s ship always faces up, but when they are moving, you need it to point in different directions, 45 degrees apart (up, down, left, right, up+left, up+right, down+left, and down+right). So, you’ll use CSS to rotate the static image, and for each possible rotation direction, you’ll create a CSS class to apply. That’s what’s happening here. The transform CSS style allows you to perform all sorts of various transformations on an object, rotate being just one of them. The interesting thing about webix.html.createCss(), though, is that you don’t specify the name of the CSS class. Webix controls that (though, critically, it does return it from the call). But, of course, in order to apply this class, you need to know that name. So, you’ll record that name as a member of a CSS_ROTATE_X member on the wxGame instance, where X is the rotation degrees. So, when you want to rotate the ship so that it points to the right when the player moves it right, you will apply the class name stored in wxGame["CSS_ROTATE_90"] (since that’s a 90-degree rotation from its static image pointing upward).
Finally, you have the familiar webix.ready() call, which fires wxGame.start() :
start() {
  webix.ui({
    type : "clean",
    cols : [
      { },
      { width : 800, type : "clean",
        rows : [
          { },
          { type : "clean", height : 600,
            rows : [
              { view : "toolbar", id : "header", height : 40,
                elements : [
                  { id : "score", width : 90, view : template", template : "Score: 0000" },
                  { view : "chart", type : "barH", padding : 5,
                    value : "#count#", id : "energyBar", color : "green",
                    xAxis : {
                      start : 0, end : 100, step : 1, lines : false,
                      color : "#ffffff"
                    },
                    data : [ { id  : "energy", count : 0 } ]
                  }
                ]
              },
              { id : "playfield", css : "cssPlayfield" }
            ]
          },
          { }
        ]
      },
      { }
    ]
  });
  this.playfield = $$("playfield").$view;
  for (let y = 0; y < 4; y++) {
    for (let x = 0; x < 3; x++) {
      this.enemies.push(new Enemy({
        type : y + 1, x : x, y : y, playfield : this.playfield
      }));
    }
  }
  this.player = new Player({ playfield : this.playfield });
  this.crystal = new Crystal({ playfield : this.playfield });
  this.planet = new Planet({ playfield : this.playfield });
  this.player.animate = webix.wrap(this.player.animate, this.transferEnergy.bind(this));
  document.onkeydown = this.keyHandler.bind(this);
  document.onkeyup = this.keyHandler.bind(this);
  setInterval(this.run.bind(this), 100);
}
First, the basic screen structure is built. Here, you’re just using the basics of Webix that by now you’ve come to know (and, I’d bet, love!). To make life simpler (trying to make the game responsive to the screen size is a much more difficult endeavor), I’ve decided on an 800×600 viewport in which to make the game. So in essence, you have a columnar layout with three columns and two spacers flanking an 800-pixel wide area. Inside that center area goes three rows: a spacer, the top bar where the score and energy bar go, and then the main play area with a height of 600. That first spacer is needed to push it all down, so you wind up with the viewport centered on the page.
The top bar is a Webix toolbar with two elements: a simple text area (via a Webix ui.template component) for the score and a horizontal bar chart for the energy bar. The data for the bar chart is a single series with an ID of energy, which is relevant because you’ll need that in order to update the value of the chart later.
You’ll note that the playfield area doesn’t have any content, just an ID and the cssPlayfield style class applied to provide the background and overflow clipping. That’s as it should be. Everything that shows up there will be drawn separately by the game code.
After the construction of the layout, you have some more work to do. First, a reference to the playfield is retrieved. To be more specific, the DOM node that encapsulates the playfield is retrieved, which you can get for most any Webix component via its $view property. This is done for speed; you’ll need this reference a lot throughout the code, and you don’t want to have to look it up each time with $$().
Next, it’s time to create some enemy vermin! Looking back at Figure 10-1, you can see that there are four rows of them with three vermin in each. So, there are two loops: the y loop for the rows and the x loop for the vermin in each row. For every enemy, an instance of the Enemy class is created and is added to the enemies array on the wxGame instance. Each enemy is of a specific type, numbered 1 through 4, which just so happens to be what the y loop is when you add 1 to it. The x and y config values are not x and y on the screen but x and y in terms of which row (y) and enemy on that row (x) each is. These will be used to construct a unique ID for each enemy, as you’ll see shortly. To create an enemy and put them on the screen, a reference to the playfield is required, so that is passed in to the Enemy constructor as well.
After that, you need to create the other game objects, namely, the player, crystal, and planet. You’ll get into the game objects themselves in the next section, but for now it’s enough to know that each is an object that encapsulates things like the animations frames each needs, the logic behind flipping between them, and things such as moving, showing, and hiding the objects.
After that comes a new Webix facility, the webix.wrap() function. What this does is allow you to “wrap” a function and have another execute before it does. In other words, it intercepts the execution of the target function. In this case, for reasons that will become clear later, I need to execute some additional code whenever the animate() method of the player object is called. I didn’t want to put the logic in that function because that function is actually part of a common base class that Player extends from. While I certainly could have overridden animate() in the Player class, in good object-oriented form, taking care to call animate() on the superclass too, of course, that wouldn’t have given me an opportunity to show you webix.wrap(), would it? This probably isn’t the best way to do it (overriding animate() arguably is), but this works and is a good demonstration of webix.wrap(), which just requires you to pass a reference to the function to wrap, and the function to wrap it with, in that order. The transferEnergy() function, as its name implies, is used to transfer energy to and from the ship (and, at the risk of sounding like a broken record: as you’ll see later).
Next, you need some event handlers for key up and key down events, and keyHandler() is that handler. I call bind() on it to ensure the this reference in it points to the wxGame instance.
Finally, it’s time to kick off that main game loop I mentioned earlier, and setInterval() does that. I decided on 100ms as the interval here because in my testing, most modern machines with a modern browser can handle that speed without issue. Note that there are more complex methods to deal with the situation when it can’t, called time-based animation, but that’s beyond the scope of this book and for a simple example like this isn’t really necessary. The run() method is the main game loop (which I also bind() to the wxGame instance), and that’s the next stop.

Over and Over Again: run( )

The main game loop is actually quite simple, as you can see here. Note that most of the functions called here will be examined after this, but I think the names are pretty clear, and you shouldn’t, I expect, have any difficulty understanding this code conceptually even if you don’t know all the details beyond the functions it calls just yet.
run() {
  this.player.move();
  this.player.animate();
  this.crystal.animate();
  for (let e in this.explosions) {
    if (this.explosions.hasOwnProperty(e)) {
      this.explosions[e].animate(this.explosions);
    }
  }
  for (let i = 0; i < this.enemies.length; i++) {
    this.enemies[i].animate();
    this.enemies[i].move(this.playfield);
    if (wxGame.collision(this.enemies[i])) {
      this.explosions[`e${this.explosionCount}`] = new Explosion({
        playfield : this.playfield, explosionNumber : this.explosionCount++,
        x : this.player.xLoc - 10, y : this.player.yLoc - 5
      });
      this.adjustScore(-50);
      this.player.energy = 0;
      $$("energyBar").updateItem("energy", { count : 0 });
      this.player.hide();
      this.player.toStartingPosition();
    }
  }
  this.player.touchingCrystal = wxGame.collision(this.crystal);
  this.player.touchingPlanet = wxGame.collision(this.planet);
}
A few tasks need to be accomplished with each 100ms iteration of this method, beginning with moving the player. As you’ll see next, the keyHandler() method doesn’t actually move the player in response to keypresses; it only sets flags to tell us which way to move it. The move() method of the player is what looks at those flags and actually moves the player, but that’s coming a bit later.
The crystal and player game objects are animated, but that doesn’t happen by itself; you have to write code for that, and that code is inside the animate() method of those objects, so they get called next.
The same is true for the enemies, which is where that enemies array comes into play as it gives us an easy way to loop through the enemies and animate them. Enemies also have to move, of course, so their move() method is also called.
Next, things get a little more complicated because any time the player collides with an enemy, an explosion needs to occur. So, the wxGame.collision() method is called to determine when a collision occurs. Since collisions can only occur between a player and an enemy, you only have to pass a reference to each enemy to check the player against. When this function returns true, then an Explosion game object needs to be created. There can be more than one explosion on the screen at once (not because the player can collide with more than one enemy—it can’t because of the spacing between the enemies—but when the player brings energy back to the planet, all of the enemies explode simultaneously). So, the explosionCount member is used to number each explosion (and used to generate a unique ID for each). The explosion’s location needs to be offset a little from the player’s coordinates (the actual screen coordinates, that is, which the xLoc and yLoc attributes of the player tells you) because of the geometry of the graphics used. There’s no magic here; it’s just trial and error to get it to look like it’s centered on the player! Unlike the enemies, x and y in this case do mean real screen location (which means left and top style attributes correspondingly since all these game objects are just img elements, as you’ll see later).
In addition to exploding, the player needs to lose a few points too, so a call to the adjustScore() method does that, which accepts a positive number to increase the score or a negative number to decrease it. Also, any energy the ship is carrying should be cleared out, so player.energy is set to zero. This also means that the energy bar needs to be emptied immediately, so its value is set to zero. To update the chart, you need to alter its data, which is where the updateItem() method comes in. This takes an ID and a hash of values to set and updates a specific data item in the collection of data items the chart is rendering. Since you have only a single series in this chart, though, you have only a single data item, and it’s the one with an ID of energy, and its count property is the property that the chart uses to render itself, so it’s a pretty obvious call. One last thing needs to happen, which is that the player must be hidden until the explosion animation cycle completes, plus the player needs to be returned to its starting position. The hide() method takes care of the first part, and the aptly named toStartingPosition() takes care of the second.
Two more tasks need to be accomplished with each game loop iteration, and that’s to see whether the player is touching the crystal or the planet. The same sort of call to collision() as was done to check for collisions with the vermin is done, and a flag is set on the player for either condition, which will be used in some other code later.

Up and Down: keyHandler( )

As you saw earlier, the keyHandler() method is bound to both the key up and key down events, and that code is as follows:
keyHandler(inEvent, inKeyDown) {
  const evt = (inEvent) ? inEvent : (window.event) ? window.event : null;
  const keyCode = (evt.charCode) ? evt.charCode:
    ((evt.keyCode) ? evt.keyCode : ((evt.which) ? evt.which : null));
  switch (keyCode) {
    case this.KEY_UP: this.player.dirUp = inKeyDown; break;
    case this.KEY_DOWN: this.player.dirDown = inKeyDown; break;
    case this.KEY_LEFT: this.player.dirLeft = inKeyDown; break;
    case this.KEY_RIGHT: this.player.dirRight = inKeyDown; break;
  }
}
The first line ensures that you have an event object because not all browser types pass one in to the handler; some require the handler to get it from the window object. The second line then uses that object to determine the key code of the key that was pressed or released. These two lines are relatively standard and nothing Webix-specific.
The switch statement then looks to see whether the key code is one of the four defined in the constructor corresponding to the four cursor keys. For any match, one of the dirXX flags on the player instance is set. As previously mentioned, these flags will be used in the player’s move() method, which you just saw is called from run().

When Worlds Collide: collision( )

Most video games, including this one, require the ability to detect when two images (two game objects) run into each other. For instance, you need to know when your player’s ship hits one of the enemy vermin. There are numerous collision detection algorithms, but many of them are not available to you in a browser setting because they require access to pixel-level data. For instance, checking each pixel of one image against each pixel of another, while giving 100 percent accurate detection, isn’t possible in a browser (at least not without using the canvas element).
Instead, a simpler method is available to you than what is used here: bounding boxes. This is a simple method that basically just checks the four bounds of the objects. If the corner of one object is within the bounds of the other, a collision has occurred.
As illustrated in the example in Figure 10-3, each game object has a square (or rectangular) area around it, called its bounding box, which defines the boundaries of the area the object occupies. Note in the diagram how the upper-left corner of object 1’s bounding box is within the bounding box of object 2. This represents a collision. You can detect a collision by running through a series of simple tests comparing the bounds of each object. If any of the conditions are untrue, then a collision cannot possibly have occurred. For instance, if the bottom of object 1 is above the top of object 2, there’s no way a collision could have occurred. In fact, since you’re dealing with a square or rectangular object, you have only four conditions to check, any one of which being false precludes the possibility of a collision.
This algorithm does not yield perfect results. For example, in this game, you will sometimes see the ship “hitting” an object when they clearly did not touch (most obvious with the asteroids). This is because the bounding boxes can collide without the object itself actually colliding because the graphics aren’t all themselves perfect squares or rectangles. This could only be fixed with pixel-level detection, which, again, is not available to you. However, the bounding boxes approach gives an approximation that yields “good enough” results in many cases, including this one, so all is right with the world.
A455420_1_En_10_Fig3_HTML.jpg
Figure 10-3
The basic idea behind bounding boxes
The actual code behind the collision() therefore becomes pretty straightforward.
collision(inObject) {
  if (!this.player.isVisible || !inObject.isVisible) { return false; }
  const left1 = this.player.xLoc;
  const left2 = inObject.xLoc;
  const right1 = left1 + this.player.pixWidth;
  const right2 = left2 + inObject.pixWidth;
  const top1 = this.player.yLoc;
  const top2 = inObject.yLoc;
  const bottom1 = top1 + this.player.pixHeight;
  const bottom2 = top2 + inObject.pixHeight;
  if (bottom1 < top2) {
    return false;
  }
  if (top1 > bottom2) {
    return false;
  }
  if (right1 < left2) {
    return false;
  }
  return left1 <= right2;
}
If the player isn’t visible, as determined by examining its isVisible attribute, or if the player isn’t visible, then there’s no need to check for a collision because game objects are only ever not visible when they’re exploding. After that, you get the values to be compared, which means the coordinates of the top, bottom, left, and right bounds of the player and the test object. Finally, it’s just the four simple checks described to tell you if a collision has occurred.

To and From: transferEnergy( )

Transferring energy from crystal to ship, or from ship to planet, occurs every time the animation frame of the player changes. Or, more precisely, it can occur because whether it does occur or not depends on whether the ship is in contact with either object (and whether there’s energy to transfer, of course). As you’ll recall, the animate() method of the player object is called with every tick of the main game loop, which is the run() method, and since the webix.wrap() method was used to intercept the call to animate(), that means transferEnergy() will also be called with each game loop iteration.
transferEnergy() {
  if (this.player.touchingCrystal && this.player.energy < 100) {
    this.player.energy = this.player.energy + 5;
    $$("energyBar").updateItem("energy", { count : this.player.energy });
    if (this.player.energy === 100) {
      while (this.crystal.randomlyPosition()) { this.crystal.randomlyPosition(); }
    }
  } else if (this.player.touchingPlanet && this.player.energy > 0) {
    this.player.energy = this.player.energy - 5;
    $$("energyBar").updateItem("energy", { count : this.player.energy });
    if (this.player.energy === 0) {
      while (this.planet.randomlyPosition()) { this.planet.randomlyPosition(); }
      this.blowUpAllEnemies();
      this.adjustScore(100);
      $$("energyBar").updateItem("energy", { count : 0 });
    }
  }
}
The first branch of the if statement covers the case when the player is touching the crystal , as the player.touchingCrystal flag denotes. It also accounts for when the player touches the crystal even though their energy is full, in which case you skip the work here too. If both conditions are met, though, then with each tick of the game loop you add 5 to the player’s energy stores, and you also set the value on the bar chart to the player’s current energy value, which causes it to fill up. When the energy reaches 100, then the crystal is randomly repositioned. Why the while loop, you ask? That’s because each call to crystal.randomlyPosition() could result in the crystal being positioned still in contact with the ship. You don’t want that. So, the while loop will keep calling crystal.randomlyPosition() until it returns false, which means the crystal is not in contact with the ship.
The second if branch is for when the player is touching the planet. There, the player’s energy is reduced by 5 each iteration, and when it hits zero, it’s the planet this time that needs to be repositioned. Then, all the enemies need to explode, which is what blowUpAllEnemies() does. The player has to score some points here, so adjustScore() is called. Finally, the energy bar has to be cleared again, so its value is set to zero, as previously described.

Making It Worth It: adjustScore( )

The adjustScore() method you’ve seen called a few times now, and it’s just about as simple as you’ve probably imagined it was.
adjustScore(inAmount) {
  this.score = this.score + inAmount;
  if (this.score < 0) {
    this.score = 0;
  }
  $$("score").setHTML(`Score: ${this.score}`);
}
In fact, the only interesting bit here is the use of setHTML(), which is a method of the ui. template component that allows you to set any arbitrary HTML in the component you want.

Winning: blowUpAllEnemies( )

One final method is present in main.js, and that’s the method called when the player returns all the energy to the planet and that blows up all the enemies.
blowUpAllEnemies() {
  for (let i = 0; i < this.enemies.length; i++) {
    const enemy = this.enemies[i];
    enemy.hide();
    enemy.moveTo(enemy.startingX, enemy.startingY);
    this.explosions[`e${this.explosionCount}`] = new Explosion({
      playfield : this.playfield, explosionNumber : this.explosionCount++,
      x : enemy.xLoc, y : enemy.yLoc
    });
  }
  webix.delay(function() {
    for (let i = 0; i < this.enemies.length; i++) { this.enemies[i].show(); }
  }, this, [ ], 1000);
}
This works a lot like how the explosion is created when the player collides with an enemy, but here you need one explosion per vermin. Each is positioned according to the location of the vermin (and it doesn’t really need those adjustments like was done for the player explosion because the geometry of the graphics lines up pretty well without it). Every enemy is hidden before its corresponding explosion is created, and it is also moved back to its original starting position via the call to its moveTo(), passing it the startingX and startingY members attached to each enemy (which are populated when the enemy is created, as you’ll see shortly).
Now, that’s great for blowing up the vermin, but the game has to continue after that, which means they need to be shown again. How do you do that? Well, in the interest of showing you another new Webix function, I’ve gone with usage of the webix.delay() function. This works a lot like the JavaScript-standard setTimeout() function, but with some additional features. First, this function takes the code you want to execute, which here is simply an iteration over the array of enemies, calling show() on each. The second argument is what scope that code should execute in, meaning what the this keyword points to when it executes. The third argument is an array of arguments to pass to the code, which is just an empty array here since you don’t need to pass any. Finally, the last argument tells webix.delay() how long to wait until calling the code, in milliseconds. Since there are five frames of animation for an explosion and since each frame takes 100 milliseconds (because that’s how long there is between main game loop ticks, and the frames are flipped from there), that means you have to wait at least 500 milliseconds before showing the vermin again. I doubled that for good measure, so 1,000 milliseconds, or one second, is it.

Object Orientation for Fun and Profit: The Game Object Hierarchy

By this point, you’ve examined the core game code, but that’s only part of the equation. Throughout, you’ve seen reference to the various game objects, namely, crystal, explosion, planet, player, and enemy. Next, you’ll look at those classes, beginning the hierarchy they are built from, shown in Figure 10-4.
A455420_1_En_10_Fig4_HTML.jpg
Figure 10-4
The object hierarchy of game objects
The five game objects are each represented by a class, and each of them extends from a common base GameObject class. This structure allows you to push as much of the common code that each object shares into that base class. It’s always good to not repeat yourself, right? So, let’s begin by looking at the GameObject class in detail and start to see the code behind the functions that you saw called upon from the core code.

GameObject.js

As with the WXGAME class in main.js, GameObject.js defines a class named GameObject, so it begins the same.
class GameObject {
}
However, in this case, there is no instantiation of a single instance like with WXGAME because this is a base class that all the other game objects extend from, not something that is instantiated on its own. As with most classes, this one opens with a constructor.
constructor(inConfig) {
  this.baseName = inConfig.baseName;
  this.pixWidth = inConfig.pixWidth;
  this.pixHeight = inConfig.pixHeight;
  this.frameCount = inConfig.frameCount;
  this.currentFrame = 0;
  this.frameSkipCount = 0;
  this.frameSkip = 0;
  this.xLoc = 0;
  this.yLoc = 0;
  this.isVisible = true;
}
All game objects share a few common attributes. First, you need a baseName, which is the name of the graphics files associated with the object, minus any numbers (i.e., just crystal, not crystal-1 and crystal-2 like the image files are named). Next, the object needs to know its width and height, and that’s what pixWidth and pixHeight are for. Then, since all game objects, except for the planet, are animated, the code will need to know how many frames of animation there are, and frameCount denotes that. All of these are passed into the constructor via the inConfig object.
After those, you have some attributes that are the same for all game objects, at least to start, and so aren’t passed in. The currentFrameCount is used internally to keep track of what animation frame is visible. The frameSkipCount and frameSkip attributes are used to slow the animation down. If you changed frames with every game loop tick, every 100ms in other words, the animations would look kind of manic! You’ll see how these are used later, but that’s their purpose. Finally, isVisible obviously tells you whether the object is currently visible and all objects start out visible by default.

Pixar Would Be Proud: loadFrames( )

After the constructor, there needs to be a method that can load all the frames of animation for a given object. That’s exactly what loadFrames() is for.
loadFrames(inConfig) {
  for (let i = 0; i < this.frameCount; i++) {
    const id = `${this.baseName}${inConfig.n}${i}`;
    const img = webix.html.create("img",
      { id : id, src : `img/${this.baseName}-${i}.png`,
        width : this.pixWidth, height : this.pixHeight
      }
    );
    img.style.position = "absolute";
    img.style.left = `${inConfig.x}px`;
    this.xLoc = inConfig.x;
    img.style.top = `${inConfig.y}px`;
    this.yLoc = inConfig.y;
    if (inConfig.hidden) {
      img.style.display = "none";
    }
    if (inConfig.z) {
      img.style.zIndex = inConfig.z;
    }
    webix.html.insertBefore(img, null, inConfig.playfield);
    this[`frame${i}`] = webix.toNode(id);
  }
}
As you saw, frameCount tells you how many frames there are, so the code loops with that value. Each animation frame winds up being a separate DOM node, and since you need to be able to access each, that means each must have a unique id, so the id value is constructed using the baseName and the loop count at the end. There’s also an inConfig.n value that is optional and is inserted even before the frame number. This is specifically for explosions since there can be more than one of those at a time, so each needs to have an extra value in the id, and that’s where inConfig.n comes into play. So, for example, the player will wind up with two nodes, one with an id of player1 and the other player2, and an explosion will have an ID of explosion11, exposion12, and so on.
Speaking of creating a DOM node, that’s exactly the next step! The webix.html.create() function lets you do that easily. The first argument is the type of element to create, an <img> element in the case of game objects. As you can see, that previously described unique id is included in the second argument, which is an object containing all the attributes associated with the element. These are any attributes that an <img> take can take, so src is one that is clearly necessary! The baseName again comes into play here to construct that src attribute value (so img/player-1.png, for example).
Once the element is created, you can set some style attributes on it. (Yes, you could do this by specifying a style attribute in the second argument to webix.html.create(), but that means building a string, and I find this style of coding it cleaner.) The element is positioned absolutely, and its left and top attributes are set using the inConfig.x and inConfig.y values passed in. You also store those as xLoc and yLoc for easier access later (which saves you from having to access DOM node attributes, so it’s a little faster this way). The game object could be hidden initially via the inConfig.hidden attribute, and if so, then display is set to none. There’s also a concern with z-index in that the player should always be on top, even if it’s not created last (which it can’t be because some other code depends on it having been created earlier). It’s only a concern for the player, but being able to pass inConfig.z to set zIndex lets you deal with it consistently in all cases.
Next, you have two new Webix functions. First, all you’ve done so far is create an <img> element in memory. It’s not actually on the screen at this point. In fact, it’s not even in the DOM yet. So, you need to insert it into the DOM, and that’s where webix.html.insertBefore() comes into play. You pass this function the object to insert, then a reference to the DOM node to insert the new one before (its “sibling,” so to speak), and then a parent node if the sibling doesn’t exist. In this case, you just want to append the element to the playfield node, which is passed in as inConfig.playfield, and not actually before any other element, so that’s why null is the second argument. The third kicks in, and you get an append, as the last child, of the playfield node.
Finally, you need to have a reference to every <img> element created for each animation frame. While you could have gotten that reference from the call to webix.html.create(), since it returns the reference to the new node, I instead decided to use webix.toNode() to show you something new. This function allows you to get a reference to any DOM node by ID. Here, the reference is stored as an attribute of the game object instance, with each frame being a new attribute named frameX, where X is the frame number. That will allow later code to access these DOM nodes quickly and easily. In fact, the next thing you’re going to look at makes use of that!

Make It Move: animate( )

Animating a game object is fairly simple and, as you know, is triggered by a call to each object’s animate() method from the run() method.
animate(inCollection) {
  if (!this.isVisible) { return; }
  this.frameSkipCount++;
  if (this.frameSkipCount < this.frameSkip) {
    return;
  }
  this.frameSkipCount = 0;
  this[`frame${this.currentFrame}`].style.display = "none";
  this.currentFrame++;
  if (this.currentFrame === this.frameCount) {
    this.currentFrame = 0;
    if (this.animationCallback) {
      this.animationCallback(inCollection);
    }
  }
  this[`frame${this.currentFrame}`].style.display = "";
}
First, you abort if the object isn’t currently visible because why do the work of animating it if it can’t be seen? If that check is passed, then the frameSkipCount member is increased, and until it equals the frameSkip value , no animation is done. This means that a certain number of main game loop ticks can be skipped before an object’s animation frame is changed, effectively slowing the animation.
Once it’s time to animate, the first thing to do is to hide the current frame. Remember that each frame is a separate DOM node, so only one can be showing at any given time for a given game object. The attribute name is formed using the currentFrame value, and from there it’s a simple matter of setting its display style attribute to none.
Next, currentFrame is bumped up. When it equals frameCount, then it’s time to restart the animation cycle. There’s also the opportunity for some code to be executed when the animation cycle completes via a callback attribute of the game object that can optionally be set. This is specifically for explosions, as you’ll see later. When the callback function is called, it is passed the inCollection argument that is passed to animate(), which is only ever done for explosions and is the collection of all explosions (and again, why that is will become apparent when you look at the Explosion class).
Finally, the new animation frame is shown by getting a reference to it through those frameX attributes I described earlier. Setting the style attribute to an empty string, which is the default for that attribute, makes it visible.

Yes, I See You: show( )

There is also a show() method on every game object, and it’s simple.
show() {
  for (let i = 0; i < this.frameCount; i++) {
    this[`frame${i}`].style.display = "";
  }
  this.isVisible = true;
}
It’s just a matter of setting the style attribute of each animation frame <img> node to an empty string and of course marking the object as visible too.

Playing Hide (But Not Seek?): hide( )

Where there’s a show() method, there’s likely to be a hide() method, and you won’t be disappointed to know that there in fact is!
hide() {
  for (let i = 0; i < this.frameCount; i++) {
    this[`frame${i}`].style.display = "none";
  }
  this.isVisible = false;
}
I’m gonna go out on a limb here and say that doesn’t need an explanation.

A Little Randomness in Your Day: randomlyPosition( )

The crystal and the planet get randomly positioned at the start of the game, and when energy transfer is complete to and from them, that’s where the randomlyPosition() method comes in.
randomlyPosition() {
  const min = Math.ceil(70);
  const max = Math.floor(730);
  this.xLoc = Math.floor(Math.random() * (max - min + 1)) + min;
  for (let i = 0; i < this.frameCount; i++) {
    this[`frame${i}`].style.display = "none";
    this[`frame${i}`].style.left = `${this.xLoc}px`;
  }
  const didCollide =  wxGame.collision(this);
  if (!didCollide) {
    for (let i = 0; i < this.frameCount; i++) {
      this[`frame${i}`].style.display = "";
    }
  }
  return didCollide;
}
First, a random number between 70 and 730 (inclusive) is chosen. These values result in the object never going off either edge of the playfield area. Once you have a location, every animation frame is hidden and then moved to the new location. Only the left style attribute needs to be changed since this is only randomizing the horizontal location; the vertical location never changes.
Once that’s done, you need to see whether the object now collides with the player. If not, then animation frames are all shown (which doesn’t matter for the planet since there’s only one frame, and for the crystal this will be undone with the next game loop tick and only a single frame shown). The collision result is returned so that those while loops you saw earlier can kick in and randomlyPosition() will be called again (and again, and again, and…) until a noncollision location is determined. This isn’t the most efficient way to go about doing this, but it’s pretty simple and does the job well enough.

Go Where I Tell You: moveTo( )

The final common function that all game objects should provide is being able to move them to a specific location on the screen directly, which is what moveTo() is for.
moveTo(inX, inY) {
  for (let i = 0; i < this.frameCount; i++) {
    this[`frame${i}`].style.left = `${inX}px`;
    this[`frame${i}`].style.top = `${inY}px`;
  }
}
This simply takes in X and Y values, which are set as the left and top style attributes, respectively, of every animation frame for the object. Its visibility isn’t impacted, there are no worries about collision, and it’s just an immediate move. This is primarily present for the enemies, as you’ll see soon.
That done, your look at the GameObject class is concluded. Now let’s move on to some specific game objects, starting with the Crystal class.

Crystal.js

The Crystal class is one of the simpler game object classes, but it’s a good starting point because the others are not much more complex and, except for the Enemy class, look pretty similar to start.
"use strict";
class Crystal extends GameObject {
  constructor(inConfig) {
    super((function() {
      return webix.extend(inConfig,
        { pixWidth : 32, pixHeight : 30, frameCount : 4, baseName : "crystal" }
      );
    }()));
    super.loadFrames({
      x : 0, y : 2, playfield : inConfig.playfield, hidden : true
    });
    this.randomlyPosition();
  }
}
The constructor starts with a call to the constructor of the superclass, GameObject. The trick here is that the inConfig object that’s passed into the constructor needs to have some additional information added to it specific to this game object. Of course, that’s a problem because the super() call must be the first thing in the constructor of a JavaScript class. So, to deal with that, I’ve used the immediately invoked function pattern here. That is, an anonymous function is created and then immediately called, and the return value from this function becomes the argument passed to super(). What this anonymous function does is take inConfig and extend it using the webix.extend() function. This takes a target object as the first argument, so inConfig , and then “extends” it by taking all the attributes of a second object and adding them to the target. The return value is the target object but now including all the attributes of the second (which here is just an object defined inline containing pixWidth, pixHeight, frameCount, and baseName). If you look back on main.js, you’ll see that for all game objects, inConfig contains playfield, while enemies also include a type and x and y attributes, which is why this is all done.
After the super() call, the loadFrames() method that you saw in the base class is then called. That gets all the animation frames loaded up, inserted into the DOM, and references stored on the object being constructed here.
Finally, in the case of the crystal, it needs to be randomly positioned. Note here that there’s no need to care about collision checks because the crystal would never collide with the player at this point. At this point, the Crystal instance is fully constructed. There’s no more code in this class because the base GameObject class includes any additional functionality it needs.
Note that for the remainder of the game object source files, I’ll just be showing the methods each contains. I’ll skip the "use strict" and opening class XXX extends GameObject line for brevity. But rest assured, they are indeed there!

Planet.js

Let’s look at the Planet class next since like the Crystal class, it is quite simple.
constructor(inConfig) {
  super((function() {
    return webix.extend(inConfig,
      { pixWidth : 64, pixHeight : 64, frameCount : 1, baseName : "planet" }
    );
  }()));
  super.loadFrames({
    x : 0, y : 492, playfield : inConfig.playfield, hidden : true
  });
  while (this.randomlyPosition()) { this.randomlyPosition(); }
}
Yep, it’s almost identical to the Crystal class except for one detail. The randomlyPosition() method is wrapped in a while loop so that it can be retried until a position is chosen that doesn’t collide with the player. This also means that the player must have been created before the planet is; otherwise, the call to collision() in randomlyPosition() will fail. As with the Crystal class, there’s no more code beyond this as GameObject handles everything else.

Explosion.js

The Explosion class is next, and it starts off very much like Crystal and Planet do.
constructor(inConfig) {
  super((function() {
    return webix.extend(inConfig,
      { pixWidth : 50, pixHeight : 50, frameCount : 5, baseName : "explosion" }
    );
  }()));
  this.explosionNumber = inConfig.explosionNumber;
  super.loadFrames({
    x  : inConfig.x, y : inConfig.y, n : inConfig.explosionNumber,
    playfield : inConfig.playfield, hidden : true
  });
}
One difference here is that each explosion is given a number, which is used to create the unique id of the DOM node, as you saw in loadFrames(). The number is attached to the Explosion instance being constructed, and it is also passed to loadFrames() as the n attribute. In addition, an explosion is initially hidden.
Finally, you have a game object class with some additional functionality! Recall earlier that when the explosion’s animation cycle concludes, a callback function is called. Now it’s time to see what happens in that function.
animationCallback(inCollection) {
  for (let i = 0; i < this.frameCount; i++) {
    const id = `explosion${this.explosionNumber}${i}`;
    webix.html.remove(webix.toNode(id));
  }
  delete inCollection[`e${this.explosionNumber}`];
  if (!wxGame.player.isVisible) {
    wxGame.player.show();
  }
}
As it happens, not all that much! First, explosions aren’t like the crystal or player or even the enemies in that they are shown and then aren’t needed anymore; they don’t need to hang around. So, you have to clean up the DOM nodes for each of the <img> tags associated with each animation frame. The webix.html.remove() function is used for that. You hand it a reference to a DOM node, which you get with the previously shown webix.toNode() function, and remove it. That’s all well and good. It cleans up the DOM nicely, but you still have a game object hanging around. To avoid leaking memory, that is removed from the wxGame instance, which is where that inCollection being passed as an argument comes into play. That’s an object where a reference to each explosion is stored, so it’s a simple matter of deleting the attribute from that object associated with the constructed attribute name for this explosion (e1, e2, e3, etc., depending on however many explosions there are).
One final task is needed: showing the player if they are currently hidden. This occurs only when the explosion is shown as a result of the player running into an enemy, but it does no harm when the explosion is the result of all enemies exploding.

Enemy.js

The Enemy class is the first you hit that is a bit more complex, and that’s because there’s more logic involved in creating them and positioning them. That work all happens in the constructor.
constructor(inConfig) {
  super((function() {
    return webix.extend(inConfig,
      { pixWidth : 48, pixHeight : 48, frameCount : 2 }
    );
  }()));
  const x = inConfig.x;
  const y = inConfig.y;
  const type = inConfig.type;
  for (let i = 0; i < this.frameCount; i++) {
    const id = `enemy${x}${y}${i}`;
    const img = webix.html.create("img",
      { id : id, src : `img/enemy${type}-${i}.png`,
        width : this.pixWidth, height : this.pixHeight
      }
    );
    img.style.position = "absolute";
    img.style.display = "none";
    this.moveDirection = "right";
    if (type % 2 === 0) {
      this.moveDirection = "left";
      this.frameSkip = 2;
    }
    let leftAdjust = 0;
    if (type === 1) { leftAdjust = 0; this.moveSpeed = 10; }
    if (type === 2) { leftAdjust = 80; this.moveSpeed = 6; }
    if (type === 3) { leftAdjust = 120; this.moveSpeed = 14; }
    if (type === 4) { leftAdjust = 200; this.moveSpeed = 8; }
    this.xLoc = x * 300 + leftAdjust;
    this.startingX = this.xLoc;
    img.style.left = `${this.xLoc}px`;
    this.yLoc = y * 120 + 60;
    this.startingY = this.yLoc;
    img.style.top = `${this.yLoc}px`;
    webix.html.insertBefore(img, null, inConfig.playfield);
    this[`frame${i}`] = webix.toNode(id);
  }
}
If you compare this to loadFrames() in the base GameObject class, you’ll see a lot of similarity, and usually duplicate code is to be avoided. However, here, I felt there was enough difference to warrant copying it and then modifying it as necessary.
After the usual super() call, you get to work creating each of the animation frames, just like in loadFrames(). First, the x, y, and type attributes from inConfig are grabbed into local variables to make things more concise. Then you loop by frameCount and create an <img> for each using webix.html.create() as before. The id of each is a little different here in that it consists of the base name (which you know here is enemy), followed by the x and y values and the loop iteration variable. That ensures you have a unique id for each where x and y correspond to the column and row they’re logically in on the screen relative to each other. Each <img> is positioned absolutely like always and is also hidden. That way, the player won’t see the initial movements as you get them all created and into place (they might not have, depending on how fast the browser works anyway, but better safe than sorry).
Next, you need to determine how fast and in what direction each enemy moves. You start off assuming they’re moving to the right, but for types 2 and 4 you change that to the left (so that each row of enemies alternate movement direction) and also adjust the animation speed via the value of frameSkip so that types 2 and 4 animate a little slower. That just adds some visual variety to the proceedings.
Next, you need to account for the starting horizontal location of each enemy. If you picture the screen, each row of enemies is a different type, starting with 1 at the top. For each row, each enemy starts a little more to the right than the row above it. This creates consistent gaps between the enemies as they move. That’s where the leftAdjust variable comes into play. The value is determined based on the enemy type, and the values are just trial-and-error, but what I thought looked good and, critically for gameplay purposes, presented enough space to be not impossible yet small enough gaps that it wouldn’t be a piece of cake to get through. In addition, the speed of each row is determined in the same way. Each row of vermin moves at a different speed, which means how many pixels they move horizontally, as determined by the value of the moveSpeed attribute attached to the object instance.
With all that determined, it’s a simple matter of positioning the <img> setting the left and top style attributes to the values of xLoc and yLoc. These are calculated from the leftAdjust value and the x and y values (the multiplied values are again trial-and-error based on the gaps I wanted between them). In addition, the starting location for each is recorded as startingX and startingY so that after the player returns all the energy to the planet and the enemies blow up, you can reposition them in the right place without running through all the logic again. Finally, the enemy is inserted into the DOM with webix.html.insertBefore() and a reference stored to it via a call to webix.toNode().
With the enemy constructed, the other thing that this class needs to supply is a move() function, called on every iteration of the main game loop, to move each enemy.
move() {
  if (!this.isVisible) { return; }
  if (this.moveDirection === "right") {
    this.xLoc = this.xLoc + this.moveSpeed;
    if (this.xLoc >= 850) {
      this.xLoc = -50;
    }
  } else {
    this.xLoc = this.xLoc - this.moveSpeed;
    if (this.xLoc <= -50) {
      this.xLoc = 850;
    }
  }
  this["frame0"].style.left = `${this.xLoc}px`;
  this["frame1"].style.left = `${this.xLoc}px`;
}
Of course, this logic needs to be done only if the enemy is visible. If it’s not, then the explosions are occurring, so there’s no need to move them in that case. If it is visible, though, then the moveDirection is interrogated to see whether to increase or decrease the xLoc attribute value. When moving right, there is a point where the vermin has moved off the edge of the playfield to the right far enough, and you need to position it off the playfield to the left so that it appears to “wrap around” and move across the playfield again. That value is 850, and the same sort of logic applies when moving left except that the value is now -50 to trigger a reset off the right edge of the playfield. These values are of course based on the edges of the playfield (0 and 800, since the width is 800) plus (or minus) 50, which accounts for the width of a vermin. That way, it’s always completely unseen when it hits those reset points. Finally, regardless of which direction the vermin are moving and regardless of whether it wrapped around the opposite side or not, the enemy is repositioned.

Player.js

The final class you need to examine is Player, and it starts off very much like Crystal and Planet did.
constructor(inConfig) {
  super((function() {
    return webix.extend(inConfig,
      { pixWidth : 40, pixHeight : 34, frameCount : 2, baseName : "player" }
    );
  }()));
  super.loadFrames({
    x : 0, y : 0, playfield : inConfig.playfield, hidden : true, z : 100
  });
  this.toStartingPosition();
  this.dirUp = false;
  this.dirDown = false;
  this.dirLeft = false;
  this.dirRight = false;
  this.energy = 0;
  this.touchingCrystal = false;
  this.touchingPlanet = false;
}
You again have a call to super(), followed by a call to loadFrames(). In the case of the latter, the z attribute is passed so that the zIndex style attribute will be set (any value higher than zero would have worked here; I just like a nice, round century!). This ensures that the player always appears on top of other game objects, which only really matters for the crystal and planet, but it just doesn’t look right for the ship to be behind those! After that is a call to the toStartingPosition() method, which you’ll be looking at next. But, before that, you have a couple of additional attributes on this class. The four dirXX attributes are the flag values that you saw set in the WXGAME.keyHandler() method. This tells you which direction the player is moving. The energy attribute is how much energy the ship currently has onboard, and touchingCrystal and touchingPlanet are the flags that tell you whether the ship is currently touching the crystal or planet, respectively, so that WXGAME.transferEnergy() knows when to do its work, as you saw earlier.
Now, moving on to that toStartingPosition() method:
toStartingPosition() {
  for (let i = 0; i < this.frameCount; i++) {
    const frame = this[`frame${i}`];
    this.xLoc = 376;
    frame.style.left = `${this.xLoc}px`;
    this.yLoc = 512;
    frame.style.top = `${this.yLoc}px`;
  }
}
Well, that’s clearly nothing too complicated! Each animation frame <img> element is moved to some hard-coded values that correspond to centering the player on the bottom of the playfield area.
The final bit of code in this class, and in fact the last bit of code to examine for this project, is the move() method. This method moves the player with each run() execution.
move() {
  if (!this.isVisible) { return; }
  const xMoveSpeed = 5;
  const yMoveSpeed = 5;
  let xAdj = 0;
  let yAdj = 0;
  let degrees = 0;
  if (this.dirUp && !this.dirDown && !this.dirLeft && !this.dirRight) {
    yAdj = -yMoveSpeed;
    degrees = 0;
  } else if (this.dirUp && !this.dirDown && !this.dirLeft && this.dirRight) {
    xAdj = xMoveSpeed;
    yAdj = -yMoveSpeed;
    degrees = 45;
  } else if (!this.dirUp && !this.dirDown && !this.dirLeft && this.dirRight) {
    xAdj = xMoveSpeed;
    degrees = 90
  } else if (!this.dirUp && this.dirDown && !this.dirLeft && this.dirRight) {
    xAdj = xMoveSpeed;
    yAdj = yMoveSpeed;
    degrees = 135;
  } else if (!this.dirUp && this.dirDown && !this.dirLeft && !this.dirRight) {
    yAdj = yMoveSpeed;
    degrees = 180;
  } else if (!this.dirUp && this.dirDown && this.dirLeft && !this.dirRight) {
    xAdj = -xMoveSpeed;
    yAdj = yMoveSpeed;
    degrees = 225;
  } else if (!this.dirUp && !this.dirDown && this.dirLeft && !this.dirRight) {
    xAdj = -xMoveSpeed;
    degrees = 270;
  } else if (this.dirUp && !this.dirDown && this.dirLeft && !this.dirRight) {
    xAdj = -xMoveSpeed;
    yAdj = -yMoveSpeed;
    degrees = 315;
  }
  for (let i = 0; i < 360; i = i + 45) {
    webix.html.removeCss(this["frame0"], wxGame[`CSS_ROTATE_${i}`]);
    webix.html.removeCss(this["frame1"], wxGame[`CSS_ROTATE_${i}`]);
  }
  webix.html.addCss(this["frame0"], wxGame[`CSS_ROTATE_${degrees}`]);
  webix.html.addCss(this["frame1"], wxGame[`CSS_ROTATE_${degrees}`]);
  const newX = this.xLoc + xAdj;
  if (newX > 2 && newX < 756) {
    this.xLoc = newX;
  }
  const newY = this.yLoc + yAdj;
  if (newY > 0 && newY < 516) {
    this.yLoc = newY;
  }
  this["frame0"].style.left = `${this.xLoc}px`;
  this["frame1"].style.left = `${this.xLoc}px`;
  this["frame0"].style.top = `${this.yLoc}px`;
  this["frame1"].style.top = `${this.yLoc}px`;
}
Of course, it only makes sense to move the player when it’s visible, so that’s the first check done, and you abort if it isn’t visible. Assuming it is, then the code needs to determine two things: which direction it’s moving and how to rotate the image. There are four flags (as Picard would say, “THERE! ARE! FOUR! LI–err, FLAGS!”) for up, down, left and right, and they can be set to true simultaneously, which gives you a grand total of 16 possible combinations (four “bits,” 2^4=16). However, there are really only eight that have meaning here: up, down, left, right, up+left, up+right, down+left, and down+right. Any other combination won’t be caught by the if block here and will result in the player not moving (and being “rotated” to its normal “up” orientation since the degrees variable defaults to zero, which effectively means no rotation). Each branch of the if statement corresponds to one of those combinations.
Whichever branch is hit, assuming any are, the values of xMoveSpeed and yMoveSpeed, either one or the other or both depending on the direction of travel, are stored as xAdj and/or yAdj. These values are negated when the values need to be decreased instead of increased.
In addition to determining these movement adjustment values, the if branches also determine the number of degrees the ship needs to be rotated. With that determination made, the rotation is performed on every animation frame <img> element for the player. Here, two new Webix functions are used: webix.html.removeClass() and webix.html.addClass(). You’ll recall in the constructor of WXGAME that the CSS classes were created corresponding to each rotation amount and references stored to them on wxGame. Now, when you know the degrees to rotate, you can get a reference to the appropriate CSS class and use webix.html.addClass() to apply it, thereby rotating the ship appropriately for the direction it’s traveling. However, before you can even do that, you need to ensure that only one class is ever applied. The easiest way to do this is to just clear any class that might currently be applied by looping through the possible degree increments and calling webix.html.removeClass(). Both webix.html.addClass() and webix.html.removeClass() take as arguments a reference to the DOM node to operate on and the name of the class to add or remove. Assuming the CSS class exists on the page, which it does since you created them all in the WXGAME constructor, the rotation is done.
With the rotation out of the way, the last step is to physically move the player. This just means adding xAdj to xLoc and yAdj to yLoc and then “clamping” the values on the high and low ends so that the player can’t ever move off the edge of the playfield. With that done, you just need to set new left and top style attributes on the two animation frames for the player and you’re all done!

Summary

In this chapter, the final one of this book, you did something with Webix that is a bit unusual: you wrote a game! In the process, you saw some new “helper”-type functions that Webix provides including ones for creating CSS classes, intercepting function invocations, and extending objects. You also got a basic flavor for how JavaScript games can be written and the fundamental architecture that goes into them.
I hope you’ve enjoyed this book and that you’ve learned about Webix and come to appreciate the power it brings to web development. Now, go forth, be fruitful, and create great web apps with Webix. Thanks very much for giving me the opportunity to serve as your guide on this journey of discovery!
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset