Как мы писали стратегический кликер на хакатон ReactRiot
- среда, 28 июня 2017 г. в 03:15:38
Привет. Не так давно мы с rjericho увидели статью Как я участвовал в хакатоне Angular Attack, и что из этого вышло. У нас в Барнауле хакатоны начали проводиться всего год назад. При этом они больше были ориентированы на быстрый старт IT-стартапа. Поэтому на них не получалось насладиться использованием всяких прикольных хипстерских библиотек, а приходилось пилить продукт на старых добрых PHP или Java. Однако нам давно хотелось поучаствовать в каком-нибудь фановом мероприятии, где можно просто запилить то, что душе угодно и не придумывать, как это монетизировать.
Был найден хакатон от тех же организаторов, что и AngularAttack: ReactRiot. То, что о реакте мы знали только по статьям на хабре только добавляло интерес к данному мероприятию.
Для начала нужно было побольше узнать о самом реакте. В этом помогли хабр, официальные доки и codesandbox.io. На данном сайте были некоторые примеры, которые и помогли определиться с выбором основных библиотек.
Для менеджмента состояния был выбран MobX. Redux также выглядел интересно, однако показалось, что для хакатона он не подходит, так как требует больше времени на продумывание архитектуры и добавление actions, что не очень подходит для формата хакатона, когда через сутки после начала придумываешь гениальную фичу, которая никак не влезает в архитектуру.
Для стилизации компонентов был взят glamorous и сам glamor. Понравилось, что весь код одного компонента может быть в одном файле, а не раскидан по папкам css и js и не влияет на другие компоненты.
Самое трудное в хакатоне — это идея :). Так как для нас целью этого хакатона было изучение новой технологии и получение фана, то было решено делать игру. А чтобы она ещё и красиво ложилась в принципы реакта и mobx, т. е. можно было создать компонент и использовать его много раз на странице, а также при действиях пользователя всё реактивненько обновлялось, то решили делать что-нибудь, похожее на кликер.
Изначальной идеей был симулятор шахтёрского поселка с различными видами шахтёров, шахт и вспомогательных зданий, которые влияют на шахтёров (вроде увеличения производительности и т.п). Однако когда я начал делать мокап под это дело, оказалось, что вместить такое на один экран так, чтобы сразу было понятно что делать тут делать, очень непросто. Поэтому идея была полностью переделана.
От первоначальной идеи осталось только наличие у населения показателя недовольства, при увеличении которого они начинают бунт (англ. riot — отсылка к названию хакатона).
Если вкратце:
Полное описание идеи с которой мы начинали хакатон можно посмотреть тут. Правда она отличается от того, что в итоге получилось, так как на некоторые фичи не хватило времени. Про другие фичи вспоминали только в середине хакатона, когда вставить их было уже затруднительно.
И тут бы возникла ещё одна проблема команды из двух программистов — графическая составляющая проекта. Однако по примеру stickytape подходящие иконки юнитов, ресурсов и зданий были взяты из твиттер-пака эмодзи (Кроме лого топора. Оказывается его нет в юникоде и соответственно в эмодзи. Поэтому подходящий по стилистике топор пришлось искать в другом месте).
Час до хакатона — самое время начать думать об архитектуре проекта :). Однако успели только придумать структуру папок.
В components
находятся jsx компоненты реакта. В logic
— модели — стейты MobX.
В static
собирался бандл вебпака, были вставлены сторонние css, шрифты и картинки.
В styles.js
были некоторые общие стили, которые использовались в нескольких компонентах, вроде такого:
import { css } from 'glamor'
export const boldBorder = css({
border: '3px solid'
})
Не уверен насчёт правильно организованной работы с такими стилями. Было бы неплохо, если в комментариях подсказали что-нибудь по этому поводу.
Разработка началась с создания небольших информационных компонентов. Были написаны компоненты по отображению информации по одному юниту, зданию и ресурсу.
После чего был сверстан основной трёхколоночный вид и добавлены ещё пара компонентов. Тут хорошо помогли библиотеки react-bootstrap и react-rangeslider.
В итоге получилось вот так:
Затем был написан компонент отображения сетки юнитов и зданий и добавлены некоторые иконки.
Наконец настало время писать логику. Первым было написано приращение ресурсов за юнитов и постройка новых юнитов. Тут и случился первый wtf.
Обновление игры у нас идёт по тикам: основные тики — по одной секунде. Также были введены более мелкие тики обучения для более плавного отображения прогресса обучения. При клике на юнита начиналась анимация его постройки и если с одним юнитом всё было ещё хорошо, то при клике сразу на нескольких начинались лаги. Даже не так — ЛАГИ. Всё начинало так дико тормозить, что я уже думал просто убрать эту анимацию. Однако время было уже позднее и, решив что утро вечера мудренее, мы пошли спать.
Утро второго дня началось с осознания нашей главной ошибки: мы неправильно готовим MobX. Наши минимальные компоненты, отображающие основную информацию не являются observer'ами, а тупо принимают информацию о том, что показывать через props. А это в корне противоречит методу работы MobX.
Было:
<Unit
key={i}
trainingAvailable={this.props.canBuy(unit)}
trainingProgress={unit.queueProgress}
queueSize={unit.queueLength}
name={unit.name}
cost={unit.costStr}
description={unit.description}
imgSrc={unit.imgSrc}
onTrain={() => this.props.onTrain(unit)} />
После исправления данной ошибки, скорость приложения стабилизировалась.
Стало:
<Unit
key={i}
unit={unit}
trainingAvailable={this.props.canBuy(unit)}
onTrain={() => this.props.onTrain(unit)} />
Также в процессе исправления данного просчёта было обнаружено, что динамически использовать glamor.css
внутри render()
это не очень хорошо. На каждое изменение стиля, используемого при анимации он создавал новое правило в таблице стилей, что возможно тоже просаживало производительность.
После исправления вчерашних косяков была добавлена сетка постройки зданий
Затем была доделана логика смертности населения и накопления недовольства, отправка солдат на войну, а также добавлена нулевая скорость игры для создания паузы.
После чего настала пора балансировать все коэффициенты, цены и эффекты зданий так, чтобы и нельзя было играть просто тыкая на всё подряд, но и так, чтобы она была не слишком хардкорной. Это была пожалуй самая весёлая часть. Из-за придуманной от балды формулы смертности было интересно наблюдать, как обучающиеся по очереди лесорубы срубали одно дерево и сразу же умирали, из-за высокого коэффициента преступности только что обученные фермеры сразу же становились преступниками, съедали все запасы и от голода начинали бунтовать. Однако если смертность была отбалансирована ещё довольно легко, то при калибровке преступности мы долго не могли найти такой баланс, чтобы при небольшом стартовом количестве людей они не становились сразу же преступниками. В итоге пришлось ввести механизм превращения преступников обратно в безработных тоже с некоторым коэффициентом.
После наведения баланса оставалось добавить уведомления о бунте и случайных событиях (миграциях и наводнениях), а также добавить плашку голосования от организаторов. Причём плашку эту они сделали за 6 часов до конца хакатона и когда она добавилась, нам пришлось один блок переносить с правой части интерфейса в левую, чтобы по высоте всё входило.
В процессе хакатона были, хоть и на начальном уровне, но изучены реакт и MobX. Самостоятельно были опробованы некоторые грабли :). Один из главных наших выводов: архитектуру лучше всё таки хоть немного продумать заранее. Так конечно получается больше рофлов, но на второй день код сменил цвет на коричневый и некоторые вещи делались ужасными костылями. Код можно найти здесь. Потыкать саму игру можно по адресу town.surge.sh