WarsawJS: My 10 days with Phaser.js

81

Transcript of WarsawJS: My 10 days with Phaser.js

Piotr Kowalski"My 10 days with Phaser.js"2015-09-16@piecioshka

Who am I?

Kierownik Działu Aplikacji Webowych

Cyfrowy Polsat, Warsaw

JavaScript Ninja. Mac lover. Pebble evangelist.

Organizer WarsawJS

"Kto chce szuka sposobu, kto nie chce szuka powodu."

piecioshka.pl/blog github.com/piecioshka

twitter.com/piecioshka soundcloud.com/piecioshka

Where am I?

I made a game

dragonballplay.com

Advantages of Phaser.js

• Big community

• A lot of lines of code (a lot of JSDocs)

• Good quality of code (components)

• Separation of layers in API, e.g. states and

keyboards

• Cool phaser.io/docs

Disadvantages of Phaser.js

• I didn't notice...

"State idea"

• The gameplay is based on state switching , e.g.

choose language, select character from menu,

fight, defeat or win

• State definition - class with 4 methods: preload ,

create , update , render and optionally init

• Only one state is active !

init method

• It runs as the first method

preload method

• Method which downloads the assets

• images

• sounds

• json files

create method

• Runs ones while generating the state

• In that method I create all objects that are

occurring in a game state

update method

• Phaser.Game#raf via

requestAnimationFrame or setTimeout

• 38881: Phaser.Game#update

• The core game loop

render method

• Method runs after update process

• Inside it we can enable displaying debug

details

Day I: Here we go!31↑

Use ECMAScript 6.

webpack and Babel.js take care of compiling

to ECMAScript 5 (for browser).“

webpack.config.jsmodule.exports = { resolve: { extensions: ['.es6.js', '.js', ''] }, entry: './app/scripts/main', output: { filename: 'bundle.js', path: './app/dist' }, module: { loaders: [{ test: /\.es6\.js/, exclude: /node_modules/, loader: 'babel?stage=0' }] }}

01.02.03.04.05.06.07.08.09.10.11.12.13.

Advantages of ECMAScript 6

• Modules - no global variables

• Importing - require only dependency

• Smooth syntax of class definition with cool inheritance

• Default parameters and Arrow function

// Destructuring assignment

let [x, y] = ['abc', 100]

// x = 'abc', y = 100

01.

02.

03.

HTML (index.html)No more HTML files, because everything is in the Canvas.

1. Create container for game: <div id="game"></div>

2. Add library file: scripts/vendor/phaser.js

3. Add our game file: dist/bundle.js

Phaser.js v2.4.3

Type Files

Normal 2.8M phaser.js

726K phaser.min.js

Only need to 2,2M phaser-arcade-physics.js

754K phaser-arcade-physics.map

567K phaser-arcade-physics.min.js

Why not in modules?

• Throw an error: "PIXI is not defined"

• webpack will have big file to manage

• Do I really need it?

Write first lines of code

• Create a game object// Create game object

