Sunday, July 31, 2016

Building multiplayer games with Angular_part 2

Building the game

As we previously discussed, there are a few different ways to build the actual game. When building a DOM-only game, we wouldn’t drop out into another JS library. However, since we are interested in making a game that is pixel-manipulation-heavy, we’ll need to use a different path.

One way we can build an intense game is to talk directly to the <canvas> element. We can use primitives to load our images, sprites, animations, our own event loop, etc. etc.

Alternatively, we can use a game library that was purpose-built to power web games. Rather than need to handle all the dirty-work of building a game library and the game, we’ll use a free, open-source gaming library called phaser.

Phaser handles a LOT of the dirty-work for us and allows us to use a DSL (domain-specific language) to interact with our elements. It also allows us to interact directly with the <canvas> element in the cases where we need to drop down into the element itself (very rare).

Phaser takes over all of the tedious tasks, such as:

  • game physics
  • preloading assets
  • sprite handling
  • animation
  • game input (keyboard/touch)
  • sound handling
  • game scaling
  • etc.

Rather than building the interface to the DOM element, we can let Phaser do it’s job and we can focus on building our game.

INCREDIBLY simple introduction to Phaser

Phaser has some excellent documentation available on the web, so we won’t dive deeply into building the game with phaser. However, we will provide a basic introduction to phaser so you can get started right away.

Creating the game

Before we can actually build a game, we need to place the game on the page. There are a few different ways to do this. The simplest way to create a phaser game is by passing the game object when we create the game. This looks like:

1
2
3
4
5
6
7
8
9
10
11
var game =
  new Phaser.Game(
    800, 600,         // width x height
    Phaser.AUTO,      // the game context, 2D/3D
    'game_canvas',    // id of the DOM element to add the game
    {
      preload: function() {},
      create: function() {},
      update: function() {}
    }
  )

In the code, we’re creating a game by passing a few arguments. The first two are the width and the height of the canvas we’re working on. The third argument is the type of context to create with the <canvas>  element. If we pass Phaser.AUTO, phaser will take care of creating the element appropriate for the browser loading the game.
We use Phaser.AUTO pretty much always. It’s a good default.
The fourth argument is a string id of the DOM element we want to append the <canvas> element. If we don’t pass an element id or we pass a blank one, phaser will attach it’s element to the <body> element.

Finally, the last argument is the game object. This object contains all of the game methods phaser needs to run the game. We’ll cover a few of the core game methods shortly.

This method of building objects is the absolute simplest method. It’s appropriate to build small games/samples with this type of game. On the other hand, anything more a bit more complex, we’ll likely need to break out into building our game in a different way.

Building the game with state objects

Rather than having a single game object, phaser allows us to add state objects where we can build our game in states. To build a state object-based game, we’ll remove the last argument and attach states to the phaser game, like so:

3
4
5
6
7
8
9
10
11
12
var game =
  new Phaser.Game(
    800, 600,         // width x height
    Phaser.AUTO,      // the game context, 2D/3D
    'game_canvas'     // id of the DOM element to add the game
  );
var menuState = {
  preload: function() {},
  create: function() {}
};
game.state.add('MainMenu', menuState);
game.state.start('MainMenu');         // start the MainMenu state

In this way, we can set up completely different states for our game with new objects. It’s a much better way to create complex games. We’ll use this method to create state objects.

Let’s build the MainMenu state. It will look something like this:

https://school.codequs.com/p/H1Y5Qj7_

game functions

The preload() function is where we can load all of our game assets. Since we can’t really start playing a game until our images, sprites, sounds, etc. are loaded, we’ll need to load them. The preload() function is where we can do this. It’s the second method called on the state (after the init() function).

To preload an image, we’ll define it with a name key and the url to load the asset:
  1. function preload() {
  2.   game.load.image('hero', 'assets/player_blue.png');
  3. };
To load a spritesheet:
  1. function preload() {
  2.   game.load.spritesheet('explosion', 'explode.png', 128, 128, 16);
  3. };
If you’re unfamiliar with a spritesheet, it’s basically an image with the different pieces of the animation on the same image. Additionally, it’s usually a good way to keep the number of assets low for other applications. In this case, we’ll use it to store the animations.

In this case, the explosion spritesheet (the name of the asset registered with the phaser game) contains 16 different images, each at 128×128.

For reference, the explode.png file looks like:

https://school.codequs.com/p/H1Y5Qj7_
This explode.png is available as part of the phaser example repo.
To load a audio:

  1. function preload() {
  2.   game.load.audio('dink', 'assets/dink.mp3');
  3. };

To load a bitmap fonts:
  1. function preload() {
  2.   game.load.bitmapFont('architectsDaughter',
  3.       'assets/fonts/r.png',
  4.       'assets/fonts/r.fnt');
  5. };
To load a <script> tag in the preloader section:
  1. function preload() {
  2.   game.load.script('webfont',
  3.     '//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js');
  4. };
The create()function
The create() function is where we’ll actually go ahead and start placing these examples on the page. This is where we’ll set up our assets, place the sprites on the page, load animations, etc. Let’s start with our scrolling background of the MainMenu state.

The menu background looks like it’s moving up and to the right as though it’s a starfield


