Привет, есть необходимость заблокировать или игнорировать ввод с клавиш.
Например пользователь нажал влево и пока объект не долетел до левой стенки нельзя никак изменить направление движение объекта.
Приветствую!
Я понял так:
Игрок задаёт направление движения персонажу(влево, вправо, вверх, вниз). Можно выбрать только одно направление, пока персонаж не достигнет границы(левой, правой, верхней, нижней). При достижении одной из границ, мы можем поменять направление движения, но не можем пройти “сквозь” границу.
- Для начала нужно добавить привязки ввода в
game.input_binding:

- Создадим границы(левая, правая, верхняя, нижняя границы) с помощью кода:
local LEFT_WALL_X = 200
local RIGHT_WALL_X = 400
local TOP_WALL_Y = 400
local BOT_WALL_Y = 200
- Во время инициализации скрипта
function init(self)
msg.post(".", "acquire_input_focus") -- разрешаем обрабатывать данные для этого скрипта
self.can_move = true -- игрок может двигаться
self.dir = vmath.vector3() -- вектор направления движения(куда персонаж движется)
self.speed = 100 -- скорость игрока
end
- Обработка входных данных в скрипт
function on_input(self, action_id, action)
if self.can_move then
if action_id == hash("left") and action.pressed then
self.dir = vmath.vector3(-1, 0, 0)
self.can_move = false
print(self.dir)
elseif action_id == hash("right") and action.pressed then
self.dir = vmath.vector3(1, 0, 0)
self.can_move = false
print(self.dir)
elseif action_id == hash("up") and action.pressed then
self.dir = vmath.vector3(0, 1, 0)
self.can_move = false
print(self.dir)
elseif action_id == hash("down") and action.pressed then
self.dir = vmath.vector3(0, -1, 0)
self.can_move = false
print(self.dir)
end
end
end
Если игрок может двигаться(sel.can_move == true), тогда начинаем обрабатывать key_triggers из game.input_binding.
Если игрок нажимает клавишу влево, то задаём направление в self.dir.
По условию вопроса, игрок не может менять направление, пока не достигнет границы.
- Каждый кадр
function update(self, dt)
if self.dir and vmath.length(self.dir) > 0 then
local pos = go.get_position()
local new_pos = pos + self.dir * self.speed * dt
if new_pos.x < LEFT_WALL_X then
new_pos.x = LEFT_WALL_X
self.can_move = true
self.dir = vmath.vector3()
elseif new_pos.x > RIGHT_WALL_X then
new_pos.x = RIGHT_WALL_X
self.can_move = true
self.dir = vmath.vector3()
end
if new_pos.y > TOP_WALL_Y then
new_pos.y = TOP_WALL_Y
self.can_move = true
self.dir = vmath.vector3()
elseif new_pos.y < BOT_WALL_Y then
new_pos.y = BOT_WALL_Y
self.can_move = true
self.dir = vmath.vector3()
end
go.set_position(new_pos)
end
end
Обновляет позицию персонажа каждый кадр, двигая его и не позволяя выходить за заданные границы.
if self.dir and vmath.length(self.dir) > 0 then
Проверяем, есть ли направление движения (self.dir) и не нулевой ли это вектор. Если направление нулевое — объект не двигается и функция заканчивается.
local pos = go.get_position()
local new_pos = pos + self.dir * self.speed * dt
Получаем текущую позицию объекта (pos). Вычисляем новую позицию, прибавляя к текущей вектор направления (self.dir), умноженный на скорость (self.speed) и время между кадрами (dt). Это стандартная формула для движения с постоянной скоростью.
if new_pos.x < LEFT_WALL_X then
new_pos.x = LEFT_WALL_X
self.can_move = true
self.dir = vmath.vector3()
elseif new_pos.x > RIGHT_WALL_X then
new_pos.x = RIGHT_WALL_X
self.can_move = true
self.dir = vmath.vector3()
end
Если новая позиция по оси X меньше левой границы — ставим позицию ровно на левую границу, блокируем дальнейшее движение (self.can_move = true) и сбрасываем направление (движение прекращается). Аналогично, если они выходит за правую границу. Аналогично и для верхней и нижней границ, только проверка идёт по оси y.
go.set_position(new_pos)
Устанавливаем объекту новую позицию — уже ограниченную границами.
Исходный код:
local LEFT_WALL_X = 200
local RIGHT_WALL_X = 400
local TOP_WALL_Y = 400
local BOT_WALL_Y = 200
function init(self)
msg.post(".", "acquire_input_focus")
self.can_move = true
self.dir = vmath.vector3()
self.speed = 100
end
function update(self, dt)
if self.dir and vmath.length(self.dir) > 0 then
local pos = go.get_position()
local new_pos = pos + self.dir * self.speed * dt
if new_pos.x < LEFT_WALL_X then
new_pos.x = LEFT_WALL_X
self.can_move = true
self.dir = vmath.vector3()
elseif new_pos.x > RIGHT_WALL_X then
new_pos.x = RIGHT_WALL_X
self.can_move = true
self.dir = vmath.vector3()
end
if new_pos.y > TOP_WALL_Y then
new_pos.y = TOP_WALL_Y
self.can_move = true
self.dir = vmath.vector3()
elseif new_pos.y < BOT_WALL_Y then
new_pos.y = BOT_WALL_Y
self.can_move = true
self.dir = vmath.vector3()
end
go.set_position(new_pos)
end
end
function on_input(self, action_id, action)
if self.can_move then
if action_id == hash("left") and action.pressed then
self.dir = vmath.vector3(-1, 0, 0)
self.can_move = false
print(self.dir)
elseif action_id == hash("right") and action.pressed then
self.dir = vmath.vector3(1, 0, 0)
self.can_move = false
print(self.dir)
elseif action_id == hash("up") and action.pressed then
self.dir = vmath.vector3(0, 1, 0)
self.can_move = false
print(self.dir)
elseif action_id == hash("down") and action.pressed then
self.dir = vmath.vector3(0, -1, 0)
self.can_move = false
print(self.dir)
end
end
end
Результат:

