Love2D三消游戏出现Stack Overflow错误,求解决方法
解决Love2D瓷砖匹配游戏的栈溢出问题
问题背景
使用Love2D开发瓷砖匹配消除游戏时,触发stack overflow错误,错误栈显示matches、checkforNil、removeMatches三个函数陷入无限递归调用。
错误栈信息
main.lua:147: stack overflow Traceback [love "callbacks.lua"]:228: in function 'handler' main.lua:94: in function 'matches' main.lua:80: in function 'checkforNil' main.lua:89: in function 'removeMatches' main.lua:148: in function 'matches' main.lua:80: in function 'checkforNil' main.lua:89: in function 'removeMatches' main.lua:148: in function 'matches' main.lua:80: in function 'checkforNil' main.lua:89: in function 'removeMatches' ... main.lua:89: in function 'removeMatches' main.lua:148: in function 'matches' main.lua:80: in function 'checkforNil' main.lua:89: in function 'removeMatches' main.lua:148: in function 'matches' main.lua:161: in function 'swap' main.lua:183: in function <main.lua:175> [love "callbacks.lua"]:154: in function <[love "callbacks.lua"]:144> [C]: in function 'xpcall'
可复现代码
local game = {} -- Table to store the grid elements gridTable = {} local selectedTile = nil -- Function to initialize the gridTable with colors function game:initializeGrid() for i = 1, 64 do gridTable[i] = { x = ((i - 1) % 8) * 65, y = (math.floor((i - 1) / 8) * 65), color = love.math.random(7) } end end -- Function to draw the grid elements function game:drawGrid() for i, cell in ipairs(gridTable) do if cell.color then love.graphics.setColor(self:getColor(cell.color)) love.graphics.rectangle("fill", cell.x, cell.y, 50, 50) end end if selectedTile then love.graphics.setColor(1, 1, 1) love.graphics.rectangle("line", gridTable[selectedTile].x, gridTable[selectedTile].y, 50, 50) end end -- Helper function to get color by index function game:getColor(index) local colors = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 1, 0}, {0, 1, 1}, {1, 0, 1}, {0.5, 0.5, 0.5} } return colors[index] end function game:checkforNil() -- looks for nil values in the table for i = 64, 56, -1 do --grid is 8x8 so 64 items local counter = 8 -- looks through each row for j = i, 1, -8 do local tempY, tempColor = 0, 0 if gridTable[j].y == nil then -- if value at j is nil we swap the nil value with what's about it and repeats until all values are filled for test = j, 1, -8 do if gridTable[test].y ~= nil then tempY = gridTable[test].y tempColor = gridTable[test].color gridTable[j].y = ((counter) * 50) + (((counter) - 1) * 15) gridTable[j].color = tempColor gridTable[test].y = nil gridTable[test].color = nil break end end end for k = 1, 8 do -- loops through the top and assigns random colours if gridTable[k].y == nil then gridTable[k].y = 50 gridTable[k].color = love.math.random(7) end end counter = counter - 1 end end self:matches(gridTable) end function game:removeMatches(grid) for _, v in ipairs(listofMatches) do --lists of matches contain where in the table there are 3 or more colours of the same grid[v].y = nil --and removes them grid[v].color = nil end listofMatches = {} --resets the list self:checkforNil() -- calls the function that pushes bricks down end function game:matches(grid) --searches everytime we swap for a match matchTest = false --if we find at least one match we'll set this to true listofMatches = {} local matches = 0 for i = 1, 63 do --loops through the table of which there are 64 items for _, y in ipairs(listofMatches) do if i == y then goto continue --if i and i+1 are a match we skip to i+2 to see if that matches too end end ::continue:: for j = i + 1, 64 do --in this loop we are looking for horizontal matches local currentColor = grid[i].color --sets the current colour we are searching for if currentColor ~= grid[j].color then --we break out of searching for the same colour and if the if matches > 1 then --matches are 3+ we set match test to tru and insert into list of matches matchTest = true for k = i, j - 1 do table.insert(listofMatches, k) end end matches = 0 break else matches = matches + 1 end end end for num = 1, 8 do --looking for vertical for vertical = num, 48, 8 do for _, y in ipairs(listofMatches) do if vertical == y then goto continue end end ::continue:: for down = vertical + 8, 64, 8 do local currentColor = grid[vertical].color if currentColor ~= grid[down].color then if matches > 1 then matchTest = true for k = vertical, down - 8, 8 do table.insert(listofMatches, k) end end matches = 0 break else matches = matches + 1 end end end end if matchTest then --if there is a match test we want to remove matches so call the function and add points self:removeMatches(grid) game.points = (game.points or 0) + 500 matchTest = false end end function game:swap(swap1, swap2) --swapping blocks and then checking if there's a match local temp1 = gridTable[swap1].color local temp2 = gridTable[swap2].color gridTable[swap1].color = temp2 gridTable[swap2].color = temp1 self:matches(gridTable) end -- Love2D callback functions function love.load() game:initializeGrid() end function love.draw() game:drawGrid() love.graphics.setColor(1, 1, 1) love.graphics.print("Points: " .. (game.points or 0), 10, 10) end function love.mousepressed(x, y, button) if button == 1 then local tileIndex = game:getTileIndex(x, y) if tileIndex then if not selectedTile then selectedTile = tileIndex else if selectedTile ~= tileIndex then game:swap(selectedTile, tileIndex) selectedTile = nil else selectedTile = nil end end end end end function game:getTileIndex(x, y) for i, cell in ipairs(gridTable) do if x > cell.x and x < cell.x + 50 and y > cell.y and y < cell.y + 50 then return i end end return nil end
问题分析
栈溢出的核心原因是无限递归循环:
matches检测到匹配后调用removeMatchesremoveMatches处理完成后调用checkforNil填补空位checkforNil结束后立即调用matches再次检测- 新生成的瓷砖可能再次触发匹配,导致上述流程无限重复,直到栈内存耗尽
此外还有两个次要问题:
- 垂直匹配检测时未重置
matches计数器,可能导致错误的匹配判定 listofMatches和matchTest为全局变量,容易引发状态混乱
解决方案
1. 打破无限递归循环
将后续匹配检测移到Love2D的帧循环love.update中执行,避免瞬间填满调用栈。
修改checkforNil函数,移除末尾的self:matches(gridTable),添加需要检测匹配的标记:
function game:checkforNil() -- looks for nil values in the table for i = 64, 56, -1 do --grid is 8x8 so 64 items local counter = 8 -- looks through each row for j = i, 1, -8 do local tempY, tempColor = 0, 0 if gridTable[j].y == nil then -- if value at j is nil we swap the nil value with what's about it and repeats until all values are filled for test = j, 1, -8 do if gridTable[test].y ~= nil then tempY = gridTable[test].y tempColor = gridTable[test].color gridTable[j].y = ((counter) * 50) + (((counter) - 1) * 15) gridTable[j].color = tempColor gridTable[test].y = nil gridTable[test].color = nil break end end end for k = 1, 8 do -- loops through the top and assigns random colours if gridTable[k].y == nil then gridTable[k].y = 50 gridTable[k].color = love.math.random(7) end end counter = counter - 1 end end -- 标记需要在帧循环中检查匹配 game.needsMatchCheck = true end
添加love.update回调处理匹配检测:
function love.update(dt) if game.needsMatchCheck then game.needsMatchCheck = false game:matches(gridTable) end end
2. 修复垂直匹配计数器问题
在垂直匹配检测的外层循环中,每次开始新列检测前重置matches计数器:
for num = 1, 8 do --looking for vertical matches = 0 -- 重置计数器 for vertical = num, 48, 8 do for _, y in ipairs(listofMatches) do if vertical == y then goto continue end end ::continue:: for down = vertical + 8, 64, 8 do local currentColor = grid[vertical].color if currentColor ~= grid[down].color then if matches > 1 then matchTest = true for k = vertical, down - 8, 8 do table.insert(listofMatches, k) end end matches = 0 break else matches = matches + 1 end end end end
3. 全局变量改为局部变量
将listofMatches和matchTest移到matches函数内部,避免全局状态污染:
function game:matches(grid) --searches everytime we swap for a match local matchTest = false -- 改为局部变量 local listofMatches = {} -- 改为局部变量 local matches = 0 -- 原有循环逻辑保持不变 ... if matchTest then -- 将匹配列表作为参数传递给removeMatches self:removeMatches(grid, listofMatches) game.points = (game.points or 0) + 500 end end
修改removeMatches函数接收参数:
function game:removeMatches(grid, listofMatches) for _, v in ipairs(listofMatches) do grid[v].y = nil grid[v].color = nil end self:checkforNil() end
4. 可选:递归深度限制(备用方案)
如果坚持使用递归,可添加深度限制防止无限递归:
function game:matches(grid, depth) depth = depth or 0 if depth > 10 then -- 限制最多递归10层 return end -- 原有代码 ... if matchTest then self:removeMatches(grid, listofMatches) game.points = (game.points or 0) + 500 self:matches(grid, depth + 1) end end
但更推荐使用帧循环方案,符合Love2D的游戏设计模式。
总结
核心问题是无限递归调用,通过将后续匹配检测移到love.update帧循环中可彻底解决栈溢出。同时修复计数器和变量作用域问题,能提升代码稳定性与可维护性。
内容的提问来源于stack exchange,提问作者user25690029
相关产品推荐
相关产品推荐

