如何在HTML5 Canvas中水平翻转精灵?

3
我正在尝试使用纹理集制作动画:

enter image description here

当角色面向右侧时,它的表现很好。我尝试水平翻转它,但位置不正确:

enter image description here

这是我的当前代码:

<canvas id="c" width="200" height="100" style="background: #000"></canvas>

var metaData = [
        {x:0,y:0,w:35,h:38,offsetX:3,offsetY:9},
        {x:37,y:0,w:31,h:37,offsetX:6,offsetY:10},
        {x:70,y:0,w:65,h:47,offsetX:0,offsetY:1},
        {x:137,y:0,w:65,h:47,offsetX:0,offsetY:1},
        {x:204,y:0,w:61,h:46,offsetX:1,offsetY:1},
        {x:267,y:0,w:42,h:46,offsetX:1,offsetY:1},
        {x:311,y:0,w:43,h:44,offsetX:1,offsetY:3},
        {x:356,y:0,w:38,h:37,offsetX:6,offsetY:10},
        {x:396,y:0,w:35,h:34,offsetX:6,offsetY:13},
        {x:433,y:0,w:33,h:37,offsetX:7,offsetY:10},
        {x:468,y:0,w:36,h:40,offsetX:5,offsetY:7},
        {x:506,y:0,w:34,h:39,offsetX:6,offsetY:8}
],
dx = 0, //position x
dy = 0, //position y
index = 0; //frame index

(function draw() {
    context2D.clearRect(0,0,c.width,c.height);

    var cur = metaData[index];

    if(facingRight) {
        context2D.drawImage(
            img,
            cur.x, cur.y,
            cur.w, cur.h,
            dx + cur.offsetX, dy + cur.offsetY,
            cur.w, cur.h
        );
    } else {
        context2D.save();
        context2D.translate(cur.w,0);
        context2D.scale(-1,1);
        context2D.drawImage(
            img,
            cur.x, cur.y,
            cur.w, cur.h,
            dx, dy + cur.offsetY,
            cur.w, cur.h
        );
        context2D.restore();
    }

    index = ++index % metaData.length;

    setTimeout(draw,100);
})();

我使用 scale(-1,1) 来翻转精灵,但我不知道如何使它保持在同一位置,就像面向右侧一样。我应该修正偏移值吗?

请帮忙,非常感谢 :)


@Kaiido 我让它们变成了相同的尺寸,这个纹理图集是由TexturePacker打包的,它生成了一个JSON文件,我使用这些信息来制作元数据。 - undefined000
我不知道这个TexturePacker,但这些不是纹理,而是精灵。为了方便使用,您需要将它们对齐,并设置它们为相同大小的不可见框。目前它们根本没有对齐。它们甚至没有在y轴上对齐。(角色精灵最好底部对齐) - Kaiido
@Kaiido 我在 PhotoShop 中对它们进行了对齐,并使用 TexturePacker 进行了打包,它给我提供了正确的 JSON 文件位置,如下所示: { "filename": "00.png", "frame": {"x":0,"y":0,"w":35,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":9,"w":35,"h":38}, "sourceSize": {"w":65,"h":49}, "pivot": {"x":0.5,"y":1} },每个帧的大小为 65X49,在面对右侧运动时效果良好,但翻转时会出现问题。 - undefined000
1个回答

3

如果你使用图像编辑器,将所有的精灵都适配到同一大小的区域中,整个精灵表的代码会更简单(只需要一个“w”、一个“h”、“x=w*i”等)。这样可以节省很多时间,而不需要奇怪的元数据。

例如,你最大的精灵约为70像素宽,因此应将所有其他精灵放入这样的框中:

empty sprite-sheet with border

现在,似乎所有的精灵都共享前脚的位置。因此,你应该将其用作对齐所有精灵的锚点。

类似于这样:

sprite-sheet with sprites and border 请注意,对于所有精灵,前脚始终相对于自己的框处于相同的位置。

现在,编写这个精灵表的动画甚至翻转它都非常容易:

const ssheet = new Image();
ssheet.src = 'https://istack.dev59.com/kXKIc.webp'; // same without borders
ssheet.onload = startSheetAnim;

function startSheetAnim(evt) {
  const ctx = c.getContext('2d');
  const h = 49;
  const w = 70;
  let i = 0;

  function anim() {
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.clearRect(0, 0, c.width, c.height);
    ctx.drawImage(ssheet,
      (i * w), 1, w, h,
      0, 0, w, h
    );
    // scale (flip-x) and translate
    ctx.setTransform(-1, 0, 0, 1, w * 2, 0);
    ctx.drawImage(ssheet,
      (i * w), 1, w, h,
      0, 0, w, h
    );

    i = (i + 1) % 12
    setTimeout(anim, 100);
  }
  anim();
}
<canvas id="c"></canvas>


谢谢您提供这么详细的答案!但我还有一个问题,我从一些网站获取精灵图,它们总是打包精灵并裁剪透明像素,这个精灵表 减少了不必要的透明区域以提高性能,我可以直接使用它还是需要重新定位它们? - undefined000
在我看来,重新定位它们会更容易...我不知道为什么他们一开始就这样打包。如果是为了图像大小,那么他们做错了:我的精灵表比你问题中的那个压缩更好,因此更轻。空像素通常不会太重,而且你可以避免使用JSON。 - Kaiido
不,不是以敏感的方式。对于较少的CPU使用量,您可能会有大致相同的GPU影响。 - Kaiido
非常感谢!我认为我可以从你的经验中学到很多 :) - undefined000
@Kaiido 我尝试使用代码 ctx.setTransform(-1, 0, 0, 1, w - offset, 0); 来重叠他们脚的位置,使其翻转,就像这样,但我不知道如何计算offset,最终我使用了**-15**来使其正常工作。你能再次帮助我吗?提前感谢。 - undefined000
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接