В сегодняшнем уроки мы создадим WASD — управление персонажа с помощью ECS evovled:

Затронутые темы:
Что такое delta-time (dt) и зачем он нужен?
Векторы. Движение игрового объекта
Векторы. Движение к цели. Вычисление длины вектора vmath.length(). Нормализация вектора vmath.normalize()
Подготовка к написанию кода:
-
Создаём и добавляем необходимые файлы в структуру проекта:
-
Структура игрового объекта:

-
Создаём игровой объект с фабрикой игровых объектов в
main.collection:
-
Настраиваем привязки ввода в
game.input_binding:
Вне функций:
Развернуть:
Подключаем evolved модуль в скрипт:
local evolved = require("modules.evolved")
Создаём константы для удобства
-- Получаем центральные координаты экрана игры, используя размеры из конфига `game.project`
local WIDTH_CENTER = tonumber(sys.get_config("display.width")) / 2
local HEIGHT_CENTER = tonumber(sys.get_config("display.height")) / 2
-- для оптимизации производительности и удобства работы с системой ввода
local KEY_LEFT = hash("left")
local KEY_RIGHT = hash("right")
local KEY_UP = hash("up")
local KEY_DOWN = hash("down")
-- Константа для скорости
local MAX_SPEED = 150
В init():
Развернуть:
Создаём таблицу, которая будет хранить состояния клавиш(нажатие, отпускание):
self.key = {
left = false,
right = false,
up = false,
down = false
}
Создаём фрагменты:
self.velocity = evolved.id()
self.position = evolved.id()
self.go_url = evolved.id()
self.steer = evolved.id()
Собираем сущность из фрагментов:
self.player_e = evolved.builder()
:set(self.velocity, vmath.vector3(0, 0, 0))
:set(self.position, vmath.vector3(WIDTH_CENTER, HEIGHT_CENTER, 0))
:set(self.go_url, factory.create("#factory"))
:set(self.steer, vmath.vector3(0, 0, 0))
:spawn()
Создаём запрос для системы движения:
self.movement_query = evolved.builder()
:include(self.velocity, self.position, self.go_url)
:build()
Создаём систему движения:
self.movement_system = evolved.builder()
:query(self.movement_query)
:execute(function(chunk, entities, count)
local positions = chunk:components(self.position)
local velocities = chunk:components(self.velocity)
local go_urls = chunk:components(self.go_url)
local dt = self.dt
for i=1, count do
positions[i] = positions[i] + velocities[i] * dt
go.set_position(positions[i], go_urls[i])
end
end)
:build()
Создаём запрос для системы обработки ввода:
self.steer_query = evolved.builder()
:include(self.steer, self.velocity)
:build()
Создаём систему “руления”:
self.steer_system = evolved.builder()
:query(self.steer_query)
:execute(function(chunk, entities, count)
local steers = chunk:components(self.steer)
local vels = chunk:components(self.velocity)
for i=1, count do
local steer_dir = steers[i]
if vmath.length(steer_dir) > 0 then
vels[i] = vmath.normalize(steer_dir) * MAX_SPEED
else
vels[i] = vmath.vector3(0, 0, 0)
end
end
end)
:build()
Запрашиваем фокус ввода:
msg.post(".", "acquire_input_focus")
В update():
Развернуть:
Сохраняем delta-time для использования в системы движения, запускаем системы:
function update(self, dt)
self.dt = dt
evolved.process(self.steer_system)
evolved.process(self.movement_system)
end
В on_input():
Развернуть:
function on_input(self, action_id, action)
if action_id == KEY_LEFT then self.key.left = action.pressed or (self.key.left and not action.released) end
if action_id == KEY_RIGHT then self.key.right = action.pressed or (self.key.right and not action.released) end
if action_id == KEY_DOWN then self.key.down = action.pressed or (self.key.down and not action.released) end
if action_id == KEY_UP then self.key.up = action.pressed or (self.key.up and not action.released) end
local x = (self.key.right and 1 or 0) - (self.key.left and 1 or 0)
local y = (self.key.up and 1 or 0) - (self.key.down and 1 or 0)
evolved.set(self.player_e, self.steer, vmath.vector3(x, y, 0))
end
Полный код:
Развернуть:
local evolved = require("modules.evolved")
local WIDTH_CENTER = tonumber(sys.get_config("display.width")) / 2
local HEIGHT_CENTER = tonumber(sys.get_config("display.height")) / 2
local KEY_LEFT = hash("left")
local KEY_RIGHT = hash("right")
local KEY_UP = hash("up")
local KEY_DOWN = hash("down")
local MAX_SPEED = 150
function init(self)
self.key = {
left = false,
right = false,
up = false,
down = false
}
-- Создаём фрагменты
self.velocity = evolved.id()
self.position = evolved.id()
self.go_url = evolved.id()
self.steer = evolved.id()
-- Создаём сущность
self.player_e = evolved.builder()
:set(self.velocity, vmath.vector3(0, 0, 0))
:set(self.position, vmath.vector3(WIDTH_CENTER, HEIGHT_CENTER, 0))
:set(self.go_url, factory.create("#factory"))
:set(self.steer, vmath.vector3(0, 0, 0))
:spawn()
-- Запрос для системы движения
self.movement_query = evolved.builder()
:include(self.velocity, self.position, self.go_url)
:build()
-- Система движения
self.movement_system = evolved.builder()
:query(self.movement_query)
:execute(function(chunk, entities, count)
local positions = chunk:components(self.position)
local velocities = chunk:components(self.velocity)
local go_urls = chunk:components(self.go_url)
local dt = self.dt
for i=1, count do
positions[i] = positions[i] + velocities[i] * dt
go.set_position(positions[i], go_urls[i])
end
end)
:build()
--Создаём запрос обработки ввода
self.steer_query = evolved.builder()
:include(self.steer, self.velocity)
:build()
-- Система управления движения
self.steer_system = evolved.builder()
:query(self.steer_query)
:execute(function(chunk, entities, count)
local steers = chunk:components(self.steer)
local vels = chunk:components(self.velocity)
for i=1, count do
local steer_dir = steers[i]
if vmath.length(steer_dir) > 0 then
vels[i] = vmath.normalize(steer_dir) * MAX_SPEED
else
vels[i] = vmath.vector3(0, 0, 0)
end
end
end)
:build()
msg.post(".", "acquire_input_focus")
end
function update(self, dt)
self.dt = dt
evolved.process(self.steer_system)
evolved.process(self.movement_system)
end
function on_input(self, action_id, action)
if action_id == KEY_LEFT then self.key.left = action.pressed or (self.key.left and not action.released) end
if action_id == KEY_RIGHT then self.key.right = action.pressed or (self.key.right and not action.released) end
if action_id == KEY_DOWN then self.key.down = action.pressed or (self.key.down and not action.released) end
if action_id == KEY_UP then self.key.up = action.pressed or (self.key.up and not action.released) end
local x = (self.key.right and 1 or 0) - (self.key.left and 1 or 0)
local y = (self.key.up and 1 or 0) - (self.key.down and 1 or 0)
evolved.set(self.player_e, self.steer, vmath.vector3(x, y, 0))
end


