# “Bouncing Light” Phaser prototype rendered in 3D thanks to three.js and Phaser 3D library

Did you ever dream a Phaser version capable of rendering in 3D? There’s a official library, available to Phaser Patreon bakers, which helps you to add the third dimension to your games by integrating three.js in a quick and intuitive way.

If you plan to use what you are going to see in this example, please consider supporting Phaser on Patreon.

By the way, most loyal readers should already have seen an example on this blog when I wrote how to fake a 3D effect whit Phaser 3D.

This time we’ll see how to turn a plain and flat 2D game into a shiny 3D game using Phaser 3D without changing any single line of the original 2D prototype.

We are going to convert the first step of Bouncy Light prototype, which is an easy and quick game which uses Arcade physics.

The trick is to keep the original game mechanics, and use three.js and Phaser 3D library only to draw a 3D scene which is updated according to what happens in its 2D counterpart.

It’s not a real 3D world, but a 3D representation of what is happening in the 2D world.

Look:

Just click/tap and hold to make ball move, release to stop it.

Obviously the ball does not move, itâ€™s the entire environment which moves towards the ball, but most of all there is a 3D representation of the 2D world.

I left both worlds to let you see how 2D world affects 3D world, but to have a complete 3D game you just have to hide the 2D sprites.

This way you will be able to give a new twist to your existing simple 2D games, have a look at the completely commented source code:

```let game;
let gameOptions = {

// ball gravity
ballGravity: 1200,

// bounce velocity when the ball hits a platform
bounceVelocity: 800,

// ball start x position, 0 = left; 1 = right
ballStartXPosition: 0.2,

// amount of platforms to be created and recycled
platformAmount: 10,

// platform speed, in pixels per second
platformSpeed: 650,

// min and max distance range between platforms
platformDistanceRange: [250, 450],

// min and max platform height, , 0 = top of the screen; 1 = bottom of the screen
platformHeightRange: [0.5, 0.8],

// min and max platform length
platformLengthRange: [40, 160],

// local storage name where to save best scores
localStorageName: "bestballscore3d",

// game scale between 2D and 3D
gameScale: 0.1
}
let gameConfig = {
type: Phaser.AUTO,
backgroundColor:0x87ceeb,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: "thegame",
width: 750,
height: 500
},
physics: {
},
scene: playGame
}
game = new Phaser.Game(gameConfig);
window.focus();
}
class playGame extends Phaser.Scene{
constructor(){
super("PlayGame");
}
}
create() {

// method to create the 3D world
this.create3DWorld();

// method to add the 2D ball

// method to add the 3D ball

// method to add game listeners
}

// method to create the 3D world
create3DWorld(){

// 3D world creation
this.phaser3D = new Phaser3D(this, {

fov: 25,

// camera x, y and z position
x: 50,
y: 110,
z: 110
});

// point the camera at a x, y, z coordinate
this.phaser3D.camera.lookAt(50, 20, 0);

this.phaser3D.enableGamma();

// add a soft, white ambient light
color: 0xffffff,
intensity: 0.4
});

color: 0xffffff,
intensity: 1,
angle: 0.4,
decay: 0.1,
x: 0,
y: 250,
z: 80
});

// enable the spotlight to cast shadow
}

// method to create the 2D ball

// this is just the good old Arcade physics body creation
this.ball = this.physics.add.sprite(game.config.width * gameOptions.ballStartXPosition, 0, "ball");

// set ball gravity
this.ball.body.gravity.y = gameOptions.ballGravity;

// we are only checking for collisions on the bottom of the ball
this.ball.body.checkCollision.down = true;
this.ball.body.checkCollision.up = false;
this.ball.body.checkCollision.left = false;
this.ball.body.checkCollision.right = false;

// modify a bit the collision shape to make the game more kind with players
this.ball.setSize(30, 50, true);
}

// method to create the 3D ball

// create a red sphere
radius: this.ball.displayWidth / 2 * gameOptions.gameScale,
widthSegments: 64,
heightSegments: 64,
color: 0xff0000,
x: 0,
y: 0,
z: 0
});

// set the ball to cast a shadow
}

// creation of a physics group containing all platforms

// let's proceed with the creation
for(let i = 0; i < gameOptions.platformAmount; i++){

}
}

// method to set a random platform X position
setPlatformX(){
return this.getRightmostPlatform() + Phaser.Math.Between(gameOptions.platformDistanceRange[0], gameOptions.platformDistanceRange[1]);
}

// method to set a random platform Y position
setPlatformY(){
return Phaser.Math.Between(game.config.height * gameOptions.platformHeightRange[0], game.config.height * gameOptions.platformHeightRange[1]);
}

// st platform X position
let platformX = (this.getRightmostPlatform() == 0) ? this.ball.x : this.setPlatformX();

// create 2D platform
let platform = this.platformGroup.create(platformX, this.setPlatformY(), "ground");

// set platform registration point
platform.setOrigin(0.5, 1);

// platform won't move no matter how many hits it gets
platform.setImmovable(true);

// set a random platform width
platform.displayWidth = Phaser.Math.Between(gameOptions.platformLengthRange[0], gameOptions.platformLengthRange[1]);

// add 3D platform as a 2D platform property
}

// method to add a 3D platform, the argument is the 2D platform

// create a green box
width: 1,
height: 50,
depth: 20,
color: 0x40ff80,
x: 0,
y: (game.config.height - platform2D.y) * gameOptions.gameScale - 25,
z: 0
});

// scale the 3D platform to make it match 2D platform size
platform3D.scale.x = platform2D.displayWidth * gameOptions.gameScale;

return platform3D;
}

// method to add the score, just a dynamic text
this.score = 0;
this.topScore = localStorage.getItem(gameOptions.localStorageName) == null ? 0 : localStorage.getItem(gameOptions.localStorageName);
}

// method to update the score
this.score += inc;
this.scoreText.text = "Score: " + this.score + "\nBest: " + this.topScore;
}

// listeners to make platforms move and stop
this.input.on("pointerdown", function(){
this.platformGroup.setVelocityX(-gameOptions.platformSpeed);
}, this);
this.input.on("pointerup", function(){
this.platformGroup.setVelocityX(0);
}, this);
}

// method to get the rightmost platform
getRightmostPlatform(){
let rightmostPlatform = 0;
this.platformGroup.getChildren().forEach(function(platform){
rightmostPlatform = Math.max(rightmostPlatform, platform.x);
});
return rightmostPlatform;
}

// method to be executed at each frame
update(){

// collision management ball Vs platforms
this.physics.world.collide(this.platformGroup, this.ball, function(){

// bounce back the ball
this.ball.body.velocity.y = -gameOptions.bounceVelocity;
}, null, this);

// loop through all platforms
this.platformGroup.getChildren().forEach(function(platform){

// if the platform leaves the screen to the left...
if(platform.getBounds().right < -100){

// increase the score

// recycle the platform moving it to a new position
platform.x = this.setPlatformX();
platform.y = this.setPlatformY();

// set new platform width
platform.displayWidth = Phaser.Math.Between(gameOptions.platformLengthRange[0], gameOptions.platformLengthRange[1]);

// adjust 3D platform scale and y position
platform.platform3D.scale.x = platform.displayWidth * gameOptions.gameScale;
platform.platform3D.position.y = (game.config.height - platform.y) * gameOptions.gameScale - 25;
}

// adjust 3D platform x position
platform.platform3D.position.x = platform.x * gameOptions.gameScale;
}, this);

// if 2D ball falls down the screen...
if(this.ball.y > game.config.height){

// manage best score
localStorage.setItem(gameOptions.localStorageName, Math.max(this.score, this.topScore));

// restart the game
this.scene.start("PlayGame");
}

this.ball3D.position.y = (game.config.height - this.ball.y) * gameOptions.gameScale;
this.ball3D.position.x = this.ball.x * gameOptions.gameScale;

}
}
```

If you look at the original source code, you will notice the 2D management did not change, and I just added the routine to move 3D bodies to match 2D bodies movements.

Do you plan to convert some of your games from 2D to 3D? Then consider becoming a Phaser Patron, you will get Phaser 3D, a lot of examples and you will also support future Phaser development. Download the entire project to see how easy it was for me to convert the game from 2D to 3D.

215 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 10000000
// 2 Cars
// 2048
// Avoider
// Ballz
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Breakout
// Bricks
// Columns
// CubesOut
// Dots
// DROP'd
// Dudeski
// Eskiv
// Filler
// Fling
// Globe
// HookPod
// Hundreds
// InkTd
// Iromeku
// Lumines
// Magick
// MagOrMin
// Maze
// Memdot
// Nano War
// Nodes
// o:anquan
// Ononmin
// Pacco
// Phyballs
// Platform
// Poker
// Pool
// Poux
// Pudi
// qomp
// Racing
// Renju
// SameGame
// Security
// Sling
// Slingy
// Sokoban
// Splitter
// Sproing
// Stack
// Stairs
// Stringy
// Sudoku
// Tetris
// Threes
// Toony
// Turn
// TwinSpin
// vvvvvv
// Wordle
// Worms
// Yanga
// Zhed
// zNumbers