Вполне вероятно, что смещение белого прямоугольника - это баг отрисовки. А, может быть, и нет, т.к. либа юзается не по доке:
1) Обработка событий встроена, workspace.start() сам по себе запускает бесконечный цикл пуллинга событий, и делать это вручную через while true нет смысла:
wk.eventHandler = function(e1, e2, e3, e4, e5, e6)
...
end
wk:start(0.01)
2) Непонятно, зачем конкретно в твоём случае используется таймаут поллинга в 0.01 сек. Эта фича используется в довольно редких ситуациях, когда нужно делать что-то с заданным интервалом. Например, опрашивать внешние компоненты типа реакторов или перемещать визуальные элементы в играх раз в N сек. Если тебе требуется лишь анализ нажатия клавиш, то:
bg.eventHandler = function(e1, e2, e3, e4, e5, e6)
if e1 == "key_down" then
-- A
if e3 == 30 then
...
wk:draw()
-- D
else if e3 == 32 then
...
wk:draw()
end
end
end
wk:start(0.01)
3) Изменять экранные координаты объекта бесполезно, т.к. они рассчитываются автоматически и используются только во время вызова метода draw(). Чтобы изменить позицию элемента в родительском контейнере, используй localX/localY:
-- Нельзя
player.x = player.x - 2
-- Можно
player.localX = player.localX - 2
4) Нет смысла удалять и пересоздавать объекты картинок при каждом ивенте. Если тебе нужно попросту заменить картинку в существующем виджете, то:
local playerpic = wk:addChild(gui.image(50,25,images['Man Right']))
bg.eventHandler = function(e1, e2, e3, e4, e5, e6)
if e1 == "key_down" then
-- A
if e3 == 30 then
player.facing = 'Left'
playerpic.image = images['Man ' .. player.facing]
wk:draw()
end
end
end
5) Можно вообще создать собственный виджет "player", определив ему метод отрисовки и не собирать его из GUI.image. Он и работать будет быстрее, и все поля будут храниться в одном месте, и любой кастом можно будет оформить за пару строк кода:
local player = wk:addChild(gui.object(50, 25, 8, 4))
player.facing = 'Left'
player.draw = function(player)
screen.drawImage(player.x, player.y, images['Man ' .. player.facing]
end