我有一个精灵,正在使用普通的精灵表格blitting在html画布上进行动画。在某些关键事件上,我想改变精灵的方向(即翻转它或将其旋转180度),而不改变画布上的其他任何东西。
有人知道如何做到这一点吗?
我遇到了一个游戏问题,我的游戏有向上、向下和向左的动画,但没有向右的动画。所以我需要翻转向左的动画来绘制向右的动画。
对于每个精灵,我会跟踪它在画布中的当前顶部和左侧位置,以及精灵表中每个单元格的顶部和左侧位置。
我曾经看过之前的答案,其中简单的水平翻转只是转换原点并翻转(反比例缩放)轴,但这不考虑到翻转原点会弄乱精灵的注册点(即其在画布上的顶部和左侧位置)。
这个问题表现为精灵被正确镜像,但其位置偏移了精灵的宽度。我通过考虑精灵的宽度来解决了这个问题。请注意,我正在使用CanvasRenderingContext2D.prototype.drawImage和9个参数进行绘图,因为我正在从精灵表中切割精灵:
// check if we need to flip image (special case for right movement)
if(sprite.translated){
context.save();
context.translate(context.canvas.width, 0);
context.scale(-1, 1);
context.drawImage(sprite.sheet,
cell.left,
cell.top,
sprite.width,
sprite.height,
// secret sauce: change the destination's X registration point
context.canvas.width - sprite.left - sprite.width,
sprite.top,
sprite.width, sprite.height);
context.restore();
} else {
// Assumes cells are all the same width and height, set in sprite
context.drawImage(sprite.sheet, cell.left, cell.top, sprite.width,
sprite.height, sprite.left, sprite.top, sprite.width, sprite.height);
}
虽然我很感激Shtééf的回答,但经过一番研究,我发现旋转你实际使用来显示的画布似乎并不理想。在尝试创建复杂动画(比如Street Fighter 2而不是astroids)时,保存、旋转和恢复会导致画布甚至在Chrome中闪烁。
然而,我找到了一个可用的策略。这里的想法是你实际上创建两个画布,一个用于游戏,另一个则是某种后备缓冲区,它将用于旋转或缩放你的精灵。你基本上转换后备缓冲区画布,绘制相关图像,然后将其传输到主画布并恢复(或不恢复)后备缓冲区。通过这种方式,你只旋转隐藏的画布,并且只影响相关精灵而不是整个游戏板。
代码看起来像这样(正在进行中):
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);
这让我在理解如何在没有使游戏板上的所有内容移动的情况下翻译我的精灵时避免了很多头痛。它似乎也比修改主上下文执行得更好。
简单地重新绘制精灵,使用rotate
转换。 HTML Canvas 2D上下文中的变换
画布只是一个屏幕外缓冲区。除非您告诉它清除,否则它不会被清除,并且除非您告诉它,否则不会有任何其他更改。
有许多不同的情况可能需要重新绘制精灵周围或周围区域。否则,您将获得一种类似幽灵的效果,在新绘制下部分旧精灵仍然可见,或者其他绘图被遮挡。一些原因是:
因此,这可能需要更多的工作,并且有几种不同的方法可以实现。您可以简单地重新绘制整个场景,或仅在该位置重新绘制特定对象,也许使用clip
方法。
img
或div
,并使用绝对定位和CSS3变换。这基本上是一种诡计,将您场景的渲染委托给浏览器。rotate
改变了转换矩阵,这是画布状态的一部分。旋转后的所有内容都会受到影响。您可以使用 save
和 restore
在旋转之前对画布状态进行快照,然后在绘制完成后恢复该快照。通常,您会像块一样应用 save
和 restore
来围绕您的绘图操作。 - Stéphan Kochenctx.save(); // save current state
ctx.rotate(Math.PI); // rotate
ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
ctx.restore(); // restore original states (no rotation etc)