Note: This applies to Luxe 1.0.0-alpha.2.  This might not be true forever.  

Luxe does some neat things with managing assets and creating animations, but like everything else, it has its own set of quirks that are important to be aware of.  Now that I've got it working I think it will be easy to set up in the future (if I can remember how to properly format the JSON files.  More on that later) but I'm writing this in hopes that it will save someone else an hour or two of frustration and so when I forget later on I can come back later and remind myself...

Managing Assets

Luxe uses an underlying framework called Snow to manage the loading and unloading of assets.  Luxe and Snow work hand in hand and all the Snow features are usable through Luxe, so you shouldn't have to mess with Snow much, but it is worth knowing a little bit about it in case you run into trouble.  One problem that games can have is when assets are asynchronously loaded.  Basically it means that it takes time for a graphic to get loaded from the web or hard disk or whatever, and you might try to use it before it is done.  Snow handles this with an object called a Promise.  As I understand it, a Promise object says "I'm loading something but it isn't done yet.  But it will.  I promise."  The problem is that you can still try to do something like display a promise object and it can cause problems.  So if you can't get things to display or they show up as a big white box, check the debug logs and see if it mentions Promises.  In that case, your object didn't load yet but you tried to use it.

So to avoid all those problems, Luxe uses an object called a parcel.  A parcel is a collection of objects that are loaded and unloaded into memory at one time.  Setting up a Parcel isn't hard, per se, but it is quirky.  Once you understand it things flow smoothly, but getting there can be tricky.  

My goal is to get this animation into my game.

From this sprite sheet.

Setting up the parcel

To load this animation I'm going to need two things in my Parcel.  The first is the spritesheet and the second is a JSON file that explains how the spritesheet should be read.  I'll get into how to format that JSON file in a minute, but for now I could just create a empty file called anim.json and work on importing that.  I'm going to create my parcel in my ready() function so it is created when my application initializes.  I also want to declare the variable beforehand so it is available to the entire program so I can dispose if it later.

Here's my parcel setup:

//in Main.hx
var parcel:Parcel;

//in ready()
parcel = new Parcel({
           jsons:[ { id:'assets/anim.json' } ],
           textures : [
           { id: 'assets/cat.png' }
            ],
 });

Parcels are no exception to Luxe's normal way of constructing objects through typeDefs instead of long constructors.  Right now I struggle with it, but I think after some use I might come to like it.  Parcels can contain arrays of textures, jsons, shaders, bytes, texts, fonts, and sounds.  These are both files in my assets directory.  Getting all the [ ]s and { }s in the right place is kind of a pain and a single missed one can cause all sorts of headaches, but once you get it parcels are quick to create.  You get good at creating stuff by typeDef after awhile with Luxe...

Setting up the Preloader

I like preloaders.  If you don't have a preloader the users will be staring at a blank screen while the application loads assets and might think your application is broken or frozen.  Luxe makes it easy to set up a preloader and the default one is very functional, so we might as well use it.  I created mine right below the parcel.

       new ParcelProgress({
            parcel      : parcel,
            background  : new Color(1,1,1,0.85),
            oncomplete  : assets_loaded
        });

The default PreLoader is just a black bar that fills up, but you could override it and make you own.  During construction, you can specify the colors of all the different options to customize it a little, but the only required things are the parcel you want to show progress for and the function that should run after it is complete.  This function is really important for reasons that will hopefully become clear in a minute.  

Load the Parcel

Last and easiest step.

parcel.load();

This kicks off the parcel loading function and starts loading the assets.  Once the assets are loaded they are available through Luxe.resources.

The problem

You might (if you were me) assume that since we set up a preloader and kicked off our parcel.load() function you can use the assets that you loaded.  Well, you are wrong.  The important thing to realize when you kick off the parcel.load() function is that your code continues to run.  So maybe after you run parcel.load() you want to create a sprite using one of the graphics in the parcel.  So this code:

//In the ready() function
var sprite:Sprite = new Sprite( {
name:"cat",
texture:Luxe.resources.texture("assets/cat.png")
} );

This code should work, but will actually display a white box.  The reason is because the cat.png texture hasn't loaded yet, so the Luxe.resource.texture call returned a Promise object.  Of course, we tried to use this promise object to load into an image, so Luxe just gives us a white box.  

The trick is that we need to wait to use any of the assets until we know that they are finished loading.  That's where the assets_loaded function we referenced in the ParcelProgress object comes into play.  We want to do all of our initialization of anything that needs assets from the parcel there instead of in the ready() function.  

	public function assets_loaded(_) {
		trace("All assets loaded");
		var s:Sprite = new Sprite( {
                name:"cat",
                texture:Luxe.resources.texture("assets/cat.png"),
                });
		
	}

