You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

错误原因

  1. 地面Fixture未设置UserData:当方块与地面碰撞时,碰撞回调中的a或b会指向地面的Fixture,调用getUserData()返回nil,用nil作为table索引触发table index is nil错误。
  2. 碰撞目标判断逻辑不严谨: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:39:51