簡體   English   中英

如何在類似街機的游戲中實現環繞效果?

[英]How can I achieve a wraparound effect in an arcade-like game?

我正在開發Spacewar游戲的克隆版 在游戲中,船只可以行駛到地圖的邊緣並環繞到另一側,有時會分成兩半,在屏幕的每一側都是一半(當船只進入彎角時,它們會分成四部分)。 您可以在這里玩游戲。

現在,我正在使用模數運算符將div包裹在屏幕周圍,但是它並沒有像我希望的那樣將塊分割成兩半。 有什么辦法可以在JavaScript或jQuery中實現?

這取決於您如何渲染飛船。 我假設您正在使用精靈。 讓:

  • xs,ys是目標屏幕分辨率
  • txs,tys是精靈分辨率
  • x0,y0是精靈位置(左上角)
  • x軸向右移動, y軸向下移動, (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個實例(每個實例均基於其自己的坐標集)進行優化,以便if不再位於循環內。

  2. 分割包裹的精靈

    這種方法要快一些( if在渲染循環中為s,則不需要額外的操作),並且也不需要篡改渲染代碼。 這個想法是照常渲染非包裝的精靈,如果檢測到包裝,則在包裝的位置渲染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着色器中完成。

包裝精靈

您提供的有趣鏈接。 他們已經實現了完整的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)找到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 
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM