Showing posts with label gamedev. Show all posts
Showing posts with label gamedev. Show all posts

Monday, May 9, 2016

Add Gamepad Support to a Phaser Game

It seems like I haven't written a blog post in forever! It's actually been less than 2 weeks. Right before I took a break, I wrote a little about my new NES-style Bluetooth gamepad. I've been trying off-and-on ever since then to get my 8-bit style platformer to work with it, and today I finally had some success!

First of all, it needs to be said that support for gamepads in the browser is very inconsistent. The W3C's Gamepad API document is still a working draft after all these years (I first read about it and tried it out in 2013). It seems as though Mozilla and Google have some different opinions on how it should work, because the way you interact with the devices varies significantly between Firefox and Chrome. Phaser provides gamepad support through the Gamepad object, but the documentation carries a warning about the volatility of the specification.

Here's what I learned when I tried to use it...

I started with some of the examples on Phaser's site. They worked, most of the time. Let me explain. In theory, working with a gamepad in Phaser is simple. You get a gamepad object, setup a callback to handle the detection of a gamepad device, and bind to buttons in that callback. Then you start the gamepad polling.

function create() {

    // ... setup stuff ...

    var jumpButton = null;

    controller = game.input.gamepad.pad1;

    controller.addCallbacks(this, {
        onConnect: function() {
            // you could use a different button here if you want...
            jumpButton = controller.getButton(Phaser.Gamepad.BUTTON_1);
        }
    });

    game.input.gamepad.start();

    // ... other stuff ...

}

function update() {

    // ... other stuff ...

    if (jumpButton.isDown) {
        // jump code goes here!
    }

    // ... other stuff ...

}

Much like you do for keyboard input, you can set up the buttons you want to listen for in create and then perform actions based on their state in update. And this works pretty well - in Firefox. Chrome, on the other hand, has some issues. Phaser's example code, much like my example above, works most of the time in Chrome when the code gets executed very quickly after the page loads. But if you put enough setup code in front of your gamepad initialization you'll be wondering, like I was, why your gamepad never connects.

I had to dig into the Phaser code in order to figure this out. It works consistently in Firefox because Firefox waits until the first time a button is pressed on a gamepad before it emits a gamepadconnected event from the window object. Phaser catches that and sets everything up, calling the onConnect function when complete. In Chrome, however, gamepads just show up magically at some point after the page is loaded, in an array-like object accessed by calling navigator.getGamepads(). Phaser checks this list constantly, and when things appear for the first time, it makes all the internal setup calls. And right there's the problem! If the gamepads appear BEFORE my onConnect callback function is set up, I missed the boat. A default, no-op callback got executed instead and my gamepad buttons never get set up!

There was no work-around for this that I felt was acceptable, so I actually forked Phaser and fixed the problem in the Gamepad object's code. It was a fairly simple fix - I just don't start polling for those gamepad objects until after the call has been made to game.input.gamepad.start().

I forked off of version 2.4.7 and submitted a pull request, so hopefully my fix makes it in to the next Phaser release and the rest of you won't have to deal with this problem like I had to! If you can't wait, try using my fork and gamepad branch.
UPDATE: My pull request was merged, but not in time for 2.4.8. Look for this fix in the 2.4.9 release!
If you're just interesting in playing the game I've been working on, you can do that here: http://amphibian.com/eight-bit. The full source code is available on GitHub. If you're just interested in viewing today's comic, you can do that here:

Amphibian.com comic for 9 May 2016

Wednesday, April 13, 2016

Use Tiled's Map Background Color in Phaser

I actually did some work on my Phaser platformer this week! Yeah! I resolved an issue that was submitted on GitHub concerning the water physics in Level 7, and I started thinking some more about another thing which had been bothering me.

Even though I can set a map's background color in the Tiled map editor, Phaser doesn't parse that property when it builds a Tilemap object. I had been keeping track of the background colors for each level separately, but that violates one of my principles - the map editor should be the one and only source of data about each level map.

The Background Color map property in Tiled

I was able to make a few minor changes to work around this limitation. In the stage's create function, I first pull the tilemap data manually for the map's key. This is the same data that is used behind-the-scenes when you call game.add.tilemap(key).

