Progress Update: Converting The Flash Game Blasteroids To HTML5 Using Phaser, Part 7

Hey,

In this article, the series of converting the Flash game Blasteroids to HTML5 continues. This update introduces sound effects to the game.

The target technology I’m using for process is Phaser, and the primary tools I’m using for development are Visual Studio Code and the amazing Phaser Editor. The latter provides an integrated environment for HTML5 game development, similar to what Adobe Animate did for Flash.

Note: Having access to the original FLA, source code and assets is crucial if you want to convert you Flash game to HTML5. Although Adobe Animate is not the target tool in the process, it’s still important to have.

However, Adobe Animate can be the target tool in some projects, depending on how the original Flash game was built. When converting Flash games to HTML5, there is no one-size-fits-all method, and you will need to inspect the original project to see which approach to take.

Finally, it’s worth noting that not all features in the Flash game may convert over to HTML5. An example includes use of advanced color filter effects. You might be able to use shaders for color effects, but building shaders may be more trouble than they’re worth.

FYI: As most of the updates now involved processes already discussed in previous articles, I won’t go into details on those updates. But, more items, and weapons were added.

This article will focus on adding sound; which is one of the easier aspects to convert over.

Check out this short demo video of the current progress of the game:

As you’re probably aware by now, Adobe is discontinuing Flash by end of this year. If you have Flash games, they will no longer work afterwards, as browsers, such as Google Chrome, will completely block Flash content.

So, if you want to preserve your Flash, you’ll have to re-build it in another platform, and if it’s to remain as a web-based game (meaning you’re NOT converting it from web to mobile), then HTML5 (Canvas/WebGL) is likely your best bet.

I previously announced that I was taking on the challenge to convert a Flash game I made for a previous client to HTML5. I will continue to post progress updates on the game until the conversion has been completed.

The current list of articles is here:

  • Part 1 – Setting up the player
  • Part 2 – Displaying objects, keyboard input, and object wrapping collisions
  • Part 3 – Missiles, animations, and HUD
  • Part 4 – Adding text, health, and icons to HUD
  • Part 5 – Collision With Missiles, Asteroids, and Player Ship
  • Part 6 – First Power-Up Item
  • Part 7 – Adding Sound Effects
  • Part 8 – Almost done!

Let’s get started. The topics in this article:

About Sounds In Phaser

Phaser uses either the Web Audio API for playing audio, or the HTML <audio> tag as an alternative option.

When you setup Phaser by creating a Phaser.Game object, you can specify an optional AudioConfig object. By default, Phaser will attempt to use the Web Audio API, and fall back to the HTML audio if the former is not available.

The type of audio files and their encoding are important, because not all browsers can play all audio formats. For each sound effect, you can specify multiple formats, for example MP3 and OGG. You can check this table to see the current formats that are supported for various browsers.

In Phaser, you can preload sounds similar to the other assets, in the preload function of a Scene:

this.load.audio('someSoundKey', [
  'assets/audio/someSoundEffect.ogg',
  'assets/audio/someSoundEffect.mp3'
]);

In the above example, the sound effect is given the key someSoundKey, and two audio formats are assigned to it. And you don’t have to worry about all the files being loaded! The loader will determine which file to load based on the browser, and load just that ONE file.

You can also load audio by placing them inside JSON file (or JSON-formatted object):

var audioPack = {
  "section": {
    "files": [
      {
        "type": "audio",
        "key": "SndMediumExplosion",
        "url": [
          "assets/audio/impacts/SndMediumExplosion.mp3",
          "assets/audio/impacts/SndMediumExplosion.ogg"
        ]
      },
      {
        "type": "audio",
        "key": "SndMissileSuper",
        "url": [
          "assets/audio/missiles/SndMissileSuper.mp3",
          "assets/audio/missiles/SndMissileSuper.ogg"
        ]
      }
    ]
  }
};

Phaser Editor makes it really easy to add sounds to your project. For this project, we’ll be creating a JSON pack file, and adding the sounds to that. Then we’ll preload it in the preload function of the Scene.

Retrieving Your Original Sounds

But before we can load our sounds, we must first get the original sound files themselves.

Note: As I mentioned before, it’s always best to have the original source files and assets if you’re attempting to convert a Flash game to HTML5.