this.game = new Phaser.Game(800, 400, Phaser.Canvas, 'game)

• One for all game

• Available from the each of game state

01.

02.

Day II: Design (vol. 1)157 LOC (126↑)

• Add states: MenuState - select character

// Add state to setthis.game.state.add(name, handler)// Activate passed statethis.game.state.start(name)

01.02.03.04.

Day II: Design (vol. 2)

• assets - directory contains everything except code,

e.g. graphics , sound

• Display first in game background - simple image with game

dimension and position in point 0,0

// Load image file and put it to cachethis.load.image(key, path)// Fetch from cache image and put to Canvasthis.add.image(x, y, key)

01.02.03.04.

Day II: Design (vol. 3)

• scripts/models/ directory with characters definition, e.g. Son Goku

• Add states: FightState , SearchingState (screen with collected balls)

• scripts/configuration.js - file in isolated game configuration

• Create instance of player class

• Add some buttons

// Add button to Canvasthis.add.button(x, y, key, handler)

01.02.

The first big problem

• Sharing objects between states?

Ideas?

• Global variables?

• Never!

• Create custom game properties!

Day III: We are moving!258 LOC (101↑)

• Create map with Tiled - cool tool for create game maps

this.load.tilemap(key, path, data, format)// data: if path was passed, equals null// format: Phaser.Tilemap.TILED_JSON

• Add tileset to map (our spritesheet)

this.load.spritesheet(key, path, width, height)// width: tile width// height: tile height

• Create layer on map and extend it to the whole game area

01.02.03.

01.02.03.

Balls positions

• Definition of positions dragon balls in separate files, for why?

• backend can generate that files

• separate configuration from business logic

• Loading JSON file

// Fetch file and put response into cachethis.load.json(key, path)// Return parsed objectobject = this.cache.getJSON(key)

01.02.03.04.

Create the first sprite - character// Add sprite to gamethis.add.sprite(x, y, key)

Add dragon balls• Create group object for them, for better collision management

01.02.

Simple message• Create message

// Add labelmessage = this.add.text(x, y, text)

• Animation: fadeIn

message.alpha = 0this.add.tween(message).to(config, time, type, true)// config: { alpha: 1 }// time: Phaser.Timer.SECOND// type: Phaser.Easing.Linear.None

01.02.

01.02.03.04.05.

Navigation vol. 1if (keyboard.isDown(Phaser.Keyboard.LEFT) { player.x -= 5}

Warning!If we would like to use collision detection,

we should update player.velocity.x not only player.x .

01.02.03.

Navigation vol. 2// Update velocity of playerplayer.velocity.x -= 5

Remember to clear velocity before that process!

// Reset velocityplayer.velocity.x = 0

01.02.

“01.02.

Collision - a very broad subject

• Player

// Enable arcade mode for our player

this.physics.arcade.enable(player)

• Map

map.setCollisionByIndex(1) // Start counting from 1

layer = map.createLayer(name)

this.physics.arcade.collide(player, layer)

Player will be collide with tile with index = 0 from now!

01.

02.

01.

02.

03.

Collected items - dragon balls

• Group of balls should have a body

balls = this.add.group()balls.enableBody = true

• Group should have defined a physics

// Add body to group of ballsthis.physics.arcade.enable(balls)// Start collide between player and group of ballsthis.physics.arcade.collide(player, balls, handler)

01.02.

01.02.03.04.

Day IV: Time from clock

384 LOC (126↑)

• Create states: ShenronState , GameOverState ,

TrainingState

• Handle keyboard on MenuState

• Create countdown - measure time to GameOver

// Clock with reference to statethis.time.events.add(time, handler, context)

01.02.

Day V: Music!710 LOC (326↑)

• Create fake spritesheets for characters

• Add logo and sound

this.load.audio(key, path)sound = this.add.audio(key)sound.play()

• Create enemies on FightState

• Resize hitbox during fight

• Create state MealState - regeneration state, HP will be reset

01.02.03.

Day VI: Display bars858 LOC (148↑)

• Display player labels (HP and EXP),

and player avatars on FightState

• Disable sound globally (saved state in localStorage )

• Add everywhere we can DragonBallPlay logo

Day VII: Player collision1028 LOC (170↑)

• Support mouse events on MenuState

// Support mouse overbutton.events.onInputOver.add(handler, context)

• Check players collision

isOverlap() => { return this.physics.arcade.overlap(player, enemy)}

• Decrease HP of enemy when player hits him

01.02.

01.02.03.

Day VIII: Create ArtificialIntelligence

1270 LOC (242↑)

• Create screen which create only message

• Display game control before main part of game

• Rename FightState to VersusState - better name

• FightState is base class with children VersusState and

TrainingState

• First idea for AI - create list of enemy moves

Day IX: Easter egg1533 LOC (263↑)

• Fixed AI - choose random move from predefined list

• Create Utilities module with static methods

• Create state: LanguageState

• Detach all text to isolated JSON files

• Create DefinitionTyped.js

• Revert default player parameters on GameOverState

• Create Easter Egg : new character available on MenuState

Day X: Last day1732 LOC (199↑)

• Rename SearchingState to CollectingState

• Choose randomly 1 map from 3 on CollectingState

• Add collision with 2 tiles: 1 and 3

// Upgrade collisionmap.setCollision([1, 3])

• Converting all images to PNG - why is it a bad idea for backgrounds?

• Create states: PlayerPresentationState , EnemyPresentationState ,

WinnerState

01.02.

Future plans

• Block players moves when someone dies on VersusState

• Add countdown on TrainingState

• Display app version somewhere with small font size

• Extend display time for MessageState

• Fixed players collision, that they can't enter on themselves

• Support shortcuts

• Fixed Konami code

• Support Gamepad API

• Support Notification API

• Draw characters and create simple favicon

The worst partof code

scripts/states/VersusState.es6.js

The best partof code

scripts/main.es6.js

What I learned?

• Draw something when you are finish implementation

• Keyboard handler doesn't have to be in update method

• Each of state has his own clock , which is destroyed

when state is destroyed

My mistakes

• Duplicate code

Phaser.State#rnd.integerInRange(min, max)

instead of:

Utilities.random(min, max)

• I missed the manager to keep the storyline, so I decided to...

buy a couple of books about Phaser.js

Donation

patreon.com/photonstorm

(Richard Davey @photonstorm)

Thanks!

Links• Slides: http://piecioshka.github.io/warsawjs-presentation-my-10-days-with-phaser-js

• My games:

• https://github.com/piecioshka/www.dragonballplay.com [COMPLETE]

• https://github.com/javascript-ninjas/game-fastest-joby [COMPLETE]

• https://github.com/piecioshka/ss15-dumplings [COMPLETE]

• https://github.com/piecioshka/game-electrode [WIP]

• https://github.com/piecioshka/game-snooker [WIP]

• ECMAScript 7: https://babeljs.io/docs/usage/experimental

• Special editor: http://phasereditor.boniatillo.com

• Font: http://www.dafont.com/saiyan-sans.font

See you nextmonth atWarsawJS