Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
18. September 2015

 

The following is a video showing the process discussed in this earlier tutorial.  For all the source code mentioned, see the earlier link.

 

 

The video is also available in high definition here.

Programming


17. September 2015

 

Right off the hop, that title is a bit misleading…  Phaser doesn’t really support GLSL shaders, or at least not by that name.  What it supports is Filters, which it inherits from the Pixi renderer.  Right off the hop you should be aware that this only works on the WebGL renderer, so the fallback canvas renderer cannot and probably never will support filters.

 

This process isn’t overly difficult, but as far as I’ve been able to tell, there are no existing examples on using shaders/filters in Phaser with Typescript, so a few of the TypeScript specific aspects can cause a bit of a stumbling block.  For this first example I am simply going to port the gray filter example to TypeScript.  This filter simply turns whatever it is attached to gray, simple enough.  Let’s jump right in with the code:

/// <reference path="phaser.d.ts"/>


class Uniforms {
    gray: any;
}  
class MyShader extends Phaser.Filter {

    uniforms: Uniforms;

    constructor(game: Phaser.Game, uniforms: any, fragmentSource: string[]) {
        this.uniforms = new Uniforms();
        this.uniforms.gray = { type: '1f', value: 1.0 };

        this.fragmentSrc = [

            "precision mediump float;",

            "varying vec2       vTextureCoord;",
            "varying vec4       vColor;",
            "uniform sampler2D  uSampler;",
            "uniform float      gray;",

            "void main(void) {",
            "gl_FragColor = texture2D(uSampler, vTextureCoord);",
            "gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126 * gl_FragColor.
                                r + 0.7152 * gl_FragColor.g + 0.0722 * 
                                gl_FragColor.b), gray);",
            "}"
        ];

        super(this.game, this.uniforms, this.fragmentSrc);
    }

}

