Lua исправлены проблемы с движением плитки - PullRequest
0 голосов
/ 16 мая 2018

Я воссоздаю миссис Пакман, используя Lua, и только недавно узнал, что серия плиток используется для столкновения и движения;Я пытаюсь воссоздать это.Вот карта тайлов:

nodemap = {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1},
    {1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1,1},
    {1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1,1},
    {1,1,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,1,1},
    {1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1},
    {1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1},
    {1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1},
    {1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1},
    {1,1,1,1,0,1,1,1,1,1,0,1,1,1,0,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,1,1,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1,1},
    {1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1},
    {1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1},
    {1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1},
    {1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1},
    {1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1},
    {1,1,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,1,1},
    {1,1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1,1},
    {1,1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1,1},
    {1,1,0,1,1,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,1,1,0,1,1},
    {1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1,1},
    {1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1,1},
    {1,1,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,1,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
}

Это создает лабиринт без ошибок, но проблема, с которой я сталкиваюсь, заключается в том, что Пакман плавно перемещается по тайлам, не выпрыгивая из тайла в тайл ...Итак, у меня есть этот код для «плавного» перемещения Пакмана:

-- Make Pacmans position consist of a single decimal (because over time a number of 1.899999994 will occur and that's ugly)
mrspacman.x = math.floor(mrspacman.x*10)/10
mrspacman.y = math.floor(mrspacman.y*10)/10

--If an arrowkey was pressed (Think of this as "A new direction was queued")
if (mrspacman.nextDirection) then

    -- If Pacman is in the center of a tile, then
    if (mrspacman.x == math.floor(mrspacman.x)) and (mrspacman.y == math.floor(mrspacman.y)) then

        -- If the tile in front of Pacman is empty, set direction to that
        if (nodemap[mrspacman.y-math2.sin(mrspacman.dir)][mrspacman.x+math2.cos(mrspacman.dir)]~=1) then
            mrspacman.dz = mrspacman.dir

            -- Disable this queue
            mrspacman.nextDirection = false
        end
    end
end

-- If Pacman is NOT in the center of a tile
if (mrspacman.x ~= math.floor(mrspacman.x)) or (mrspacman.y ~= math.floor(mrspacman.y)) then

    -- Constantly move forwards
    mrspacman.x = mrspacman.x + (math2.cos(mrspacman.dz)*mrspacman.speed)
    mrspacman.y = mrspacman.y - (math2.sin(mrspacman.dz)*mrspacman.speed)
else

    -- If the tile in front of Pacman is empty, move to that tile
    if (nodemap[mrspacman.y-math2.sin(mrspacman.dz)][mrspacman.x+math2.cos(mrspacman.dz)] ~= 1) then
        mrspacman.x = mrspacman.x + (math2.cos(mrspacman.dz)*mrspacman.speed)
        mrspacman.y = mrspacman.y - (math2.sin(mrspacman.dz)*mrspacman.speed)
    end
end
  • mrspacman.dz = Угол наклона.

  • mrspacman.dir = записанное нажатие клавиши.

  • mrspacman.speed = .1 (десятичное число, обеспечивающее плавное движение).

Выполнение этогокод приводит к зависанию Пакмана, когда нажата обратная клавиша, и его положение портится при повороте в угол, проходе сквозь стены ... Это можно увидеть здесь .

Какэто можно исправить?

ОБНОВЛЕНИЕ

Я добавил таблицу с именем math2, состоящую из cos и sin fx (только вернет значения для 4-х основных направлений):

math2 = {
    cos = function(angle)
        vectors = {1,0,-1,0}
        return vectors[(angle/90)+1]
    end,

    sin = function(angle)
        vectors = {0,1,0,-1}
        return vectors[(angle/90)+1]
    end
}

1 Ответ

0 голосов
/ 16 мая 2018

Так что вопрос довольно широкий:

Как это можно исправить? Ответ: путем отладки вашей программы.

Проблема довольно расплывчатая, и тестовый пример на видео довольно большой и не сфокусированный. Чтобы понять, что происходит, вам нужно подробно рассмотреть хотя бы некоторые этапы программы. А именно, проблема в том, что mrspacman может пройти сквозь стену, если плитка слева от стены пуста. Это намекает на то, что в коде, который проверяет наличие стены, может быть какая-то ошибка.

Код в вопросе не является полным, поэтому ошибки могут возникать извне. Тем не менее, в последний час я собрал примитивную среду для проверки кода на вопрос. (указан в конце ответа)

Поиграв с кодом, я теперь считаю, что весь пакет был написан не только без единой тригонометрической функции, но и без чисел с плавающей запятой вообще.

С числами с плавающей точкой дело в том, что они плохо подходят для точных вычислений. Некоторые компиляторы и вычислительные системы будут даже ругать вас за это.


Перед редактированием рассматриваемый код имел выражения с синусами и косинусами:

math.floor(math.sin(mrspacman.dir*pi/180))

Я сейчас оценил его на своем компьютере и действительно для значений dir, которые равны 0, 90, 180, 270, он оценивается как 0, 1, 0, -1. Но это на 64-битной машине. Я не уверен, что TI-Nspire можно доверять, чтобы обеспечить ту же точность.


Во время моих тестов я столкнулся с другой проблемой с поплавками:

mrspacman.x = math.floor(mrspacman.x*10)/10

приведет к ошибке в десятичной цифре: для mrspacman.x, равной 4.3, оно будет равно 4.2, что приведет к остановке pacwoman. Как ни странно, при оценке непосредственно в командной строке, он будет вести себя правильно. В любом случае, общая реализация этой функции добавляет некоторый уклон перед настилом: math.floor(mrspacman.x*10+0.5)/10.


Кроме того, были небольшие икоты с ориентацией осей, которые быстро обнаруживались благодаря всей отладочной информации, сбрасываемой платформой.

И это все. Код работает (хотя я просто оставлю this здесь без дополнительных аргументов). Если код все еще не работает на вашем устройстве, вам нужно искать проблемы в другом месте.

Код отладки

Я оставил ваши переменные глобальными, какими они, вероятно, были в вашей программе, и это, вероятно, не имеет значения в однофайловом скрипте. Но в общем случае хорошей практикой является создание любой переменной local, если только для этого нет веской причины.

nodemap = {--I've used small field for debugging
  {1,1,1,1,1,1,1},
  {1,0,0,0,0,0,1},
  {1,0,1,0,1,1,1},
  {1,0,0,0,1,1,1},
  {1,1,1,1,1,1,1},
}

mrspacman={dz=0,dir=false,x=2,y=2,speed=0.1,nextDirection=false}

local dlog=function(...)--debug function
  print(string.format(...))
end

math2 = {
    cos = function(angle)
        vectors = {1,0,-1,0}--this would be better of by being a local variable
        return vectors[(angle/90)+1]
    end,

    sin = function(angle)
        vectors = {0,1,0,-1}--this too
        return vectors[(angle/90)+1]
    end
}


local graphic_lines={}
for _,line in ipairs(nodemap) do
  local l=table.concat(line):gsub("0"," ")
  table.insert(graphic_lines,l)
end

local time_step=1
function draw()--quick and dirty output to terminal
  print(string.format("\nStep %i",time_step))
  time_step=time_step+1
  local x,y = mrspacman.x,mrspacman.y
  local px,py=math.floor(x+0.5),math.floor(y+0.5)
  for index,line in ipairs(graphic_lines) do
    if index~= py then 
      print(line) 
    else
      print( line:sub(1,px-1) .. "@" .. line:sub(px+1))
    end
  end
  print(string.format('Real position (x,y): (%f , %f)',x,y))
  print(string.format('Current direction: %i',mrspacman.dz))
  if mrspacman.nextDirection then
    print(string.format("Trying to turn at %i",mrspacman.dir or "error"))
  end
end


local inputs={--i've mixed up directions due to my renderer
  w=90,
  a=180,
  s=270,
  d=0,
}
local skips={['']=true,[' ']=true}

function input(macro_input)--read wasd from terminal
  print('your move')
  local key= (macro_input and macro_input()) or io.read();
  if skips[key] then return end
  local dir = inputs[key];
  if not dir then print('wrong key') return end
  mrspacman.dir = dir
  mrspacman.nextDirection=true
end

function sim_step()
dlog('"Smoothing" (%f,%f) through (%f,%f) to (%f,%f)',mrspacman.x,mrspacman.y,mrspacman.x*10,mrspacman.y*10,math.floor(mrspacman.x*10)/10,math.floor(mrspacman.y*10)/10)
dlog('"Fixing smoothing" (%f,%f) through (%f,%f) to (%f,%f)',mrspacman.x,mrspacman.y,(mrspacman.x+0.05)*10,(mrspacman.y+0.05)*10,math.floor((mrspacman.x+0.05)*10)/10,math.floor((mrspacman.y+0.05)*10)/10)

mrspacman.x = math.floor(mrspacman.x*10+.5)/10--these lines are different
mrspacman.y = math.floor(mrspacman.y*10+.5)/10--

--If an arrowkey was pressed (Think of this as "A new direction was queued")
if (mrspacman.nextDirection) then
    dlog("trying to turn")
    -- If Pacman is in the center of a tile, then
    if (mrspacman.x == math.floor(mrspacman.x)) and (mrspacman.y == math.floor(mrspacman.y)) then
        dlog("Pacman is in the center of a tile")
        -- If the tile in front of Pacman is empty, set direction to that
        dlog("lookup dy: %f dx: %f",-math2.sin(mrspacman.dir),math2.cos(mrspacman.dir))
        dlog("lookup y: %f x: %f",mrspacman.y-math2.sin(mrspacman.dir),mrspacman.x+math2.cos(mrspacman.dir))
        dlog(nodemap[mrspacman.y-math2.sin(mrspacman.dir)][mrspacman.x+math2.cos(mrspacman.dir)]==1 and "Its a wall" or "")
        if (nodemap[mrspacman.y-math2.sin(mrspacman.dir)][mrspacman.x+math2.cos(mrspacman.dir)]~=1) then
            dlog("turning")
            mrspacman.dz = mrspacman.dir

            -- Disable this queue
            mrspacman.nextDirection = false
          else
            dlog"not turning"
        end
    end
end

-- If Pacman is NOT in the center of a tile
if (mrspacman.x ~= math.floor(mrspacman.x)) or (mrspacman.y ~= math.floor(mrspacman.y)) then
    dlog("Pacman is NOT in the center of a tile, moving with no collision check")
    -- Constantly move forwards
    dlog("deltaX, deltaY : %f, %f",(math2.cos(mrspacman.dz)*mrspacman.speed),-(math2.sin(mrspacman.dz)*mrspacman.speed))
    dlog("before (%f, %f)",mrspacman.x,mrspacman.y)
    mrspacman.x = mrspacman.x + (math2.cos(mrspacman.dz)*mrspacman.speed)
    mrspacman.y = mrspacman.y - (math2.sin(mrspacman.dz)*mrspacman.speed)
    dlog("after (%f, %f)",mrspacman.x,mrspacman.y)
else
    dlog("moving with collision check")
    -- If the tile in front of Pacman is empty, move to that tile
    if (nodemap[mrspacman.y-math2.sin(mrspacman.dz)][mrspacman.x+math2.cos(mrspacman.dz)] ~= 1) then
        dlog("can move")
        mrspacman.x = mrspacman.x + (math2.cos(mrspacman.dz)*mrspacman.speed)
        mrspacman.y = mrspacman.y - (math2.sin(mrspacman.dz)*mrspacman.speed)
    else
        dlog("hit wall")
    end
end

end


local gen_macro = function(sequence)--function generating predefined set of keypresses
  sequence=sequence or ""
  local step=0
  return function()
    step=step+1
    if (step>sequence:len()) then return end--return to manual control
    return sequence:sub(step,step)
  end
end

local simple_macro= function() return gen_macro("wasd") end--simple sample, "--will do nothing
local horizontal_bend = function()--will bring pacman to bottom left
  local _x10=string.rep(" ",10);
  local sequence=_x10:rep(2).."s".._x10:rep(2).."a".._x10:rep(2)
  return gen_macro(sequence)
end

local angry_monkey=function(length)--gen random sequence of buttons
  length=length or 0
  local keys={"a","s","d","w"," "}
  local seq = {}
  for i=1,length do
    table.insert(seq,keys[math.random(#keys)])
  end
  return gen_macro(table.concat(seq))
end

local macro
--macro=simple_macro()
--macro=horizontal_bend()
--macro = angry_monkey(100)

--dlog=function() end --uncomment this to disable debug

while true do
  draw()
  input(macro)
  sim_step()
--  if time_step>10 then return end
end
...