Spritesheets With Minko

Update: the full source code for this tutorial is available in minko-examples on GitHub.

Spritesheets are a nice way to create nice effects with an extensive artistic control. You just have to create a texture with a series of cool-looking sprites to have a nice animation. They are widely used for particles, fog and explosions for example. We used spritesheets to render the explosions and the clouds in our latest game: The Mirage.

When you have the spritesheet itself, you need two things to get it working in your application:

  1. A “frame id” value that will be updated to tell which frame of the spritesheet should be sampled.
  2. A shader that will sample the spritesheet accordingly.

And here is what you get:


The first point is very interesting because it will allow me to introduce the new MVC architecture inside Minko 2. To animate stuff – and when I say “animate” I mean “update any kind of value depending on time” – we use a controller called the “AnimationController”. The very interesting point here is that animating a 3D matrix to translate an object or animating our “frame id” works exactly the same way.

It really was one of our main objectives with Minko 2: make everything a lot easier. And being able to animate scene properties – whether they are used by the scene itself or the shaders – with a single API is a good proof that our work has paid. One would say that there is no such thing as a scene “property” that shall not be used in a shader. And at some extend it is a valid assumption. Thus, having a simplified general purpose API to automate the scene-to-shader marshalling is a must have 🙂

The Shader

I’ll start by creating the shader and then see how we can add the animation. The vertex shader just makes sure the quad always faces the camera. This effect is achieved by transforming (0, 0, 0) into view space and then use the actual (x, y, z) position of the vertex as an offset. Because (0, 0, 0) will not be affected by any rotation, we end up with a view space quad facing the camera. It has nothing to do with spritesheets per say, but in the end they are mostly used for particles effects so rendering camera-facing quads is a must have:

Then we have to sample the spritesheet. Here is how a classic explosion spritesheet looks like:

The pixel shader just needs to translate/scale the actual per-vertex UVs according to the frame we want to sample:

You can then setup your scene like this:

You can send change the value of the “spritesheetFrameId” property and see how the rendering changes. Now how to make the spritesheet “playable” like a real animation?

The Controller

The first (crappy) solution is to implement a timer or something like this. Don’t get me wrong: it works. Yet, you’ll have only the basics of the animation: you’ll be able to play it. You’ll have to add some code to be able to stop it, play it reversed, etc… And I’m not even speaking about naming frames to have multiple animations in a single spritesheet.

But wait! As I explained earlier, Minko provides the AnimationController. Controller like in “Model View Controller”. In Minko 2, your scene is the View, the “properties” and the data binding API handle the Model and you can use/create/add Controllers. The AnimationController is one of those controllers and it is specially designed to animate stuff.

Here is how we can use it to animate our spritesheet:

You can then use all the methods of the AnimationController to animate your spritesheet:

  • play/stop
  • goToAndPlay/goToAndStop
  • setPlaybackWindow

The AnimationController.setPlaybackWindow is very cool because it makes it possible to dynamically setup the “bounds” of the timeline in order to play only a part of it. It’s very useful to store multiple animations in one timeline and then play each of them separately. To put it in a nutshell, you have the same control than you have on a MovieClip. But everything is happening on the GPU and the timeline is created programmatically and can animate any property.

Here we use the ScalarRegularTimeline because our “spritesheetFrameId” property is a scalar value. You have other timelines to animate 3D matrices or colors for example. Here is the full code for the scene setup:

If you have questions, you can post in the comments on or Aerys Answers.

  • how to make black color appear as transparent btw? it still black even i try to replace black color with transparent there in png, maybe it has to enable alpha texture somewhere?

    thanks

    • Promethe

      If you don’t customize the blending factors, the alpha value is always dropped. So setting it to 0 – in the shader or in the texture – will still output black pixels.

      To setup the settings of the rendering pass, you can override the Shader.initializeSettings() protected method. As you can see in the BasicShader.initializeSettings() implementation, the settings values can be read from the data binding API. The best practice is to read the “blending” mesh property.

      You might think it’s trivial. But being able to read a data binding value in your shader and have it working this way is a lot more complicated than you might think. A simple example is how does it work when two meshes have the same shader, but a different value for the “blending” property? Well the short answer is that both the shader and it’s settings can be “forked” to match what’s provided by the data binding. And trust me, that was very complicated for us to achieve. But it makes your work incredibly easy: you implement a single shader in a single class, but you can take every situation into account, whether it works on actual shader values or on the context setup of the pass.

      In other words, you should add this to the shader:

      And modify the mesh init. like this:

      This, my friend, is the power of the data binding API 🙂 In the end, it will push only the necessary stuff in the rendering pipeline and do all the required forks for you whenever it’s necessary. You can also extend the BasicShader class. It takes care of implementing the usual stuff in its initializeSettings() method.

      You should also remember that the rendering order affects the blending result. Therefore, you need to adjust the priority of the pass depending on the blending factors.

  • Thanks! now it’s working with some 1px bleeding on the left in some frame,
    it’s bleeding in black version too, it’s just too black too see it happen

    any advice would be appreciate 🙂

    • Promethe

      You should try changing the filtering/wrapping value passed to sampleTexture(). You could also read those values from the mesh bindings. This way you can change them on the properties of each mesh directly.

      Again, that’s something very complicated done in a very easy way here: the texture sampling settings are fixed in the shader. So it will transparently and dynamically recompile a shader whenever you change one of the texture sampling setting.

      It’s super cool because – again – you write just one shader. And yet it’s completely dynamic regarding what you have in the property of the meshes it will be affected to.

  • Kiril

    Its fun and graphics are well done, for 24h.
    One thing that is distracting when playing it on a 24inch screen is the aliasing that’s occurring on along the jets wing and the all around the little saucers, is there a way to deal with this within Minko?

  • Pingback: Stage3D developer tips « 岩屋(乐在其中)()