HaxeFlixel: Making a Custom Preloader

Today I figure I shall write a tutorial for a part of Flash/Haxe gamedev that isn’t really written much, especially for HaxeFlixel, one of the more popular frameworks for the Haxe programming language. While not very necessary for desktop and mobile games where data is loaded from the client side and loading times is usually very quick, preloading is used a lot in web based content where data is loaded from a server, so there would be less delays during playthrough. Even then, you are still able to customize the preloading itself to make it more appealing and to suit the game you are making, add links and other info or to include ads, as such I am going to show you the basics of creating your own preloader for a HaxeFlixel flash game.

Preload HaxeFlixel Default Preloader

First off, I’d like to point out that there is a Youtube video on the subject called HaxeFlixel Hacking the Preloader, and whenever I look up the subject some people refer to that video. Do not follow that video’s instructions, the problem is that the video’s solution is to copy the entire FlxPreloader class and cut out large chunks of code, so you can replace it with your own. In my opinion, this solution is very messy and leaves a risk of failure if you don’t know what you are doing. It is also outdated as the video is a year old and the coders behind HaxeFlixel has since restructured the FlxPreloader so it’s much easier to make your own. Apologies to the maker of the video, but for beginners and people inexperienced with the preloader system, there are safer solutions now then back then.

For this tutorial, I’m developing with FlashDevelop 4.6.2.5 and HaxeFlixel 4.0.0.

1. Extending FlxBasePreloader

FlxBasePreloader is a class that extends NMEPreloader, an OpenFL class that contains the most basic no-graphics preloader. You tend to see it if you set the preloader to either FlxBasePreloader or build a HaxeFlixel project as a HTML5 game.

What FlxBasePreloader also does is add two extra properties: minDisplayTime and allowedURLs. As the names imply, minDisplayTime is a Float variable that delays the percent value so that loading will last a specific minimum amount of time, in case you want to show stuff like ads or special animations while the game is loading. allowedURLs is an Array<String> variable that is used if you want to lock the game to run on specific websites, which helps if you want to avoid people stealing your game to host it on their own websites. Setting these two are optional, and can be done in the constructor when we extend the FlxPreloaderBase class, but for the sake of testing out the preloader I recommend setting the minDisplayTime to 5 so we can see the preloader for 5 seconds before completing.

So, let’s create a CustomPreloader class, create a new class and set the Base Class to flixel.system.FlxBasePreloader. The final result should look like this:

package ;

import flixel.system.FlxBasePreloader;

/**
* ...
* @author
*/
class CustomPreloader extends FlxBasePreloader
{
    public function new(MinDisplayTime:Float=0, ?AllowedURLs:Array<String>)
    {
        super(MinDisplayTime, ?AllowedURLs);
    }
}

Now, before we do anything we need to remove the ? from the super constructor so it can compile. If you want to see what the preloader looks right now, go into Project.xml and change the class in line <app preloader=”flixel.system.FlxPreloader”/> to the CustomPreloader class, for example:

<app preloader=”CustomPreloader” />

If you run the game now in Flash and set the MinDisplayTime to value higher than 0, all you’ll see is a black screen, now it’s time to change that.

2. Adding Images to Preloader

Now another point to make clear here is that none of the HaxeFlixel classes will work in this class, because the FlxBasePreloader extends NMEPreloader, which uses flash. As such, we need to use flash and openFL libraries and objects. For this preloader, we are going to have one image and some text, so we will be importing these libraries:

import flixel.system.FlxBasePreloader;
import flash.display.*;
import flash.Lib;
import openfl.display.Sprite;

We are also going to override the FlxBasePreloader function create(), so we have a place to initialize objects. For images we are going to use the Sprite object, these handle image data in Flash, NMEPreloader itself extends Sprite to handle rendering as a buffer. In order to load in our image, it needs to be embedded into a class itself. Here is how OpenFL can embed image files:
@:bitmap(imagePath) class LogoImage extends BitmapData { }

Where imagePath is the file path of the image in the project.

We could also use OpenFL’s Assets class, but at this time whenever I try to use openfl.Assets.getBitmapData(imagePathOrId) Flash throws an argument error saying Invalid BitmapData. Then you need to initialise a Sprite object and set its child object as the Bitmap we have embedded, which is done like this:

logo.addChild(new Bitmap(new LogoImage(0,0)));

You can then use the Sprite’s object variables to set the image how you like, including X, Y, scaleX and scaleY and finish it off by adding it to the Preloader class’ buffer:

addChild(logo);

So now the class should something kinda like this:

package ;
import flixel.system.FlxBasePreloader;
import flash.display.*;
import flash.Lib;
import openfl.display.Sprite;

@:bitmap("assets/images/CustomPreload/gamepopper-logo.png") class LogoImage extends BitmapData { }

class CustomPreloader extends FlxBasePreloader
{

    public function new(MinDisplayTime:Float=5, ?AllowedURLs:Array<String>)  
    {
        super(MinDisplayTime, AllowedURLs);
        
    }
    
    var logo:Sprite;
    
