【问题标题】:How do I keep the renderBlocks() function from going into an infinite loop?如何防止 renderBlocks() 函数进入无限循环?
【发布时间】:2021-07-16 00:16:48
【问题描述】:
-- This is Minecraft, but 2D, and in Lua.

-- **************************************
-- ID 0: Air
-- ID 1: Stone
-- ID 2 Logs
-- ID 3: Leaves
-- ID 4: Planks
-- ID 5: Crafting Table
-- ID 6: Furnance
-- ID 7: Player position detector block
-- **************************************

blockTable = { -- Stores all the blocks
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6}
}
tableRow = 1 -- Y value for block render pointer
tableColumn = 1 -- X value for block render pointer
runOnce = true -- Run a loop once
tickNum = 0 -- Number of ticks since startup
function renderBlock(X, Y, K) -- Render a block
    if K == 1 then -- if blockID == 1, draw a grey square
        screen.setColor(100,100,100)
        print(X, Y)
        screen.drawRectF(X, Y, 8, 8)
    end
    if K == 6 then -- If the blockID == draw a grey square
        screen.setColor(100,100,100)
        screen.drawRectF(X, Y, 8, 8)
    end
end

function renderBlocks() -- Render all the blocks (Scans though the blockTable in a raster pattern)
        while runOnce == true -- Run this code once
    do
    while #blockTable >= tableRow
    do
        while #blockTable[tableRow] >= tableColumn
        do
            print("TC " .. tableColumn)
            tableColumn = tableColumn + 1
            
            blockID = blockTable[tableRow][tableColumn]
            renderBlock(tableRow, tableColumn, blockID)
            if tableColumn > #blockTable[tableRow] then tableColumn = 1 end
        end
        runOnce = false
    end
        print("TR " .. tableRow)
        tableRow = tableRow + 1
    end
end

function onTick()
    print("Tick! Tick count: " .. tickNum) -- Print the current tick, as well as say that there has been a tick
    tickNum = tickNum + 1
end

function onDraw()
    while runOnce == true -- Run this code once
    do
    renderBlocks()
    runOnce = false
    end
end

这是我在框架 IDE 中的代码:Link

我不知道为什么会这样,我在这里不知所措。

【问题讨论】:

  • 在此处的帖子中添加您的代码。它不是为了帮助您而必须查看其他网站。
  • 一般来说,为了避免无限循环,您需要有一个结束条件,并且您需要在循环的每个开始或结束(while 或 repeat)或在调用递归之前检查它功能。
  • tableRow 何时会更改为使#blockTable >= tableRow 为假?
  • 这可能是因为您只是简单地反转了行和列来进行检查,这使得您的代码在无限循环中运行。由于内部表中的元素多于外部表中的内部表。所以也许#blockTable[tableRow] >= tableColumn 或#blockTable[tableRow] >= tableColumn 总是正确的。没有检查过。无论哪种方式,我都会向您推荐下面的 awnser,因为它更具可读性(更短!)并且有效。

标签: lua infinite-loop


【解决方案1】:

在对表执行循环时,使用 for 循环比使用 while 或重复循环要好得多,后者更难理解和修复错误。

如果您想获取索引(表中的数字)和关联的值,请使用for index,value in ipairs(yourtable) do,如果您想获取键(通常用作值的键的字符串:yourTable["key"] = 0),请使用for key,value in pairs(yourtable) do ) 以及与之关联的值。

for row,innerTable in ipairs(blockTable) do
        
    for col,value in ipairs(innerTable) do
            
        print("TC " .. col)
            
        blockID = value
        renderBlock(row, col, blockID)
            
    end
        
end

最后一个应该在你的 renderBlocks 函数中完成。但是:

while runOnce == true do

    -- Doing something
    runOnce = false

end

不是很好的东西。如果只调用一次,不要循环!

此外,更好地识别代码有助于更快地了解正在发生的事情。并在处理不同问题的代码块之间添加一些空格。

编辑:所以你的函数可能看起来像这样:

function renderBlocks()
    if runOnce then
        runOnce = false
        for row,innerTable in ipairs(blockTable) do
            for col,value in ipairs(innerTable) do
                blockID = value
                renderBlock(row, col, blockID)
            end
        end
    end
end

