В предыдущем рассказе я описал базовые шаги по настройке проекта. Прочитайте его, если еще не сделали этого. В этот раз речь пойдет о немного более сложных вещах, таких, как управление состоянием, компоненты высшего порядка и Gamepad API. Шаг 6. Движение Как вообще-то передвигать персонажа (который есть набор отдельных 3D-объектов) в пространстве? Я уже рассказывал ранее в 4 шаге о том, об объединении объектов в группу. Еще раз: вы можете перемещать группу с линейной скоростью; в сумме с подходящей анимацией это будет выглядеть как ходьба или бег, в зависимости от вашей задумки. То есть если стоит задача передвигать NPC в каком-то направлении, скажем, на север, то нужно всего лишь: Повернуть его в этом направлении Мало-по-малу передвигать Самый удобный, по-моему, способ - это использовать композицию. Для этого я создам файл /hoc/Movable.jsx со следующим поведением: Этот компонент берет всех потомков и перемещает их в направлении вектора heading в каждом новом кадре. Свойство Velocity отвечает за то, как далеко он это делает. Обратите внимание на onMove: о нем мы поговорим чуть позже. Кстати, добавился еще хелпер /helpers/getRotation.js который делает некоторую магию школы кватернионов, чтобы собственно поворачивать группу. Теперь, если обернуть персонажа в компонент <Movable /> то он будет двигаться. Зацените демо: Шаг 7. Поддержка геймпада Раньше, вплоть до начала карантина, у меня не было геймпада. Я купил его просто попробовать. И оказалось, что это довольно удобный способ управления (никогда бы не подумал, но Diablo II: Resurrected с геймпадом - это что-то с чем-то). А в chrome поддержка геймпадов была еще давным-давно. Так что же мешает поэкспериментировать? Gamepad API - довольно простая штука; можно даже брать код из примеров. Но чтобы заморачиваться еще меньше, я буду использовать библиотеку Gamepads. Используя контекст в реакте можно просто описать весь связующий код в одном месте и получать информацию о нажатиях кнопок, направлении и силе нажатия на стики прямо в компоненте Movable. Вот тут можно посмотреть реализацию: /GamepadControls.jsx — колбеки для геймпада и клавиш WASD на клавиатуре (ссылка); /Player.jsx — небольшая обертка над моделью игрока, которая и будет реагировать на ваши действия (ссылка). Теперь я заменю всех NPC в index.js этим: И все! Используйте геймпад, если он у вас, конечно, есть, или клавиатуру. Следующий важный вопрос: "Как соединить всех зомби, игрока и файрболы? Как враги узнают, где находится игрок?". И ответ - вы его знаете - глобальное состояние. Шаг 8. Управление состоянием Бесспорно, никакой фронтед-проект в 2021 не обходится без слоя управления состоянием. Игры не исключение. Вы, конечно, можете брать вашу любимую библиотеку, пусть то Redux, MobX, effector или что-то еще. В этом вообще никакой разницы. Я выбрал Zustand. Все сущности можно представить коллекциями. Добавляем сущность (зомби или файрбол) в нужную коллекцию, рендерим, потом в нужный момент (файрбол врезается в зомби, или улетает слишком далеко) просто удаляем. Думаю, все не раз это проделывали, только сущности были другими, к примеру, товары в интернет-магазине. Добавились следующие хранилища в папке /store: player zombies fireballs Надеюсь, названия методов говорят сами за себя. Player хранит позицию (она должна быть доступна всем) и жизни игрока; zombies - id каждого врага, их местоположение и hp. Наконец, fireballs используется для управления количеством и направлениями всех файрболов в сцене. Теперь компонент Player выглядит так: А Fireball - так: И еще зацените компонент Zombie особенно вот эти места: Теперь зомби преследуют вас, куда бы вы ни пошли. Полный код этого примера доступен тут, за одним исключением: в codesandbox какие-то проблемы с парсингом .fbx, так что все модели там заменены на цветные кубики. Спасибо за внимание! В следующий раз я разберу вопрос столкновений и UI. Дополнение Есть вероятность, что ваш геймпад не заработает в демке. Проблема в маппинге. У меня самый простой геймпад со стандартным маппингом, а за нестандартным обращайтесь к документации библиотеки Gamepads.