If you already have the original audio files stored on disk, you can press here to skip to the next section. If you only have the FLA file, and it has the sounds embedded inside it, continue reading below.

Before you can import those sounds into your Phaser game, you’ll need to extract them from the FLA file.

In previous versions of Flash Professional, you used to be able to extract sounds by placing them on the stage’s timeline, exporting them as a movie, then selecting an audio format. However, with Adobe Animate, that option has been removed, and there is no built-in or straightforward way of extracting sounds. Dammit. 😠

But there is hope! ⭐😮

It can be done by using Flash JavaScript (JSFL) code. With JSFL, you can create custom functionality for Adobe Animate that is not exposed in Animate itself. JSFL has an extensive API to help streamline the authoring process. For example, you can write scripts to automate repetitive tasks or add custom tools to the Tools panel.

We’ll be using a script that will allow us to extract sounds from an FLA library, and save them to disk.

There is a JSFL script that was created by João César, and his script is in this repo here.

Currently the script is unable to export MP3 sounds, only WAV. So I forked his repo, made some changes, and submitted a push request (result pending at the time of this article). You can find my update here.

You’ll need to download the Export Library Sounds.jsfl script.

After you’ve downloaded the script, you can export the audio library items from the FLA file to your disk by, following these steps:

  1. Open the FLA file in Adobe Animate.
  2. From the library, select the audio items you want to export. If the audio items are in a folder, you can select the folder instead.
  3. From Animate’s main menu, select Commands, then Run Command....
  1. Select the Export Library Sounds.jsfl script you download. This will run the script.
  2. A window will open, prompting you to select an output directory (folder) on your disk were the script should export the selected audio.
  3. Once you select a folder, press Select Folder, and the script will attempt to export all the audio items you selected. You should then be able to see all your audio files in the folder you selected. You can check the Output tab in Adobe Animate if you’d like to check the status of each audio item tried.

Adding Your Sounds To Phaser Editor

At this point, you should have the audio files on your disk. Now, we can import them into Phaser Editor.

First, place the audio files inside a folder stored under the WebContent folder in the Phaser Editor project.

You can add the file to the main JSON pack file, if you’ve only a few of them. However, because Blasteroids has lots of audio files, I prefer to make a separate JSON pack file just for the sounds. So, we’ll do that!

From Phaser Editor (using version 2.1.6) main menu, select New, then Asset Pack File. Then create a pack JSON file. You can name it whatever you want.
(Alternatively, you can press the New button, then select Asset Pack File from the pop-up window.)

The pack will will automatically open.

Press the Add File Key button, select Audio – audio from the list, then press OK.

A pop-up window should open listing all the sounds Phaser Editor found in your project. You can select the ones you want to add to this JSON pack file. In my case, I added all of them.

Loading And Playing Sounds

Now that you have a nice little JSON pack, you can preload this pack so all your sounds are are available for play.

In the code for your Phaser Scene, add a preload function if one is not already there, then add a line of code like this:

this.load.pack("gameSounds", "name-of-your-asset-pack.json");

I decided to use "gameSounds" as the key for this asset pack, but you can use whatever makes sense to you. Just remember it must be unique; no other assets should use the same key name.

If you just need to play a sound only once (then forget about it), you can use code like this:
this.scene.sound.play("key-of-the-sound");
Each sound inside the assets pack has a unique key.

If you’ll want to play a sound multiple times, and/or if you’ll need an event that fires once a sound finishes playing, you can create a Phaser Sound object. Use code like this to do so:
var sound = this.scene.sound.add("key-of-the-sound");
You can then play the returned sound object by writing:
sound.play();

In Flash, if you wanted to be notified when a sound finished playing, you’d attach a Event.SOUND_COMPLETE handler to its SoundChannel.

Your code might look something like this.

var sound:Sound;
...
var soundChannel:SoundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);

In Phaser, we can do something similar with the sound object. You can write something like this:

var sound = this.scene.sound.add("key-of-the-sound");
sound.on("complete", onSoundComplete);
sound.play();
...
function onSoundComplete() {
  //code runs here when sound finishes playing
}

Note: You can learn more about Phaser’s SoundManager here. Depending on if you’re using Web Audio or HTML5 audio, each has its respective sound manager class:
WebAudioSoundManager
HTML5AudioSoundManager