class SimpleGame {
    game: Phaser.Game;
    image: Phaser.Sprite;
    myShader: MyShader;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload,
            render: this.render
        });
    }
    preload() {
        this.game.load.image("logo","GFSLogo.png");
    }
    render() {
    }
    create() {
        this.image = this.game.add.sprite(0, 0, "logo");
        this.image.filters = [new MyShader(this.game, null, null)];
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

The major difference here is we inherit from the Phaser.Filter class.  One other major difference, since TypeScript is a typed language, we need to specify our Uniforms as a class.  I took the lazy approach and made each parameter an any.  uniforms are paramaters passed in to the shader, although in this case Phaser itself passed in the value for vTextureCoord, vColor and uSampler.  It’s through using uniforms that your game logic can control the actions of the shader.

 

Next we define the fragment shader source as an array of strings.  Fragment shaders can be thought of little mini shader programs that are executed for each potentially drawn pixel in the resulting image.  This particular shader simply samples the current pixel at the current location within the texture and multiplies that value’s rgb by a slightly more “gray” version of itself.  gl_FragColor can be thought of as the return type of the fragment shader, and it contains that pixel to be displayed.  A discussion of GLSL is way beyond the scope of this tutorial but more details and resources are available here.

 

Finally in the actual game code you can see how the shader is applied to the image we loaded:

this.image.filters = [new MyShader(this.game, null, null)];

The .filters member is an array, so multiple filters can be applied if desired.  When you run this example you should see:

image

 

You can apply different shaders by changing the fragmentSrc value, just keep in mind you need to setup each parameter in the Uniforms class.

 

If the idea of encoding your shaders into a string array is off putting, there exists code to load shaders from file.

Programming


15. January 2015

 

I’ve been doing a long running Phaser with TypeScript tutorial series that illustrates basically everything you need to know to create a complete game using the Phaser HTML5 game engine.  I decided to put together a video tutorial showing how to put it all together to make a complete game.

 

Now, keep in mind, the game is incredibly simple… I wanted to keep the entire thing to under an hour after all ( I failed, it’s an hour and a half ), so I had to keep the scope reasonable in size.  So as a result I’ve created Extreme Walking Simulator!  Yeah, you walk… and that’s it actually.  You walk forever and ever in front of the same looping background.  However, you are left with a complete but simple game but most importantly, a framework of code that can be expanded upon to build much more complex games.  It also illustrates program flow, drawing sprites, playing music, changing states, handling input and all the other things a “full” game has to do.

 

In addition to the code, all of the assets used are available on GitHub.  This includes my incredible title music, the Blend files used for the main character and the level scene.  Everything you need to create this game is there in the Assets folder.  Feel free to play around and use it however you want.  That said, the textures just came from Google Image Search, so no clue if there is a copyright on any of them, so don’t use them in a commercial project! I believe all of my Github stuff is set to Creative Commons license…  if it’s not, assume you can do anything you want with it and that you have absolutely no warranty.

 

Ok, enough preamble, let’s jump right in!  The tutorial is available in two parts on YouTube in 1080p ( or embedded below):

 

 

Following are the game itself, then the source listings, finally the two videos in an embedded player.

 

EXTREME WALKING SIMULATOR!

 

EDIT: Removed to prevent music auto playing, to play the game click here instead. Warning, loud

Controls:

  • Click the title screen to get started
  • Tap right arrow to start walking/speed up
  • Tap left arrow to slow down/stop
  • Press ESC to stop the torture!

 

 

The Source Code

 

index.html

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Hello Phaser</title>
    <link rel="stylesheet" href="app.css" type="text/css" />
    <script src="phaser.js"></script>
    <script src="GameObjects/MyScene.js"></script>
    <script src="GameObjects/Player.js"></script>
    <script src="States/TitleScreenState.js"></script>
    <script src="States/GamePlayState.js"></script>
    <script src="States/GameOverState.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <div id="content"></div>
</body>
</html>

 

app.ts

module Game {
    export class ExtremeWalkingSimulator {
        game: Phaser.Game;


        constructor() {
            this.game = new Phaser.Game(1280, 720, Phaser.AUTO, 'content', {
                create: this.create, preload: this.preload
            });
        }

        preload() {
            // Graphics
            this.game.load.image("title", "Graphics/TitleScreen.png");
            this.game.load.image("scene", "Graphics/scene720p.png");
            this.game.load.image("gameover", "Graphics/GameOver.png");

            //Spritesheets
            this.game.load.atlasXML("HERO_WALKING", "Graphics/Hero_Walking.png", "Graphics/Hero_Walking.xml");
            this.game.load.atlasXML("HERO_IDLE", "Graphics/Hero_Idle.png", "Graphics/Hero_Idle.xml");

            // Audio
            this.game.load.audio("TitleSong", ["Sounds/TitleSong.mp3", "Sounds/TitleSong.ogg",
"Sounds/TitleSong.wav"]); } create() { this.game.state.add("TitleScreenState", GameFromScratch.TitleScreenState, true); this.game.state.add("GamePlayState", GameFromScratch.GamePlayState, false); this.game.state.add("GameOverState", GameFromScratch.GameOverState, false); this.game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; } } } window.onload = () => { var game = new Game.ExtremeWalkingSimulator(); };

 

Player.ts

module GameFromScratch {
    export enum PlayerState { IDLE, WALKING }

    export class Player extends Phaser.Sprite {
        game: Phaser.Game;
        playerState: PlayerState;
        RIGHT_ARROW: Phaser.Key;
        LEFT_ARROW: Phaser.Key;
        ESCAPE: Phaser.Key;
        walkingSpeed: number;

        public static MAX_SPEED: number = 30;

        constructor(game: Phaser.Game, x: number, y: number) {
            this.game = game;

            
            this.walkingSpeed = 0;

            //Wire up input handlers
            this.RIGHT_ARROW = this.game.input.keyboard.addKey(Phaser.Keyboard.RIGHT);
            this.RIGHT_ARROW.onDown.add(Player.prototype.MoveRight, this);

            this.LEFT_ARROW = this.game.input.keyboard.addKey(Phaser.Keyboard.LEFT);
            this.LEFT_ARROW.onDown.add(Player.prototype.MoveLessRight, this);

            this.ESCAPE = this.game.input.keyboard.addKey(Phaser.Keyboard.ESC);
            this.ESCAPE.onDown.add(Player.prototype.GameOver, this);
            super(game, x, y, "HERO_WALKING", 0);

            this.anchor.set(0.0, 1.0);
            this.StartIdle();
        }

        update() {
            if (this.playerState == PlayerState.WALKING) {
                this.x += (this.walkingSpeed / Player.MAX_SPEED) * (60 / this.game.time.elapsedMS);

                // This logic depends on scene being added first.
                var stageWidth = this.game.stage.getChildAt(0).getBounds().width;
                if (this.x > stageWidth * .75)
                    this.x = stageWidth * .25;
            }
            super.update();
        }

        // Worse function name ever!
        MoveLessRight() {
            if (this.playerState != PlayerState.IDLE) {
                this.walkingSpeed--;
                if (this.walkingSpeed > 0)
                    this.animations.currentAnim.speed = this.walkingSpeed;
                else
                    this.StartIdle();
            }
        }

        MoveRight() {
            if (this.playerState == PlayerState.IDLE) {
                this.StartWalking();
            }
            else {
            if (this.walkingSpeed < Player.MAX_SPEED)
                this.walkingSpeed++;
                this.animations.currentAnim.speed = this.walkingSpeed;
            }
        }

        StartWalking() {
            this.playerState = PlayerState.WALKING;
            this.walkingSpeed = 5;
            this.loadTexture("HERO_WALKING", 0);
            this.animations.add("walk");
            this.animations.play("walk", this.walkingSpeed, true);
        }

        StartIdle() {
            this.loadTexture("HERO_IDLE", 0);
            this.playerState = PlayerState.IDLE;
            this.animations.add("Idle");
            this.animations.play("Idle",15,true);
        }

        GameOver() {
            this.game.state.start("GameOverState");
        }

    }
}

 

MyScene.ts

module GameFromScratch {

    export class MyScene extends Phaser.Sprite {
        game: Phaser.Game;
        nextFrame: Phaser.Sprite;

        constructor(game: Phaser.Game, x: number, y: number) {
            super(game, x, y, "scene", 0);
            this.nextFrame = new Phaser.Sprite(this.game, this.width, 0, "scene", 0);
            this.game.add.existing(this.nextFrame);
        }
    }
}

 

TitleScreenState.ts

module GameFromScratch {
    export class TitleScreenState extends Phaser.State {
        game: Phaser.Game;
        music: Phaser.Sound;

        constructor() {
            super();
        }

        titleScreenImage: Phaser.Sprite;

        preload() {

        }
        create() {
            this.titleScreenImage = this.add.sprite(0, 0, "title");
            this.titleScreenImage.scale.setTo(this.game.width/this.titleScreenImage.width, this.game.height
/
this.titleScreenImage.height); this.music = this.game.add.audio("TitleSong"); this.music.volume = 100; this.music.loop = true; this.music.play(); this.input.onTap.addOnce(this.titleClicked, this); } titleClicked() { this.music.stop(); this.game.state.start("GamePlayState"); } } }

 

GamePlayState.ts

module GameFromScratch {
    export class GamePlayState extends Phaser.State {
        game: Phaser.Game;
        player: GameFromScratch.Player;
        myScene: GameFromScratch.MyScene;

        constructor() {
            super();
        }

        preload() {
        }
        create() {
            this.myScene = new MyScene(this.game, 0, 0);
            this.player = new Player(this.game, 0, this.game.height - 50);

            this.game.add.existing(this.myScene);
            this.game.add.existing(this.player);

            this.game.world.setBounds(0,0,this.myScene.width * 2, this.myScene.height);
            this.game.camera.follow(this.player);
        }
    }
}

 

GameOverState.ts

module GameFromScratch {
    export class GameOverState extends Phaser.State {
        game: Phaser.Game;
        gameOverSprite: Phaser.Sprite;

        constructor() {
            super();
        }

        preload() {
        }
        create() {
            this.gameOverSprite = this.add.sprite(0, 0, "gameover", 0);
            this.gameOverSprite.scale.setTo(this.game.width / this.gameOverSprite.width, this.game.height /
this.gameOverSprite.height); this.input.onDown.add(() => { this.game.state.start("TitleScreenState", true); }, this);; } } }

 

 

 

The Videos

 

Part One:

 

Part Two:

Programming


7. January 2015

 

It’s most a maintenance release, so the list of new features is fairly sparse:

 


New Features
  • Phaser.Loader now supports BLOB urls for audio files (thanks @aressler38 #1462)
  • Line.reflect will calculate the reflected, or outgoing angle of two lines. This can be used for Body vs. Line collision responses and rebounds.
  • Line.normalAngle gets the angle of the line normal in radians.
  • Line.normalX and Line.normalY contain the x and y components of the left-hand normal of the line.
  • Line.fromAngle will sets this line to start at the given x and y coordinates and for the segment to extend at angle for the given length.
  • BitmapData.drawGroup draws the immediate children of a Phaser.Group to a BitmapData. Children are only drawn if they have their exists property set to true. The children will be drawn at their x and y world space coordinates. When drawing it will take into account the child's rotation, scale and alpha values. No iteration takes place. Groups nested inside other Groups will not be iterated through.

 

You can read the complete release notes here.

 

The release 2.3 milestones are available here.

 

They also made this announcement regarding version 3:

We're hard at work on Phaser 3. Development on the brand new renderer began in earnest last year and we're already seeing exceptional results from it. You can follow our development in the forum and public repo. Even though we're working on taking Phaser 3 into ES6 and the next generation of web browsers, we haven't stopped with the 2.x branch.

 

If Phaser sounds interesting to you, of course, Gamefromscratch has an extensive tutorial series available here.

Programming News


27. November 2014

 

 

Now we are going to look at using the P2 Physics engine.  Truth of the matter is, the experience is very similar to what we just experienced with Arcade physics.  The biggest difference is P2 is more accurate, has greater options for simulation and critically, takes up more CPU power.

 

Let’s jump in straight away with a simple example.  We are going to load two identical sprites, anchor one and create a spring between them.  Code time!

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    player1: Phaser.Sprite;
    player2: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload: this.preload
        });
    }
    preload() {
        this.game.load.image("decepticon", "decepticon.png");
    }
    create() {
        this.player1 = this.game.add.sprite(this.game.width/2, 0 + 50, "decepticon");
        this.player2 = this.game.add.sprite(this.game.width/2, this.game.height, "decepticon");

        this.game.physics.startSystem(Phaser.Physics.P2JS);

        // Enabled physics on our sprites
        this.game.physics.p2.enable([this.player1, this.player2]);

        // Make our one body motionless
        this.player1.body.static = true;
        
        // Now create a sprite between our two bodies, parameters are rest length, stiffness and damping
        // Rest length is the length of the spring at rest ( where it's not under pressure )
        // Stiffness is the resistance to movement of the spring
        // Damping determines how fast the spring loses it's "boing"  Our low damping keeps our spring "boinging"
        // Boing is a word I made up to describe the up and down motion of a spring doing it's spring thing
        this.game.physics.p2.createSpring(this.player1, this.player2, 200, 2, 0.3);

        // Lets loop a timed event every 10 seconds that moves the one end of our spring back to the start
        // Mostly just so people that didn't see it run the first time in the browser have something to see!
        this.game.time.events.loop(Phaser.Timer.SECOND * 10, () => {
            this.player2.body.x = this.game.width/2;
            this.player2.body.y = this.game.height;
            }, this);

    }
}

window.onload = () => {
    var game = new SimpleGame();
};

The code is fairly well commented, so I'll just go through the gist of what's happening here.  Just like before, you have to start the Physics subsystem, this time we pass in Phaser.Physics.P2JS.  You also have to register your objects with the physics system.  In this case we do it by passing an array of Sprite to p2.enable();  We set one of our physics bodies to static, which will cause it to not be moved or updated by the physics system.  We then create a spring between the two sprites.  This will cause the second sprite to “spring” toward the first, until the rest length is met ( plus any momentum ), then the spring will, well, spring.  Finally, just to make the demo run a bit better in the browser, we schedule a looping event every 10 seconds that resets the second sprites physics body back to the starting position to start the whole game all over again. 

 

Here is our code running in the browser:

 

One of the big advantages to P2 is it can use Polygon data for more precise collision calculates than body boxes.  Consider the following sprite:

 

megatron

 

When doing bounding box based collisions tests, this would be considered a “hit”:

 

BoundingBox

 

Ideally instead we should create a tighter bounding volume for our collision tests, like so:

BoundingPoly

 

Well, the good news is we can, but we need a third party editor to create the bounding volumes.  One such tool and the one I am going to use in this example is Physics Editor by CodeAndWeb.  It’s commercial software, but there is a free version with the following restrictions:

 

