views:

72

answers:

2

I have a sprite I'm animating on an html canvas using normal sprite sheet blitting. On certain key events I'd like to change the direction of the sprite (ie, flip it, or rotate it 180 degrees) without changing anything (the other sprites) on the canvas.

Does anyone know how to do this?

+1  A: 

Simply redraw the sprite, with a rotate transformation. Transformations in HTML Canvas 2D Context

The canvas is just an off-screen buffer. It won't be cleared unless you tell it to, and nothing else will be changed unless you tell it to.

There's a bunch of different situations in which you may have to redraw the area of or around the sprite. Otherwise, you'll get a ghosting effect where part of the old sprite is still visible below the new drawing, or other drawings become obscured. Some reasons are:

  • Your sprite is partially transparent,
  • Your sprite is partially translucent,
  • Other drawings are made on top of your sprite,
  • Your sprite is non-rectangular,
  • You're doing flips that are not multiples of 90 degrees.

So that might be a bit more work, and there are several different approaches to doing that. You could simply redraw the entire scene, or just the specific objects at the location, perhaps using the clip method.

A completely different direction might be to use other HTML elements, img or div, with absolute positioning and CSS3 transformations. That's basically a bit of trickery to delegate the rendering of your scene to the browser.

Shtééf
I guess perhaps I'm misunderstanding rotate. I thought rotate (from the examples I have seen) seems to rotate everything on the canvas. Will this approach work for a single image without rotating the others? I'll try it.
Dr.HappyPants
`rotate` changes the transformation matrix, which is part of the canvas state. Everything after the rotate is then affected by the rotation. You can use `save` and `restore` to make a snapshot of canvas state before your rotation, and then restore that snapshot when you're done drawing. You usually apply `save` and `restore` like a block around your drawing operations.
Shtééf
Hmm, I've thought about this a bit and it seems like the wrong way to do this for a game. The idea that the rotation is applied to the canvas rather then then image itself seems as though it will be horribly unwieldy. For example, for each sprite that changes direction I will have to save, rotate, draw, restore. Perhaps I'm missing something? In actionscript, one can create a matrix, apply it to a bitmap and be done.
Dr.HappyPants
For example:myBitmap = BitmapData.loadBitmap("test")//flip vertical matrixflipVerticalMatrix = new Matrix()flipVerticalMatrix.scale(1,-1)flipVerticalMatrix.translate(0,myBitmap.height)//flip horizontal matrixflipHorizontalMatrix = new Matrix()flipHorizontalMatrix.scale(-1,1)flipHorizontalMatrix.translate(myBitmap.width,0)flippedBitmap = new BitmapData(myBitmap.width,myBitmap.height,false,0x FFCC00)flippedBitmap.draw(myBitmap,flipHorizontalMatrix)this.attachBitmap(flippedBitmap,1)
Dr.HappyPants
A: 

While I appreciate Shtééf's answer, after a bit of research, I have found that rotating the canvas you are actually using to display doesn't seem to be ideal. The saving, rotating and restoring while trying to create complex animations (aka think Street Fighter 2 not astroids) causes the canvas to flicker in event Chrome.

I have found however a usable strategy. The idea here is that you actually create two canvases, one will be for your game and the other will be a backbuffer of sorts and it will be used to rotate or scale your sprites. You essentially transform the backbuffer canvas, draw the image in question, then transfer it to your main canvas and restore (or not) the backbuffer. In this manner, you only rotate the hidden canvas and only effect the sprite in question not the entire game board.

The code looks something like this (work in progress):

mainContext.clearRect(lastXpos, lastYpos, lastWidth, lastHeight);
backContext.clearRect(0, 0, lastWidth, lastHeight);
lastXpos = xpos;
lastYpos = ypos;
lastWidth = width;
lastHeight = height;


backContext.save();

//check the direction of the sprite
//turn the backContext to this direction   
//SPRITE_INVERTED==-1
if (spriteXDirection == SPRITE_INVERTED || spriteYDirection == SPRITE_INVERTED) 
{
    var horScale = 0;
    var verScale = 0;

    if (spriteXDirection == SPRITE_INVERTED)
    {
    horScale = width;
    }

    if (spriteYDirection == SPRITE_INVERTED)
    {
    verScale = height;
    }


    backContext.translate(horScale, verScale);
    backContext.scale(spriteXDirection, spriteYDirection);


}


//draw the sprite not we always use 0,0 for top/left
backContext.drawImage(animations[currentPlay].sheet,
                animationX,
                animationY,
                width,
                height, 0, 0, width, height);

//Get the image data from the back context
var image = backContext.getImageData(0, 0, width, height);


//flip the back context back to center - or not, I haven't decided how to optimize this yet.
backContext.restore();


//transfer the image to your main context
mainContext.putImageData(image, xpos, ypos);

This has saved me a lot of headaches in understanding how to translate my sprites without having everything on my gameboard move all over the place. It also seems to perform better then modifying the main context.

Dr.HappyPants
Disclaimer: I am a total amateur at both game dev + html5 - so please feel from to comment or correct me and I will happily change this answer.
Dr.HappyPants
In my experience, transformations have not caused flickering. Besides that, applying a transformation to the *entire canvas* might seem big, but it's really just a state variable, a handful of numbers. Whatever floats your boat, I suppose. :)
Shtééf