一个大画布怎么能有一个动画'可视区域'

How can a large canvas have an animated 'viewable area'

本文关键字:有一个 动画 区域 可视 怎么能 一个      更新时间:2023-09-26

题目可能不明确。基本上,想象一款基于画布的赛车游戏。轨道占用了10,000 x 10,000像素的屏幕空间。然而,浏览器窗口是500 x 500像素。汽车应该在浏览器中保持居中,10,000 x 10,000画布的"可见"区域将发生变化。否则,汽车就会从消失的边缘开下去。

这个技术有名字吗?

实现这一目标的基本原则是什么?

如果汽车应该保持在相同的位置(相对于画布的位置),那么您不应该移动汽车。相反,将背景图片/轨道/地图移动到另一侧

让你的眼睛认为汽车向右移动可以通过将汽车移动到右侧,或者将地图移动到左侧来实现。第二个选项似乎是你想要的,因为汽车不会移动,而可视区域(即地图)会移动。

这是一个从头开始的快速演示:http://jsfiddle.net/vXsqM/.

它可以反过来改变地图的位置:

$("body").on("keydown", function(e) {
    if(e.which === 37) pos.x += speed; // left key, so move map to the right
    if(e.which === 38) pos.y += speed;
    if(e.which === 39) pos.x -= speed;
    if(e.which === 40) pos.y -= speed;
    // make sure you can't move the map too far.
    // clamp does: if x < -250 return -250
    //             if x >    0 return    0
    //             else it's allowed, so just return x
    pos.x = clamp(pos.x, -250, 0);
    pos.y = clamp(pos.y, -250, 0);
    draw();
});

你可以用保存的位置绘制地图:

ctx.drawImage(img, pos.x, pos.y);

如果你正在寻找一种方法来实际移动汽车,当地图不能移动任何进一步(因为你驾驶汽车靠近地图的一边),那么你必须延长夹紧,并跟踪汽车何时应该移动和多远:http://jsfiddle.net/vXsqM/1/.

                              // for x coordinate:
function clamp2(x, y, a, b) { // x = car x, y = map x, a = min map x, b = max map x
    return y > b ? -y : y < a ? a - y : x;
}

位置夹紧变得有点复杂:

// calculate how much car should be moved
posCar.x = clamp2(posCar.x, posMap.x, -250, 0);
posCar.y = clamp2(posCar.y, posMap.y, -250, 0);
// also don't allow the car to be moved off the map
posCar.x = clamp(posCar.x, -100, 100);
posCar.y = clamp(posCar.y, -100, 100);
// calculate where the map should be drawn
posMapReal.x = clamp(posMap.x, -250, 0);
posMapReal.y = clamp(posMap.y, -250, 0);
// keep track of where the map virtually is, to calculate car position
posMap.x = clamp(posMap.x, -250 - 100, 0 + 100);
posMap.y = clamp(posMap.y, -250 - 100, 0 + 100);
// the 100 is because the car (circle in demo) has a radius of 25 and can
// be moved max 100 pixels to the left and right (it then hits the side)

两件事

画布转换方法

首先,画布转换方法(以及context.save()context.restore()是您的朋友,将大大简化查看大"世界"的一部分所需的数学。如果您使用这种方法,您可以通过指定可见的世界部分和您想要绘制的所有内容的世界坐标来获得所需的行为。

这不是做事情的唯一方法*,但是转换方法意味着正是针对这类问题。你可以使用它们,或者你可以通过手动跟踪你的背景应该画在哪里来重新发明它们,等等。

下面是一个如何使用它们的例子,改编自我的一个项目:

function(outer, inner, ctx, drawFunction) {
  //Save state so we can return to a clean transform matrix.
  ctx.save();
  //Clip so that we cannot draw outside of rectangle defined by `outer`
  ctx.beginPath();
  ctx.moveTo(outer.left, outer.top);
  ctx.lineTo(outer.right, outer.top);
  ctx.lineTo(outer.right, outer.bottom);
  ctx.lineTo(outer.left, outer.bottom);
  ctx.closePath();
  //draw a border before clipping so we can see our viewport
  ctx.stroke();
  ctx.clip();
  //transform the canvas so that the rectangle defined by `inner` fills the
  //rectangle defined by `outer`.
  var ratioWidth = (outer.right - outer.left) / (inner.right - inner.left);
  var ratioHeight = (outer.bottom - outer.top) / (inner.bottom - inner.top);
  ctx.translate(outer.left, outer.top);
  ctx.scale(ratioWidth, ratioHeight);
  ctx.translate(-inner.left, -inner.top);
  //here I assume that your drawing code is a function that takes the context
  //and draws your world into it.  For performance reasons, you should
  //probably pass `inner` as an argument too; if your draw function knows what
  //portion of the world it is drawing, it can ignore things outside of that
  //region.
  drawFunction(ctx);
  //go back to the previous canvas state.
  ctx.restore();
};

如果你很聪明,你可以用它来创建不同大小的多个视口,图中图等,并放大和缩小东西。

第二,正如我在代码中所评论的那样,你应该确保你的绘图代码知道你的大"世界"的哪一部分是可见的,这样你就不会做很多工作试图画出不可见的东西。

画布转换方法就是用来解决这类问题的。使用他们!

*如果你的世界太大,以至于它的坐标不能适合一个合适的整数,你可能会遇到问题。当您的世界在任何维度上超过十亿(10^9)或长万亿(10^18)像素时,您就会遇到这个问题,具体取决于整数是32位还是64位。如果你的世界不是以像素来衡量,而是以"世界单位"来衡量,那么当你的世界的总大小和最小特征规模导致浮点不准确性时,你就会遇到问题。在这种情况下,你需要做额外的工作来跟踪事情……但是您可能仍然希望使用画布转换方法!

我的第一款游戏是一款赛车游戏,我移动的是背景而不是汽车,尽管现在我想我这么做是有原因的……我只是不懂。

要做到这一点,你需要知道一些技巧。

  1. 平铺的背景。你需要用平铺的小块来制作你的轨迹。要绘制10,000 x 10,000像素是100MPix的图像,通常这样的图像将具有32位深度(4字节),这将在内存中最终为400MB。像PNG、JPEG这样的压缩文件对你没有帮助,因为它们是用来存储和传输图像的。它们不能在不解压的情况下渲染到画布上。

  2. 沿着你的轨道移动汽车。没有什么比移动BG在汽车下面更糟糕的了。如果你需要在游戏中添加更多功能,如AI汽车……现在他们将不得不沿着地图移动,为了实现汽车碰撞,你需要做一些不困难但奇怪的空间转换。

  3. 添加摄像机实体。相机需要有位置和视口大小(这是你的画布的大小)。摄像机将决定你游戏的成败。这是能够让你在游戏中感受到速度感的实体。你可以为碰撞设置镜头抖动,如果你的游戏设置了镜头漂移,如果你的游戏设置了镜头可以滑动到你想要的位置,然后回到车的中心,等等。当然最重要的是追踪那辆车。我可以给你一些简单的建议,就是不要把车放在相机的正中央。把车开到后面一点这样你就能看到你前面的东西。汽车移动得越快,你就越应该偏移相机。此外,你不能只计算相机的位置,而是计算所需的位置,并缓慢地每帧移动当前相机的位置到所需的位置。

  4. 现在,当你有相机和一个大的平铺地图时,当你绘制平铺图时,你必须减去相机位置。您还可以计算哪些磁贴不可见并跳过它们。这种技术可以让你用更大的地图扩展游戏,或者你可以在没有加载所有贴图的情况下流式传输地图,并在后台(AJAX)提前加载即将可见的内容。

相关文章: