CanvasQuery icon indicating copy to clipboard operation
CanvasQuery copied to clipboard

Sprites in Playground

Open rezoner opened this issue 10 years ago • 15 comments

What would be your expectation regarding rendering sprites in Playground. Preferably write example USAGE in real code.

TL;DR

The problem is that there are many solutions to render sprites/animations.

A sprite rendering would have to support these (or is this is not what you expect?):

  • texture atlas
  • fixed grid
  • scale, rotation, align
  • DURATION

I've put emphasis on DURATION because it implies having time delta somewhere - and that's where the thing gets nasty as this clearly leverage Playground beyond unobtrusive core architecture that deals with display and input only.

On top of that - it's really hard to achieve that with functional (rather than objective) approach without sacrificing readability - and I don't really want to go with objects because it's pushing playground towards tightly coupled engine (like Phaser).

That's the minimal form for a functional sprite rendering. It requires you to have a sprite defined somewhere earlier.

renderSprite(app.sprites.tank, x, y, delta);

app.sprites.tank = {
  image: "tank",
  frames: 16,
  width: 32,
  height: 32,
  alignX: 0.5,
  alignY: 0.5,
  duration: 0.4
};
  • What about loops?
  • What about events? (state finished) - useful for coupling with game logic

I really think that having your own sprite object is more convenient than functional approach - and for rendering you just use layer.drawRegion

So now I am thinking more about providing just two methods to extract frame from textureAtlas and fixed-size spritesheet - something like more advanced version of layer.drawRegion

rezoner avatar Jan 30 '15 13:01 rezoner

While sprite rendering in Playground would be nice, maybe it's more suited to a plugin or a bit of example code provided for users' reference but not included in playground.js itself.

I agree with pretty much everything you said about the objective versus functional approach. Keeping track of the time elapsed in order to calculate which frame to render implies an objective approach, which doesn't seem to fit well with the rest of playground.js.

A function to grab a frame from a fixed-size spritesheet would be handy for a lot of purposes.

grabSprite( app.images.spriteSheet, frameWidth, frameHeight, frameNumber );

What would the function for grabbing a frame from a textureAtlas look like? (I'm not understanding how it would differ from drawRegion.)

nknauth avatar Jan 30 '15 17:01 nknauth

For a textureAtlas I believe we would first have to add some kind of a loader - as for atlas there usually come JSON file with frames positions, original frame size before truncation and so on.

However there is not an industry standard... except the ugly one that comes from flash - but let's leave that for a moment.

this.loadTextureAtlas("something");

/* ... */

var sprite = this.layer.grabFromTextureAtlas(atlas, frame);

This would give you solely sprite data (frame size etc) and you would have to use it manually with scale, rotate, and drawRegion considering desired align.

Otherwise we would have this tremendous function:

this.layer.drawFromTextureAtlas(image, atlas, frame, x, y, alignX, alignY, rotate, scale);

And still - you'd have to manage frame number (delta) manually - that's why I think that implementing sprites does not go well with functional approach.

rezoner avatar Jan 30 '15 17:01 rezoner

I kind of agree with @nknauth about not having sprite support in playground. Apart from the problems already mentioned, what I love about Playground/Canvasquery is its functional approach, simplicity and low level aproach, which gives me the freedom to play and adapt the library to the needs of the app/game.

I do think Atlas support would be convenient, though.. the first approach (the pair loadTextureAtlas() grabFromTextureAtlas() --maybe loadAtlas(), getAtlasFrame() ? ), looks nice.

Mmm.. what about an additional class, separated from Playground, to handle sprites? a Sprite class decoupled from playground.

Sprite{
//public properties
x, y, rotation, scalex, scaley, alignX, alignY, animations, 

//public methods
function play('anim_id', loop, speed|fps);
function step(delta);
function draw(framebuffer | func);
}

It would handle atlas parsing and frame handling. framebuffer would be a canvasquery (or canvas) object where to draw to. It would be nice if it could support a custom function for the actual drawing, once the 2d transform and other pre-draw stuff is prepared. Inside, draw could be:

function draw(func)
{
   //2d transform, stuff..
   func( /* all parameters needed for a custom draw, like the texture, final position, etc. */);
   //undo transform and stuff..
}

or if this is too dirty, just add properties to the Sprite to customize the render of it, like alpha, drawMode, hls, hlsoffset...

step() would handle the animation, based on current animation (set by play()), and the specified delta time.

animations would be an array of {id:'walk', anim: [2, 3, 4, 4, 4, 10, 5], fps:1}

and, not much more. But above all, for me the most important feature of a sprite class would be the 2d transforms handling.

Don't know if all this is silly or has any sense.. sorry :P

feiss avatar Jan 30 '15 20:01 feiss

I wonder if it could be interesting to have the Sprite class totally decoupled from Playground and CanvasQuery.. hmm.. It would be quite a thing..

feiss avatar Jan 30 '15 20:01 feiss

Thanks for feedback guys.

I agree that cq and playground should stay decoupled from the object model.

I have to use these "standard" texture atlases in the next project so expect some loader in the next iteration of playground.

Then I will see if there is a possibility to put some drawing helpers for them in cq.

rezoner avatar Feb 01 '15 20:02 rezoner

So far so good.

http://gfycat.com/FarawayPlushDuckbillplatypus

this.loadAtlases("planet");

Rendering manually is pretty easy. Wondering if there is even a need for any helper method.

var atlas = app.atlases.planet;
var frame = atlas.frames[this.currentFrame];
app.layer.drawRegion(atlas.image, frame.region, this.x + frame.offset[0], this.y + frame.offset[1]);

Of course you have to manage currentFrame based on atlas.frames.length and delta time.

rezoner avatar Feb 01 '15 21:02 rezoner

Looks perfect! :))

