Портируем старую игру в жанре «shoot 'em up» на JavaScript на коленке

    Имеется древняя игрушка LaserAge, которая написана на Flash (на очень древнем Macromedia Flash 4) и работает только под Windows. В детстве она мне очень понравилась, поэтому я решил для души портировать её, чтобы можно было играть с браузера со всех устройств.


    Цель игры заключается в том, чтобы уничтожать противников своим космическим кораблём на различных уровнях и получать бонусы, если поймать бонус — улучшается оружие. При попадании торпеды противника — даунгрейд оружия игрока.


    При уничтожении всех противников на уровне происходит переключение на следующий уровень. Всего 100 уровней.


    В терминах игры уровень — волна (Wave), а несколько волн объединены в большой уровень (Level), который представляет из себя просто смену заднего фона, т.е. всего 4 больших уровня в каждом из которых 25 волн. В последней волне большого уровня обычно бывает босс — противник с огромным значением жизни и мощным оружием.


    https://github.com/EntityFX/laseroid/blob/master/doc/LaserAgeNext.png?raw=true


    Бизнес логика игры


    Игровое пространство


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


    Область движения игрока ограничена так, что он не может сталкиваться с кораблями противника, а корабли противника с игроком.


    https://github.com/EntityFX/laseroid/blob/master/doc/Stage.png?raw=true


    Оружие


    Оружием обладает космический корабль игрока и корабли противника.
    Оружие игрока может быть ручным (стреляет при нажатии мыши) и дополнительное автоматическое (стреляет периодами).


    Оружие стреляет торпедами, алгоритм движения которых очень примитивный: торпеды противника движутся на игрока (сверху вниз), а торпеды игрока движутся снизу вверх.
    При попадании торпеды противника в игрока вычитается 1 уровень жизни (апгрейда), при 0 игра завершается поражением.


    Оружие космического корабля игрока


    • Торпеда — стреляет маленькими ракетами
      • Одинарная Торпеда — 1 уровень апгрейда
      • Двойная — 2 уровень апгрейда
      • Тройная — 3 уровень апгрейда
    • Автоматические пушки
      • Дополнительная автоматическая Торпеда слева корабля — 4 уровень апгрейда
      • Дополнительная автоматическая Торпеда справа корабля — 5 уровень апгрейда
    • Зелёная плазма — 6 и 7 уровень апгрейда (увеличивается скорострельность)
    • Фиолетовая плазма — 8 уровень апгрейда (наносит урон всем противникам по траектории полёта)
    • Зелёный лазер — 9 уровень (наносит урон всем противникам, а также активно одну секунду, тем самым можно задеть соседних противников)

    Дополнительное оружие:


    • Красная плазма — 15-19 уровень (наносит урон всем противникам, а также активно одну секунду, тем самым можно задеть соседних противников)
    • Зелёная плазма — 20-24 уровень
    • Синяя плазма — 25-29 уровень апгрейда
    • Фиолетовая плазма — 30-34 уровень апгрейда
    • Фиолетовая плазма — 30-34 уровень апгрейда
    • Дополнительная автоматическая Торпеда слева стреляет желтой плазмой — 35 — 39 уровень апгрейда
    • Дополнительная автоматическая Торпеда справа стреляет желтой плазмой — 40+ уровень апгрейда

    Таблица с характеристиками оружия игрока


    Оружие 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
                    }
                ]
            }
        ]
    }

    Бонусы


    Специальный вид противника


    https://raw.githubusercontent.com/EntityFX/laseroid/master/resources/laser-age/graphics/PowerUps_1.png, который не имеет оружия и при уничтожении порождает спрайт с бонусом


    https://raw.githubusercontent.com/EntityFX/laseroid/master/resources/laser-age/graphics/Upgrade.png, который должен поймать корабль игрока. Если игрок поймает бонус, то увеличивается его уровень (жизнь).


    Уровни


    Каждый уровень содержит множество различного вида противников, которые расположены определённым образом. Также уровень может содержать один и более бонусов.


    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 библиотеки для реализации


    Я просмотрел множество библиотек графики для JavaScript, но остановился на Hexi JS: https://github.com/kittykatattack/hexi .


    Возможности библиотеки:


    • Простота
    • Рисование примитивов
    • Рисование просты интерфейсов (кнопки, события)
    • Перемещение, масштабирование, вращение
    • Рисование спрайтов
      • Анимированные спрайты
      • Работа со спрайтами как с объектами
      • Загрузка спрайтов в виде большой текстуры-атласа. Можно разместить множество изображений в одном файлы и на выходе получить одну большую текстуру и JSON файл с описанием спрайтов (область, смещение)
    • Логика столкновений
    • Работа с устройствами ввода (клавиатура), тач-скрин.

    Пример текстуры-атласа создаваемого с помощью программы TexturePacker


    https://github.com/EntityFX/laseroid/blob/master/doc/ships-atlas-texture.png?raw=true


    Звуковая библиотека: https://github.com/kittykatattack/sound.js


    Возможности библиотеки:


    • Простота
    • Воспроизведение звуков
    • Воспроизведение музыки
    • Эффекты

    Архитектура


    Общая диаграмма классов:


    https://github.com/EntityFX/laseroid/blob/master/doc/diagrams/game.png?raw=true


    Ядро игры


    https://github.com/EntityFX/laseroid/blob/master/doc/diagrams/core.png?raw=true


    Класс Main


    Является точкой входа и контейнером игрового кода.


    Поля:


    • resources — содержит список всех загружаемых ресурсов (текстуры, звук, json)
    • sounds — словарь звуков: Ключ — название, Значение — путь
    • gameScene — объект HexiJS на
    • game — экземпляр объекта Game
    • hexi — инстанс HexiJS
    • gameStorage — сохраняет состояние игры в localStorage

    Методы:


    • init() — инициализирует HexiJS
    • load() — загружает ресурсы (текстуры, звук, json)
    • setup() — устанавливает игровую область, события нажатия кнопок, запускает фоновую музыку
    • playLoop() — точка изменения состояния игры (считает движение, коллизии, снаряды, перерисовывает пространство).
    • saveGame() — сохраняет игру
    • loadGame() — загружает игру

    Пример списка ресурсов текущей реализации игры:


    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",
        ];

    Класс Game


    Основной класс игры.


    Поля:


    • level — информация о уровне. Значение: { "wave": 1 //номер волны, "type": 1 }
    • score — информация об очках. Значение: {"points": 0 }
    • bulletsController — Экземпляр класса BulletsController. Управляет поведение торпед и лазеров оружия
    • enemyController — Экземпляр класса EnemyController. Управляет поведением всех противников на уровне (в т.ч. и бонусами)
    • player — Экземпляр Player
    • hexi — экземпляр класса Hexi (ссылка)
    • game — экземпляр объекта Game
    • gameStorage — экземпляр объекта GameStorage

    Методы:


    • clearShips() — очистка всех проиивников, бонусов
    • setupLevel() — настроить уровень (добавить противников, бонусы, расстановка)
    • nextLevel() — переход на следующий уровень
    • previousLevel() — переход на предыдущий уровень
    • forwardLevel() — перепрыгнуть на несколько уровней вперёд (на 5)
    • rewindLevel() — перепрыгнуть на несколько уровней назад (на 5)
    • restoreState(gameState: JSON) — восстановить по объекту gameState
    • resetGame() — сбросить игру (начать сначала)
    • update() — обновить игровой мир
    • enemyDestroyed() — обработчик срабатывает при уничтожении всех противников

    Класс GameStorage


    Сохраняет и загружает состояние игры .


    Поля:


    • game — экземпляр объекта Game

    Методы:


    • save() — сохранить состояние игры
    • load() — загрузить состояние игры

    Класс InputDevice


    Работает с событиями устройств ввода: click и touch кнопок, нажатие клавиш клавиатуры.


    Поля:


    • game — экземпляр объекта Game

    Методы:


    • init() — инициализирует все обработчики события и callback'и
    • loadTapped() — нажата кнопка "Load"
    • storeTapped() — нажата кнопка "Store"
    • resetTapped() — нажата кнопка "Reset"
    • pauseTapped() — нажата кнопка "Pause"

    Иерархия классов действующих лиц


    https://github.com/EntityFX/laseroid/blob/master/doc/diagrams/actors.png?raw=true


    Actor


    Класс участника.


    Поля:


    • hexi — экземпляр класса Hexi (ссылка)
    • game — экземпляр объекта Game
    • life — текущее значение жизни
    • initialLife — начальное значение жизни
    • sprite — экземпляр класса Hexi.Sprite
    • shipConfiguration — конфигурация бонуса

    Методы:


    • move() — переместить действующее лицо
    • update() — обновить действующее лицо
    • setPosition(position: {x, y}) — установить по координатам

    WeaponedActor


    Класс участника (противник или игрок) обладающем оружием.


    Поля:


    • automatedWeapons — массив автоматических оружий
    • canShoot — может ли стрелять
    • isWeaponShooting — активно ли оружие

    Методы:


    • startShoot() — запустить выстрелы оружием
    • stopShoot() — остановить выстрелы оружием
    • onShootStarted() — обработчик события, что запущены выстрелы оружием
    • onShootStopped() — обработчик события, что остановлены выстрелы оружием
    • updateShooting() — выполняет алгоритмы выстрелов

    Enemy


    Класс противника.


    Поля:


    • type — тип противника
    • syncWeapons — массив конфигураций для синхронного оружия
    • movementEngine — экземпляр класса MovementEngine

    Методы:


    • setWeapon() — установить оружие используя текущую конфигурацию
    • shootWithWeapon() — выполняет выстрел противником
    • setLifeLine() — рисует линию жизни противника
    • hit() — проверяет столкновение торпед (лазера) игрока с текущим противником

    MovementEngine


    Класс управляющий движением.


    Для придания сложности движения, используется конфигурация со слотами. В каждом слоте задаётся вектор направления vx, vy и интенсивность. Имеется возможность отключения отражения от нижней границы и режим слежения за игроком (противник всегда движется за игроком).


    Поля:


    • movementsConfiguration — конфигурация движения
    • firstMovementConfiguration — первый элемент из списка конфигураций
    • movementItensity — интенсивность движения
    • movementItensityCounter — счётчик интенсивности движения
    • movementItensitySlot — номер слота интенсивности
    • isBounceBottom — флаг на проверку отражения от нижней границы. Если false, то противник не отражается от нижней границы

    Методы:


    • setMovement() — настраивает движение
    • updateMovement() — обновляет движение по конфигурации движения

    Player


    Класс игрока.


    Поля:


    • weapons — массив оружия игрока
    • collisionSprite — спрайт коллизии (торпеды противника сталкиваются со спрайтом коллизии, а не спрайтом игрока)
    • weaponLifeLevels — значения уровня жизни для проверки на апгрейд оружия
    • invisibilityCounter — счётчик невидимости от торпед (нужен для того, чтобы при столкновении с торпедой противника игрок стал временно недосягаем для других торпед)

    Методы:


    • upgrade() — апгрейд игрока (+1 жизнь)
    • downgrade() — даунгрейд игрока (+1 жизнь)
    • shootWithLaser(currentWeapon, weapon) — выстрел лазером
    • shootWithBullets(currentWeapon, weapon) — выстрел торпедой
    • setWeapon() — установить оружие используя текущую конфигурацию
    • setLife(life: number) — установить значение жизни (меняет оружие в соответствии со значением жизни)
    • hitUpgrade(upgradeItem) — проверить столкновение со спрайтом апгрейда

    Bonus


    Класс Бонуса. При уничтожении порождает спрайт апгрейда.


    Поля:


    • type — тип бонуса
    • movementEngine — экземпляр класса MovementEngine
    • upgradeBonus — конфигурация апгрейда

    Методы:


    • shootWithUpgrade(upgradeBonus: JSON) — породить спрайт апгрейда

    EnemyController


    Управляет состоянием противников, бонусов, апгрейдами.


    Поля:


    • enemies — массив всех противников на уровне
    • bonuses — массив всех бонусных кораблей на уровне
    • player — объект игрока
    • upgrades — массив всех спрайтов апгрейда

    Методы:


    • isLevelCompleted() — проверка на завершённость уровня (уничтожены все противники и бонусы, пойманы апгрейды)
    • update() — обновляет состояние всех противников
    • clear() — очистка уровня от проиивников, бонусов

    BulletsController


    Управляет состоянием торпед (перемещение), лазерами игрока и противников.


    Поля:


    • playerBullets — массив торпед игрока
    • enemyBullets — массив торпед всех противников
    • explosionSplashes — массив спрайтов взрыва
    • playerLaser — состояние спрайта лазера игрока (Если оружие доступно).

    Методы:


    • update() — обновляет состояние всех торпед, лазеров
    • clear() — очищает уровень от всех торпед, лазеров
    • updatePlayerBullets() — изменяет состояние всех торпед игрока
    • updatePlayerLaser() — изменяет состояние лазера игрока
    • updateEnemyBullets() — изменяет состояние всех торпед противника
    • updateExplosions() — изменяет состояние всех взрывов

    Выводы


    Я постарался достаточно подробно рассказать как портировать существуюшую игру на JavaScript.


    Игру можно модифицировавть и снабдить своими текстурами, музыкой, сделать собственную конфигурацию уровня или вариацию всей игры.


    Очень хотелось бы получить от вас комментарии по архитектуре игры, варианты рефакторинга, по именованию методов и т.д.


    Согласен, архитектура не очень идеальная и есть куда стремиться!


    Спасибо и интересных Вам проектов!


    Ссылки


    http://laseroid.azurewebsites.net/ — сама игра
    https://github.com/EntityFX/laseroid — исходный код игры

    ICL Services
    Цифровые технологии для бизнеса

    Комментарии 10

      0
      Модель, логика, графика — готовы. как на счет UNITY
        –1
        Нужно было сделать максимально просто, а так разве Юнити умеет в веб?
        +3
        Так портирование или всё же воссоздание?

        по реализации:
        1. нет полноэкранного режима, из за этого стоит мышке выйти за границу как теряется управление.
        2. Нет описания горкнопок и части функций тоже. (например музыка вкл/выкл, пауза вкл/выкл и т.п., зато на читы есть)
        3. есть подозрение, что в аду уже возвели отдельное помещение для тех, кто для браузерных плееров, игр и т.п. не добавляет ползунок регулятора громкости или по умолчанию выставляет его на максимум )

          0
          1. Да, не хватает. Надо подумать так, чтобы на различных устройствах была поддержка полноэкранного режима.
          2. Добавлю. Решил про интерфейс не указывать.
          3. Про громкость не подумал, реально может заорать ночью. :-)
          0
          Спасибо! С удовольствием почитал и посмотрел реализацию. А какой редактор для рисование ER?
            0
            PlantUML, плюс есть плагин для VS Code.
            +1
            кнопки <> для управления кораблём не работают (браузер хром), кнопка паузы активируется только нажатием мышки, как итог корабль всегда попадает в положение «крайне-лево» (что чревато, если надо срочно поставить на паузу а слева пульки неприятеля падают)

            самый большой косяк — если быстро нажимать атаку корабль не стреляет, передвигаться всегда с зажатой атакой — можно случайно выбить бонус и не взять его, поскольку будет падать вместе с пульками врага

            upd
            +при управлении мышкой постоянно подлагивания при вылете курсора из поля «экрана»

            короче, без обид автор, но исходников на Галакси подобные игрушки полно, макетов в 2Д фришных тоже, от тебя только нормальное управление требовалось, но тут ты «не смог»
              0
              1. Кнопки <> для управления кораблём — не проблема, реализованы были, проверю. Это просто;
              2. Пауза: можно реализовать через нажатие «P» или Pause/End. Это просто;
              3. если быстро нажимать атаку корабль не стреляет — это реализовано специально, чтобы оружие активировалось с задержкой, если игроков это раздражает, то можно изменить логику без проблем (убрать первоначальную задержку в 0). Это относительно просто;
              4. при управлении мышкой постоянно подлагивания при вылете курсора из поля «экрана» — Да, глюки с потерей фокуса в canvas. Надо думать как это пофиксить, боюсь, что придётся не юзать апи HexiJS. Тут уже сложнее.

              Спасибо за тест!
                0
                кнопка P и Pause/Break Не работают (при нажатии на P просто стопорится игра на пол секунды, теоретически что бы держать паузу надо многократно продолжать нажимать P, такая себе пауза))), на стрелки корабль не двигается

                + и да — сложность, потеря одного бонуса (уровня пушки) чревато гэймовером, и чем выше волна тем тем выше шанс по накатанной терять урони пушки, то есть грубо говоря если после 15 волны ты потерял уровень орудия (в тебя попали), то вероятность потери следующего уровня становятся в разы выше и так далее, тут уже прогрессия работает. при всём этом, при взятии нового уровня орудия, ты не получаешь должной прибавки урона, как максимум — ты восстанавливаешь свой дэмэдж, при условии поднятии бонуса на том же уровне, на котором ты потерял свой уровень оружия (получил ранение), в худшем случае — ты откатываешься на 2-3 уровня назад по урону. то есть фактически привязка уровня орудия к количеству жизней — не имеет никакой роли, поскольку потеряв 1 лишь уровень орудия волне на 10 ты уже не сможешь восстановить полноценный урон и обречён на проигрыш волне к 20-22. принимая во внимание лёгкость первых уровней, то можно считать что при любом попадании по кораблю — можно начинать игру заново. пропуск падающего бонуса приводит так же фактически к попаданию по тебе и последующему проигрышу.

                ЗЫ и да, пару раз у меня игра зависала по время прорисовки спрайтов взрыва при попадании по кораблям противника, волнах на 23 и 16
                  0

                  А тут и нет реализации паузы, так как руки до этого не доходили. Значит глюки реализации в Hexi. Кстати, коды кнопок работают по-разному в Chrome и FF.

                  По поводу дизайна уровней — старался воспроизвести максимально оригинал, даже попиксельно координаты выставлял.

                  Зависания я замечал на случайных уровнях. Возможно ли, что в этот момент происходит сборка мусора?

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое