Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about 3D, Game development, HTML5, Javascript and Phaser.

In my opinion, Phaser is the most powerful HTML5 framework available, and I develop a lot of funny stuff with it, as you can see by yourself if you are a long time reader.

Phaser is intended to be a 2D engine, and this means – obviously – you can’t build 3D games.

What if you want to keep developing 2D games, just giving them a 3D environment? Something like a character moving only in 2 dimension, but rendered in 3 dimension.

A sort of fake 3D effect which sometimes is called 2.5D. A two dimensional world rendered in three dimensions.

In the example you are about to see, we have one of the simplest Phaser examples using Arcade physics: two balls running and colliding with game bounds and each other. Just a red and a blue ball.

But we are going to render the scene in 3D using three.js.

three.js is a free and open source 3D framework to render 3D scenes, and it’s exactly what we need.

Now the question is: why should we use Phaser to build the game and three.js to render it? Wouldn’t it be better to just use three and start coding?

While it’s a good option, you probably are more familiar with Phaser, or you have some stuff already developed with Phaser and just want to use three to add a twist to you old projects.

While you can use both three.js and Phaser with no external bridges, Phaser guys developed a set of Phaser methods which integrate three.js in your game without having to know three.js in depth.

At the moment the library is released as exclusive content for Patreon bakers, so if you plan to use in your projects please consider supporting Phaser on Patreon.

I’ll publish in some days a Phaser + three.js only version of the prototype you are about to see, just to show you the differences between using Phaser 3D library and making Phaser and three.js work together by yourself.

Now, catch this:

Ok, we have a green and a blue ball moving around the stage, just like in just another Phaser and Arcade physics hello world, but try to drag the mouse and see what happens:

While red and blue balls continue to move around the stage, the 3D camera moves and lets you see the 3D representation of the 2D scene.

Look how balls movements and bounces are exactly the same, just in two different environments.

And obviously the 3D version is way more attractive, although it’s just a fake 3D since balls can’t move along the third dimension.

It’s not a matter of just adding a dimension: you can also play with lights and shadows to give your game a whole new look.

All in a few lines of code, really intuitive:

let game;
let gameOptions = {
    gameScale: 0.1,
    ballRadius: 25
}
window.onload = function() {
    let gameConfig = {
        type: Phaser.AUTO,
        scale: {
            mode: Phaser.Scale.FIT,
            autoCenter: Phaser.Scale.CENTER_BOTH,
            parent: "thegame",
            width: 600,
            height: 800
        },
        physics: {
            default: "arcade"
        },
        scene: playGame
    }
    game = new Phaser.Game(gameConfig);
    window.focus();
}
class playGame extends Phaser.Scene{
    constructor(){
        super({
            pack: {
                files: [
                    {
                        type: "script",
                        key: "threeJS",
                        url: "three.min.js"
                    },
                    {
                        type: "script",
                        key: "Phaser3D",
                        url: "Phaser3D.js"
                    }
                ]
            }
        });
    }
    preload(){
        this.load.image("redball", "redball.png");
        this.load.image("blueball", "blueball.png");
        this.load.script("OrbitControls", "OrbitControls.js");
    }
    create(){

        this.phaser3d = new Phaser3D(this, {
            fov: 45,
            near: 1,
            far: 1000,
            x: 0,
            y: 95,
            z: 0
        });

        this.phaser3d.camera.lookAt(0, 0, 0);

        this.phaser3d.enableShadows();
        this.phaser3d.enableGamma();

        this.phaser3d.add.ambientLight({
            color: 0xffffff,
            intensity: 0.1
        });

        new THREE.OrbitControls(this.phaser3d.camera, this.scale.parent);

        let spotlight = this.phaser3d.add.spotLight({
            intensity: 0.5,
            angle: 0.4,
            decay: 0.1,
            distance: 250,
            x: 0,
            y: 220,
            z: 0
        });

        this.addThreeBound(10, game.config.height + 20, game.config.width / 2 + 5, 0);
        this.addThreeBound(10, game.config.height + 20, - game.config.width / 2 - 5, 0);
        this.addThreeBound(game.config.width, 10, 0, game.config.height / 2 + 5);
        this.addThreeBound(game.config.width, 10, 0, - game.config.height / 2 - 5);

        let ground = this.phaser3d.add.box({
            width: (game.config.width + 20) * gameOptions.gameScale,
            height: 10 * gameOptions.gameScale,
            depth: (game.config.height + 20) * gameOptions.gameScale,
            color: 0xffff00,
            x: 0,
            y: -5 * gameOptions.gameScale,
            z: 0,
            material: {
                dithering: true,
                phong: true
            }
        });

        this.redThreeBall = this.addThreeBall(0xff0000);
        this.blueThreeBall = this.addThreeBall(0x0000ff);

        this.phaser3d.castShadow(this.redThreeBall, this.blueThreeBall);
        this.phaser3d.receiveShadow(ground);

        this.phaser3d.setShadow(spotlight, 512, 512);

        this.redBall = this.addArcadeBall("redball");
        this.blueBall = this.addArcadeBall("blueball");
        this.physics.add.collider(this.redBall, this.blueBall);
    }

    addThreeBound(width, depth, posX, posZ){
        this.phaser3d.add.box({
            width: width * gameOptions.gameScale,
            height: gameOptions.ballRadius * gameOptions.gameScale,
            depth: depth * gameOptions.gameScale,
            color: 0x00ff00,
            x: posX * gameOptions.gameScale,
            y: gameOptions.ballRadius / 2  * gameOptions.gameScale,
            z: posZ * gameOptions.gameScale,
            material: {
                dithering: true,
                phong: true
            }
        });
    }

    addThreeBall(color){
        return this.phaser3d.add.sphere({
            radius: gameOptions.ballRadius * gameOptions.gameScale,
            widthSegments: 32,
            heightSegments: 32,
            color: color,
            x: 0,
            y: 25 * gameOptions.gameScale,
            z: 0,
            material: {
                dithering: true,
                phong: true
            }
        });
    }

    addArcadeBall(key){
        let posX = Phaser.Math.Between(gameOptions.ballRadius, game.config.width - gameOptions.ballRadius);
        let posY = Phaser.Math.Between(gameOptions.ballRadius, game.config.height - gameOptions.ballRadius);
        let ball = this.physics.add.sprite(posX, posY, key);
        ball.setCircle(gameOptions.ballRadius);
        ball.setCollideWorldBounds(true);
        ball.setBounce(1);
        let angle = Phaser.Math.RND.rotation();
        ball.setVelocity(400 * Math.cos(angle), 400 * Math.sin(angle));
        return ball
    }

    update(){
        this.redThreeBall.position.x = (this.redBall.x - game.config.width / 2) * gameOptions.gameScale;
        this.redThreeBall.position.z = (this.redBall.y - game.config.height / 2) * gameOptions.gameScale;
        this.blueThreeBall.position.x = (this.blueBall.x - game.config.width / 2) * gameOptions.gameScale;
        this.blueThreeBall.position.z = (this.blueBall.y - game.config.height / 2) * gameOptions.gameScale;
    }
}

What do you think about it? Ready to turn your old flat 2D games into new fake 3D games? Next time I’ll show you a real world example, meanwhile download the source code of the entire project, external libraries included.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.