Портируем старую игру в жанре «shoot 'em up» на JavaScript на коленке
- среда, 1 июля 2020 г. в 00:30:30
Имеется древняя игрушка LaserAge, которая написана на Flash (на очень древнем Macromedia Flash 4) и работает только под Windows. В детстве она мне очень понравилась, поэтому я решил для души портировать её, чтобы можно было играть с браузера со всех устройств.
Цель игры заключается в том, чтобы уничтожать противников своим космическим кораблём на различных уровнях и получать бонусы, если поймать бонус — улучшается оружие. При попадании торпеды противника — даунгрейд оружия игрока.
При уничтожении всех противников на уровне происходит переключение на следующий уровень. Всего 100 уровней.
В терминах игры уровень — волна (Wave), а несколько волн объединены в большой уровень (Level), который представляет из себя просто смену заднего фона, т.е. всего 4 больших уровня в каждом из которых 25 волн. В последней волне большого уровня обычно бывает босс — противник с огромным значением жизни и мощным оружием.
[TOC]
Представляет из себя обычную прямоугольную область, в верхней части располагаются корабли противника, а снизу игрок.
Область движения игрока ограничена так, что он не может сталкиваться с кораблями противника, а корабли противника с игроком.
Оружием обладает космический корабль игрока и корабли противника.
Оружие игрока может быть ручным (стреляет при нажатии мыши) и дополнительное автоматическое (стреляет периодами).
Оружие стреляет торпедами, алгоритм движения которых очень примитивный: торпеды противника движутся на игрока (сверху вниз), а торпеды игрока движутся снизу вверх.
При попадании торпеды противника в игрока вычитается 1 уровень жизни (апгрейда), при 0 игра завершается поражением.
Дополнительное оружие:
Таблица с характеристиками оружия игрока
Оружие | Hit Points | Скорость спрайта | Интенсивность | Тип | Дополнительно | Вид |
---|---|---|---|---|---|---|
Торпеда | 1 | 5 | 25 | Торпеда | Одинарная, двойная, тройная | ![]() |
Автоматическая Торпеда | 1 | 5 | 50 | Торпеда | Слева и Справа | ![]() |
Зелёная плазма | 3 | 7 | 30 | Торпеда | ![]() |
|
Фиолетовая плазма | 2 | 8 | 30 | Торпеда | Атакует до 3х целей | ![]() |
Красная плазма | 2 | 4 | 30 | Торпеда | ![]() |
|
Синяя плазма | 4 | 4.5 | 30 | Торпеда | ![]() |
|
Жёлтая плазма | 2 | 3.8 | 40 | Торпеда | Только автоматическая | ![]() |
Зелёный Лазер | 4 | - | 15/55 | Лазер | Атакует до 5ти целей одновременно | ![]() |
Таблица с конфигурацией оружия игрока в зависимости от уровня жизни
Уровень жизни | Конфигурация оружия |
---|---|
1 | Торпеда |
2 | Торпеда + Торпеда |
3 | Торпеда + Торпеда + Торпеда |
4 | Торпеда + Торпеда + Торпеда + Автоматическая торпеда слева |
5 | Торпеда + Торпеда + Торпеда + Автоматическая торпеда слева + справа |
6 | Зелёная плазма + Автоматическая торпеда слева + справа |
7 | Зелёная плазма + Автоматическая торпеда слева + справа |
8 | Фиолетовая плазма + Автоматическая торпеда слева + справа |
9 | Зелёный лазер + Автоматическая торпеда слева + справа |
15 — 19 | Зелёный лазер + Красная плазма + Автоматическая торпеда слева + справа |
20 — 24 | Зелёный лазер + Красная плазма + Автоматическая торпеда слева + справа |
25 — 29 | Зелёный лазер + Синяя плазма + Автоматическая торпеда слева + справа |
30 — 34 | Зелёный лазер + Фиолетовая плазма + Автоматическая торпеда слева + справа |
35 — 39 | Зелёный лазер + Фиолетовая плазма + Автоматическая желтая плазма слева + торпеда справа |
40+ | Зелёный лазер + Фиолетовая плазма + Автоматическая желтая плазма слева + желтая плазма справа |
Таблица с конфигурацией оружия противников
Оружие | Скорость спрайта | Тип |
---|---|---|
Торпеда | 2.5 | Торпеда |
Красная плазма | 3.5 | Торпеда |
Синяя плазма | 4.5 | Торпеда |
Зелёная плазма | 5 | Торпеда |
Синяя Торпеда | 3 | Торпеда |
Жёлтая плазма | 3.2 — 3.8 | Торпеда |
Белая плазма | 4 — 6 | Торпеда |
Зелёный Лазер | - | Лазер |
Для того чтобы исключить предсказуемость поведения оружия игроков, интенсивность имеет псевдослучайный характер.
Интенсивность оружия противников может иметь один и более временных слотов, в каждом отдельно задаётся минимальное и максимальное время фреймов и число повторов. Слот может быть паузой или активным состоянием (стреляет).
Пример конфигурации оружия:
"torpedo": {
"sprite": "Bullet1_1.png", //картинка спрайта
"isRandomIntensity": false, //нужно ли переключать случайно слоты - true или по порядку - false
"intensity": [
//слот 0
{
"min": 50, //минимальное число фреймов
"max": 200, //максимальное число фреймов
"type": "pause" //pause - оружие неактивно, shoot - активное (стреляет)
},
//слот 1
{
"min": 100,
"max": 200,
"type": "shoot"
},
{
"min": 50,
"max": 80,
"type": "pause"
},
{
"min": 30,
"max": 100,
"repeat": 2
}
],
"speed": 2.5, //скорость
"type": "bullet", //тип оружия
"sound": "alienTorpedo"
}
Корабль игрока может перемещаться в ограниченной области, чтобы не пересекаться с кораблями противников.
Управляется движением мыши или стрелочками ←
и →
. На экране мобильного телефона тапом и движением по экрану.
Оружие активирует при удержании левой клавиши мыши (тапом и удержанием по экрану на мобильном телефоне).
Для разнообразия на каждом уровне может быть различное число противников, каждый из которых обладает собственным набором оружия, имеет своё значение жизни, различную траекторию движения. противники могут быть обычные или боссами.
Корабль противника | Жизнь | Тип | Движения | Оружие | Вид |
---|---|---|---|---|---|
Чужой 1 | 2 | Обычный | Нормальное горизонтальное | Торпеда | ![]() |
Чужой 2 | 4 | Обычный | Нормальное все направления | Торпеда | ![]() |
Быстрый чужой | 10 | Обычный | Быстрое горизонтальное | Торпеда (Интенсивная) | ![]() |
Фрегат чужого | 10 | Обычный | Нормально-быстрое все направления | Красная плазма | ![]() |
Броневик чужого | 10 | Обычный | Медленное вниз | Торпеда (Очень интенсивная) | ![]() |
Быстрый Фрегат чужого | 30 | Обычный | Медленное вниз (следит за игроком) | Красная плазма (Очень интенсивная) | ![]() |
Красный истребитель | 30 | Обычный | Медленное вниз (следит за игроком) | Синяя плазма | ![]() |
Зелёный истребитель | 30 | Обычный | Быстро вертикально | Синяя плазма | ![]() |
Чужой 1 модификация | 2 | Обычный | Нормальное горизонтальное | Синяя Торпеда | ![]() |
Бомбардировщик | 30 | Обычный | Нормальное все направления (следит за игроком) | Зелёная плазма | ![]() |
Тяжёлый Чужой | 30 | Обычный | Нормальное все направления | Торпеда | ![]() |
Тяжёлый Фрегат Чужого | 35 | Обычный | Нормальное все направления | Синяя Торпеда + Синяя Торпеда | ![]() |
Тяжёлый броневик | 35 | Обычный | Нормальное вниз | Жёлтая Плазма + Жёлтая Плазма + Жёлтая Плазма + Жёлтая Плазма | ![]() |
Линкор | 100 | Босс | Нормальное все направления | Синяя плазма (очень интенсивная) + Зелёная плазма (очень интенсивная) | ![]() |
Крейсер | 250 | Босс | Нормальное все направления | Зелёная плазма (сверх интенсивная) | ![]() |
Тяжёлый Крейсер | 500 | Босс | Быстрое все направления | Жёлтая Плазма + Жёлтая Плазма + Синяя Торпеда + Синяя Торпеда + Синяя Торпеда + Синяя Торпеда + Белая плазма + Белая плазма | ![]() |
Эпичный Тяжёлый Крейсер | 1000 (восстанавливается) | Босс | Быстрое все направления | Жёлтая Плазма + Жёлтая Плазма + Синяя Торпеда + Синяя Торпеда + Синяя Торпеда + Синяя Торпеда + Белая плазма + Белая плазма+ Зелёная плазма (очень интенсивная) | ![]() |
JSON-конфигурация противника:
"alien10": {
"life": 35,
"weapons": [
{
"weapon": "blueTorpedo",
"position": {
"x": -6,
"y": 0
}
},
{
"weapon": "blueTorpedo",
"position": {
"x": 6,
"y": 0
}
}
],
"sprite": "AlienShip10_1.png",
"movement": "horizontalFast",
"killPoints": 2100
}
JSON-конфигурация движения противника :
"horizontalFast": {
"movements": [
{
"type": "freeMovement", //freeMovement - обычное, followPlayer - следит за игроком (движется в направление)
"speedDelta": {
"vx": -6,
"vy": 0
},
"intensity": [ //интенсивность движения в виде слотов
{
"min": 20,
"max": 150
},
{
"min": 150,
"max": 350
}
]
}
]
}
Специальный вид противника
, который не имеет оружия и при уничтожении порождает спрайт с бонусом
, который должен поймать корабль игрока. Если игрок поймает бонус, то увеличивается его уровень (жизнь).
Каждый уровень содержит множество различного вида противников, которые расположены определённым образом. Также уровень может содержать один и более бонусов.
JSON-конфигурация уровня :
"2": {
"level": 1,
"enemies": [ // список противников
{
"id": "alien1",
"position": {
"x": 200,
"y": 35
}
},
//...
{
"id": "alien1",
"position": {
"x": 525,
"y": 40
}
}
],
"bonuses": [ // список бонусов
{
"id": "bonus1",
"position": {
"x": 350,
"y": 10
}
}
]
},
Я просмотрел множество библиотек графики для JavaScript, но остановился на Hexi JS: https://github.com/kittykatattack/hexi .
Возможности библиотеки:
Пример текстуры-атласа создаваемого с помощью программы TexturePacker
Звуковая библиотека: https://github.com/kittykatattack/sound.js
Возможности библиотеки:
Общая диаграмма классов:
Является точкой входа и контейнером игрового кода.
Поля:
Методы:
Пример списка ресурсов текущей реализации игры:
Main.resources = [
"images/environment1.png",
"images/environment2.png",
"images/environment3.png",
"images/environment4.png",
"images/interface.png",
"images/life-icon.png",
"images/ships-texture.json",
"images/bullet-texture.json",
"sounds/alien-torpedo-shoot.wav",
"sounds/alien-red-plasma-shoot.wav",
"sounds/hero-torpedo-shoot.wav",
"sounds/explode.wav",
"sounds/hero-green-plasma-shoot.wav",
"sounds/alien-green-plasma-shoot.wav",
"sounds/alien-blue-torpedo-shoot.wav",
"sounds/alien-yellow-laser.wav",
"sounds/pulse-plasma.wav",
"sounds/laser.wav",
"sounds/track0.ogg",
"sounds/track1.ogg",
"sounds/track2.ogg",
"sounds/track3.ogg",
"sounds/track4.ogg",
"data/hero-configuration.json",
"data/levels-configuration.json",
"data/enemy-configuration.json",
"data/ui-configuration.json",
];
Основной класс игры.
Поля:
{ "wave": 1 //номер волны, "type": 1 }
{"points": 0 }
Методы:
Сохраняет и загружает состояние игры .
Поля:
Методы:
Работает с событиями устройств ввода: click и touch кнопок, нажатие клавиш клавиатуры.
Поля:
Методы:
Класс участника.
Поля:
Методы:
Класс участника (противник или игрок) обладающем оружием.
Поля:
Методы:
Класс противника.
Поля:
Методы:
Класс управляющий движением.
Для придания сложности движения, используется конфигурация со слотами. В каждом слоте задаётся вектор направления vx, vy и интенсивность. Имеется возможность отключения отражения от нижней границы и режим слежения за игроком (противник всегда движется за игроком).
Поля:
Методы:
Класс игрока.
Поля:
Методы:
Класс Бонуса. При уничтожении порождает спрайт апгрейда.
Поля:
Методы:
Управляет состоянием противников, бонусов, апгрейдами.
Поля:
Методы:
Управляет состоянием торпед (перемещение), лазерами игрока и противников.
Поля:
Методы:
Я постарался достаточно подробно рассказать как портировать существуюшую игру на JavaScript.
Игру можно модифицировавть и снабдить своими текстурами, музыкой, сделать собственную конфигурацию уровня или вариацию всей игры.
Очень хотелось бы получить от вас комментарии по архитектуре игры, варианты рефакторинга, по именованию методов и т.д.
Согласен, архитектура не очень идеальная и есть куда стремиться!
Спасибо и интересных Вам проектов!
http://laseroid.azurewebsites.net/ — сама игра
https://github.com/EntityFX/laseroid — исходный код игры