This is a starting point for writing games using the Phaser javascript library with the P2 physics engine.
It sets up a project to use coffeescript and webpack.
using
- clone
npm install
npm run dev
.- visit localhost:8080
example
take a look at the snood-pinball-coffee folder in the phaser_wrapper repo. This can be played at maxpleaner.github.io/pinball. This boiler was extracted from that project so the file organization is the same.
guide to source code
-
webpack.config.js
is the entry point for development mode webpack. It starts a static server on port 8080 and serves index.html as its root. It uses coffee-loader to compile all coffeescript files into a single, in-memory bundle.js which is loaded from index.html.
This gets started withnpm run dev
. See thepackage.json
for the definition of this command. -
webpack.config.production.js
will prepare the app for production. It does not run its own static server. Rather, it compiles all the coffeescripts into aprod-bundle.js
that can be deployed to a static-website host such as github pages.
Start this withnpm run deploy
. You'll need to manually create a deployment folder and copy the index.html and style.css in there along with the prod-bundle. Also the script src in index.html needs to be changed from bundle.js to prod-bundle.js. -
style.css
- hardly used here. -
loader.coffee
- the entry of the application, the first thing read by webpack. Loads other files then starts the game.
note this is where the game width and height are configured. Also there's a setting in here to toggle debugMode. Setting this to true will outline all sprite bounds and hitboxes. The only things added towindow
are done so in here. -
index.html
- a super simple html page that is basically empty. It loads the Phaser game as a script and lets it take over. -
assets: There are two folders for assets. The
asset-src
is intended to be used for incomplete assets - for example the original version of an image that is modified for use in the game.assets
is indended as a place for finished assets of any kind. Files in here have their paths specified inassets.coffee
. This boiler ships with one example asset + physics file. -
lib folder: This one is more complex than the rest and is where the core of the game lives.
game_loader.coffee
(required byloader.coffee
). It loadsplay_state.coffee
and sets it as the initial state of the game.play_state.coffee
a very minimal file that just requires the files inplay_state/
. Each of those files is a single function and they get combined into one object which is a proper Phaser state.assets
: A manifest of assets. All assets should get their paths assigned to variables here and then be referenced frompreload.coffee
using those variables.play_state/init.coffee
- this is the first function called by phaser. Like create, it's only called once upon the game's initial load. This file doesn't do a whole lot. It loads the state/assets/util files and tells Phaser to use P2 physics.play_state/preload.coffee
- all assets need to get added here in addition to theassets
file. There is a different load method that needs to be called for different assets types. See the Phaser docs on that.play_state/create.coffee
- this is the most important file in terms of dictating the behavior of the game. Here, assets are added to the page, custom hitboxes are added, and collision behavior is defined. The methods inutil.coffee
can be used here.state.coffee
- properties that make up the initial state of the game.util.coffee
- Alot of the files in this repo are sparse, bututil.coffee
has an API that can be used to build the game. There are a bunch of methods in there:random_int(min, max)
random_from_list(list)
collide_world_bounds(sprite)
- limit the sprite's movement to the game boardadd_physics_file(sprite, physics_file, physics_file_key)
- physics files are used to create polygonal hitboxes.
They can be created with PhysicsEditor and exported in the lime/corona json format. The physics file needs to be loaded in preload as well as the sprite, and the name assigned to it there is the value that gets passed here asphysics_file
. Thephysics_key
is whatever name was given for the asset in PhysicsEditor. This can be inspected by opening the json file.
tricky things
I haven't yet found a good way to scale polygonal sprites, adjust their anchor x/y, or change their center of mass. Doing all these things messes up the polygonal hitbox.set_game_size_and_background(width, height, background_key)
- background key refers to an asset that was loaded in the preload functionset_game_size(width, height)
add_p2_sprite(x, y, key)
- this is called from the create function. It puts a sprite on the page and sets it up to use P2 physics.make_static(sprite)
prevent it from responding to collisionsturn_off_gravity(sprite)
- prevent it from responding to gravitycreate_collision_group
this needs to be done for every kind of asset that collides. This just creates an anonymous one; it's customized later.set_sprite_collision_group(sprite, collision_group)
assigns the collision group to the sprite.add_group
- creates a new anonymous sprite group with P2 physics enabledadd_sprite_material(sprites..., key)
- the key here is just an arbitrary name. See below for an explanation of materials vs collisionsadd_world_material(key)
- probably just needs to be called once, allows for customizing the collision physics with the world bounds.add_contact_material(material1, material2, opts)
- customize the physics of a collision between two materials. The options here all have defaults set, but here are the keys: friction, restitution (bounciness), stuffness, relaxation, frictionStiffness, frictionRelaxation, and surfaceVelocity.
It's probably worth explaining the difference between materials and collisions here because it's a little confusing. With any two objects that collide (i.e. they push each other), there needs to be a collision group for each and a connection declared at both ends (this is explained below). Beyond this, there's no reason to write any more code regarding collisions except if there's a custom Javscript function you want to run when the collision happens.
In either case, after calling create_collision_group
and set_collision_group
on two sprites it's necessary to call collide
:
sprite1.body.collides sprite2_collision_group, (sprite1, sprite2) =>
# do custom stuff here
You'd also need to declare sprite2.body.collides sprite1
(note that the callback is optional and there's no reason to declare it in both places).
Materials, on the other hand, don't offer callbacks but they do allow customizing the physics of the collision. These are totally optional in all situations.