2015-11-16 160 views
4

我需要編寫一個非常簡單的2D HTML帆布遊戲,包含一個角色和幾面牆。地圖(俯視圖)是一個多維數組(1=wallsHTML Canvas遊戲:二維碰撞檢測

map = [ 
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], 
    [1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], 
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], 
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0], 
    [1,0,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1], 
    [1,0,0,1,0,0,0,0,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,1], 
    [1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1], 
    [1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], 
    [1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1], 
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1], 
    [1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1], 
    [1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1], 
    [0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,1], 
    [1,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,1], 
    [1,1,1,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,1], 
    [1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,1,0,0,1,1], 
    [1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1], 
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] 
]; 

的字符不應該能夠行走在牆上......所以他只能遊走在「0」。我已經獲得了地圖渲染和角色的行走部分,但我無法弄清楚如何檢查碰撞。

一個非常簡單的版本可以在JSBin找到。您可以使用箭頭鍵或WASD移動(黑色方塊)。

我已經嘗試過通過使用像這樣做的一個非常簡單的碰撞檢測:

function checkCollision(x, y) { 
    if (map[ Math.round(x) ][ Math.round(y) ] !== 0) { 
    return true; // Collision 
    } 
    return false; 
} 

但是這並不完全工作(見JSBin)。與Math.round字符和牆重疊...如果我使用Math.ceilMath.floor它更糟糕。

有沒有什麼辦法可以改善這種「碰撞檢測」,讓角色無法越過紅牆?

+0

的JavaScript在吸用大數字的工作... – kappaallday

回答

1

您的玩家可能隨時在地圖上重疊四格。所以你需要檢查所有四個方塊的碰撞(對應於角色的左上角,右上角,左下角,右下角)。爲了讓它在走廊上「擠壓」(假定字符的大小與拼貼的大小相同),您可能還需要將其調整一個或兩個像素(因此以下爲1/config.tileSize)。

function checkCollision(x, y) { 
    var x1 = Math.floor(x + 1/config.tileSize), 
     y1 = Math.floor(y + 1/config.tileSize), 
     x2 = Math.floor(x + 1 - 1/config.tileSize), 
     y2 = Math.floor(y + 1 - 1/config.tileSize); 
    if (map[y1][x1] !== 0 || map[y2][x1] !== 0 || map[y1][x2] !== 0 || 
     map[y2][x2] !== 0) { 
    return true; // Collision 
    } 
    return false; 
} 

看到這個版本的JSBin

+0

謝謝,這很好。我明白了你爲什麼要檢查所有四個角落,但我還不確定這個'1/config.tileSize'。這應該是一個'神奇的數字'或什麼使它工作得很好? – Daniel

+1

它只相當於1個像素。這部分地糾正了@ 6502提到的可能的舍入錯誤。如果玩家處於[0.999,0.9999]並且[0,0]處有牆,則可能不想觸發碰撞。不幸的是,我們不知道玩家的確切位置。存儲像素座標而不是玩家的地圖座標可能會更好,以避免此問題。 – Stuart

+0

嘗試刪除'1/config.tileSize',你會發現玩家有時會被困在角落。 – Stuart

1

我的答案有點不同。而不是有一個二維數組,我會使用一個簡單的數組並計算行(如果你甚至需要它們)。現在您只需說是演員的位置的一個索引查詢的地圖數組:

var map = [0,0,0,1,1,0,0,1,1,0,0,...]; 

//heres how to calculate the x/y if you need it for something else 
var pos_x = position % NUM_OF_ROWS; 
var pos_y = Math.floor(position/NUM_OF_ROWS); 

//for collisions now you just check the value of the array at that index 
leftKey.addEventListener('keypress', function() { 
    test(position - 1); 
}); 
rightKey.addEventListener('keypress', function() { 
    test(position + 1); 
}); 
upKey.addEventListener('keypress', function() { 
    test(position + NUM_OF_ROWS); 
}); 
downKey.addEventListener('keypress', function() { 
    test(position - NUM_OF_ROWS); 
}); 

function test(n) { 
    if (map[n] === 0) { 
     //if there's no collision, update the position. 
     position = n; 
    } else { 
     console.log('Collided!'); 
    } 
} 
1

您需要考慮兩個方面:第一是碰撞檢測,二是響應。我們從檢測開始。你正在檢查一個點,但實際上你有一個瓷磚尺寸,所以你必須考慮厚度。角色的座標以及您的圖塊的座標是左上角。僅比較左上角是不夠的,您還必須檢查其他角落。例如,玩家廣場的右側是charX + config.tileSize

第二個方面是碰撞響應。這裏可以使用的最簡單的機制是檢查角色碰撞的下一個位置,並且只有在沒有時才移動角色。你應該最好單獨檢查兩個軸,讓角色沿着牆壁「滑動」(否則它會卡在牆壁中,並沿着牆壁向對角線移動)。

+0

謝謝,但我認爲在我的情況下,玩家廣場的右側是'charX + 1',不是嗎? – Daniel

2

有幾個問題:

首先,你應該避免使用0.1作爲座標的一步,因爲它是一個「壞號」浮點(這是週期性的時用二進制表示)。更好的是0.125(1/8)。增加/減少1/8將保證您的號碼始終保持爲1/8的精確倍數,不會累積任何錯誤。

你應該定義一個「OK(X,Y)」功能檢查,如果(可能是小數)點(x,y)是有效的還是牆內......一個簡單的實現可能是:

return map[y|0][x|0] == 0; // <expr>|0 is used to convert to integer 

最後,你應該計算new_charXnew_charY和只接受來自移動charXcharY到如果所有的四點新的位置:

(new_charX+s, new_charY+s) 
(new_charX+1-s, new_charY+s) 
(new_charX+s, new_charY+1-s) 
(new_charX+1-s, new_charY+1-s) 

s = 1/16(即移動半步)有效。

例子:http://jsbin.com/wipimidije/edit?js,output

+0

謝謝。我首先選擇了0,1,因爲它加起來就像'1'和'2'這樣的整數。我認爲在我的第一次測試中我有類似0,3的東西,但顯然總是造成0,1的差距。由於0,125也加起來我會改變,雖然老實說,我不是那麼偉大的數學得到的區別:) – Daniel

+1

@demrks:問題是,計算機使用二進制系統,而不是一個小數。 '0.1'十進制看起來很好,但是在二進制中有無限的位,因此近似於計算機。像1/8,1/16,1/32,1/64這樣的數字在用二進制表示時是有限的,因此它們可以精確地表示,並且在重複添加時不會有舍入問題。唯一可以精確表示的浮點數是N/D,其中N和D是整數,D是精確的2(1,2,4,8,16,32,64,128,256,512,...)的冪。 ..) – 6502

0

首先,我會改變磚「值」如果你改變了角色行走在1的,但在0的,你可以通過鍵入 如果(瓦[X檢查,如果瓷磚是適合步行] [Y])... 然後,我會先計算下一個位置,然後使移動如果玩家能夠...

Var nextpos = new position; 

If(KEYLEFT){ 
    Nextpos.x = currpos - 1; 
} 

If(nextpos > 0 && nextpos < mapsize && tile[nextpos.x][nextpos.y]) 
    Player.pos = nextpos;