That’s it for this article! Playing sounds is one of the more relatively easier tasks when converting a game from Flash to HTML5.

Finally, if you’d rather skip all the learning and have me take a look at your Flash game instead, reach out to me one of these ways:

Continue to stick around as I continue making progress on this game!

Thanks, and talk to you later.

– C. out.

Progress Update: Converting The Flash Game Blasteroids To HTML5 Using Phaser, Part 6

Hey,

In this article, the series of converting the Flash game Blasteroids to HTML5 continues. The target technology I’m using throughout this process is Phaser, and the primary tools I’m using for development are Visual Studio Code and the amazing Phaser Editor. The latter is an amazing tool, as it aims to provide an integrated environment for HTML5 game development, similar to what Adobe Animate did for Flash.

Adding the first power up item is presented in this article. Most of what’s presented in this article has been covered in previous articles. Nonetheless, they are important aspects of the conversion process as they will be used repeatedly throughout.

Check out this short demo video of the current progress of the game.

As you’re probably aware by now, Adobe is discontinuing Flash by end of this year. If you have Flash games, they will no longer work afterwards, as browsers, such as Google Chrome, will completely block Flash content.

So, if you want to preserve your Flash, you’ll have to re-build it in another platform, and if it’s to remain as a web-based game (meaning you’re NOT converting it from web to mobile), then HTML5 (Canvas/WebGL) is likely your best bet.

I previously announced that I was taking on the challenge to convert a Flash game I made for a previous client to HTML5. I will continue to post progress updates on the game until the conversion has been completed.

The current list of articles is here:

  • Part 1 – Setting up the player
  • Part 2 – Displaying objects, keyboard input, and object wrapping collisions
  • Part 3 – Missiles, animations, and HUD
  • Part 4 – Adding text, health, and icons to HUD
  • Part 5 – Collision With Missiles, Asteroids, and Player Ship
  • Part 6 – First Power-Up Item (this article)

Let’s get started. The updates covered in this article are:

  1. Adding the power-up item graphic
  2. Adding a new collision shape
  3. Player Collects An Item

1. Adding The Power-Up Item Graphic

The power-up item we’re adding is one of the simplest ones in the game: a health booster. When you collect this item, it restores a little health to your ship. Unlike traditional Asteroids game, Blasteroids is not one-hit-kill, meaning, one hit from asteroids, enemy ships, or enemy fire won’t kill you (unless the hit took out all your health).

Setting Up The Item AnimatioN

In the original Flash game, power-up items are displayed with two animation states, normal, and highlighted, as you can see from the video demo at the beginning of this article.

In Adobe Animate, the animation timeline sets up the two states like this:

We’ll need to extract both states as bitmap images, as we’ve done previous animations. You can see this animation is nine frames in length. However, we don’t need to extract all nine, because there are only two animation states. So, we can export the graphics only on frames one and five.

Note: Adobe Animate presents a new dialog for exporting images. It presets some new functions you may find useful, but all we need here is to ensure that “Clip to Stage” is unchecked so they we extract only the complete image, without any white space or truncation. And of course, you can still use the legacy dialog if you prefer.

Once those two images are extracted, you can import them into Phaser Editor. You’ll then set up an animation for the item. It’s just like how the animation for the player’s ship missile was setup back in Part 3 of this article series.

Setting Up The Item Collection Animation

When the player collects an item, it’s removed from the scene. And while we could simply make the item disappear, we can add a little polish to that.
😏

If you need to, have another look at the demo video. When an item is collected, it plays a quick animation before being removed.

The original Flash did this using Greensock (GSAP) tweening ActionScript 3 code, like the following:

...
blendMode = BlendMode.LAYER;

var tlVars:Object = {
  onComplete: onTimelineCollectDone
};

var timeLine:TimelineLite = new TimelineLite(tlVars);

var ctVars:ColorTransformVars = new ColorTransformVars();
ctVars.brightness = 2;

var vars:TweenLiteVars = new TweenLiteVars();
vars.colorTransform = ctVars;
timeLine.append(new TweenLite(this, 0.25, vars));

vars = new TweenLiteVars();
vars.addProp("alpha", 0);
timeLine.append(new TweenLite(this, 0.25, vars));