    override function create():Void 
    {
        this._width = Lib.current.stage.stageWidth;
        this._height = Lib.current.stage.stageHeight;
        
        var ratio:Float = this._width / 800; //This allows us to scale assets depending on the size of the screen.
        
        logo = new Sprite();
        logo.addChild(new Bitmap(new LogoImage(0,0))); //Sets the graphic of the sprite to a Bitmap object, which uses our embedded BitmapData class.
        logo.scaleX = logo.scaleY = ratio;
        logo.x = ((this._width) / 2) - ((logo.width) / 2);
        logo.y = (this._height / 2) - ((logo.height) / 2);
        addChild(logo); //Adds the graphic to the NMEPreloader's buffer.
        
        super.create();
    }   
}

If running successfully, you should be able to see your image in the preloader!

3. Adding Text

Adding text is almost as similar to adding images, but instead we use the TextField object and we now embed fonts.

First we need to add some more libraries:

import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFormat;

Font is the object that stores the font that we want to use, TextFormat stores the format we want our text to be in (font, size, colour, alignment ect) and TextField renders our text to a specified position and area.

Unfortunately, at this time I cannot get embedding fonts to work for me, if someone can provide input on what issue I have then let me know, but the method I’m going to explain comes from the FlxPreloader class and from OpenFL tutorials I can find on the subject. For now, you can use the “Nokia Cellphone FC Small”, which is the default font for HaxeFlixel Preloaders and works perfectly.
If you do not have a font file to embed, you can also use either “Arial” or “Nokia Cellphone FC Small” when setting Font Names, which are the default fonts for HaxeFlixel Assets.

Similarly with bitmaps, we embed fonts into a class like such:
@:font(filePath) class CustomFont extends Font {}

Where filePath is the path of the .ttf font file.

Then before we use our font, we need to register the font with OpenFL, which we add this to the create() function:

Font.registerFont(CustomFont);

Then we initialise the TextField object, and set the defaultFormat property to a new TextFormat object containing the font, colour ect, and the usual properties for position (X,Y), size (width, height) and text based properties (embedFonts, multiline, selectible) and then add the textfield to the preloader like we did with our sprite.

When setting the font, you need to specify the font’s name. If you do not know the exact name of the font (usually found in the font file), you can create a font object of embedded font class type and use the fontName property like so:

var font:CustomFont = new CustomFont();
font.fontName;

package ;

import flixel.system.FlxBasePreloader;
import flash.display.*;
import flash.text.*;
import flash.Lib;
import openfl.display.Sprite;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFormat;

@:bitmap("assets/images/CustomPreload/gamepopper-logo.png") class LogoImage extends BitmapData { }
@:font("assets/data/GameBoy.ttf") class CustomFont extends Font {}

class CustomPreloader extends FlxBasePreloader
{

	public function new(MinDisplayTime:Float=5, ?AllowedURLs:Array<String>) 
	{
		super(MinDisplayTime, AllowedURLs);
		
	}
	
	var logo:Sprite;
	var text:TextField;
	
	override function create():Void 
	{
		this._width = Lib.current.stage.stageWidth;
		this._height = Lib.current.stage.stageHeight;
		
		var ratio:Float = this._width / 800; //This allows us to scale assets depending on the size of the screen.
		
		logo = new Sprite();
		logo.addChild(new Bitmap(new LogoImage(0,0))); //Sets the graphic of the sprite to a Bitmap object, which uses our embedded BitmapData class.
		logo.scaleX = logo.scaleY = ratio;
		logo.x = ((this._width) / 2) - ((logo.width) / 2);
		logo.y = (this._height / 2) - ((logo.height) / 2);
		addChild(logo); //Adds the graphic to the NMEPreloader's buffer.
		
		Font.registerFont(CustomFont);
		text = new TextField();
		text.defaultTextFormat = new TextFormat("Early GameBoy Regular", Std.int(24 * ratio), 0xffffff, false, false, false, "", "", TextFormatAlign.CENTER);
		text.embedFonts = true;
		text.selectable = false;
		text.multiline = false;
		text.x = 0;
		text.y = 5.2 * this._height / 6;
		text.width = _width;
		text.height = Std.int(32 * ratio, Int);
		text.text = "Loading";
		addChild(text);
		
		super.create();
	}
	
}

Now if this runs properly, you should now see a white “Loading” text near the bottom of the screen! However the preloader screen looks static, most gamers are put off by static loading screens as it doesn’t tell them that the game is loading properly and not frozen, so lets get to animating this preloader.

4. Animating the Preloader

To do this, we are going to override the update() function. This function is called each frame, and passes a variable called Percent that stores the amount of data loaded as a float between 0 and 1.

You could do anything to animate the preloader, depending on your knowledge of flash, but to keep things easy, we will simply update the text to show the current amount loaded onto our scene. Make sure the TextField object you made is global so it can be accessed in the update function and set the update function like this:

override function update(Percent:Float):Void
{
    text.text = "Loading " + Std.int(Percent * 100) + "%";
    super.update(Percent);
}