【讨论】:

  • 主要的 onDraw() 函数在 Lua 脚本之外的每个时间点都会被调用,如果我不这样做,它会运行不止一次。另外,我对缩进不是很好,我从来没有真正学过那些东西..
  • 只使用 if runOnce 然后 --[[ 这里的 for 循环 ]] 结束。如果在 if 语句中将 runOnce 设置为 false,这也只会执行一次。此外,在检查条件时,如果所讨论的值是真或假,则不需要将其与真进行比较,这就是我不进行比较的原因。
  • 对于 lua 中的缩进,简短的想法是,当您使用以一个制表符或 4 个空格开始块缩进的关键字并使用 end 关键字再次将其删除时。这使您的不同代码块具有更好的可读性,并且更容易立即看到什么是块以及它在哪里结束。
  • 我用一个示例编辑了我的答案,该函数现在看起来如何。我看到了另一个潜在的问题。您将 runOnce 用于 renderBlocks 函数和 onDraw 函数。但是先调用哪一个会将 runOnce 设置为 false 而另一个不调用。如果两者都需要调用一次,最好使用单独的变量,如 renderBlockHasNotRun 和 onDrawHasNotRun...
【解决方案2】:

您的 renderBlocks() 函数中有几个问题

function renderBlocks()
  while runOnce == true do
    while #blockTable >= tableRow do
      while #blockTable[tableRow] >= tableColumn do
        print("TC " .. tableColumn)
        tableColumn = tableColumn + 1

        blockID = blockTable[tableRow][tableColumn]
        renderBlock(tableRow, tableColumn, blockID)
        if tableColumn > #blockTable[tableRow] then 
          tableColumn = 1 
        end
      end
      runOnce = false
    end
    print("TR " .. tableRow)
    tableRow = tableRow + 1
  end
end

首先我们可以看看最里面的循环。在这里,我们遇到了用于评估循环的悔改以及如何递增的问题。

while #blockTable[tableRow] >= tableColumn do
  print("TC " .. tableColumn)
  tableColumn = tableColumn + 1 -- incremented here

  blockID = blockTable[tableRow][tableColumn]
  renderBlock(tableRow, tableColumn, blockID)
  if tableColumn > #blockTable[tableRow] then 
    tableColumn = 1 -- reset here
  end
end

在循环体中你执行tableColumn = tableColumn + 1 和稍后在同一个主体中执行if tableColumn > #blockTable[tableRow] then tableColumn = 1 end 所以这是一个问题,当tableColumn 增加到一个会结束循环的值时,我们将它重置为1 防止永无止境的循环。

现在让我们看看下一个循环。这里我们从不增加tableRow,它只是在循环结束后增加,所以循环不可能结束。

while #blockTable >= tableRow do
    while #blockTable[tableRow] >= tableColumn do
        ...
    end
    runOnce = false
end

我相信你是故意的

print("TR " .. tableRow)
tableRow = tableRow + 1

发生在这个循环内而不是在它之外。

【讨论】:

    【解决方案3】:

    您在这里缺少一些块定义。不要认为这是将其发送到无限循环的原因,否则您可能看不到结果。

    function renderBlock(X, Y, blockID)  --  Render a block
      if blockID == 0 then  --  Air
        --  screen.setColor(10, 20, 220, 0) -- is there an alpha channel?
        screen.setColor(10, 20, 220)
    
      elseif blockID == 1 then  --  Stone
        screen.setColor(100, 100, 100)
    
      elseif blockID == 2 then  --  Logs
        screen.setColor(200, 50, 50)
    
      elseif blockID == 3 then  --  Leaves
        screen.setColor(20, 220, 40)
    
      elseif blockID == 4 then  --  Planks
        screen.setColor(180, 30, 10)
    
      elseif blockID == 5 then  --  Craft Table
        screen.setColor(180, 30, 10)
    
      elseif blockID == 6 then  --  Furnace
        screen.setColor(100, 100, 100)
    
      elseif blockID == 7 then  --  Player detect
        screen.setColor(220, 30, 30)
      end
    
      print(X, Y)
      screen.drawRectF(X, Y, 8, 8)
    end
    

    for 循环很容易使用。设置初始条件,然后设置最终的完成条件,并让它运行课程。

    function renderBlocks() -- Render all blocks (Scans though blockTable in a raster pattern)
      for row = 1,  #blockTable do
        for col = 1, #blockTable[row] do
          print("TC " ..col)
          local blockID = blockTable[row][col]
          renderBlock(row, col, blockID)
        end
        print("TR " ..row)
      end
    end
    

    您可能不需要runOnce。如果你发现你在做其他事情,请继续,但嵌套 for 循环不应该需要它。

    function onDraw()
      renderBlocks()
    end
    

    【讨论】:

    • 可能需要为您的数据块翻转blockTable[col][row]。你明白了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    相关资源
    最近更新 更多