timeLine.play();
...

You can see some color transformations are being applied. As we’ve already learned from this article, there’s no equivalent for applying color filter effects in Phaser 3. So, we’ll need to convert this from code to a series of bitmaps to use as an animation.

This animation can be done using an animation of three keyframes.

The first one is the original image with no effects applied.

The second keyframe will set the brightness to full white. As you can see from the above code, it sets the brightness to 2. The equivalent of this in Adobe Animate is to set the Brightness property to 100%.

The final keyframe sets the alpha property to 0%.

Now that the keyframes are in place, we can set up the duration from one keyframe to the next.

In the code above, each tween runs for 0.25 seconds (Greensock measures durations using seconds instead of milliseconds). We already know how to convert seconds to frames with this formula:
frames = seconds x frames-per-second

Note: frames-per-second is set to 30, the running speed of the game.

So the number of frames is 0.25 x 30 = 7.5 frames. You can round to either 7 or 8 frames.

Finally, you’d create Classic Tweens between the keyframes to create finalize the animation.

Once that’s done, then the animation can be exported as a series of bitmaps to be imported into Phaser Editor as made into an animation. You can add this animation as a separate one in the same JSON file that you created the first item animation as shown here:

2. Adding A New Collision Shape

Now that the idle and collect animations have been created, we can set up the item game object for collisions, so the player can pick it up.

If you want, you can assign the existing square node to this item, similar to how the player game object was setup here.

But, we’re not gonna do dat.
😎

Since the icon image is circular, we’re gonna set up a new circle shape!

Just like we previously created a ContactRectEntityFactory factory file, the steps are very similar to the rectangular contact factory we made.

  1. Create a factory class called ContactCircleEntityFactory with the factory name circleContact.
  2. Create a display object class for the factory that extends Phaser.GameObjects.Image.
    When you register circleContact with Phaser.GameObjects.GameObjectCreator.register, be sure not to add the display object to the display or update lists.
  3. Just like the player object, create a new Scene in Phaser Editor. Name it HealthItemEntity. This will be the factory display item.
  4. In Phaser Editor, add the image that will visually represent the health item. Set its Var Name to itemDisplay, and check the Name box in the Game Object section. Then add a circular image to the HealthItemEntity scene and assign it the circleContact factory. You’ll need to create a circle graphic in your graphics tool of choice. This image acts as a temporary placeholder that will determine the size (radius) of the actual circular physics body. Phaser Editor will generate code in the format of:
    var node = this.add.circleContact(x, y, texture, frame);
A blueish “nodeCircle” image has been placed onto the scene of this item.
  1. In EntityFactory constructor, add this to the this.add object:
    circleContact: this._make_circleContact;
    After the edit (this includes edits from previous articles), the add object should look like this:
this.add = {
  image: this._make_image,
  sprite: this._make_sprite,
  rectContact: this._make_rectContact,
  circleContact: this._make_circleContact
};
  1. Create a new make_circleContact method for the EntityFactory class:
EntityFactory.make_circleContact
make_circleContact(x, y, texture, frame) {
  var img = new ContactCircleEntityFactory(this.scene, x, y, texture, frame);
  this._add(img);
  return(img);
}
  1. In the Entity class, create a Matter physics body for every display object that’s an instance of the ContactCircleEntityFactory class.

You can obtain the bounds of a circle entity by calling the Phaser.GameObjects.getBounds() method on that contact object. Assuming you created the circular image from step 3, once you have the bounds, you can divide the width (or height) of the bounds by two to obtain the radius of the circle. You’ll need the radius when creating the circle physics body. Consider the following code for creating the body:

createCircleBody
function createCircleBody(circleEntity) {
  var bounds = circleEntity.getBounds();
  return(
    this.scene.matter.add.circle(
      bounds.centerX,
      bounds.centerY,
      bounds.width / 2,
      {
        isSensor: true
      }
    ));
}

Similar to this.scene.matter.add.rectangle, which creates a rectangular body, this time, this.scene.matter.add.circle is used to create a circular body. The highlighted line 7 specifies the radius of the circle, which is required when creating a circular body.

Now, when you create your item entity, it will have a circular physics body attached to it.

4. Player Collects An Item

