Как заблокировать получение input (РЕШЁН)

Привет, есть необходимость заблокировать или игнорировать ввод с клавиш.
Например пользователь нажал влево и пока объект не долетел до левой стенки нельзя никак изменить направление движение объекта.

1 Like

Приветствую!

Я понял так:
Игрок задаёт направление движения персонажу(влево, вправо, вверх, вниз). Можно выбрать только одно направление, пока персонаж не достигнет границы(левой, правой, верхней, нижней). При достижении одной из границ, мы можем поменять направление движения, но не можем пройти “сквозь” границу.

  1. Для начала нужно добавить привязки ввода в game.input_binding:
    image
  2. Создадим границы(левая, правая, верхняя, нижняя границы) с помощью кода:
local LEFT_WALL_X = 200
local RIGHT_WALL_X = 400
local TOP_WALL_Y = 400
local BOT_WALL_Y = 200
  1. Во время инициализации скрипта
function init(self)
	msg.post(".", "acquire_input_focus") -- разрешаем обрабатывать данные для этого скрипта
	self.can_move = true -- игрок может двигаться
	self.dir = vmath.vector3() -- вектор направления движения(куда персонаж движется)
	self.speed = 100 -- скорость игрока
end
  1. Обработка входных данных в скрипт
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.
По условию вопроса, игрок не может менять направление, пока не достигнет границы.

  1. Каждый кадр
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

Результат:
moving

Можно еще попробовать решить эту проблему с помощью collistion object

1 Like

Ого большое спасибо за такой подробный ответ!
Надо попробовать.

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

мне вообще посоветовали обойтись без физики и использовать простую карту проходимости (двухмерный массив где стена это ноль а пространство в котором можно перемещаться это 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 вопроса :slight_smile:
Правильно ли посоветовали отказаться от физики и смотреть в сторону карты проходимости?
И второй вопрос что я делаю не так при построении карты проходимости, почему получаю ошибку при попытке получить номер тайла на позиции игрока?

Если с помощью 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

Можно подогнать экран под уровень:

Результат:
Не Tomb of the Mask Walkthrough, но всё же)

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

Лучше поздно, чем никогда:

2 Likes