Прогресс, сложности... Ну, как обычно...
Всем привет!
Хочу поделиться своим текущим прогрессом и проблемами — никуда от них не деться! В прошлый раз я рассказывал о начале разработки своего инструмента для игры на базе библиотеки для 3D-рендера Three.js.
Сначала я решил создать менеджер состояний. В этот класс добавляются состояния, которые наследуются от базового класса State
:
export class State extends ObjectBase {
// Инициализация
init(): boolean {
return true;
}
// Загрузка/подготовка ресурсов
load(): boolean {
return true;
}
// Выгрузка ресурсов
unload(): boolean {
return true;
}
// Вход в состояние
enter(): boolean {
return true;
}
// Выход из состояния
exit(): boolean {
return true;
}
// Отрисовка игры
render() {}
}
В свою очередь он наследуется от класса ObjectBase
, который даёт свойство идентификатора (ID
) для быстрой определения в списках. Они попадают в менеджер, назначается текущее состояние и выполняется его обработка. Тут ничего необычного: создаём состояние меню, игры… Описываем логику его работы и переключения. Но внутри текущего состояния у нас могут быть субсостояния: пауза в игре, настройки в меню
Следующим на очереди стала система обработки пользовательского ввода. За отлов событий отвечает браузер; задача разработчика — подписаться на них и обработать результаты.
// Пример
window.addEventListener('keydown', (event) => {
// Нажали кнопку на клавиатуре
});
window.addEventListener('keyup', (event) => {
// Отпустили кнопку на клавиатуре
});
Таких событий в браузере предостаточно. Класс обработки ввода подписывается на события и записывает, что пользователь нажал. Мне достаточно потом проверить данные.
if (inputManager.isKeyboardDown(KeyCode.A)) {}
if (inputManager.isMouseDown(MouseKeyCode.Left)) {}
if (inputManager.isTouchDown(0)) {}
if (inputManager.isKeyboardUp(KeyCode.A)) {}
if (inputManager.isMouseUp(MouseKeyCode.Left)) {}
if (inputManager.isTouchUp(0)) {}
// События Pressed (зажата) в браузере нет, это уже моя обработка
if (inputManager.isKeyboardPressed(KeyCode.A)) {}
if (inputManager.isMousePressed(MouseKeyCode.Left)) {}
if (inputManager.isTouchPressed(0)) {}
Такие возможности хоть и есть, но они не совсем удобны. Поэтому я добавил создание Action
. Это знакомо пользователям игровых движков Godot Engine, Defold и других.
interface Action {
// Наименование
name: string;
// Клавиатура
keys?: string[];
// Мышь
mouseButton?: number[];
// Касание
touch?: number;
// Обработчики
onDown?: () => void;
onPressed?: () => void;
onUp?: () => void;
}
В таком виде проще манипулировать вводом, сохранять, назначать и обрабатывать. Можно назначить сразу обработчики, а можно считывать состояние:
inputManager.addAction({
name: 'jump',
keys: [KeyCode.W, KeyCode.Space, KeyCode.ArrowUp],
mouseButton: [MouseKeyCode.Left],
onDown: () => console.log('Jump down!'),
});
// Обработка
if (inputManager.isActionDown('jump')) {}
if (inputManager.isActionUp('jump')) {}
if (inputManager.isActionPressed('jump')) {}
Далее кратко опишу остальные классы:
- Менеджер событий: регистрация, подписка на события и их вызов с параметрами.
- Менеджер команд: создание команд и групп команд, выполнение, отмена действий (с историей), могу быть постоянными.
- Менеджер задач: похож на менеджер команд, но без истории; задачи могут выполняться с задержкой, иметь приоритеты.
- Менеджер поведений: создание и привязка поведений к объектам (аналогично скриптам и поведениям в Construct/GDevelop).
- Класс работы со звуками.
- Класс работы с ресурсами.
- Система частиц (собственная реализация, так как в Three.js её нет). В ней ещё нет абсолютно всех возможностей, которые есть в игровых движках, но в зависимости от необходимости буду реализовать.
- Менеджер пользовательского интерфейса (также собственная реализация). Текущий функционал позволяет создавать надписи, кнопки и размещать в соответствии в указанными якоря на экране игрока. Будет расширяться по мере необходимости.
Вот такой набор в данный момент у меня получился. Некоторые классы похожи даже, а некоторыми может и пользоваться я не буду, всё зависит от проекта. Конечно, к этому прибавляются библиотеки по реализации шаблона ECS (miniplex), анимации (tween.js) и другие. Этого всего мне вполне хватает для разработки игры. Для проверки этого я решил сделать простую игру, как я делаю при изучении игровых движков. Взял за основу клон игры Stack, что я делал на Ct.js. Но я остановился…
Во время всего этого у меня возникли проблемы связанные с дизайном игры Взлом Эвереста. Графика для меня самый сложный вопрос, я не художник и не могу позволить себе изобразить всё, что захочу. В прошлых проектах мне удавалось находить решения.
Я пытался экспериментировать в разных направлениях. Был и пиксель-арт (участие в мероприятии по созданию обложки игры для приставки GameBoy):
Использование прошлых моделей:
Попытки в 3D с программой Blockbench:
Простой юнит: открылся — выстрелил — закрылся
Планировал, помимо такой простой анимации и визуальных эффектов, добиться приемлемой картинки. Но всё это не вызывает у меня чувства удовлетворения и радости. Поэтому я решил вернуться к направлению, в котором, по моему мнению, у меня лучше получается — это 2D с векторной графикой. Есть идеи по отрисовке юнитов, интерфейса, анимаций. Буду пробовать, но однозначно возвращаюсь в 2D.
А что с инструментам? Я буду использовать те наработки, которые сделал за это время. За исключением того, что я меняю библиотеку для рендера: вместо Three.js буду использовать Pixi.js. Это легко меняется, так как у моих классов почти нет привязки к классам библиотеки, и можно легко жонглировать (в случае необходимости заменить её на Phaser или другой фреймворк или библиотеку, что не повлияет на функционал игры). Я пишу «почти», потому что менеджер пользовательского интерфейса и система частиц уже привязаны к Three.js. Но для Pixi.js есть отдельные библиотеки для этих функций, поэтому эти классы исключаются. Сейчас занимаюсь маленьким проектом для знакомства с новыми возможностями и прорабатываю стиль игры. Такой прогресс у меня получился на текущий момент.
Спасибо, что дочитали до конца! =)
- 02 декабря 2024, 14:10
- 08
2 комментария