There we go!  Now we have waited until our asset is loaded to initialize our variable!  We should be all set now, right?

Wrong!

The second problem

The second problem is related to the first, but is even more tricky.  Our application continued running while we were waiting for our assets to load and gave us some funky results when we tried to use unloaded assets, but now that we are waiting to initialize them it should be all set, right?  

When I tried this it seemed to make things worse.  Now my preloader bar just went up half way and stopped.  I couldn't get to the debug sceen to see what was going on.  I had no indication of where my problem was.  It took some work, but I found my problem.  It was actually the same problem as before.  While my assets were loading my code continued to run.

The update loop was firing at 60fps even while the preloader was doing its thing.  My update loop was trying to do things with my sprite object, but before when it just couldn't load the texture, now the sprite wasn't even initialized!  

The trick is that not only can I not initialize my variables until my assets are done loading, I can't even run my update loop.  With two lines of code I can stop that.

//Right at the top of the update loop
if (sprite == null)
return;

There we go.  Now the sprite won't be initialized until the assets_loaded function is called and the update loop doesn't run until the sprite object is created.  Finally everything is working like it is supposed to.

Adding animations to the sprite

Of course now that I've got everything loaded my sprite is displaying the whole spritesheet, but lets take care of that.  Luxe has this powerful system of Components that can be added to Entities to make them do things.  A Sprite is an extension of a Visual object, which is an extension of Entity, so we can add components to them.  The specific component we want is called SpriteAnimation.  A SpriteAnimation does some neat things, but requires a specific method of initialization, like most things in Luxe.  In particular, you have to load an animation from a JSON file.  Remember that blank anim.json file we loaded in the parcel?  We need to add some stuff to it.  

{
    "run" : {
        "frame_size":{ "x":"200", "y":"200" },
        "frameset": ["1-9"],
        "loop": "true",
        "speed": "18"
    },
	"jumpup" : {
        "frame_size":{ "x":"200", "y":"200" },
        "frameset": ["10"],
        "loop": "true",
        "speed": "18"
    },
	"jumpdown" : {
        "frame_size":{ "x":"200", "y":"200" },
        "frameset": ["11"],
        "loop": "true",
        "speed": "18"
    }
	
}

Here is my JSON file.  It is made up of an array of Objects.  Each Object has a name ("run", "jumpup", "jumpdown") that will end up being the animation name.  Inside each object are keys and values that the SpriteAnimation object uses to build the different animations.

  • frame_size - What size is each frame in the spritesheet?
  • frameset - This is where the power comes in.  You can be very specific with what you want your animation to do and how it should proceed.  "1-9" just simply plays frames 1 through 9 in order.  I could say "1-5", "hold3", "3-2" to display frames 1 - 5, then display 5 for two more frames, then move backwasds from 3 - 2.  
  • loop - This is a tricky one.  If you set this to false it won't loop your animations.  However, when I set my single frame animations to loop:false it didn't allow me to change animations afterwards.  Not sure why but I'll investigate sometime.
  • Speed: This is the number of frames per second the animation should play at.  If you set this explicitly in the SpriteAnimation it seems to override this setting.

Create the SpriteAnimation object

Before we can create the SpriteAnimation object we first will need a JSONResource object to pass it.  Luckily they are easy to create.  Since I already have my JSON objects loaded from the parcel, I can use Luxe.resources.json() to get it.

var animObj = Luxe.resources.json('assets/anim.json');

Now I can create my SpriteAnimation object.

//Declared so the whole game can use it.
var anim:SpriteAnimation;

//In the load_asset() function so the parcel is finished loading.
//Add this SpriteAnimation component to the Sprite called s that I created earlier.
anim = s.add(new SpriteAnimation( { name:"anim" } ));
//Add in the animation details that we specified in the JSON file.
anim.add_from_json_object(animObj.asset.json);

anim.animation = 'run';
anim.play();

One of the things that I find strange is that the SpriteAnimation isn't attached to a texture at all.  It uses the texture that is loaded into the sprite.  This might be nice because if I have different sprites that have the same format of sprite sheet I can use the same SpriteAnimation object for all of them.

 That's all there is to it!  Now when I run my application I have a running cat.  If I want to change the direction of the animation, scale it, or move it around I just make the changes to my Sprite object and Luxe takes care of the rest for me.  

The asset loading and animation process for Luxe has a learning curve to it, but it is likely just because the engine is still in Alpha and the documentation hasn't caught up yet.  I'm not sure how permanent this method is, but it is a powerful and fairly flexible way of handling animations, so it will work for now.

Add comment


Security code
Refresh