Повтор знаменитой игры с тираннозавром при помощи Flutter и Flame

Егор Долгов · 07.03.2021
This article was written for an older Flutter version and is being updated. Some code may not compile on the current version.

!

Автор оригинальной статьи — Renan C. Araujo

Если вы еще не знакомы с Flutter, то у меня хорошие новости: вам предстоит познакомиться с одним из самых удобных и гибких способов создания пользовательского интерфейса. Так что не теряйте зря времени и узнайте больше о Flutter прямо сейчас!

Приложение и его исходный код доступны по ссылке

С тех пор, как я открыл для себя Flutter, я повидал огромное множество примеров того, насколько многого можно достичь с пользовательским интерфейсом этого фреймворка. Но что насчет создания игры?

Идея

Итак, я начал задаваться вопросом, возможно ли сделать игру при помощи Flutter и какие действия для этого могут потребоваться. Так как у меня нет никакого опыта в разработке игр, я решил, что не буду создавать ее с нуля, а возьму за основу уже существующую (довольно простую и из открытого исходного кода). Идеальным вариантом стал лучший помощник в борьбе со скукой при отсутствии интернета — Ти-Рекс от Гугл Хрома (Google Chrome’s TRex).

Оригинальная версия !

Дисклеймер: Я не являюсь разработчиком игр и даже близкого представления о том, как работают игры, не имею. Так что посмотрим, что из этого выйдет.

Поиски источников

Этот репозиторий Github содержит источник Javascript с оригинальными выполнением и спрайтами. После быстрого взгляда на код, становится весьма понятно, как вообще работает эта игра.

Спрайты !

Часть кода «Бегуна» - Runner

Runner.prototype.update = function () {
            this.updatePending = false;

            var now = getTimeStamp();
            var deltaTime = now - (this.time || now);
            this.time = now;

            if (this.playing) {
                this.clearCanvas();

                if (this.tRex.jumping) {
                    this.tRex.updateJump(deltaTime);
                }

              /* ....... */

Существует «класс» под названием Runner, который контролирует динозавра, горизонт (с облаками, самой линией горизонта и препятствиями), наложение игры и показатель счета. Все это обновляется в игровом цикле через requestAnimationFrame .

Скорость и ускорение контролируются дельтой времени, которая показывает промежуток времени, проходящий между двумя обновлениями. Такие действия пользователя, как клик или касание запускает обновление в некоторых переменных, которые будут использоваться в следующих обновлениях. Это все, грубо говоря, и есть то, как работает игра.

Перейдем к Flutter

Ключевое слово — Flame . Все необходимые для выполнения игрового цикла абстракции, усилия для рисования объектов на холсте и коды для помещения спрайтов в объект — все это имеется во Flame, минималистичном 2-D движке.

Итак, мы поняли, как работает Ти-Рекст на HTML5, а при помощи Flutter, на Flame, половина нашей работы уже сделана заранее.

Такие классы, как Horizon и TRex запросто переносятся в концепт компонента Flame. Runner — это наша игра. Методы Update и Draw, присутствующие в некоторых методах. Имеют аналоги во Flutter (Update и Render).

Класс TRexGame — это наш «Runner»:

class TRexGame extends BaseGame{

  TRex tRex;
  Horizon horizon;
  GameOverPanel gameOverPanel;
  TRexGameStatus status = TRexGameStatus.waiting;

  double currentSpeed = GameConfig.speed;

  TRexGame({
    Image spriteImage
  }) {
    tRex = new TRex(spriteImage);
    horizon = new Horizon(spriteImage);
    gameOverPanel = new GameOverPanel(spriteImage);
    this..add(horizon)..add(tRex)..add(gameOverPanel);
  }

  void onTap() {
    if(gameOver){
      restart();
      return;
    }
    tRex.startJump(this.currentSpeed);
  }

  @override
  void update(double t) {
    tRex.update(t);
    horizon.updateWithSpeed(0.0, this.currentSpeed);

    if(gameOver) return;

    if(tRex.playingIntro && tRex.x >= TRexConfig.startXPos ) {
      startGame();
    } else if (tRex.playingIntro) {
      horizon.updateWithSpeed(0.0, this.currentSpeed);
    }
  }
  /* .... continues .... */

Однако, параллели заканчиваются на этом. Главная разница между нашей имплементацией и Хрома заключается в размере области просмотра (значения x,y были совершенно разными), возможности изменения ориентации экрана и в частоте кадров. Эти проблемы вызвали много багов при портировании игры, некоторые из них были даже весьма забавными:

Облака на земле !

Geronimooooooooooo !

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

Результат

!

На самом деле, это еще не конечный результат. Нужно реализовать еще несколько функций, однако, сегодня мы закончим на этом.

Ознакомьтесь с кодом на Github или скачайте apk для проверки на своем телефоне.

Вот список того, что нам еще предстоит сделать:

  • Реализовать систему подсчета очков;
  • Создать TRex duck;
  • Поместить летающих птеродактилей;
  • Вставить звуки.

Вот превью игры на моем телефоне с Android с горизонтальной ориентацией: !

Заключение

Создание игр — это весело, а с Flutter и Flame это еще и просто!

Здесь есть и свои трудности, отличающиеся от тех, с которыми мы сталкиваемся в разработке приложений. Эта игра стала непростой задачкой даже с имеющимся готовым кодом и геймплеем. Так что я даже представить не могу, насколько это сложно — создавать игру с нуля.

После года работы с Flutter мне довелось работать с такими вещами, которые я даже представить себе не мог, к примеру, slivers и delegates. На этот раз я хотел зайти немного дальше. При работе с Flame, вы будете взаимодействовать с виджетами и классами из библиотеки painting во Flutter.

При помощи Flame и Flutter уже создано огромное количество игр, и одной из них является Tales of a lost mine . Это первая большая игра, сделанная полностью с Flutter. Вот отрывок геймплея:

!

Вы уже можете скачать ее в play store и познакомиться с ней поближе.

До встречи!