function create() {

    // get the asset key for this level
    mapKey = "key-for-this-level";

    // pull the tilemap data from the asset cache
    var tilemapData = game.cache.getTilemapData(mapKey);

    // all the data from Tiled is in the data field
    var mapData = tilemapData.data;

    // set the stage's background color from the map's background color.
    // note that Tiled didn't capitalize the "c"...
    game.stage.backgroundColor = mapData.backgroundcolor;

    // go about making the make the normal way
    var map = game.add.tilemap(mapKey);
    map.addTilesetImage("ground", "tiles");

    // ... the rest of the stuff ...

}

The tilemap data object returned from the asset cache has itself a data field, which is essentially the Tiled JSON map object. The backgroundcolor property is there can can be used to set the backgroundColor property for the stage. Remember, you can view the full source code to this game in its public repo on GitHub!

It really was that simple. In only a few minutes, I feel like I accomplished something on this game for the first time in a month! It doesn't take much to make me feel good about my achievements. Now, go read today's comic and feel good about that achievement!

Amphibian.com comic for 13 April 2016

Wednesday, March 2, 2016

Using "shutdown" to Save State in Phaser

Today is the comic that I've been working on in all my spare time for the past week, but have been thinking about for about a year. For such a long time, I've wanted to work a Dragon Warrior parody into a comic. That game was such a major part of my childhood. I played it for countless hours on my NES, along with its sequels.

If you're not as familiar with it as I am (my wife had never heard of it), it was the game that introduced console turn-based RPGs to the American market. You play a warrior trying to save a princess and slay a dragon by defeating various monsters to gain gold and experience. The gold gets you better armor and weapons, while the experience ups your level for more strength, magic, etc. As you walk around the world, a battle starts randomly. You and the monster take turns hitting each other until one of you dies. Assuming you win, the game returns to the map screen and you walk around some more.

If you haven't seen the comic yet, take a look at it. My parody of the game is embedded in the 3rd frame, and is completely playable.

The walking-around game state.

When I implemented this in Phaser, I made the walking-around-on-the-map one game state and the fighting-a-monster (or a printer in my case) another game state. After the fight ends, I wanted to go back to the walking-around state but put the frog at the same map position he was before the fight started - not reset everything in the state back to the default. But the way Phaser works is that when you start a game state, the create function is always called. How could I ensure that the frog ended up back where he belonged each time that happened?

The fighting state. Kick that printer!

The answer is to make use of the shutdown function on the state. I hadn't used this one before; my game state objects never defined it. But it is important if you wanted to save the current values of anything in your game before switching states, as it is called by Phaser when one state is being stopped but before another starts. I used it to save the frog's current position, which I then used the next time create was called.

Here is a sample of the code, to help in the explanation.

function walkAround() {

    // default frog's position
    var fPos = { x: 200, y: 500 };

    function create() {

        // create the frog wherever fPos says to put him
        frog = this.add.sprite(fPos.x, fPos.y, "frog", 0);

        // ... other stuff ...

    }
    
    function shutdown() {
        
        // save the frog's current position to use
        // next time create is called.
        fPos.x = frog.position.x;
        fPos.y = frog.position.y;
        
    }

    return {
        create: create,
        shutdown: shutdown
    };

}

function fight() {

    // ... all the stuff for the fighting state ...

    return {
        // an actual state object
    };

}

var s1 = walkAround();
var s2 = fight();
 
var game = new Phaser.Game(width, height, Phaser.CANVAS, "cell-2");
 
game.state.add("walking",  s1);
game.state.add("fighting", s2);

game.state.start("walking");

The fPos variable gets a default value when it is initialized. This is the value used the very first time create is called. But after shutdown is called, fPos changes. Since the state object isn't ever destroyed, fPos retains its value and the next time create is called, the frog is put in his last known location.

This technique will work for anything in your game state that you'd like to save and restore, such as when your player switches to a menu state and then returns to play.

Make sure you check out the comic and play the game! If you'd like to see the complete source code for the game (or anything else on the comic) you can find the repo on GitHub.

Amphibian.com comic for 2 March 2016

Friday, February 26, 2016

"Loading" Screens in Phaser Games

