# Popfizz Week6 exploring Phaser.io example2 ## Main concepts ### Javascript #### - Same as last week ### Phaser #### - New assets #### - World Building(new) #### - The platforms(new) #### - Animations(new) #### - Bouncing Bombs(new) #### - gameover ## INSTRUCTIONS Based on tutorial (http://phaser.io/tutorials/making-your-first-phaser-3-game/part1) ### Setup * Need to upload assets ### Introduction * Basic template ``` var config = { type: Phaser.AUTO, width: 800, height: 600, scene: { preload: preload, create: create, update: update } }; var game = new Phaser.Game(config); function preload () { } function create () { } function update () { } ``` * An instance of a Phaser.Game object is assigned to a local variable called `game` and the configuration object is passed to it. * The `type` property can be either Phaser.CANVAS, Phaser.WEBGL, or Phaser.AUTO. This is the rendering context that you want to use for your game. The recommended value is Phaser.AUTO which automatically tries to use WebGL. * The `width` and `height` properties set the size of the canvas element that Phaser will create. * The `scene` property of the configuration object will be covered in more detail further on in this tutorial. ### Loading assets(things to discuss) ``` function preload () { this.load.image('sky', 'assets/sky.png'); this.load.image('ground', 'assets/platform.png'); this.load.image('star', 'assets/star.png'); this.load.image('bomb', 'assets/bomb.png'); this.load.spritesheet('dude', 'assets/dude.png', { frameWidth: 32, frameHeight: 48 } ); } ``` * This will load in 5 assets: 4 images and a sprite sheet. * First parameter is known as the asset key (i.e. 'sky', 'bomb'). * This string is a link to the loaded asset and is what you'll use in your code when creating Game Objects. ### Display an Image ``` function create () { this.add.image(400, 300, 'sky'); this.add.image(400, 300, 'star'); } ``` * The values 400 and 300 are the x and y coordinates of the image. * Phaser 3 all Game Objects are positioned based on their center by default. * The background image is 800 x 600 pixels in size, so if we were to display it centered at 0 x 0 you'd only see the bottom-right corner of it. If we display it at 400 x 300 you see the whole thing. ### World Building(new) ``` var platforms; function create () { this.add.image(400, 300, 'sky'); platforms = this.physics.add.staticGroup(); platforms.create(400, 568, 'ground').setScale(2).refreshBody(); platforms.create(600, 400, 'ground'); platforms.create(50, 250, 'ground'); platforms.create(750, 220, 'ground'); } ``` * this.add.image is creating a new Image Game Object and adding it to the current Scenes display list. * `this.physics` means we're using the Arcade Physics system, but before we can do that we need to add it to our Game Config to tell Phaser our game requires it. ``` var config = { type: Phaser.AUTO, width: 800, height: 600, physics: { default: 'arcade', arcade: { gravity: { y: 300 }, debug: false } }, scene: { preload: preload, create: create, update: update } }; ``` ### The platforms(new) ``` platforms = this.physics.add.staticGroup(); ``` * This creates a new Static Physics Group and assigns it to the local variable platforms. * `Dynamic` and `Static`: - A dynamic body is one that can move around via forces such as velocity or acceleration. It can bounce and collide with other objects and that collision is influenced by the mass of the body and other elements. - A Static Body simply has a position and a size. It isn't touched by gravity, you cannot set velocity on it and when something collides with it, it never moves. * `Group`: They are ways for you to group together similar objects and control them all as one single unit. Groups are capable of creating their own Game Objects via handy helper functions like `create`. A Physics Group will automatically create physics enabled children, saving you some leg-work in the process. ``` platforms.create(400, 568, 'ground').setScale(2).refreshBody(); ``` * `setScale(2)` : scale a image x2 * `refreshBody()` : Code needs to tell the physics world about the changes we made. ``` platforms.create(600, 400, 'ground'); platforms.create(50, 250, 'ground'); platforms.create(750, 220, 'ground'); ``` * creat a platform in a x and y cordinate with 'ground'. ### The player ``` create() { ... player = this.physics.add.sprite(100, 450, 'dude'); player.setBounce(0.2); player.setCollideWorldBounds(true); this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }), frameRate: 10, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'dude', frame: 4 } ], frameRate: 20 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }), frameRate: 10, repeat: -1 }); } ``` * The first part of the code creates the sprite: #### Physics Sprite(new) ``` player = this.physics.add.sprite(100, 450, 'dude'); ``` * This creates a new sprite called `player`, positioned at 100 x 450 pixels from the bottom of the game. * The sprite was created via the Physics Game Object Factory `this.physics.add` which means it has a Dynamic Physics body by default. ``` player.setBounce(0.2); player.setCollideWorldBounds(true); ``` * It is given a slight bounce value of 0.2. This means when it lands after jumping it will bounce ever so slightly. #### Animations(new) ![](https://i.imgur.com/WvoMiEg.png) * If you glance back to the preload function you'll see that 'dude' was loaded as a sprite sheet, not an image. * There are 9 frames in total, 4 for running left, 1 for facing the camera and 4 for running right. We define two animations called 'left' and 'right'. Here is the left animation: ``` this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }), frameRate: 10, repeat: -1 }); ``` * The 'left' animation uses frames 0, 1, 2 and 3 and runs at 10 frames per second. The 'repeat -1' value tells the animation to loop. * This is our standard run-cycle and we repeat it for running in the opposite direction, using the key 'right' and a final one for 'turn'. ### Add physics ``` player.body.setGravityY(300) ``` * When a Physics Sprite is created it is given a body property, which is a reference to its Arcade Physics Body. This represents the sprite as a physical body in Phasers Arcade Physics engine. * This is an arbitrary value, but logically, the higher the value, the heavier your object feels and the quicker it falls. In order to allow the player to collide with the platforms we can create a Collider object. ``` this.physics.add.collider(player, platforms); ``` * This object monitors two physics objects (which can include Groups) and checks for collisions or overlap between them. ### Keyboard controls Colliding is all good and well, but we really need the player to move. ``` cursors = this.input.keyboard.createCursorKeys(); ``` * This populates the cursors object with four properties: up, down, left, right, that are all instances of Key objects. Then all we need to do is poll these in our update loop: ``` if (cursors.left.isDown) { player.setVelocityX(-160); player.anims.play('left', true); } else if (cursors.right.isDown) { player.setVelocityX(160); player.anims.play('right', true); } else { player.setVelocityX(0); player.anims.play('turn'); } if (cursors.up.isDown && player.body.touching.down) { player.setVelocityY(-330); } ``` * The first thing it does is check to see if the left key is being held down. If it is we apply a negative horizontal velocity and start the 'left' running animation. * If they are holding down 'right' instead we literally do the opposite. * By clearing the velocity and setting it in this manner, every frame, it creates a 'stop-start' style of movement. * The player sprite will move only when a key is held down and stop immediately they are not. * The final part of the key check sets the animation to 'turn' and zero the horizontal velocity if no key is held down. * The final part of the code adds the ability to jump. However we also test if the player is touching the floor, otherwise they could jump while in mid-air. ### Collecting stars It's time to give our little game a purpose. Let's drop a sprinkling of stars into the scene and allow the player to collect them. ``` stars = this.physics.add.group({ key: 'star', repeat: 11, setXY: { x: 12, y: 0, stepX: 70 } }); ``` * The process is similar to when we created the platforms Group. As we need the stars to move and bounce we create a dynamic physics group instead of a static one. * `key: 'star'`: It sets the texture key to be the star image. This means that any children created as a result of the config object will all be given the star texture by default. * `repeat: 11`: It sets the repeat value to be 11. Because it creates 1 child automatically, repeating 11 times means we'll get 12 in total. * `setXY` : This is used to set the position of the 12 children the Group creates. Each child will be placed starting at x: 12, y: 0 and with an x step of 70. This means that the first child will be positioned at 12 x 0, the second one is 70 pixels on from that at 82 x 0, the third one is at 152 x 0, and so on. ``` stars.children.iterate(function (child) { child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8)); }); ``` * It iterates all children in the Group and gives them a random Y bounce value between 0.4 and 0.8. The bounce range is between 0, no bounce at all, and 1, a full bounce. If we were to run the code like it is now the stars would fall through the bottom of the game and out of sight. To stop that: ``` this.physics.add.collider(stars, platforms); ``` * We can use another Collider object ``` this.physics.add.overlap(player, stars, collectStar, null, this); ``` * this we will also check to see if the player overlaps with a star or not. If found then they are passed to the 'collectStar' function: ``` function collectStar (player, star) { star.disableBody(true, true); } ``` Quite simply the star has its physics body disabled and its parent Game Object is made inactive and invisible, which removes it from display. Running the game now gives us a player that can dash about, jump, bounce off the platforms and collecting the stars that fall from above. ### Scores and Scoring There are two final touches we're going to add to our game: an enemy to avoid that can kill the player, and a score when you collect the stars. #### First, the score. ``` var score = 0; var scoreText; ``` * One to hold the actual score and the text object itself. * ``` scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' }); ``` * `scoreText` is set-up in the `create` function * 16 x 16 is the coordinate to display the text at. * 'score: 0' is the default string to display and the object that follows contains a font size and fill color. When the player picks-up a star their score increases and the text is updated to reflect this: ``` function collectStar (player, star) { star.disableBody(true, true); score += 10; scoreText.setText('Score: ' + score); } ``` * So 10 points are added for every star and the scoreText is updated to show this new total. ### Bouncing Bombs(new) In order to round our game out it's time to add some baddies. The idea is this: When you collect all the stars the first time it will release a bouncing bomb. The bomb will just randomly bounce around the level and if you collide with it, you die. All of the stars will respawn so you can collect them again, and if you do, it will release another bomb. This will give the player a challenge: get as high a score as possible without dying. ``` bombs = this.physics.add.group(); this.physics.add.collider(bombs, platforms); this.physics.add.collider(player, bombs, hitBomb, null, this); ``` * Group for the bombs and a couple of Colliders * The bombs will of course bounce off the platforms, and if the player hits them we'll call the hitBomb function. All that will do is stop the game and turn the player red: ``` function hitBomb (player, bomb) { this.physics.pause(); player.setTint(0xff0000); player.anims.play('turn'); gameOver = true; } ``` We need to release a bomb. To do that we modify the collectStar function: ``` function collectStar (player, star) { star.disableBody(true, true); score += 10; scoreText.setText('Score: ' + score); if (stars.countActive(true) === 0) { stars.children.iterate(function (child) { child.enableBody(true, child.x, 0, true, true); }); var x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400); var bomb = bombs.create(x, 16, 'bomb'); bomb.setBounce(1); bomb.setCollideWorldBounds(true); bomb.setVelocity(Phaser.Math.Between(-200, 200), 20); } } function collectStar (player, star) { star.disableBody(true, true); score += 10; scoreText.setText('Score: ' + score); if (stars.countActive(true) === 0) { stars.children.iterate(function (child) { child.enableBody(true, child.x, 0, true, true); }); var x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400); var bomb = bombs.create(x, 16, 'bomb'); bomb.setBounce(1); bomb.setCollideWorldBounds(true); bomb.setVelocity(Phaser.Math.Between(-200, 200), 20); } } ``` * We use a Group method called countActive to see how many stars are left alive. If it's none, we use the iterate function to re-enable all of the stars and reset their y position to zero. This will make all of the stars drop from the top of the screen again. * The next part of the code creates a bomb. First we pick a random x coordinate for it, always on the opposite side of the screen to the player, just to give them a chance. Then the bomb is created, it's set to collide with the world, bounce and have a random velocity. The end result is a nice little bomb sprite that rebounds around the screen.