Phaser gives us an easy way to create this effect by setting a property called autoScroll. First, we need to place the menu on the screen. We can do this pretty simply:
  1. function create() {
  2.   var world = game.world;
  3.   var background = game.tile.tileSprite(0, 0,
  4.     world.width, world.height, 'menu_background');
  5. }
We’ll place the menu_background key as the base tileSprite. We’re using a tileSprite here because we want the whole background to take up the whole content. In this way, our menu_background.png must be tileable, aka be seemless across so that the leftside lines up to the right and such that the bottom lines up with the top.

https://school.codequs.com/p/H1Y5Qj7_


To set up the autoscrolling, we’ll call the phaser method autoScroll() on the background image and give it two coordinates, the first one is the amount to scroll on the x axis and the second on the y axis:
  1. function create() {
  2.   var background = game.tile.tileSprite(0, 0,
  3.     world.width, world.height, 'menu_background');
  4.   background.autoScroll(-50, -20);
  5. }
With our background in place, let’s go ahead and add the logo to the background. Another easy task as we can simply place the sprite on the element and position it:
  1. function create() {
  2.   // background
  3.   var textPadding = 20;
  4.   var sprite = game.add.sprite(world.centerX,
  5. world.centerY - textPadding, 'logo');
  6. }
Lastly, let’s add a single menu button. This is pretty easy as well. We’ll add a text sprite that phaser will create. Rather than load a sprite we create, this will allow phaser to create it on dynamic text.
  1. function create() {
  2.   // background
  3.   // logo
  4.   var newGameBtn = game.add.text(world.centerX,
  5. world.centerY + textPadding, 'New game');
  6. }
Phaser allows us to customize this text button with a lot of different options (see here for all of them) by passing a fourth object parameter. For instance, to set the font, the text color, and the text alignment:

3
4
5
6
7
8
9
function create() {
  // background
  // logo
  var newGameBtn = game.add.text(world.centerX,
world.centerY + textPadding, 'New game', {
    font: '16px Architects Daughter',
    align: 'center',
    fill: '#fff'
  });
}
Phaser needs to know if this text will be able to accept input on it or not. In other words, we need to tell phaser if it can be interacted with. To tell phaser we want it to be interacted with, we’ll set inputEnabled on the element:
  1. function create() {
  2.   // background
  3.   // logo
  4.   var newGameBtn = game.add.text(world.centerX,
  5. world.centerY + textPadding, 'New game');
  6.   newGameBtn.inputEnabled = true;
  7. }
Now that input can be accepted, we’ll need to set some input handlers. For instance, when the mouse or touch element clicks the button, we’ll want something to happen. Same as if the mouse is over the element or it scrolls away from it. We can tell phaser about these different settings by attaching to the existing events on the element.

For instance:
  1. function create() {
  2.   // background
  3.   // logo
  4.   var newGameBtn = game.add.text(world.centerX,
  5. world.centerY + textPadding, 'New game');
  6.   newGameBtn.inputEnabled = true;
  7.   // Add input handlers
  8.   newGame.events.onInputOver.add(overNewgame, this);
  9.   newGame.events.onInputOut.add(outNewgame, this);
  10.   newGame.events.onInputDown.add(onNewgameDown, this);
  11. }
Finally, let’s create the animation of expanding the newGameBtn when the mouse is over the actual element. We’ll use something called a tween.

A tween is essentially an effect that takes the starting position, the ending postion, and a duration. It’s job is to try to smoothly set the appropriate element properties for the duration of time so that it ends up at the ending position by the end of the interval.

Adding a tween in phaser is incredibly easy to do. In our overNewgame function, let’s add a tween to the newGameBtn. This function will get called when the input is hovering over the button:
  1. function overNewgame() {
  2.   game.add.tween(newGameBtn.scale)
  3.     .to({
  4.       x: 1.3,
  5.       y: 1.3
  6.     },
  7.     300,
  8.     Phaser.Easing.Exponential.Out,
  9.     true);
  10. };
In this tween, we’re playing with the scale of the newGameBtn object. The initial scale of the object starts out at (1, 1). In the tween, we’re setting the ending position to be 1.3x the width of the original button and 1.3 of the height. Over 300 miliseconds (the third parameter – the duration), the tween will try to change the width and the height to their appropriate scales.

The fifth parameter is the easing to use.We won’t go in-depth into them in this tutorial.

The final parameter is the autostart parameter. If this is set to true, then the tween will start automatically. If we don’t set this, then the tween won’t fire until we call start on the tween. Since this is a relatively simple one, we can simply set it up here and have it autostart.

Lastly, to handle the sound when the menu item is hovered over, we’ll call play on the audio element:

3
4
5
6
7
8
function overNewgame() {
  game.add.tween(newGameBtn.scale)
    .to({
      x: 1.3,
      y: 1.3
    }, 300, Phaser.Easing.Exponential.Out, true);
  dink.play(); // audio file
};

The update()function
The update function is the game loop function. It is called everytime the game itself needs to update. We don’t need it for the MainMenu state, but we will for the game loop.

To see the entire MainMenu state, check out this gist: http://j.mp/13y6H2z
Source: newsletter

If you feel useful for you and for everyone, please share it!
Suggest for you:


No comments:

Post a Comment