From here, we can collision-check when the player touches an item. While each item has a different effect, we’ll only cover the health item here.

Since all Matter physics bodies are sensors (they don’t respond with colliding with other bodies), we can use something like this:
this.scene.matter.overlap(playerEntity, itemEntity);
The function will return true if the player ship and the item are touching.

Once the player has touched the item, you should immediately remove it, so that the player can’t collect it again!

But, we don’t want to do that right away, because we wouldn’t be able to see the “item collection” animation we set up earlier.

Since collision is not handled automatically in Blasteroids by Phaser (it’s using polling by checking collisions each frame), you can add a new property to Entity. This property can be used to determine if the entity will be checked for collisions. Let’s call it Entity.canCollide, and by default (in your Entity constructor), you can set this property to true.

So when an item has been picked up, you can set this property to false.

Ok, say you store all your game entities in an array. Let’s call it gameEntities. Then, your main polling loop might look something like this:

for (var index1 = 0; index1 < this.gameEntitities.length - 1; index1++) {
  var entity1 = this.gameEntitities[index1];
  if (!entity1.canCollide) {
    continue;
  }

  for (var index2 = index1 + 1; index2 < this.gameEntitities.length; index2++) {
    var entity2 = this.gameEntitities[index2];
    if (!entity2.canCollide) {
      continue;
    }

    if (this.scene.matter.overlap(entity1, entity2)) {
      this.handleCollision(entity1, entity2) || this.handleCollision(entity2, entity1);
    }
  }
}
handleCollision
function handleCollision(entity1, entity2) {
  if (entity1 instanceof Player) {
    if (entity2 instanceof Item) {
      entity2.canCollide = false;

      //(if health item
      //add health to player, and update health text on hud
      //otherwise, handle whatever item was just collected)

      //play collection animation
      const ITEM_DISPLAY_NAME = "itemDisplay";
      var itemDisplay = entity2.getByName(ITEM_DISPLAY_NAME);

      const DATA_ANIM_ITEM_KEY = "collectAnimKey";
      var animKey = itemDisplay.getData(DATA_ANIM_ITEM_KEY);
      entity2.anims.play(animKey);

      itemDisplay.on(Phaser.Animations.Events.SPRITE_ANIMATION_COMPLETE,
        this.onItemCollectionAnimationComplete, entity2);

      return(true);
    }
  }

  return(false);
}
onItemCollectionAnimationComplete
onItemCollectionAnimationComplete() {
  this.destroy();
}

To get the name of the animation key for the collect animation, you could store it as a data property in the item entity scene.

In this image, the name of date key is collectAnimKey and its value is the same value that you used as the key of the collection animation when you set it up. Also, you’ll notice that we get the item display, because the base GameObject class has the Data Manager as a property. In Phaser Editor, when you assign a data property to an object in a scene, Phaser Editor generates code similar to this:
itemDisplay.setData("collectAnimKey", "itemHealth_collect");

When we’re creating an entity from our entity factory EntityFactory.DisplayMixin method, it copies the display object onto our Entity class. And in the case of our health item, we named that display object “itemDisplay”. We can get a reference to this display object with getByName. Then, we can get a data property from the underlying Data Manager using the getData method.

Note: Every item will be named “itemDisplay”. However, the name of the key of the animation key must be unique for each item to avoid animation naming conflicts within Phaser.

Also, since the item was not immediately removed when collected, because it’s playing the collection animation, you can remove the item once the animation has completed playing. This can be done using the Phaser.Animations.Events.SPRITE_ANIMATION_COMPLETE event handler. When this event is dispatched, you can finally destroy the item entity.

FYI: The Player entity class was defined back here.

The Item class has not been defined yet, but it would be similar to Player.

Item class
Item = class extends Entity {
  constructor(scene, x, y, itemEntityFactory) {
    super(scene, x, y, itemEntityFactory);
  }
}

The itemEntityFactory would be a reference to the scene that was created for the item. In this case, this would be HealthItemEntity.

That’s it for this article! Now we’ve converted an item to help the player out. Because believe you, once we get this game going with all the other threats coming at you, you’re gonna need all the help you can get. 💪🏾

Stick around as I continue making progress, converting this Flash browser game to HTML5 browser game!

Thanks, and talk to you later.
– C. out.