I noticed an annoying thing when playing my Phaser platformer on Amphibian.com - it was taking a really long time to start the levels. The problem was mainly that I was re-loading the game assets every time a level was started, and that I wasn't pre-loading them way back before the title screen.

Loading...

I wanted to fix this issue and add a "Loading" screen at the beginning before the main stage select menu was available. The way to do this is to add a few more states to the game.

First, I added a boot state. All this state will do is load the title background image and an image that says "Loading." This happens in the preload function, and should go fairly quickly. In the create function for this state, which will be called as soon as preload completes, it will immediately start the next state - the real preloader. Check out the following code from the game (remember, the full source code is available on GitHub):

var boot = {
  
    preload: function() {
        this.game.load.image("title", "assets/images/title.png");
        this.game.load.image("title-loading", "assets/images/title-loading.png");
    },
    
    create: function() {
        this.state.start("preloader");
    }
        
};

The second state I added was called preloader. This will serve as the game's real preloader. I moved the asset pack loading from my other two existing states (the menu and the playable level) to the preload function of this state. But before I start loading assets, I display the two assets I loaded in the boot state's preload. I can use those assets here because they were loaded before this function - even though this is a preload function it's not the first one. Basically, you can use assets in preload as long as they've already been loaded by a previous state's preload. This will show the player the nice title screen and let them know that the game is loading. After all the assets are loaded, the create function is called for this state, which changes to the stageSelect state.

var preloader = {
  
    preload: function() {
        
        this.add.image(0, 0, "title");
        this.loadingText = this.add.image(450, 200, "title-loading");
        
        this.load.pack("main", "assets/pack.json");

    },
    
    create: function() {
        this.state.start("stageSelect");
    }
        
};

Now when I set up my game, I add all 4 states and then start the boot state.

var game = new Phaser.Game(width, height, Phaser.CANVAS, "gameArea");

game.state.add("boot", boot);
game.state.add("preloader", preloader);
game.state.add("stageSelect", stageSelectState);
game.state.add("level", theStage);

game.state.start("boot");

It still takes a while to load all the assets, but at least now you know what's going on. And once you make it to the menu, all the levels will start immediately for you. Much better. The process of loading assets still takes a while because the background music files are so large. I'll probably fix that by reducing their quality a little. Don't worry, you won't notice.

You might notice something wrong with today's comic, however...

Amphibian.com comic for 26 February 2016

Wednesday, February 24, 2016

Swimming in a Phaser Platformer

The theme for Level 7 of my 8-bit style Phaser platformer has been selected - Liquid Cooled! It's a water level with a name that also describes a high-performance computer needing more than just a normal CPU fan.

But a water level means the frog has to swim through it, instead of jump. I had to make a few changes to my code in order to support an optional "swim mode" behavior. I handled this by putting a water flag in my level objects. When true, it means the physics should be more...liquid.

The swimming level. All platformers should have at least one.

To start, the gravity needed to be adjusted. I have the normal gravity set to 1500 in my game, but I played around with different numbers and 250 seemed most appropriate for a swimming effect. In real life, gravity doesn't decrease when you're in water...but the effect of your body's buoyancy makes it seem as if it does. Same thing here.

function create() {

    // ... other stuff ...

    if (waterLevel) {
        this.physics.arcade.gravity.y = 250;
    } else {
        this.physics.arcade.gravity.y = 1500;
    }

    // ... other stuff ...

}

The other thing that needed to change was the jump behavior. On land, the frog can't start a new jump while still in mid-air. But in the water, doing so gives the impression of swimming. Also, the amount of upward thrust supplied by a jump should be lessened under water. The speed at which the frog moves left and right should also be reduced.

function update() {

    // ... other stuff ...

    var xVel = waterLevel ? 100 : 150;
    var yVel = waterLevel ? -250 : -400;
    var jumpFrames = waterLevel ? 26 : 31;
    
    if (spacebar.isDown) {

        if ((frog.body.onFloor() || frog.locked || waterLevel) && jumpTimer === 0) {
            // jump is allowed to start
            jumpTimer = 1;
            frog.body.velocity.y = yVel;
            frog.cancelLock();
            jumpSound.play();
        } else if (jumpTimer > 0 && jumpTimer < jumpFrames && !frog.body.blocked.up && !frog.body.touching.up) {
            // keep jumping higher
            jumpTimer++;
            frog.body.velocity.y = yVel + (jumpTimer * 7);
        } else if (frog.body.blocked.up || frog.body.touching.up) {
            // permanently end this jump
            jumpTimer = 999;
        }

    } else {
        // jump button not being pressed, reset jump timer
        jumpTimer = 0;
    }

    if (cursors.left.isDown) {
        frog.body.velocity.x = -xVel;
    } else if (cursors.right.isDown) {
        frog.body.velocity.x = xVel;
    }

    // ... other stuff ...

}