feiss avatar Feb 02 '15 00:02 feiss

http://canvasquery.com/playground-atlases

also check Release Notes

We could have something like common repository for "universal" elements like sprites. I am using pretty much same sprite object as @feiss described

Also get ready for rebranding - this will not affect your code in any way - but I am getting rid of Query in CanvasQuery - it scares of all the professionals.

rezoner avatar Feb 02 '15 10:02 rezoner

? don't understand what do you mean with that repository.. something like a heterogenous this.resources hash object?

feiss avatar Feb 02 '15 12:02 feiss

Noes, something completely separated from playground code itself - kind of a website or repo, where you can throw a "sprite class" and redirect people there.

rezoner avatar Feb 02 '15 12:02 rezoner

Oh, nice. Something like code snippets for playground/canvaswhatever. Could help a lot to newbies, and a perfect place for plugins..

feiss avatar Feb 02 '15 14:02 feiss

Today I have came up with this:

this.layer.save();
this.layer.align(0.5, 0.5);
this.layer.translate(100, 100);
this.layer.drawAtlasFrame(atlas, current, 0, 0);
this.layer.restore(); 

Without align it would look like this:

var atlas = app.atlases.planet;
var frame = atlas.frames[this.currentFrame];

this.layer.save();
this.layer.translate(100, 100);
this.layer.drawAtlasFrame(atlas, current, - frame.width * 0.5, - frame.height * 0.5);
this.layer.restore();

Guess we can also introduce Spritesheet - same thing as atlas except for tilesets with fixed size frames.

align doesn't change anything about Canvas vs CQ compatibility - so I consider it optional drawAtlasFrame requires playground but who uses cq separately anyway.

rezoner avatar Feb 03 '15 15:02 rezoner

who uses cq separately anyway

/me raises hand (just for a personal hobby project, nothing serious)

feiss avatar Feb 03 '15 16:02 feiss

I will consider moving images and atlas loaders to cq.

rezoner avatar Feb 03 '15 16:02 rezoner

oh please, don't bother to think about it because of my last stupid comment. It just a stupid little project in which I don't need playground, that's it..

Images and atlas loaders fit very well in playground, it is the natural place for them (although --don't hit me, just rambling-- it also has sense that a Layer could know how to load itself..)

feiss avatar Feb 03 '15 17:02 feiss