What this code does is simply set the text to “Loading ” plus the Percent value times 100 (cast as an Int to round to the nearest integer), ending with a % symbol. Run the project and you can see the text now increases until it reaches 100.

5. What next?

Here are some other things that you can do to add a bit more interactivity to your preloader:

To stop the preloader from leaving when fully loaded, override the onLoaded() function and make sure _loaded is set to false.

override public function onLoaded()
{
    super.onLoaded();
    _loaded = false;
}

If you want to include mouse or keyboard input, you have to use EventListeners. You can use EventListeners on objects or on the entire project area by using Lib.current.stage.addEventListener(), and you create functions to respond to those events.

Make sure that if you want an event that completes the loading process, make sure you only perform the action if Percent is equal to 1, to avoid any issues caused by not loading the entire flash file.

If you want to include any external libraries for ads or animations, you can include an external .swf or .swc file by adding these lines to the Project.xml file:

<haxedef name="as3_native" if="flash"/>
<haxeflag name="-swf-lib" value="NewgroundsAPI.swc" if="flash"/>

Now you can use flash libraries like you are coding in Actionscript3, and they should function with OpenFL code like Flash would.

Finally, make sure you include #if !js and #end around all the code within the class, this is to avoid any compiling issues that occur when building to HTML5, as none of the flash specific stuff works on the HTML5 native build. Desktop and Mobile platforms do not use the preloader, so you don’t need to worry about compile issues from that.

Here’s the full code from the tutorial:

package ;

import flixel.system.FlxBasePreloader;
import flash.display.*;
import flash.text.*;
import flash.Lib;
import openfl.display.Sprite;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFormat;

@:bitmap("assets/images/CustomPreload/gamepopper-logo.png") class LogoImage extends BitmapData { }
@:font("assets/data/GameBoy.ttf") class CustomFont extends Font {}

class CustomPreloader extends FlxBasePreloader
{
    #if !js
    public function new(MinDisplayTime:Float=5, ?AllowedURLs:Array<String>) 
    {
        super(MinDisplayTime, AllowedURLs);
        
    }
    
    var logo:Sprite;
    var text:TextField;
    
    override function create():Void 
    {
        this._width = Lib.current.stage.stageWidth;
        this._height = Lib.current.stage.stageHeight;
        
        var ratio:Float = this._width / 800; //This allows us to scale assets depending on the size of the screen.
        
        logo = new Sprite();
        logo.addChild(new Bitmap(new LogoImage(0,0))); //Sets the graphic of the sprite to a Bitmap object, which uses our embedded BitmapData class.
        logo.scaleX = logo.scaleY = ratio;
        logo.x = ((this._width) / 2) - ((logo.width) / 2);
        logo.y = (this._height / 2) - ((logo.height) / 2);
        addChild(logo); //Adds the graphic to the NMEPreloader's buffer.
        
        Font.registerFont(CustomFont);
        text = new TextField();
        text.defaultTextFormat = new TextFormat("Early GameBoy Regular", Std.int(24 * ratio), 0xffffff, false, false, false, "", "", TextFormatAlign.CENTER);
        text.embedFonts = true;
        text.selectable = false;
        text.multiline = false;
        text.x = 0;
        text.y = 5.2 * this._height / 6;
        text.width = _width;
        text.height = Std.int(32 * ratio);
        text.text = "Loading";
        addChild(text);
        
        super.create();
    }
    
    override function update(Percent:Float):Void 
    {
        text.text = "Loading " + Std.int(Percent * 100) + "%";
        super.update(Percent);
    }
    #end
}

So there you go, a basic guide to preloaders in HaxeFlixel. I hope you all found that helpful and any more experienced developers who would like to offer any corrections or extra tips please let me know via email or social networks or write a comment below.

Thank you Gama11 for your input to add to this tutorial.

Advertisements

10 thoughts on “HaxeFlixel: Making a Custom Preloader

  1. Hi there.
    Man I so glad to see this article, I made that video you talk about and is named HACKING because is a huge hack, not best practices I know, but since there was NO info about how to do it I just did what worked for me at the moment, anyway, this guide is amazing and will use this method for my future projects.
    Thank you.

  2. Hi gamepopper,
    i’m making a game with haxeflixel and flashdevelop and this tutorial is a life saver but the problem is I want to integrate newgrounds api with my game didn’t find any info on how to do that on their website or anywhere else.
    kindly please could you make a tutorial on how to integrate and use newgrounds api with haxeflixel and flashdevelop ?

  3. Pingback: Newgrounds API with Haxeflixel | GAMEPOPPER

  4. Thank you for the article! Just a question, do you know if it’s possible to also customize the “pause” thing when you move your cursor/focus out of the game?

    • I’m sure it’s possible, it depends on what you want to do. The majority of functions that are found in Actionscript3 translate very well to Haxe in this preloader, so the best advice is to look up on how AS3 events work.

  5. Thank you so very much! Except that it’s FlxPreloaderBase now instead of FlxBasePreloader, everything works out fine 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s