lua - обслуживание таблиц (связано с системой частиц)

Приведенная ниже функция update() вызывается для каждого кадра игры. Если частица Drop имеет значение y больше 160, я хочу удалить ее из таблицы. Проблема в том, что я получаю ошибки «попытка сравнить число с nil» в строке, указанной ниже:

local particles = {};

function update()
    local num = math.random(1,10);
    if(num < 4) then
        local drop = Drop.new()
        table.insert ( particles, drop );
    end

    for i,val in ipairs(particles) do
        if(val.y > 160) then --ERROR attempt to compare number with nil
            val:removeSelf(); --removeSelf() is Corona function that removes the display object from the screen
            val = nil;
        end
    end
end

Что я делаю неправильно? Очевидно, что val равно нулю, но я не понимаю, почему итерация таблицы находит val в первую очередь, поскольку я установил его равным нулю, когда его значение y становится больше 160.


person clua7    schedule 31.05.2011    source источник


Ответы (4)


Спасибо за ответы, все они были полезны. Вот что в итоге сработало для меня. Вызов table.remove необходим для правильной работы цикла.

for i = #particles, 1, -1 do
    if particles[i].y > 160 then
        local child = table.remove(particles, i)
        if child ~= nil then
            display.remove(child)
            child = nil
        end
    end
end
person clua7    schedule 01.06.2011

Вы ищете не в том месте, проблема не в том, что val это nil, а val.y это nil. См. этот пример:

> x=nil
> if x.y > 10 then print("test") end
stdin:1: attempt to index global 'x' (a nil value)
stack traceback:
    stdin:1: in main chunk
    [C]: ?
> x={y=nil}
> if x.y > 10 then print("test") end
stdin:1: attempt to compare number with nil
stack traceback:
    stdin:1: in main chunk
    [C]: ?

Кроме того, когда вы устанавливаете val в nil, это может ничего не делать (я считаю, что val является копией):

> t={"a", "b", "c", "d"}
> for i,val in ipairs(t) do print(i, val) end
1   a
2   b
3   c
4   d
> for i,val in ipairs(t) do if i==3 then print("delete", val); val=nil end end
delete  c
> for i,val in ipairs(t) do print(i, val) end
1   a
2   b
3   c
4   d

Редактировать: чтобы удалить элемент из таблицы, вы хотите table.remove:

> t[3]=nil
> for i,val in ipairs(t) do print(i, val) end
1   a
2   b
> t[3]="c"
> for i,val in ipairs(t) do print(i, val) end
1   a
2   b
3   c
4   d
> for i,val in ipairs(t) do if i==3 then print("delete", val); table.remove(t, i) end end
delete  c
> for i,val in ipairs(t) do print(i, val) end
1   a
2   b
3   d
person BMitch    schedule 01.06.2011

Решение JeffK должно работать, но я думаю, что оно сработает не потому, что он просматривает список в обратном направлении, а потому, что он устанавливает particles[i] = nil вместо val = nil. Если вы запускаете val = nil, вы только устанавливаете локальную копию val в nil, а не запись в таблице.

Попробуй это:

for i,val in ipairs(particles) do
    if(val.y > 160) then
        particles[i]:removeSelf()
        particles[i] = nil;
    end
end
person Alex    schedule 01.06.2011
comment
Я попробовал ваш код. Я не получаю ошибку сейчас, но странно то, что он удалит только один объект. После этого он больше никогда не входит в цикл for. Как будто таблица частиц каким-то образом обнуляется. - person clua7; 01.06.2011
comment
Что, вероятно, происходит, тогда первый элемент в списке становится нулевым, а затем, поскольку первый элемент равен нулю, цикл больше никогда не запускается (поскольку итерация останавливается, когда она достигает нулевого индекса). Возможно, вы сможете решить эту проблему, уплотнив массив в конце цикла, чтобы в нем не было пробелов, хотя это может быть сложно. - person Alex; 01.06.2011
comment
Да, когда будет достигнуто значение nil, цикл for остановится. Я обновил свой ответ table.remove, который правильно выполняет удаление. - person BMitch; 01.06.2011

Я не думаю, что вам разрешено изменять содержимое таблицы, пока ipairs перебирает ее. Я смутно припоминаю, что читал мою печатную копию Справочного руководства по Lua 5.1. , но я не могу найти его сейчас. Когда вы устанавливаете для val значение nil, он удаляет элемент из частиц таблица.

Вы можете попробовать обработать таблицу в обратном порядке, так как ваша функция выполняет полную проверку таблицы particles, условно удаляя некоторые элементы:

for x = #particles, 1, -1 do
    if particles[x].y > 160 then
        particles[x]:removeSelf()
        particles[x] = nil
    end
end
person JeffK    schedule 31.05.2011
comment
Согласно документации, это предостережение относится только к парам next и . Использование ipairs для перебора таблицы в числовом порядке должно быть в порядке. Однако он просто обнуляет val, а не запись в таблице lua. org/manual/5.1/manual.html#pdf-next - person Alex; 01.06.2011
comment
Я попробовал этот код (после добавления -1 к условному выражению и замены частиц [x].val на частицы [x].y), но я получаю это после удаления частицы: попытка проиндексировать поле '?' (нулевое значение) - person clua7; 01.06.2011
comment
Код обновлен согласно комментариям clua7. Ошибка нулевой ссылки вызывает недоумение. Вызов removeSelf() отключает объект от механизма отображения Corona, а установка для него значения nil удаляет его. из таблицы particles, готовой к сборке мусора. Затем x должно уменьшаться, переходя к следующему объекту в таблице. Какая строка сообщается, когда вы пытаетесь проиндексировать поле '?' (нулевое значение)? - person JeffK; 01.06.2011