Create a game like Cube Koala with Phaser, and use Tiled as a level editor. First, take a look at Cube Koala if you are not familiar with the game already. It has a really simple yet strong game mechanic, where you rotate the world around the player to reach the gaol.
We replicate this behavior with the Phaser game engine in HTML5. We use the P2.js physics engine, which is part of Phaser, to simulate the physics. But first of all we have to build a level with Tiled which we load and compile in Phaser later on.
Create a level
We use a tile based level, which means we need a tileset and Tiled as a level editor. Keep in mind that we have to compile the tilemap later, if there are rotated or mirrored tiles we have to add this abilities to our compiler as well. This is not a huge problem to add, it’s just not included in the basic script below.
Concept
The idea is to move the world center to the center of the screen, the world center is by default on the top left of the screen (0,0). Add the tilemap level with an offset to move the player to the world center, which acts as pivot for the level rotation. Since we have to keep the player at the world center, we have to move the level instead of the player. To move the level we use the gravity force, wich is currently applied to the player, and apply it to the level. Therefore we have the level moving accordingly to the world physics while the player is still at the center and can rotate around itself. To rotate the level we rotate the whole world and alter the gravity to match the new world angle.
To keep things simple, the script below shows the most basic version without rotated or mirrored tiles, no spikes or hit detection, no exit or level reset.
use and to rotate the world
var game;
function init()
{
game = new Phaser.Game(640, 960, Phaser.AUTO, '', { preload: preload, create: create,preRender:preRender, update: update });
}
function preload()
{
//SET UP STAGE
game.stage.backgroundColor ="#333";
game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
game.scale.setMinMax(640, 960, 640, 960);
game.scale.pageAlignHorizontally = true;
game.scale.pageAlignVertically = true;
this.scale.forceOrientation(true, false);
game.forceSingleUpdate = true;
//LOAD TILE MAP
game.load.tilemap('level_1', 'assets/level_1.json', null, Phaser.Tilemap.TILED_JSON);
//LOAD TILESET
game.load.image('tiles', 'assets/TileSet.png');
}
function create()
{
//INIT PHYSICS
game.physics.startSystem(Phaser.Physics.P2JS);
game.physics.p2.gravity.y = 200;
game.physics.p2.restitution = .2;
game.physics.p2.friction = .01;
game.physics.p2.useElapsedTime = true;
//CENTER WORLD
game.world.setBounds(-game.width/2,-game.height/2,game.width,game.height);
//GET TILEMAP
var map = game.add.tilemap("level_1");
//LOAD TILE IMAGES
var tileSet = map.addTilesetImage('TileSet', 'tiles');
//SET COLLISIN TILES
map.setCollision([1,2,3,6,8,11,12,13])
//CREATE A DYNAMIC TEXTURE
var texture = game.add.bitmapData(map.widthInPixels,map.heightInPixels);
//CREATE A SPRITE WITH TEXTURE
var tileLayer = game.add.sprite(-map.widthInPixels/2, -map.heightInPixels/2, texture);
//LEVEL GROUP
this.level = game.add.group();
this.level.add(tileLayer);
//ADD MAIN PHYSICS BODY
this.body = game.physics.p2.createBody(0, 0, 0, false);
//LOOP TROUGH TILE ARRAY
for (var y = 0; y < map.layer.data.length; y++) {
for (var x = 0; x < map.layer.data[y].length; x++)
{
//CHECK IF CURRENT TILE IS THE PLAYER
if(map.layer.data[y][x].index == 15)
{
//DRAW AN OTHER TEXTURE INSTEAD THE PLAYER TEXTURE
tileSet.draw(texture.context,map.layer.data[y][x].worldX,map.layer.data[y][x].worldY,7);
//CREATE A SPRITE FORM PLAYER TEXTURE
var playerTexture = game.add.bitmapData(tileSet.tileWidth,tileSet.tileHeight);
tileSet.draw(playerTexture.context,0,0,map.layer.data[y][x].index);
//CENTER PLAYER
this.player = game.add.sprite(0,0, playerTexture);
game.physics.p2.enable(this.player);
//OFFSET LEVEL ACCORDINGLY
this.level.x = tileLayer.width/2 - map.layer.data[y][x].worldX-(tileSet.tileWidth/2);
this.level.y = tileLayer.height/2 - map.layer.data[y][x].worldY-(tileSet.tileHeight/2);
}
else
{
//DRAW THE TILE
tileSet.draw(texture.context,map.layer.data[y][x].worldX,map.layer.data[y][x].worldY,map.layer.data[y][x].index);
//ADD COLLISION
for (var i = 0; i < map.collideIndexes.length; i++)
{
if(map.layer.data[y][x].index == map.collideIndexes[i])
{
var shape = this.body.addRectangle(32,32,(map.layer.data[y][x].worldX)+(tileSet.tileWidth/2),map.layer.data[y][x].worldY+(tileSet.tileHeight/2));
};
};
};
};
};
//MOVE LEVEL BODY TO RIGHT POSITION
this.body.x = tileLayer.x+this.level.x;
this.body.y = tileLayer.y+this.level.y;
//ADD BODY TO P2 WORLD
game.physics.p2.addBody(this.body);
//ADD INPUTS
this.cursors = game.input.keyboard.createCursorKeys();
}
function preRender()
{
if(this.body)
{
//APPLY GRAVITY FORCE OF PLAYER.BODY TO LEVEL.BODY
this.body.y -= this.player.position.y
this.player.body.velocity.y = 0;
this.player.position.y = 0;
this.body.x -= this.player.position.x
this.player.body.velocity.x = 0;
this.player.position.x = 0;
//MOVE LEVEL ACCORDINGLY
this.level.x = this.body.x-this.level.children[0].x
this.level.y = this.body.y-this.level.children[0].y
};
}
function update()
{
if(this.cursors.left.justDown)
{
//ROTATE LEVEL
game.world.angle +=45;
//ALTER GRAVITY
game.physics.p2.gravity.y = Math.cos(game.world.rotation) * 100;
game.physics.p2.gravity.x = Math.sin(game.world.rotation) * 100;
};
if(this.cursors.right.justDown)
{
//ROTATE LEVEL
game.world.angle -=45;
//ALTER GRAVITY
game.physics.p2.gravity.y = Math.cos(game.world.rotation) * 100;
game.physics.p2.gravity.x = Math.sin(game.world.rotation) * 100;
};
}