Lines 5-7 in the code snippet above set the values for x-velocity, y-velocity, and length of a jump (in frames). These used to be hard-coded to the non-water values, but now there's a water option. In the check to see if a jump can start, line 11, I've added the condition that if this is a water level, jumps can start even if the frog is not on the ground or locked to a floating platform. The jump sets the y-velocity to the appropriate value, and the cursor keys set the x-velocity.

With these relatively simple adjustments in place, it really feels like the frog is swimming through Level 7. You can play it yourself on Amphibian.com, and make sure you also check out today's comic while you're there! If you're more interested in the complete source code for the game, you can find that on GitHub. But seriously, read the comic. It's funny. Most of the time.

Amphibian.com comic for 24 February 2016

Monday, February 22, 2016

Setting Up Level Bosses with Phaser

I've reached the phase of my development on my Phaser platformer where I refactor everything. This weekend, it was the level bosses. If you looked at much earlier versions of the game or read my blog posts from a couple months ago, you might remember that I had one sandbox level with a dinosaur as the boss at the end. He was pretty much hard-coded in there, so adding different bosses for each level was difficult.

To make it better, I added a field to my level objects called boss. I assign a function to that field, which creates the boss Sprite. I can assign different "boss" functions to each level.

function beaver(game, group, frog) {
    // ... set up the boss beaver
}

function dinosaur(game, group, frog) {
    // ... set up the boss dinosaur
}

var levelOne = { // Vector Factory
        map: "Level1",
        music: "assets/audio/Ouroboros.mp3",
        background: "#141414",
        boss: beaver
};

var levelTwo = { // Binary Trees
        map: "Level2",
        music: "assets/audio/Club_Diver.mp3",
        background: "#000066",
        boss: dinosaur
};

// ... define other levels here ...

var gameState = {
        levels: [],
        currentLevel: -1,
        lives: 3
};

// populate the levels array (add more the same way)    
gameState.levels[0] = levelOne;
gameState.levels[1] = levelTwo;

In my game's create function, I call the boss function of whatever level is currently being played. I then add the kill behavior to the boss Sprite, which is to end the level and return to the stage select screen.

function create() {

    // ... other stuff ...

    // create a group for all the enemies
    enemies = this.add.group();

    // create our hero sprite
    frog = createFrog();

    // --------------- set up the boss for this level
        
    var theBoss = gameState.levels[gameState.currentLevel].boss(this, enemies, frog);
    theBoss.events.onKilled.add(function() {
        // this level is complete! change state!
        this.state.start("stageSelect");
    }, this);

    // ... other stuff ...

}

Now it's fairly easy to add new bosses and even switch them around from one level to another. But you may wonder why I chose to always pass in those three parameters to the boss functions - game, group, and frog.

The first thing that the boss setup function must do is to create the boss Sprite, and to accomplish that I need the game object, the group in which to place this boss, and a reference to the frog object so the boss's behavior can change based on attributes such as the frog's proximity or other such things.

// function create the boss beaver
function beaver(game, group, frog) {

    // create this boss:
    //     150 pixels from the far right edge of the level,
    //     100 pixels from the top of the level,
    //     using the spritesheet with the key of "beaver"
    var obj = game.add.sprite(game.world.width - 150, 100, "beaver", 0, group);

    // ... other stuff ...

    return obj;

}

If these little snippets of code aren't enough, you can see the game's complete source code on GitHub.

Try the game out for yourself at Amphibian.com, and also be sure to read today's comic while you're there (or go right to the comic with the link below!).

Amphibian.com comic for 22 February 2016

Friday, February 19, 2016

My Level Design, So Far

With most of the game mechanics getting pretty solid in my platformer, I've been devoting more time to the level design. Here's what I have so far.

