Lua+Love2D中setUserData/getUserData返回nil:碰撞销毁方块报错排查
Love2D碰撞检测
getUserData返回nil报错修复 问题描述
基于Love2D框架开发Lua代码,生成两个受重力影响的方块,期望二者碰撞时通过setUserData和getUserData标记后销毁,但调用getUserData时返回nil,触发如下错误:
Error src/Test2.lua:12: table index is nil Traceback [love "callbacks.lua"]:228: in function 'handler' src/Test2.lua:12: in function <src/Test2.lua:10> [C]: in function 'update' src/Test2.lua:50: in function 'update' main.lua:51: in function 'update' [love "callbacks.lua"]:162: in function <[love "callbacks.lua"]:144> [C]: in function 'xpcall'
原创建方块代码
Test = Class{} function Test:init(type, x, y, userData, world) self.world = world self.x = x self.y = y self.type = type self.boxBody1 = love.physics.newBody(self.world, self.x , self.y ,'dynamic') if self.type == 'square' then self.boxShape = love.physics.newRectangleShape(10, 10) end self.boxFixture1 = love.physics.newFixture(self.boxBody1, self.boxShape) self.boxFixture1:setRestitution(0.5) self.boxFixture1:setUserData(userData) end function Test:update(dt) end function Test:render() love.graphics.polygon('fill', self.boxBody1:getWorldPoints(self.boxShape:getPoints())) end
原碰撞销毁代码
Test2 = Class{} function Test2:init() self.world = love.physics.newWorld(0, 300) self.destroyedBodies = {} self.square = {} function beginContact(a, b, coll) local types = {} types[a:getUserData()] = true types[b:getUserData()] = true -- 检测是否是两个方块的碰撞 if types['square1'] and types['square2'] then -- 获取对应的Fixture local square1 = a:getUserData() == 'square1' and a or b local square2 = a:getUserData() == 'square2' and a or b table.insert(self.destroyedBodies, square1:getBody()) table.insert(self.destroyedBodies, square2:getBody()) end end self.world:setCallbacks(beginContact) table.insert(self.square, Test('square', VIRTUAL_WIDTH/2, VIRTUAL_HEIGHT/2, 'square1', self.world)) table.insert(self.square, Test('square', VIRTUAL_WIDTH/2, VIRTUAL_HEIGHT/3, 'square2', self.world)) self.groundBody = love.physics.newBody(self.world, 0, VIRTUAL_HEIGHT - 30, 'static') self.edgeShape = love.physics.newEdgeShape(0, 0, VIRTUAL_WIDTH, 0) self.groundFixture = love.physics.newFixture(self.groundBody, self.edgeShape) end function Test2:update(dt) self.world:update(dt) for k, boxBody1 in pairs(self.destroyedBodies) do if not boxBody1:isDestroyed() then boxBody1:destroy() end end self.destroyedBodies = {} for i = #self.square, 1, -1 do if self.square[i].boxBody1:isDestroyed() then table.remove(self.square, i) end end end function Test2:render() for k, square in pairs(self.square) do square:render() end love.graphics.setColor(255, 0, 0) love.graphics.setLineWidth(3) love.graphics.line(self.groundBody:getWorldPoints(self.edgeShape:getPoints())) end
错误原因
- 地面Fixture未设置UserData:当方块与地面碰撞时,碰撞回调中的
a或b会指向地面的Fixture,调用getUserData()返回nil,用nil作为table索引触发table index is nil错误。 - 碰撞目标判断逻辑不严谨:
a:getUserData() == 'square1' and a or b的写法存在漏洞,如果a不是square1,直接取b,但b可能也不是square1,导致后续操作出错。
修复方案
1. 给地面Fixture添加UserData
在创建地面Fixture后,设置其UserData为标识值,避免nil索引:
self.groundFixture = love.physics.newFixture(self.groundBody, self.edgeShape) self.groundFixture:setUserData('ground') -- 添加地面标识
2. 优化碰撞回调的判断逻辑
在beginContact中先过滤掉非目标Fixture的情况,确保只处理square1和square2的碰撞,同时修正目标Fixture的获取逻辑:
function beginContact(a, b, coll) local userA = a:getUserData() local userB = b:getUserData() -- 过滤无效或非目标碰撞 if not userA or not userB then return end if (userA ~= 'square1' and userA ~= 'square2') or (userB ~= 'square1' and userB ~= 'square2') then return end -- 只处理square1和square2的互相碰撞 if (userA == 'square1' and userB == 'square2') or (userA == 'square2' and userB == 'square1') then local square1 = userA == 'square1' and a or b local square2 = userA == 'square2' and a or b table.insert(self.destroyedBodies, square1:getBody()) table.insert(self.destroyedBodies, square2:getBody()) end end
修改后的完整Test2代码
Test2 = Class{} function Test2:init() self.world = love.physics.newWorld(0, 300) self.destroyedBodies = {} self.square = {} function beginContact(a, b, coll) local userA = a:getUserData() local userB = b:getUserData() -- 过滤无效或非目标碰撞 if not userA or not userB then return end if (userA ~= 'square1' and userA ~= 'square2') or (userB ~= 'square1' and userB ~= 'square2') then return end -- 只处理square1和square2的互相碰撞 if (userA == 'square1' and userB == 'square2') or (userA == 'square2' and userB == 'square1') then local square1 = userA == 'square1' and a or b local square2 = userA == 'square2' and a or b table.insert(self.destroyedBodies, square1:getBody()) table.insert(self.destroyedBodies, square2:getBody()) end end self.world:setCallbacks(beginContact) table.insert(self.square, Test('square', VIRTUAL_WIDTH/2, VIRTUAL_HEIGHT/2, 'square1', self.world)) table.insert(self.square, Test('square', VIRTUAL_WIDTH/2, VIRTUAL_HEIGHT/3, 'square2', self.world)) self.groundBody = love.physics.newBody(self.world, 0, VIRTUAL_HEIGHT - 30, 'static') self.edgeShape = love.physics.newEdgeShape(0, 0, VIRTUAL_WIDTH, 0) self.groundFixture = love.physics.newFixture(self.groundBody, self.edgeShape) self.groundFixture:setUserData('ground') -- 添加地面标识 end function Test2:update(dt) self.world:update(dt) for k, boxBody1 in pairs(self.destroyedBodies) do if not boxBody1:isDestroyed() then boxBody1:destroy() end end self.destroyedBodies = {} for i = #self.square, 1, -1 do if self.square[i].boxBody1:isDestroyed() then table.remove(self.square, i) end end end function Test2:render() for k, square in pairs(self.square) do square:render() end love.graphics.setColor(255, 0, 0) love.graphics.setLineWidth(3) love.graphics.line(self.groundBody:getWorldPoints(self.edgeShape:getPoints())) end
内容的提问来源于stack exchange,提问作者AA-SLF
相关产品推荐
相关产品推荐