Можно еще попробовать решить эту проблему с помощью collistion object…
Ого большое спасибо за такой подробный ответ!
Надо попробовать.
Надо было конечно сразу сказать за чем мне это нужно, возможно я совсем не то хочу сделать и есть более простой и более подходящий вариант добиться нужного результата.
Пытаюсь повторить механику этой игры:
мне вообще посоветовали обойтись без физики и использовать простую карту проходимости (двухмерный массив где стена это ноль а пространство в котором можно перемещаться это 1) и использовать go.animate()
я взял свою tileMap и решил обойти её и составить по ней карту проходимости,
но столкнулся с проблемой
я хочу получить номер тайла на позиции игрока например, но получаю ошибку
например при x = 125, y = 180
но если подставить как на рисунке x = 9, y = 10 то возвращается нормальное значение, я не понимаю что не так делаю.
tilemap.getBounds() возвращает 1 1 9 10, как видно в консоли, где x = 1 y = 1 (координаты нижнего левого угла tileMap) 9 и 10 это количество рядов и колонов в tileMap
Вот сама tileMap:
Теперь у меня 2 вопроса ![]()
Правильно ли посоветовали отказаться от физики и смотреть в сторону карты проходимости?
И второй вопрос что я делаю не так при построении карты проходимости, почему получаю ошибку при попытке получить номер тайла на позиции игрока?
Если с помощью go.animate() и двухмерный массив…
Создаём тайловую карту для визуального отображения:
local TILE_SIZE = 16 -- Размер тайла
-- Карта проходимости (0 - стена, 1 - проход)
local passability = {
{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,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,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},
}
local function is_passable(x, y)
-- Проверяем, что координаты в пределах карты и проход возможен
if y >= 1 and y <= #passability and x >= 1 and x <= #passability[1] then
return passability[y][x] == 1
end
return false
end
function init(self)
msg.post(".", "acquire_input_focus")
-- Начальная позиция игрока в координатах тайлов
self.tile_x = 2
self.tile_y = 2
self.moving = false
-- Установим позицию игрока по центру тайла
local start_pos = vmath.vector3(
self.tile_x * TILE_SIZE + TILE_SIZE / 2,
self.tile_y * TILE_SIZE + TILE_SIZE / 2,
0
)
go.set_position(start_pos)
end
local function animate_move(self, tile_x, tile_y)
local target_pos = vmath.vector3(
tile_x * TILE_SIZE + TILE_SIZE / 2,
tile_y * TILE_SIZE + TILE_SIZE / 2,
0
)
self.moving = true
-- Сохраняем self в локальную переменную для функции обратного вызова
local object = self
go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, target_pos, go.EASING_LINEAR, 0.15, 0, function()
-- Обновляем координаты
object.tile_x = tile_x
object.tile_y = tile_y
-- Проверяем, можно ли продолжить движение в текущем направлении
if object.direction then
local next_x = object.tile_x + object.direction.dx
local next_y = object.tile_y + object.direction.dy
if is_passable(next_x, next_y) then
-- Продолжаем движение в том же направлении
animate_move(object, next_x, next_y)
else
-- Столкнулись со стеной, останавливаемся
object.moving = false
object.direction = nil
end
else
-- Нет направления, останавливаемся
object.moving = false
end
end)
end
local function try_move(self, dx, dy)
if self.moving or self.direction then
return -- Игрок уже двигается или направление зафиксировано
end
local next_x = self.tile_x + dx
local next_y = self.tile_y + dy
if is_passable(next_x, next_y) then
-- Фиксируем направление
self.direction = { dx = dx, dy = dy }
animate_move(self, next_x, next_y)
end
end
function on_input(self, action_id, action)
if action.pressed then
if action_id == hash("left") then
try_move(self, -1, 0)
elseif action_id == hash("right") then
try_move(self, 1, 0)
elseif action_id == hash("up") then
try_move(self, 0, 1)
elseif action_id == hash("down") then
try_move(self, 0, -1)
end
end
end
Можно подогнать экран под уровень:
Результат:

P.S на днях сделаю урок по созданию этой механики для новичков
Лучше поздно, чем никогда:




