假設在一個物理世界,不希望兩個同類實體發生碰撞,那麼
local begin_contact_callback = function(fixture_a, fixture_b)
local entity_a_type = fixture_a:getUserData()
local entity_b_type = fixture_b:getUserData()
-- 如果碰撞的兩個實體不同
if entity_a_type ~= entity_b_type then
--
end
end
但是如果新加了可互動元素,如一種道具,只能跟玩家實體碰撞,那麼
local begin_contact_callback = function(fixture_a, fixture_b)
local a = fixture_a:getUserData()
local b = fixture_b:getUserData()
if (a == 'powerup' and b == 'player') or (a == 'player' and b == 'powerup') then
--
elseif a ~= b and a ~= 'powerup' and b~= 'powerup' then
--
end
end
如果再加上其他東西,比如只有玩家可以推動的方塊,程式碼量會飛速膨脹
假設遊戲已經有幾十種實體,我們可以根據實體在遊戲內的作用歸為五類,給每種實體系結類別和位掩碼
實體類別 | 類別對應的二進位制 | 位掩碼 |
---|---|---|
場景(如雲、花) | 0000 | 0000 |
玩家 | 0001 | 1110 |
道具 | 0010 | 1001 |
敵人 | 0100 | 1001 |
牆體 | 1000 | 1111 |
比如玩家實體和敵人實體,在函數中我們提取玩家的類別和敵人的位掩碼做位與運算
0001 玩家 類別
1001 敵人 位掩碼
----
0001 不為0 發生碰撞
再舉個例子,敵人碰撞到了道具
0100 敵人 類別
1001 道具 位掩碼
----
0000 為0 不發生碰撞
因此,在上面表格的情況下
注:如果實體不能跟牆體發生碰撞,那麼一旦生成就會直接無限墜落至無底洞
先生成實體的類別二進位制和位掩碼,比如在squre.lua
中,建立了一個實體squre
某種情況下,實體可以屬於多個類別,比如
1011
,這個實體既是牆體也是敵人、玩家,雖然邏輯上是不可能的,但相應的碰撞處理均會發生⭐ 兩個蘋果,第一個蘋果可以只是場景擺件,僅與地形碰撞;第二個蘋果可以是道具,與地形和玩家均可碰撞
square.category = tonumber('0001', 2)
square.mask = tonumber('1110', 2)
square.group = 0
繫結到fixture
上,由於設定了類別和位掩碼,組號填0意味著沒有組別
square.fixture:setFilterData(square.category, square.mask, square.group)
-- Fixture:setCategory, Fixture:setMask or Fixture:setGroupIndex
LOVE 引擎最多支援16位元二進位制的類別和位掩碼,即0000000000000000
⭐ fixture建立時預設類別為1D,位掩碼為65535D,組別均為0
-- entities/block.lua
local world = require 'world'
return function(x, y, width, height, rigidbody, category, bitmask, group)
e = {}
e.body = love.physics.newBody(world, x, y, rigidbody)
e.body:setMass(32)
e.shape = love.physics.newRectangleShape(width, height)
e.fixture = love.physics.newFixture(e.body, e.shape)
e.fixture:setFilterData(category, bitmask, group)
function e:draw()
love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
local x, y = self.body:getPosition()
love.graphics.print({{0, 1, 0}, (category .. '+' .. bitmask) or group}, x, y, nil)
end
return e
end
下面我們定義了兩個類別,分別是001
和010
local entities = {block(400, 400, 300, 10, 'static', '001', '011', 0),
block(400, 300, 50, 50, 'dynamic', '011', '011', 0),
block(400, 200, 40, 40, 'dynamic', '010', '011', 0),
block(400, 100, 30, 30, 'dynamic', '010', '011', 0)}
修改第二個和第三個方塊的位掩碼
local entities = {block(400, 400, 300, 10, 'static', '001', '011', 0),
block(400, 300, 50, 50, 'dynamic', '011', '000', 0),
block(400, 200, 40, 40, 'dynamic', '010', '001', 0),
block(400, 100, 30, 30, 'dynamic', '010', '011', 0)}
我們可以為各個實體設定組別,同組別將直接無視類別與位掩碼的計算結果,同組別且正數總是會碰撞,同組別且負數總不會碰撞。
e.fixture:setFilterData( xx , xx , group)
-- e.fixture:setGroupIndex(group)
考慮如下程式碼
local entities = {block(400, 400, 300, 10, 'static', '001', '011', 0),
block(400, 300, 50, 50, 'dynamic', '010', '001', 0),
block(400, 200, 40, 40, 'dynamic', '010', '001', 0)}
第二個方塊跟第三個方塊不會碰撞,設定組別為1
local entities = {block(400, 400, 300, 10, 'static', '001', '011', 0),
block(400, 300, 50, 50, 'dynamic', '010', '001', 1),
block(400, 200, 40, 40, 'dynamic', '010', '001', 1)}
再考慮如下程式碼,
local entities = {block(400, 400, 300, 10, 'static', '001', '011', 0),
block(400, 300, 50, 50, 'dynamic', '010', '011', 1),
block(400, 200, 40, 40, 'dynamic', '010', '011', 1)}
第二個方塊跟第三個方塊會碰撞,將組別設定為-1,即使算出來要發生碰撞,由於相同組且是負數,永遠也不會碰撞
local entities = {block(400, 400, 300, 10, 'static', '001', '011', 0),
block(400, 300, 50, 50, 'dynamic', '010', '011', -1),
block(400, 200, 40, 40, 'dynamic', '010', '011', -1)}