There are going to be 8 levels, one for each bit in a byte. I want each one to have at least some loose tie-in with a software engineering technology, much like how the Tron arcade game's difficulty levels were (mostly) named after programming languages. Those were RPG, COBOL, BASIC, FORTRAN, SNOBOL, PL1, PASCAL, ALGOL, ASSEMBLY, OS, JCL, and USER, if you've forgotten. I've programmed in 4 of them. I won't tell you which ones.

But anyway, here are my levels so far:


Level 1 - Vector Factory. Kind of a dark, industrial feel.


Level 2 - Binary Trees. It's a forest. Lots of trees. And pits.


Level 3 - Silicon Dioxide Valley. A desert. Sand and stuff.


Level 4 - Cloud Computing. Also some ice. In the sky.


Level 5 - Containers. There are lots of boxes. And a dock.


Level 6 - Deployment Pipeline. You guessed it - pipes.

I don't really have anything for 7 or 8 yet. Leave a comment below if you have any suggestions. Also, you can check out the game's complete source code on GitHub or play the current unfinished version on Amphibian.com. You can also view today's comic there as well!

Amphibian.com comic for 19 February 2016

Wednesday, February 17, 2016

Hitting the Wall - The Difference Between "blocked" and "touching" in Phaser

I've made another small improvement to my 8-bit style Phaser platformer. Now that I have some more complete levels in place, I noticed that if the frog was jumping and hit his head on the jump wasn't ending. He would just kind of hover there, in collision with the overhead tile, until the jump timer was up (I discussed my use of a timer to control the jump behavior previously).

I wanted to cancel the upward motion in the event of an overhead collision. Phaser has two ways of detecting this kind of thing in the Arcade physics engine: blocked and touching. The blocked and touching fields on the Sprite physics body are objects which each contain four boolean values - one for each direction. When true, these values indicate a collision on that side of the Sprite.

if (sprite.body.blocked.right) {
    // running into a wall on the right
} else if (sprite.body.blocked.left) {
    // running into a wall on the left
} else if (sprite.body.blocked.up) {
    // hitting something above
} else if (sprite.body.blocked.down) {
    // on the ground. same as sprite.body.onFloor() ??
} 

if (sprite.body.touching.right) {
    // hitting another sprite on the right
} else if (sprite.body.touching.left) {
    // hitting another sprite on the left
} else if (sprite.body.touching.up) {
    // hitting another sprite above
} else if (sprite.body.touching.down) {
    // hitting another sprite below
} 

So what's the difference? The blocked object refers to the Sprite colliding with a Tile. Since I am using a Tilemap for each level in my game, blocked will tell me if the frog is hitting some part of the environment. The touching object refers to the Sprite touching another sprite. I could use that field to determine if the frog hits an overhead enemy, for example.

For now, I've updated my jump control algorithm to take the Tile collision case into account.

function update() {

    // ... other stuff ...

    if (spacebar.isDown) {
    
        if ((frog.body.onFloor() || frog.locked) && jumpTimer === 0) {
            // jump is allowed to start
            jumpTimer = 1;
            frog.body.velocity.y = -400;
            frog.cancelLock();
            jumpSound.play();
        } else if (jumpTimer > 0 && jumpTimer < 31 && !frog.body.blocked.up) {
            // keep jumping higher
            jumpTimer++;
            frog.body.velocity.y = -400 + (jumpTimer * 7);
        } else if (frog.body.blocked.up) {
            // permanently end this jump
            jumpTimer = 999;
        }
    
    } else {
        // jump button not being pressed, reset jump timer
        jumpTimer = 0;
    }

    // ... other stuff ...

}

The changes are minor, but important. If the player continues to hold down the spacebar, the jump will only keep going higher if the frog is not being blocked from above. If he is being blocked, I set the jumpTimer to a high value, to ensure that the jump is over - treated basically the same way that it would be if the player let go of the spacebar. If I don't do this, on the next frame the frog will have dropped a little bit and be out of collision, which will make the second branch of the nested if-else block true again and he'll move up some more. This causes the overhead collision to re-occur, and it's a viscous cycle until we get past 30 frames!

That's all I've changed for today, but that's actually good news. It means I'm much closer to being finished with the game, since I'm working on such small details. Maybe by the end of the month it will be complete. For now, you can view the source code on GitHub and play the game in its unfinished state here.

Be sure to read today's comic! It's about load balancers. Or see-saws. Or both.

Amphibian.com comic for 17 February 2016

Monday, February 15, 2016

Switching to Frame Based Tweens in Phaser

This weekend I was doing some more work on my 8-bit style Phaser platformer. I added another enemy (a rabbit) and made some changes which will make it easier to add even more enemies. But during my extensive play-testing I noticed that I was having a lot of trouble with my moving platforms. Both the ridable clouds and the collapsing platforms seemed to move away from the frog, and then the frog's motion became very choppy as it was repositioned.

Frogs can now battle rabbits in the dark forest.

After playing around with some things, I determined that the issue was caused by a slightly lagging frame rate. I had a bunch of other things running on my laptop and the game wasn't running quite as quickly as it was before. The physics engine, which controlled the frog's movements, was making updates based on how much time was elapsing between frames. The movement of the platforms was being handled by Tweens, which by default calculate their movement in constant time. When the frame rate dips below 60 per second, the two get out of sync. Normally this would not be that noticeable but in the situation where the two objects are tied together, it caused some issues.

To resolve these problems, I switched the Tweens into "frame based" mode. This is as easy as setting a boolean flag once in the create function.

function create() {
        
    // ... other stuff ...
     
    this.tweens.frameBased = true;

    // ... other stuff ...

}

When running in frame based mode, the Tweens will use the physics engine's update timer. When I switched to this mode, the frog and the platforms remain in sync much better when the frame rate dips a little. I did have to adjust the duration values in most of my Tweens, since in frame based mode the duration should represent number of frames instead of number of milliseconds. But it was a simple adjustment.

Remember, you can view the complete source code for the game on GitHub, and play the current version at this link: http://amphibian.com/eight-bit.

Before you go, be sure to read today's Amphibian.com comic - and give me a vote on TopWebcomics.com if you have an extra second!

Amphibian.com comic for 15 February 2016

Wednesday, February 10, 2016

Don't Fall Through - "Tile Bias" in Phaser

Despite getting a lot of good level design accomplished this weekend, and adding collapsing platforms to my Phaser platformer, my free time since Sunday has been extremely limited. I haven't been able to work on much else, so today's post will be short.

One thing I would like to address is the use of "tile bias" in Phaser. In one of my levels, I had an issue where if the frog jumped from a platform near the top of the screen he would fall through the ground instead of colliding with it. It wouldn't happen 100% of the time, but it was often enough. I'd seen this type of problem before, back when I wrote my own HTML5 game engines instead of using Phaser. It is a classic problem - when there is too much time lapsing between frames (for whatever reason - slow computer, too many background tasks, etc.) the physics engine can miss collisions. A fast-moving sprite can traverse such a great distance in the time between frame updates that its bounding box ends up completely on the other side of an object with which it should have collided.

When it comes to a sprite colliding with a tilemap in Phaser, the collision calculation can be tuned by changing the value of TILE_BIAS. This was exactly what I needed to do because the issue was the frog hitting (or in this case, not hitting) a ground tile.

The default value for TILE_BIAS appears to be 16. This means that for a 32x32 tile, there's a 48x48 box when it comes to collision checks. It doesn't mean that a colliding sprite is moved back an extra 16 pixels to get it out of collision, the value is only used for the collision check. So don't worry - a sprite can still appear to be right next to a tile! I tried changing the value from 16 to 32 and that solved the problem for me. If you have a similar issue in your game, you'll probably have to play around with the number to find what works in your situation.

I change the value in my create function, at the same time I set up the other physics stuff.

function create() {
        
    game.physics.startSystem(Phaser.Physics.ARCADE);
    game.physics.arcade.checkCollision.down = false;
    game.physics.arcade.TILE_BIAS = 32;
    game.physics.arcade.gravity.y = 1500;

    // ... other stuff ...

}

Remember, if you want to view the complete source code for the game it is available on GitHub. And remember to view today's comic. Both comics so far this week have dealt with lunch. Maybe I'm hungry when I'm writing these.

Amphibian.com comic for 10 February 2016