【问题标题】:How can I achieve a wraparound effect in an arcade-like game?如何在类似街机的游戏中实现环绕效果?
【发布时间】:2017-01-29 02:07:15
【问题描述】:

我正在开发游戏Spacewar!的克隆。在游戏中,船只可以行驶到地图的边缘并绕到另一边,有时会分成两半,屏幕两侧各有一半(当船只进入角落时,它们会分成四部分)。你可以玩游戏here

现在我正在使用模数运算符将div 包裹在屏幕周围,但它并没有像我希望的那样将块分成两半。有什么方法可以在 JavaScript 或 jQuery 中实现吗?

【问题讨论】:

    标签: javascript jquery animation graphics 2d-games


    【解决方案1】:

    这取决于你如何渲染船只。我假设您正在使用精灵。让:

    • xs,ys 为目标屏幕分辨率
    • txs,tys成为精灵分辨率
    • x0,y0 是精灵位置(左上角)
    • x-axis 向右,y-axis 向下,(0,0) 是展位精灵和屏幕的左上角

    我知道有两种基本方法:

    1. 直接在精灵渲染中包裹坐标。

      这是最简单的方法,但您需要访问精灵渲染。找到它们应该看起来像这样的渲染循环:

      for (y=y0,ty=0;ty<tys;ty++,y++)
       for (x=x0,tx=0;tx<txs;tx++,x++)
        {
        color=sprite[ty][tx];
        if (color!=color_transparent) screen[y][x]=color;
        }
      

      所以只需添加 x,y 包装:

      while (x0>=xs) x0-=xs; // just normalize start position
      while (x0<  0) x0+=xs;
      while (y0>=ys) y0-=ys;
      while (y0<  0) y0+=ys;
      for (y=y0,ty=0;ty<tys;ty++,y++)
       {
       if (y>=ys) y=0; // wrap y
       for (x=x0,tx=0;tx<txs;tx++,x++)
        {
        if (x>=xs) x=0; // wrap x
        color=sprite[ty][tx];
        if (color!=color_transparent) screen[y][x]=color;
        }
       }
      

      渲染循环中的这些粗略条件正在使事情变慢一点。因此,您可以通过将代码复制到 4 个实例中来优化,每个实例在其自己的一组坐标上,这样ifs 就不再是循环内部了。

    2. 分割包装的精灵

      这种方法更快一些(在渲染循环中没有额外的ifs),也不需要对渲染代码进行任何篡改。这个想法是像往常一样渲染未包装的精灵,如果检测到包装,则在包装位置渲染 2/4 精灵副本

      所以在代码中应该是这样的:

      // just normalize start position
      while (x0>=xs) x0-=xs;
      while (x0<  0) x0+=xs;
      while (y0>=ys) y0-=ys;
      while (y0<  0) y0+=ys;
      render_sprite(x0,y0,sprite);
      // compute copies coordinates
      x1=x0; if (x1+txs>xs) x1-=xs;
      y1=y0; if (y1+tys>ys) y1-=ys;
      // render copies
      if  (x0!=x1)            render_sprite(x1,y0,sprite);
      if            (y0!=y1)  render_sprite(x0,y1,sprite);
      if ((x0!=x1)&&(y0!=y1)) render_sprite(x1,y1,sprite);
      

      顺便说一句。如果您正在考虑使用这种方法,可以在几何 GLSL 着色器中完成。

    【讨论】:

      【解决方案2】:

      包装精灵

      您提供的有趣链接。他们已经实现了完整的 CPU 仿真并运行了用汇编编写的游戏。

      改进模数

      无论如何,如果您使用画布来渲染精灵(图像),最简单的方法是使用简单的模数进行修改以处理负值。

      使用负值时,正常的模数会失效。

      x = 100 % 200; // 100
      x = 300 % 200; // 100 
      x = -100 % 200; // -100  the wrong sign should be 100
      x = -50 % 200;  // -50 wrong sign wrong direction should be 150
      

      您需要模数始终以正确的方向返回正值。要处理负数,请进行两次模运算,第一次将在您想要的范围内,但在 +/- 范围内。然后通过添加范围使负正。然后再做一次模数。

      var range = 200;
      var x = 150;
      x = ((x % range) + range) % range; // result 150
      x = -50;
      x = ((x % range) + range) % range; // result 150 correct.
      

      简单包装

      使用上面的模算法,检查边界并根据需要渲染精灵是一件简单的事情。

      // assuming ctx is canvas context 2D
      // canvas is the canvas.
      // img is a HTMLImageElement
      
      var canW = canvas.width;                               // the canvas size
      var canH = canvas.height;                              
      // draw a sprite that is wrapped around the edges of the canvas
      function drawWrappedSprite(img,x,y){
          x1 = ((x % canW) + canW) % canW;                   // wrap around correcting for negative values
          y1 = ((y % canH) + canH) % canH;
          x2 = x1 + img.width;                               // get right and bottom
          y2 = y1 + img.height;
          ctx.drawImage(img, x1, y1);                        // draw first copy
          if(x2 > canW){                                     // check if touching right side
              ctx.drawImage(img, x1 - canW, y1);             // draw left copy
              if(y2 > canH){                                 // check if touching bottom
                  ctx.drawImage(img, x1 - canW, y1 - canH);  // draw top copy
              }
          }
          if(y2 > canH){
              ctx.drawImage(img, x1 , y1 - canH);            // draw top copy
          }
      }
      

      包装旋转的精灵

      由于游戏中有旋转的精灵,上述功能将不起作用,因为旋转会改变精灵的大小。要处理旋转的精灵,您需要检查它的最大尺寸。这是精灵对角线的长度,可以通过sqrt(width * width + height * height)找到

      假设您希望精灵围绕其中心旋转,您可以通过在 x,y 中心位置减去和添加一半对角线来找到精灵最大范围(顶部、底部、左侧和右侧)。就像第一个函数一样,根据需要进行取模并绘制精灵。

      在某些情况下,即使不可见,也会在另一侧绘制精灵。如果您正在绘制大量精灵(100+),您可能想要获得确切的范围而不是最大范围,但是您必须至少转换精灵的一个角以获得水平和垂直范围。然后只使用这些值而不是函数中的diag

      // assuming ctx is canvas context 2D
      // canvas is the canvas.
      // img is a HTMLImageElement
      
      var canW = canvas.width;                               // the canvas size
      var canH = canvas.height;  
      // draws sprite rotated about its center by r
      function drawWrappedRotatedSprite(img,x,y,r){  // x,y center pos, r rotation in radians
          var diag = Math.sqrt(img.width * img.width + img.height * img.height);  // get diagonal size
          diag /= 2;                                      // half the diagonal
          x1 = (((x - diag) % canW) + canW) % canW;       // get left extent position  and 
          y1 = (((y - diag) % canH) + canH) % canH;       // wrap around correcting for negative values
          var w = - img.width / 2;                        // get image width height
          var h = - img.height / 2                        // offset in rotated space
          x2 = x1 + diag * 2;                             // get right and bottom max extent
          y2 = y1 + diag * 2;
          ctx.setTransform(1,0,0,1,x1 + diag, y1 + diag); // set origin
          ctx.rotate(r);                                  // set rotation
          ctx.drawImage(img, w, h);                      // draw image rotated around its center    
          if(x2 > canW){                                  // check if touching right side
              ctx.setTransform(1,0,0,1,x1 + diag - canW, y1 + diag); // set origin
              ctx.rotate(r);                              // set rotation
              ctx.drawImage(img,w,h);                     // draw image rotated around its center    
              if(y2 > canH){                              // check if touching bottom
                  ctx.setTransform(1,0,0,1,x1 + diag - canW, y1 + diag - canH); // set origin
                  ctx.rotate(r);                          // set rotation
                  ctx.drawImage(img,w,h);                 // draw image rotated around its center    
              }
          }
          if(y2 > canH){
              ctx.setTransform(1,0,0,1,x1 + diag, y1 + diag - canH); // set origin
              ctx.rotate(r);                              // set rotation
              ctx.drawImage(img,w,h);                     // draw top image rotated around its center    
          }
      
          ctx.setTransform(1,0,0,1,0,0); // reset the transform (should only do this after all sprites
                                         // using this function have been drawn 
      }
      

      【讨论】:

      • 对不起,这不是我的游戏。这就是我想做的。
      • @theEpsilon 哦,好吧...那么有趣的链接.. :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-07
      相关资源
      最近更新 更多