Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
19. August 2014

 

 

Now that we’ve covered input and graphics, it’s time we move on to audio.  Here is a bit of a warning upfront…

 

HTML5 audio sucks.

 

Yeah, that’s about it in nutshell.  This is one of those areas where games in HTML5 really suffer and sadly it doesn’t seem to be going away any time soon.  There are two major things to be aware of right up front.

 

First, audio file formats.  Different browsers support different file formats, but not all the same formats sadly.  You can read a pretty good chart on the topic right here.  In a nutshell, some support ogg, some support mp3, some support wav and some support m4a.  To make things even more fun, mp3 is a license encumbered format that could result in heavy licensing fees if your game is successful.  In reality, it’s never really enforced, but just the possibility should be enough to make you wary.  Fortunately, Phaser provides a way to deal with all of this that you will see shortly.

 

Second, well, Safari on iOS really kinda stinks at audio for a couple glaring reasons.  First and foremost, you can only play one sound or video at a time…  Yeah, really.  If you play a second sound, the first immediately stops.  Kinda. Big. Deal.  You can however work around this using an audio sprite, which is basically a bunch of audio files smashed together into a single file.  A second major failing is you can’t play audio on page load in Safari.  This means if you want to load your game up to a title screen with some music playing… you cant.  Audio can only be started in response to a users touch.  Yeah, this sucks too.  This one unfortunately you cannot work around, short of using a wrapper like CocoonJS for “natively” deploying your HTML5 game.

 

Through this all, you are probably going to need a tool to either merge your audio, or convert to a variety of formats.  Fortunately there is a free and excellent audio editor named Audacity available, that makes the process fairly painless.  In order to follow this tutorial, you are going to have to get an audio file and save it in mp3 and ogg format and add it to your project.

 

OK, enough about the HTML5 audio warts, let’s get to some code! 

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', { create: this.create, preload: this.preload });
    }
    preload() {
        this.game.load.audio("GameMusic", ["song.mp3","song.ogg"]);
    }
    create() {
        this.sound = this.game.add.audio('GameMusic');
        this.sound.play();
    }
}

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

 

It’s important to note, this example wont actually work in Safari on iOS due to the limitation of only being able to play audio in response to a user touch.  You would have to change it so the play() call is done in a touch handler.

 

Here what you are doing is preloading audio using game.load.audio().  The parameters are the key to refer to the audio file, and a list of files to load.  This is where the magic happens, you should provide all supported file formats, then Phaser will serve the one that performs best on the browser the user is using.  M4A files perform the best on iOS, and don’t have the legal encumbrance that MP3, so OGG and M4A would probably be all you needed, but I may be wrong here.  If in doubt, provide all 3.

 

Now let’s look at an example that allows you to control playback of the sound file:

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;
    playButton: Phaser.Button;
    pauseButton: Phaser.Button;
    stopButton: Phaser.Button;
    volUpButton: Phaser.Button;
    volDownButton: Phaser.Button;
    muteButton: Phaser.Button;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', { create: this.create, preload: this.preload, 
render
: this.render }); } preload() { this.game.load.audio("GameMusic", ["song.mp3", "song.ogg"]); this.game.load.image("button", "button.png", false); } render() { this.game.debug.soundInfo(this.sound, 0, 100); } create() { this.sound = this.game.add.audio('GameMusic'); // Set up sound event handlers on the sound object this.sound.onPlay.add(() => { alert("Played"); }); this.sound.onPause.add(() => { alert("Paused"); }); // Play Button this.playButton = this.game.add.button(0, 0, "button", () => { if (this.sound.currentTime > 0) this.sound.resume(); else this.sound.play(); } , this); this.playButton.addChild(new Phaser.Text(this.game, 17, 18, "Play", { fill: '#ff0000' })); // Pause Button this.pauseButton = this.game.add.button(95, 0, "button", () => { this.sound.pause(); } , this); this.pauseButton.addChild(new Phaser.Text(this.game, 12, 18, "Pause", { fill: '#ff0000' })); // Stop Button this.stopButton = this.game.add.button(190, 0, "button", () => { if (this.sound.isPlaying) { this.sound.stop(); this.sound.currentTime = 0; } } , this); this.stopButton.addChild(new Phaser.Text(this.game, 17, 18, "Stop", { fill: '#ff0000' })); // Volume Up Button this.volUpButton = this.game.add.button(300, 0, "button", () => { this.sound.volume += 0.1; } , this); this.volUpButton.addChild(new Phaser.Text(this.game, 17, 18, "Vol +", { fill: '#ff0000' })); // Volume Down Button this.volDownButton = this.game.add.button(400, 0, "button", () => { this.sound.volume -= 0.1; } , this); this.volDownButton.addChild(new Phaser.Text(this.game, 17, 18, "Vol -", { fill: '#ff0000' })); // Mute Button this.volDownButton = this.game.add.button(500, 0, "button", () => { // Global mute! Use this.sound.mute to mute a single sound this.game.sound.mute = !this.game.sound.mute; } , this); this.volDownButton.addChild(new Phaser.Text(this.game, 17, 18, "Mute", { fill: '#ff0000' })); } } window.onload = () => { var game = new SimpleGame(); };

 

Here is the resulting app:

 

 

It’s a bit of a monster, but most of the code is actually just wiring up the buttons.  There are a few important take away points here.  First, you can work with the local sound object, or all sounds globally using game.sound.  Second, each action on the sound file ( play, resume, stop, etc ) have an appropriate event handler you can implement.  I only did a couple in this example.  Finally, volume is a value from 0 to 1.  You can go above or below this range, but it wont actually do anything.  All said, once you get over the file format issues, playing audio is relatively straight forward.

 

Finally, let’s touch on audiosprites again for a second.  As mentioned earlier, an audiosprite is actually a bunch of audio files smashed together into a single file.

 

Consider the following sound effects.  A gun cocking ( http://www.freesound.org/people/woodmoose/sounds/177054/ ) and a gun shot ( http://www.freesound.org/people/18hiltc/sounds/228611/ ).  Load your first sound effect into audacity, like so:

image

 

Take note of the end of the file, in this case 0.785 seconds.

 

Now, select File->Import-> Audio

image

 

Import your additional sound effects, in this case I’m opening the gunshot file.  You should now have two tracks in Audacity, like so:

image

 

Now select the bottom track by double clicking.  It should highlight in gray, like so:

image

Now click at the end of the first track and paste ( CTRL + V, or EDIT->Paste ).  You should now have a single track that looks like this:

image

 

Now save this file ( might as well create OGG, MP3 and M4A versions while you are at it ).

 

Now lets take a look how we use it in code.

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', { create: this.create, preload: this.preload, 
render
: this.render }); } preload() { this.game.load.audio("sfx", ["GunSounds.ogg", "GunSounds.wav"]); } render() { this.game.debug.soundInfo(this.sound, 0, 100); } create() { this.sound = this.game.add.audio('sfx'); this.sound.addMarker("gunCock", 0, 0.785); this.sound.addMarker("gunShoot", 0.786, 1.49); this.sound.play("gunCock"); this.sound.onMarkerComplete.add(() => { this.sound.play("gunShoot"); }); } } window.onload = () => { var game = new SimpleGame(); };

 

This plays the first sound, then immediately once it is complete, it plays the other.  As you can see, you do so by setting markers within the entire sound file.  0.785 is the length of the first sound effect, then 1.49 is the length of the entire file.  You simply name each marker, then give it’s start and end locations within the file.  You can get these values using an audio editor, such as audacity.  This allows you to load all of your similar sound effects into a single quicker to load file and should help you work around the single sound stream limitations of iOS.

 

Programming


11. August 2014

 

 

In the previous tutorial, which I will admit… some time has elapsed since I wrote it, sorry about that… anyways, in the last tutorial we looked at handling keyboard input using Phaser.  In this tutorial we are going to look at handling mouse and touch events.  Just like the last part, this one is going to be relatively light on explanation and high on code.

 

Ok, let’s jump right in.  The first example shows how to poll for mouse/touch information:

 

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

// This code demonstrates polling for touch or mouse clicks
class SimpleGame {
    game: Phaser.Game;
    jetSprite: Phaser.Sprite;

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

    preload() {
        var loader = this.game.load.image("jet", "jet.png");
    }

    create() {
        var image = <Phaser.Image>this.game.cache.getImage("jet");
        this.jetSprite = this.game.add.sprite(
            this.game.width / 2 - image.width / 2,
            this.game.height / 2 - image.height / 2,
            "jet");

        this.jetSprite.pivot.x = this.jetSprite.width / 2;
        this.jetSprite.pivot.y = this.jetSprite.height / 2;
    }

    update() {

        // You can poll mouse status
        if (this.game.input.activePointer.isDown) {
            // This will be set to true if any mouse button is pressed or a finger is touched
            this.jetSprite.position.set(this.game.input.mousePointer.x,
                this.game.input.mousePointer.y);

            // You can test if the user is using a mouse
            if (this.game.input.activePointer.isMouse) {
                // In the case of a mouse, you can check mouse button status
                if (this.game.input.activePointer.button == Phaser.Mouse.RIGHT_BUTTON) {

                    // We just want to clear it, so this doesnt fire over and over, dont do this in production
                    this.game.input.activePointer.reset();
                    alert("Right button pressed");
                }
            }
            else {
                // On the other hand, if you are dealing with touch, you can have multiple touches/pointers
                // By default there are two pointers defined, so you can have up to two touches.
                // You can add more pointers using input.addPointer();
                if (this.game.input.pointer1.isDown && this.game.input.pointer2.isDown)
                    alert("Multitouch!");
            }
        }
    }
}

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

 

 

Pointer is an abstraction for both touch and mouse.  As you can see above, there are multiple pointers ( input.pointer1, input.pointer2, etc… ) but by default only two will be enabled.  If you want to handle multitouch with more than 2 fingers you need to register more using addPointer().   You can add up to 10 pointers total.  pointer1 is the first finger to touch the screen, pointer2 the second, etc.

 

Now let’s take a look at handling mouse input using callbacks instead of polling:

 

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

// This code demonstrates polling for touch or mouse clicks
class SimpleGame {
    game: Phaser.Game;
    jetSprite: Phaser.Sprite;

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

    preload() {
        var loader = this.game.load.image("jet", "jet.png");
    }

    create() {
        var image = <Phaser.Image>this.game.cache.getImage("jet");
        this.jetSprite = this.game.add.sprite(
            this.game.width / 2 - image.width / 2,
            this.game.height / 2 - image.height / 2,
            "jet");

        this.jetSprite.pivot.x = this.jetSprite.width / 2;
        this.jetSprite.pivot.y = this.jetSprite.height / 2;

        // You can handle mouse input by registering a callback as well
        // The following registers a callback that will be called each time the mouse is moved
        this.game.input.moveCallback = (pointer:Phaser.Pointer,x:number,y:number) => {
            this.jetSprite.position.set(x, y);
        }

        // This one registers a mouse click handler that will be called
        this.game.input.onDown.add(SimpleGame.prototype.mouseDown);

    }

    mouseDown(event:MouseEvent) {
        alert("Mouse is down " + event.button);
    }

}

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

 

 

In this case we register a function that will be called whenever the mouse moves.  In this case I am using an anonymous delegate.  You can also handle mouse down or touch events.  The format is slightly different as onDown is a Signal instead of a callback.  That said, a Signal is basically a wrapper around a callback function, so it’s not really all that different.  Except of course with Signals, you can register multiple for the same event.

 

Finally, in the previous two examples, we handled the input at an application level.  If it makes sense for your game, you can actually handle input at the Sprite level.  Like so:

 

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

// In this example we illustrate input events can be applied to Sprites
class SimpleGame {
    game: Phaser.Game;
    jetSprite: Phaser.Sprite;

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

    preload() {
        var loader = this.game.load.image("jet", "jet.png");
    }

    create() {
        var image = <Phaser.Image>this.game.cache.getImage("jet");
        this.jetSprite = this.game.add.sprite(
            this.game.width / 2 - image.width / 2,
            this.game.height / 2 - image.height / 2,
            "jet");

        this.jetSprite.pivot.x = this.jetSprite.width / 2;
        this.jetSprite.pivot.y = this.jetSprite.height / 2;

        // First enable the sprite to receive input
        this.jetSprite.inputEnabled = true;
        
        // Then add an event handler for input over
        this.jetSprite.events.onInputOver.add(() => {
            alert("The mouse passed over the sprite!");
        });
    }


}

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

 

 

First thing you need to do is tell Phaser that your Sprite will handle input by setting inputEnabled to true.  Next we add an input handler for onInputOver, which once again is a Signal.  There are a number of events that Sprite can respond to, all predictably enough in the events class.

 

As a side note, part of the delay in updating this tutorial series is I was actually intending to write one about using GamePads.  First I started writing code and my XBox 360 wireless receiver died horribly.  Apparently this is a very common problem with them, a tiny little fragile fuse broke.  I then ordered a replacement from Amazon and it took three weeks to arrive.  Then when it did arrive, well…

 

Let’s just say Gamepad support in HTML5 is complete and utter garbage.  There are a bunch of projects in the works, but they are all a far way away from being actually usable.  If you are looking to add gamepad support to your HTML5 project, expect it to be very browser specific.  By this I actually mean exact version of the browser…  The APIs are changing constantly and are breaking just as often.  In my opinion, it’s simply not worth it at this point.

 

Programming


13. July 2014

 

It’s funny the things in life that irritate the hell out of you.  This is one such example for me.  For HTML5 development, I generally use WebStorm as my IDE and Chrome as my default browser.  Webstorm has a plugin debugger that integrates directly into chrome.  However, whenever you run your application, there is this exceedingly annoying yellow bar across the screen saying “JetBrains IDE Support is debugging this tab”:

 

image

 

You can disable it by clicking the x, unfortunately each time you run your code it appears again.  It’s one of those things I just let annoy me, as opposed to looking in to a more permanent solution.  That, it turns out, was rather stupid, as this is fairly easily fixed.

 

In Chrome’s URL box, enter chrome://flags

image

 

Scroll down and locate Enable Silent Debugging and click Enable.

 

image

 

Relaunch Chrome and PRESTO, no annoying yellow bar!

 

image

General


21. May 2014

 

Yesterday saw the release of 2.0.5 of the Phaser game library.  Phaser is one of those libraries I am hearing about more and more recently.  It’s a JavaScript/Typescript game library featuring most of the things you would expect in a modern game engine, including:

  • WebGL and Canvas ( Pixi.js ) renderers
  • Physics
  • Sprites
  • Animation
  • Grouping
  • Input
  • Sound
  • Tilemaps
  • Caching
  • Plugins

 

Release 2.0.5 adds:


Updates
  • TypeScript definitions fixes and updates (thanks @luispedrofonseca @clark-stevenson @Anahkiasen @adamholdenyall @luispedrofonseca @WillHuxtable)
  • Input.getPointerFromIdentifier docs update to reflect where the identifier comes from. Pointer properties now set to give it fixed defaults (thanks @JirkaDellOro, #793)
  • Pointer.pointerId added which is set by the DOM event (if present in the browser). Note that browsers can and do recycle pointer IDs.
  • Pointer.type and Pointer.exists properties added.
  • QuadTree.retrieve can now accept either a Sprite with a physics body or a Phaser.Rectangle as its parameter.
  • PluginManager.add now accepts additional parameters and if given a function it will pass them all to the Plugin constructor.
  • Tilemap.getTile has a new nonNull parameter. If true it won't return null for empty tiles, but will return the actual Tile in that location.
  • Math.interpolateAngles and Math.nearestAngleBetween have been removed for the time being. They threw run-time errors previously.
  • PIXI.InteractionManager is no longer over-written if the object already exists (thanks @georgiee, #818)
  • Key.justPressed and justReleased incorrectly set the delay value to 2500ms. Now defaults to 50ms (thanks @draklaw, fix #797)
  • Stage.backgroundColor can now accept short-code hex values: #222, #334, etc.
  • Pointer.withinGame is now accurate based on game scale and updated as the Pointer moves.
  • Stage.bounds is now updated if the game canvas offset changes position. Note that it contains the un-scaled game dimensions.
 
New Features
  • New force parameter added to Group.set, setAll, setAllChildren, setProperty which controls if a property is created even if it doesn't exist.
  • Group.hasProperty will check a child for the given property and return true if it exists, otherwise false.
  • Phaser.Tween.from allows you to set tween properties that will end up where the current object is (thanks @codevinsky, #792)
  • Input.getPointerFromId will return a pointer with a matching pointerId value, if any. pointerId is a value set by the browser in the DOM event.
  • ArcadePhysics.getObjectsUnderPointer will return all children from a Group that overlap with the given Pointer.
  • InputManager.minPriorityID lets you set the minimum priority level an object needs to be to be checked by a Pointer. Useful for UI layer stacking.
  • New consts: Phaser.Tilemap.NORTH, SOUTH, EAST and WEST to use with plugins and generally just handy to have.
  • BitmapData.processPixelRGB added undefined check (thanks @muclemente, fix #808)
  • Phaser.Utils.transposeArray will transpose the given array and return it.
  • Phaser.Utils.rotateArray will rotate the given array by 90 or 180 degrees in either direction and return it.
  • BitmapData.rect provides a quick way to draw a Rectangle to a BitmapData.
  • Button.onOverMouseOnly is a boolean that causes onOver events to fire only if the pointer was a mouse (i.e. stops onOver sounds triggering on touch)
  • Tilemap.setCollision has a new boolean parameter 'recalculate' which lets you control recalculation of collision faces (thanks @max-m, #819)
  • Tilemap.setCollisionBetween has a new boolean parameter 'recalculate' which lets you control recalculation of collision faces (thanks @max-m, #819)
  • Tilemap.setCollisionByExclusion has a new boolean parameter 'recalculate' which lets you control recalculation of collision faces (thanks @max-m, #819)
  • Tilemap.setCollisionByIndex has a new boolean parameter 'recalculate' which lets you control recalculation of collision faces (thanks @max-m, #819)
  • Graphics.drawTriangles will draw an array of vertices to the Graphics object (thanks @codevinsky, #795)
  • Polygon.area will calculate the area of the Polygon (thanks @codevinsky, #795)
  • The Tiled JSON parser will now include Tiled polygons, ellipse and rectangle geometry objects in the resulting map data (thanks @tigermonkey, #791)
  • Input.addMoveCallback allows you to bind as many callbacks as you like to the DOM move events (Input.setMoveCallback is now flagged as deprecated)
  • Input.deleteMoveCallback will remove a previously set movement event callback.
  • Mouse will now check if it's over the game canvas or not and set Pointer.withinGame accordingly.
  • Mouse.mouseOutCallback callback added for when the mouse is no longer over the game canvas.
  • Mouse.stopOnGameOut boolean controls if Pointer.stop will be called if the mouse leaves the game canvas (defaults to false)
  • Tilemap.searchTileIndex allows you to search for the first tile matching the given index, with optional skip and reverse parameters.
  • Tilemap.layer is a getter/setter to the current layer object (which can be changed with Tilemap.setLayer)
  • Cache.checkKey added - allows you to pass in a Cache type and a key and return a boolean.
  • Cache.checkCanvasKey(key) - Check if a Canvas key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkTextureKey(key) - Check if a Texture key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkSoundKey(key) - Check if a Sound key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkTextKey(key) - Check if a Text key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkPhysicsKey(key) - Check if a Physics key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkTilemapKey(key) - Check if a Tilemap key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkBinaryKey(key) - Check if a Binary key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkBitmapDataKey(key) - Check if a BitmapData key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkBitmapFontKey(key) - Check if a BitmapFont key exists in the cache (thanks to @delta11 for the proposal)
  • Cache.checkJSONKey(key) - Check if a JSON key exists in the cache (thanks to @delta11 for the proposal)
  • New movement data added for a Pointer Locked mouse (Pointer.movementX/Y) (thanks @woutercommandeur, #831)
  • ScaleManager.bounds is a Rectangle object that holds the exact size of the game canvas, taking DOM offset and game scale into account.
 
Plugins

The Plugins have now all moved to their own repository

Bug Fixes
  • Line.pointOnLine corrected algorithm (thanks @woutercommandeur, fix #784)
  • Line segment collision fails under certain cicumstances (thanks @woutercommandeur, fix #760)
  • The P2 DistanceConstraint method signature has changed. Updated Phaser so maxForce is now passed as object (fix #788)
  • Moved the this._reversed flag outside of the property loop in Tween (as per tween.js issue 115)
  • Emitter.makeParticles updated to use Array.isArray() check on the key/frame values, so non-string objects can be passed in (thanks @AnderbergE, fix #786)
  • Tilemap.createFromObjects will now force the creation of the property again even if it doesn't exist (regression fix from 2.0.4)
  • Phaser.Line.intersectsPoints fixed by properly checking the boundaries (thanks @woutercommandeur, fix #790)
  • Group.set and setAll were changed in 2.0.4 to not create the property unless it existed. This broke backwards compatibility, so has been fixed.
  • Sound.play now returns the Sound object (thanks @AnderbergE, fix #802)
  • Device Silk UA test updated to avoid Safari conflict (thanks @jflowers45, fix #810)
  • Sound.stop on Samsung S4 would randomly throw a DOM error. Wrapped the audio stop in a try/catch (thanks FSDaniel)
  • RandomDataGenerator.integerInRange would return a non-integer value if you passed in a float.
  • Timer class updated so that code-resumed pauses don't mess up the internal _pausedTotal value (thanks @joelrobichaud, fix #814)
  • Timer class when paused by code after a game-level pause wouldn't set the codepaused flag (thanks @joelrobichaud, fix #814)
  • Stage.backgroundColor now properly accepts hex #RRGGBB and color values 0xRRGGBB again (fix #785)
  • Color.getRGB would return incorrect color components if a color value without alpha was given, now works with both 0xRRGGBB and 0xAARRGGBB.
  • Color.getWebRGB now works regardless if you give an 0xRRGGBB or 0xAARRGGBB color value.
  • If an object was drag enabled with bringToTop, the onDragStop event wouldn't fire until the mouse was next moved (thanks @alpera, fix #813)
  • RetroFont.text would throw WebGL errors due to an issue with Pixi.RenderTexture. Fixed in Phaser and submitted code to Pixi.
  • RenderTexture.resize would throw WebGL errors due to an issue with Pixi.RenderTexture. Fixed in Phaser and submitted code to Pixi.
  • Group.hasProperty fixed to not use hasOwnProperty, but a series of in checks (thanks @mgiuffrida for the idea, #829)
  • Tilemap.removeTile sets tiles to null but should set to index of -1 (thanks @draklaw, fix #835)

 

Plugins being moved to their own respository should make contributing to Phaser easier.  The repository is available here.

 

 

Personally I’ve been meaning to look closer at Typescript for a quite a while now and I’ve decided to do it with Phaser.  Of course I am going to document the process as I go, so if you are interested in Phaser or Typescript, stay tuned.  It’s not going to be a proper tutorial; as I have zero experience as of right now.  It should however still be useful to someone looking to get started with Phaser.

News Programming


2. July 2013

Back in March I reported that Ludei was bringing WebGL support to their HTML5 performance wrapper, CocoonJS.  Today I received the following email:

 

The latest version of our Cloud and Launcher is available now! 
The wait is finally over! CocoonJS1.4 is NOW available in Google Play! We are still pending approval with the Apple App Store, so please check daily for its debut! This version includes the following awesome features:

  • WebGL support is now available! For the first time ever, publish your 3D games to iOS and Android devices!
  • Convenient 1-click Publishing to 5 App Stores -- bundles generated for: Apple, Google Play, Amazon, Chrome Store, Pokki
  • Improved Configuration Options for the Ludei Cloud Compiler
  • Updated Third Party SDKs -- advertisements, social integration and in-app payments
  • Improved Resources Management
  • Improved Audio Implementation that supports more HTML5 features
  • Improved webview support allowing you to develop HTML5 web apps while still having all our extensions available to monetize and make your game or app successful
  • And much more!! Click here to read the blog post!

 

This means you can now develop WebGL games and use Cocoon to deploy them to iOS and Android.  I am not sure if you ever tried running a WebGL application in a mobile browser… it's appalling.  None of the official browsers support WebGL, and the few that do ( Dolphin? ) got abysmal framerates.  So, if you want to run a high performance WebGL based game on mobile devices, CocoonJS is about your only option.

If you've never heard of CocoonJS, it's basically a high performance, striped down browser optimized for gaming.  It acts as a host for your game, so you bundle your game and Cocoon together to create an App Store deployable application.

 

In the same release, Ludei also announced:

Ludei Secures $1.5M in Funding 
We just announced that we recently received $1.5M in funding from key venture capitalists and angel investors, further validating the increasing industry support of HTML5 development. Click here to read more.

So you don't need to worry about they going anywhere sometime soon!

News


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List