Javascript 大画布上的动画';可视区域';

Javascript 大画布上的动画';可视区域';,javascript,html5-canvas,Javascript,Html5 Canvas,问题的标题可能很模糊。基本上,想象一个在画布上构建的赛车游戏。该轨迹占用10000 x 10000像素的屏幕空间。但是,浏览器窗口为500 x 500像素。汽车应在浏览器中居中,10000 x 10000画布的“可视”区域将发生变化。否则,汽车就会在消失时驶离边缘 这种技术有名字吗 实现这一点的基本原则是什么?如果汽车应保持在相同的位置(相对于画布的位置),则不应移动汽车。相反,将背景图片/轨迹/贴图移到另一侧 让你的眼睛认为汽车向右移动可以通过将汽车向右移动或将地图向左移动来实现。第二个选项似

问题的标题可能很模糊。基本上,想象一个在画布上构建的赛车游戏。该轨迹占用10000 x 10000像素的屏幕空间。但是,浏览器窗口为500 x 500像素。汽车应在浏览器中居中,10000 x 10000画布的“可视”区域将发生变化。否则,汽车就会在消失时驶离边缘

这种技术有名字吗


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

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

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

这是一个从头开始的快速演示:

归根结底是改变地图的位置:

$("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();
});

如果您正在寻找一种在地图无法进一步移动时实际移动汽车的方法(因为您驾驶汽车靠近地图的一侧),那么您必须延长夹紧时间,并跟踪汽车移动的时间和距离:


我的第一个游戏是一个赛车游戏,我移动了背景而不是汽车,尽管我想现在我有理由这么做。。。我只是不太清楚

您需要了解一些技巧才能很好地实现这一目标

  • 平铺背景。你需要把你的赛道做成小块的瓷砖。要绘制10000 x 10000像素是100MPix图像,通常这样的图像将有32位深度(4字节),这将在内存中达到400MB。像PNG、JPEG这样的压缩对你没有帮助,因为它们是用来存储和传输图像的。如果不解压缩,则无法将其渲染到画布

  • 沿着你的轨道移动汽车。没有什么比在车下移动背景更糟糕的了。如果你需要在游戏中添加更多的功能,比如AI汽车。。。现在他们必须沿着地图移动,要实现汽车碰撞,你需要做一些不难但奇怪的空间变换

  • 添加相机实体。相机需要有位置和视口大小(这是画布的大小)。相机将决定你的比赛成败。这是一个实体,将给你在游戏中的速度感。。。你可以在碰撞时晃动摄像机,如果你有漂移,如果你的游戏中摄像机可以滑动通过所需的位置并回到汽车中心,等等。当然,最重要的是跟踪汽车。我可以给你们一些简单的建议,就是不要把车放在摄像机的死点。把车往后一点,这样你就可以看到你前面的东西了。汽车移动得越快,相机的偏移量就越大。此外,您不能只计算相机的位置,而是计算所需位置,然后每帧缓慢地将当前相机位置移动到所需位置

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

  • 两件事:

    画布转换方法 首先是(以及
    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位。如果你的世界不是用像素而是用“世界单位”来衡量的,那么当你的世界的总大小和最小特征尺度导致浮点不准确时,你会遇到问题。在这种情况下,你需要做额外的工作来跟踪事物……但你可能会我仍然想使用画布转换方法!

    我仍然不确定要问什么。你的意思是根据视口区域进行调整?此外,我无法想象1x10^8像素画布上的FPS效果会非常好。我想你不是想将汽车向右移动,而是想将地图向左移动。
                                  // 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)
    
    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();
    
    };