image

 

It can be purchased currently for about $20.

 

CURRENTLY, I COULD NOT GET THE CURRENT WINDOWS VERSION TO EXPORT!

There is a DLL missing, I’ve reported it to the developer, for now instead use the older version available here.

 

For this, I am just using the unregistered version.  Load it up, then click Add Sprites:

image

 

Navigate to the sprite you want to work with and click OK.  It should appear in the window like so:

image

 

I’m full of serious lazy, so I’m going to let the app do the work, simply click the Shape Tracer icon:

image

 

Now in the resulting dialog, play with the tolerance and alpha value until you have a bounding volume you like with a vertex count you can deal with:

image

 

Once you are happy with it, set the Exporter on the right to Lime + Corona ( JSON ).

image

 

Save the resulting file to your project directory.

 

Now time to actually use the results in Phaser:

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    player1: Phaser.Sprite;
    player2: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload: this.preload
        });
    }
    preload() {
        this.game.load.image("megatron", "megatron.png");
        this.game.load.physics("physicsInfo", "physics.json");
    }
    create() {
        this.player1 = this.game.add.sprite(0, 0, "megatron");
        this.player2 = this.game.add.sprite(0, 0, "megatron");

        // Being lazy, positioning sprite after creation so we have a valid width/height
        this.player1.position.set(this.player1.width / 2, this.player1.height / 2);
        
        // Now another sprite on the right side of the screen, down slightly
        this.player2.position.set(this.game.width - (this.player2.width / 2),  this.player2.height / 2 + 85);

        

        this.game.physics.startSystem(Phaser.Physics.P2JS);
        
        // Passing in true while enabling physics on an object causes the debug renderer to draw the physics body
        this.game.physics.p2.enable([this.player1, this.player2], true);

        // You need to call clearShapes() to get rid of the existing bounding box
        this.player1.body.clearShapes();
        this.player2.body.clearShapes();

        // Now load the polygon bounding data we created externally
        this.player1.body.loadPolygon("physicsInfo", "megatron");
        this.player2.body.loadPolygon("physicsInfo", "megatron");

        // Now let's get this party started
        this.player2.body.moveLeft(80);

        // Finally, when the collision occurs, move back to the beginning and start over
        this.player2.body.onBeginContact.add((body, shapeA, shapeB, equation) => {
            this.player2.body.x = this.game.width - (this.player2.width / 2);
            this.player2.body.y = this.player2.height / 2 + 85;
            }, this);
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

WARNING!  When using P2 physics, if your physics body starts even slightly outside the bounds of the screen, it will start with forces applied against it!

 

When you run this code, it creates two identical Megatrons, on each side of the screen, slightly offset, then sends one towards the other… ok, let’s just watch it:

 

As you can see, using Polygon volumes allow for much greater precision in your collision tests.  Using simple bounding boxes, the collision would occur a great deal earlier.

 

There is a lot more about P2 physics, but in all honesty, Polygon’s are probably the single biggest reason to use it.  Again it is important to remember that P2 is the slowest option, but the most precise.  Also, you have the option of mixing and matching physics engines within the same game.

 

Programming


GFS On YouTube

See More Tutorials on DevGa.me!

Month List