Hi there,
WhyMCA staff just published the video of our session on vimeo!
enjoy ![]()
ps: talk in italian
pps: talk with strong venice accent
Hi there,
WhyMCA staff just published the video of our session on vimeo!
enjoy ![]()
ps: talk in italian
pps: talk with strong venice accent
Yesterday we had our first session about Haxe where we showed how is easily for a Flash Platform developer approach Haxe.
We had more or less 40 people during our session and we are so glad about that, because also in Italy there are many valuable developers that could start to work with Haxe!
We’d like to thank WHYmca staff for the amazing opportunity and for the perfect organization in Bologna, we hope next year to make another session about Haxe.
Finally we’d like to share our slides on this blog, if you have any proposal for a tutorial or if you’d like to partecipate in this community feel free to contact us or leave a comment to this post.
NME ( http://www.haxenme.org/ ) is definitely one of the most powerful tools\frameworks you can find in the Haxe world in terms of both productivity and ease of use, it’s the “perfect fit” for a flash developer and it’s simple enough to make anyone productive in few days. Let’s focus a bit on how to push objects on the screen with NME and let’s focus on comparing the HTML5 and Flash targets.
In terms of graphics APIs NME mirrors the APIs available in Flash for AS3, so if you’re familiar with Flash you will find the key concepts and classes you’re used to deal with such as:
All these APIs’ packages mirrors the Flash APIs’ too, so flash.display.Sprite just becomes nme.display.Sprite.
The actual transcoding of the classes in nme.display.* will be an existing Flash class when targeting Flash, so if you are familiar with the runtime of Flash the behavior of the compiled swf is very predictable. Further, in case you’re not targeting the Flash runtime the APIs in nme.display.* are still available and they will be transcoded to an open and editable implementation based on existing open source projects if possible (such as the HTML5 target based on Jeash library http://jeash.com/ ).
Let’s then see some code and some tricks that may come in help when pushing some objects on the screen with NME.
We’re going to code a “Particles Fall”, a very simple program made of three functions:
Each particle is a simple class extending Shape (nme.display.Shape, a simple leaf graphics container which can’t contain further display objects) with color, radius (we’re drawing simple circles) and speed variables and a drawmethod to perform the actual drawing on a given graphics context. Here’s the code in Particle.hx…
package ;
import nme.display.Shape;
import nme.display.Graphics;
class Particle extends Shape {
public var color:Int;
public var radius:Float;
public var speed:Float;
public function new () {
super();
color = 0;
radius = 1.0;
speed = 1.0;
}
// draws the particle on the given target Graphics
// or on the own graphics
public function draw(?target:Graphics):Void
{
if(target==null || target==graphics){
target = graphics;
target.clear();
}
target.beginFill(color);
target.drawCircle(x,y,radius);
target.endFill();
}
}
We’re going to face the same program in 3 different ways in order to see the different behaviors of the same code on the non-flash targets, expecially the HTML5/Jeash one.
The display list approach:
The simplest way to display some object on the screen is leveraging the display list NME provides and delegating to each single particle its own drawing on its own graphics container. Let’s see some code…
package;
import nme.display.Sprite;
import nme.events.Event;
import nme.Lib;
class ParticlesFallDisplayList extends Sprite {
// main function
public static function main () {
Lib.current.addChild (new ParticlesFallDisplayList ());
}
// CONSTANTS:
// stage width
inline private static var STAGE_W:Int = 512;
// stage height
inline private static var STAGE_H:Int = 512;
// amount of particles to display
inline private static var NUM_PARTICLES:Int = 500;
// max radius of a particle
inline private static var PARTICLE_MAX_RADIUS:Int = 10;
// array containing all particles instances
private var particles:Array;
public function new () {
super ();
init();
}
// initialization function:
// - initializes particles array
// - generates particles and populates array
// - registers an enterframe handler for looping
private function init():Void
{
// initializing array
particles = new Array();
// generating particles
generateParticles();
// and putting them all on the display list
for(i in 0...NUM_PARTICLES)
addChild(particles[i]);
// then add loop handler
addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
// generates NUM_PARTICLES particles with a random
// radius color and speed
private function generateParticles():Void
{
var particle:Particle;
while(particles.length < NUM_PARTICLES){
// creating a particle
particle = new Particle();
// setting a random radius within the max value
particle.radius = Math.random()*PARTICLE_MAX_RADIUS;
// getting color components from radius (bigger is brighter)
var r:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
var b:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
var g:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
// and combining them to one color
particle.color = g << 8 | b ; // then setting the speed (bigger is "heavier") particle.speed = particle.radius; // letting the particle draw on its own context at 0,0 particle.draw(); // putting it randomly on the x axis within the width particle.x = Math.random()*STAGE_W; // but keeping it on top particle.y = 0.0; // storing a reference on the array particles.push( particle ); } // then sorting on radius (bigger is nearer) particles.sort( function(p1:Particle,p2:Particle):Int { if(p1.radius==p2.radius) return 0; return p1.radius>p2.radius?1:-1;
}
);
}
// loop handler
private function onEnterFrame(event:Event=null):Void
{
// iterating all particles
var particle:Particle;
for(i in 0...NUM_PARTICLES){
// retrieving the particle
particle = particles[i];
// moving the particle according to its speed
particle.y += particle.speed;
// and moving that to the top whenever it exits the boundaries
if(particle.y>STAGE_H){
particle.y = 0;
particle.x = Math.floor(Math.random()*STAGE_W);
}
}
}
}
Both the compiled swf and the generated HTML5 document run smoothly with a limited amount of particles such as 500. You may encounter some performance issues with the HTML5 document though whenever the number of particles gets increased too much or if it’s running on old browsers.
When coding haxe you’ve always to keep in mind the capabilities and the strenghts of the platform you’re targeting in order to leverage them and avoid performance issues or even lack of features.
The implementation of the display list feature made available by NME via Jeash to the HTML5 target is actually nesting nodes (mainly Canvas) in the DOM leaving the rendering job to the browser engine.
This approach comes with its pros and cons: the power of the CSS animations, but also the lack of speed when adding and removing child nodes to and from the DOM.
Hence if you’re planning to add and remove tons of display objects at the EnterFrame pace you’ve probably better to choose a different way to achieve better performances.
The graphics approach:
In the previous approach we made each particle render in its own graphics context, what if we share the main graphics context with all the particles and make them render to it? Let’s see some code…
package;
import nme.display.Sprite;
import nme.events.Event;
import nme.Lib;
class ParticlesFallGraphics extends Sprite {
// main function
public static function main () {
Lib.current.addChild (new ParticlesFallGraphics ());
}
// CONSTANTS:
// stage width
inline private static var STAGE_W:Int = 512;
// stage height
inline private static var STAGE_H:Int = 512;
// amount of particles to display
inline private static var NUM_PARTICLES:Int = 500;
// max radius of a particle
inline private static var PARTICLE_MAX_RADIUS:Int = 10;
// array containing all particles instances
private var particles:Array;
public function new () {
super ();
init();
}
// initialization function:
// - initializes particles array
// - generates particles and populates array
// - registers an enterframe handler for looping
private function init():Void
{
// initializing array
particles = new Array();
// generating particles
generateParticles();
// then add loop handler
addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
// generates NUM_PARTICLES particles with a random
// radius color and speed
private function generateParticles():Void
{
var particle:Particle;
while(particles.length < NUM_PARTICLES){
// creating a particle
particle = new Particle();
// putting it randomly on the x axis within the width
particle.x = Math.random()*STAGE_W;
// but keeping that on top
particle.y = 0.0;
// setting a random radius within the max value
particle.radius = Math.random()*PARTICLE_MAX_RADIUS;
// getting color components from radius (bigger is brighter)
var r:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
var b:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
var g:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
// and combining them to one color
particle.color = g << 8 | b ; // then setting the speed (bigger is "heavier") particle.speed = particle.radius; // storing a reference on the array particles.push( particle ); } // then sorting on radius (bigger is nearer) particles.sort( function(p1:Particle,p2:Particle):Int { if(p1.radius==p2.radius) return 0; return p1.radius>p2.radius?1:-1;
}
);
}
// loop handler
private function onEnterFrame(event:Event=null):Void
{
// clearing the main graphics context
graphics.clear();
// iterating all particles
var particle:Particle;
for(i in 0...NUM_PARTICLES){
// retrieving the particle
particle = particles[i];
// moving the particle according to its speed
particle.y += particle.speed;
// and moving that to the top whenever it exits the boundaries
if(particle.y>STAGE_H){
particle.y = 0;
particle.x = Math.floor(Math.random()*STAGE_W);
}
// rendering the particle into the main graphics context
particle.draw(graphics);
}
}
}
In this sample we totally removed the use of the display list and relied to the drawing APIs in order to achieve the same result in a flattened canvas. Inspecting the HTML5 document you’ll see that all those little canvases containing our particles are now disappeared, replaced by a whole big one containing all the graphics.
While the compiled swf runs smoothly, the generated HTML5 document results in hugely different performances depending on the browser: it’s totally choppy on Chrome (v19) but very smooth on Safari (v5.1.5) and Firefox (v12). These performances’ differences are due to each browser’s implementation of the Canvas drawing APIs, in facts we’re stressing them by calling the Particle.draw() method at the EnterFrame pace. Be advised, this approach has a further hidden limitation: Graphics.beginBitmapFill is still not supported ( http://www.haxenme.org/documentation/features/ ), hence if our particles were bitmaps we could not draw them this way.
The bitmap approach:
So far we’ve seen that adding\removing a DisplayObject by leveraging the display list APIs is an expensive operation, and that drawing to a graphics context may be even more costly in terms of performances and may be limitating because not all the Graphics APIs are fully implemented. The third approach in this tutorial is the blitting approach: we’re going to delegate to each particle its own rendering job and we’re just copying the result on a bitmap at each particle’s position. This way we’ve to draw each particle only once and because we’re not invalidating them we can leverage the speed of the copy operation from Canvas to Canvas. Let’s see the code…
package;
import nme.display.Sprite;
import nme.display.Bitmap;
import nme.display.BitmapData;
import nme.geom.Matrix;
import nme.events.Event;
import nme.Lib;
class ParticlesFallBitmap extends Sprite {
// main function
public static function main () {
Lib.current.addChild (new ParticlesFallBitmap ());
}
// CONSTANTS:
// stage width
inline private static var STAGE_W:Int = 512;
// stage height
inline private static var STAGE_H:Int = 512;
// amount of particles to display
inline private static var NUM_PARTICLES:Int = 500;
// max radius of a particle
inline private static var PARTICLE_MAX_RADIUS:Int = 10;
// array containing all particles instances
private var particles:Array;
// the actual bitmap canvas and its container display object
private var canvas:BitmapData;
private var canvasContainer:Bitmap;
inline private static var CANVAS_CLEAR_COLOR:Int = 0xFFFFFF;
public function new () {
super ();
init();
}
// initialization function:
// - initializes particles array
// - generates particles and populates array
// - registers an enterframe handler for looping
private function init():Void
{
// initializing array
particles = new Array();
// generating particles
generateParticles();
// initializing canvas
canvas = new BitmapData(STAGE_W,STAGE_H,false,CANVAS_CLEAR_COLOR);
// and adding it to the display list
canvasContainer = new Bitmap(canvas);
addChild(canvasContainer);
// then add loop handler
addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
// generates NUM_PARTICLES particles with a random
// radius color and speed
private function generateParticles():Void
{
var particle:Particle;
while(particles.length < NUM_PARTICLES){
// creating a particle
particle = new Particle();
// setting a random radius within the max value
particle.radius = Math.random()*PARTICLE_MAX_RADIUS;
// getting color components from radius (bigger is brighter)
var r:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
var b:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
var g:Int = cast(particle.radius/PARTICLE_MAX_RADIUS*0xFF);
// and combining them to one color
particle.color = g << 8 | b ; // then setting the speed (bigger is "heavier") particle.speed = particle.radius; // letting the particle draw on its own context at 0,0 particle.draw(); // putting it randomly on the x axis within the width particle.x = Math.random()*STAGE_W; // but keeping that on top particle.y = 0.0; // storing a reference on the array particles.push( particle ); } // then sorting on radius (bigger is nearer) particles.sort( function(p1:Particle,p2:Particle):Int { if(p1.radius==p2.radius) return 0; return p1.radius>p2.radius?1:-1;
}
);
}
// loop handler
private function onEnterFrame(event:Event=null):Void
{
// clearing the main canvas
canvas.fillRect(canvas.rect,CANVAS_CLEAR_COLOR);
// creating a drawing matrix
var matrix:Matrix = new Matrix();
// iterating all particles
var particle:Particle;
for(i in 0...NUM_PARTICLES){
// retrieving the particle
particle = particles[i];
// moving the particle according to its speed
particle.y += particle.speed;
// and moving that to the top whenever it exits the boundaries
if(particle.y>STAGE_H){
particle.y = 0;
particle.x = Math.floor(Math.random()*STAGE_W);
}
// setting translation coordinates in drawing matrix
matrix.tx = particle.x;
matrix.ty = particle.y;
// rendering the particle into the main graphics context
canvas.draw(particle,matrix);
}
}
}
This approach makes both the compiled swf and the HTML5 document run smoothly with a limited amount of particles. On the HTML5 target though you can notice a huge slowdown when at first all particles are getting drawn (it’s the graphics approach case!!), and a huge performance boost after that event. This should be your preferred way when you’ve to add and remove quickly from and to the display a big amount of objects or when you’ve to deal with browsers slow in manipulating the DOM.
You can get all the code and the MonoDevelop solution on github at this repository.
Enjoy and stay tuned
For the first post of this blog dedicated to Haxe we thought to review a good book to start with this amazing technology: Haxe 2 beginner’s guide.
In this book you can breathe the essence of Haxe, you will start with what is Haxe, its syntax, how to deploy for different targets in the same module (Neko, Flash, JS, C++…) and so on.
The interesting thing is that everything is explained with useful samples that help user to fix concepts and to learn better this language.
It is recent, April 2011, so the information inside are important to getting started with Haxe; the actual version of Haxe is 2.09 and soon they will release the 3rd version but this book is a good way to start with Haxe.