• [Lua][Love] 有效碰撞处理の类别与位掩码 | fixture:setFilterData


    有效的碰撞处理

    只用IF判断

    假设在一个物理世界,不希望两个同类实体发生碰撞,那么

    lua
    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
    

    但是如果新加了可互动元素,如一种道具,只能跟玩家实体碰撞,那么

    lua
    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

    比如玩家实体和敌人实体,在函数中我们提取玩家的类别和敌人的位掩码做位与运算

    powershell
    0001   玩家 类别
    1001   敌人 位掩码
    ----
    0001   不为0 发生碰撞
    

    再举个例子,敌人碰撞到了道具

    powershell
    0100   敌人 类别
    1001   道具 位掩码
    ----
    00000 不发生碰撞
    

    因此,在上面表格的情况下

    • 场景实体没有被分配类别(要保证某1位为1),不会和任何实体发生碰撞
    • 玩家实体不能相互碰撞,能与道具、敌人、墙体发生碰撞
    • 道具实体能跟墙体、玩家发生碰撞
    • 敌人实体能跟墙体、玩家发生碰撞
    • 墙体实体能跟所有类别发生碰撞(除场景)

    注:如果实体不能跟墙体发生碰撞,那么一旦生成就会直接无限坠落至无底洞

    绑定到实体

    先生成实体的类别二进制和位掩码,比如在squre.lua中,创建了一个实体squre

    某种情况下,实体可以属于多个类别,比如1011,这个实体既是墙体也是敌人、玩家,虽然逻辑上是不可能的,但相应的碰撞处理均会发生

    两个苹果,第一个苹果可以只是场景摆件,仅与地形碰撞;第二个苹果可以是道具,与地形和玩家均可碰撞

    lua
    square.category = tonumber('0001', 2)
    square.mask = tonumber('1110', 2)
    square.group = 0
    

    绑定到fixture上,由于设置了类别和位掩码,组号填0意味着没有组别

    lua
    square.fixture:setFilterData(square.category, square.mask, square.group)
    -- Fixture:setCategory, Fixture:setMask or Fixture:setGroupIndex 
    

    LOVE 引擎最多支持16位二进制的类别和位掩码,即0000000000000000

    ⭐ fixture创建时默认类别为1D,位掩码为65535D,组别均为0

    代码与效果

    lua
    -- 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
    

    下面我们定义了两个类别,分别是001010

    lua
    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)}
    

    修改第二个和第三个方块的位掩码

    lua
    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)}
    

    ⭐ 组别

    我们可以为各个实体设置组别,同组别将直接无视类别与位掩码的计算结果,同组别且正数总是会碰撞,同组别且负数总不会碰撞。

    lua
        e.fixture:setFilterData( xx , xx , group)
        -- e.fixture:setGroupIndex(group)
    

    考虑如下代码

    lua
    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

    lua
    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)}
    

    再考虑如下代码,

    lua
    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,即使算出来要发生碰撞,由于相同组且是负数,永远也不会碰撞

    lua
    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)}
    

    __EOF__

  • 本文作者: 小能正在往前冲
  • 本文链接: https://www.cnblogs.com/linxiaoxu/p/17646878.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    通信原理学习笔记2-1:模拟调制——相干解调的载波恢复、锁相环(平方环/Costas环)、变频/混频技术
    Android随笔-AsyncTask
    Shell脚本编程实践——第1关:编写一个脚本,求斐波那契数列的前10项及总和
    极智嘉(Geek+)柔性货箱到人拣选方案,助力Starlinks实现高效运营
    可见性volatile
    如何成为一名获得Adobe认证的专业人员?
    C++(17):string_view
    重要活动 | 林乐博士受邀出席“绿色金融创新策略与应用”论坛
    L9ARM体系结构与接口技术--ARM指令集仿真环境搭建(day5)
    备战秋招--spring篇
  • 原文地址:https://www.cnblogs.com/linxiaoxu/p/17646878.html