Как собрать браузерную игру? [Итерация 2] Графический интерфейс
Нажмите здесь, чтобы опубликовать эту статью в LinkedIn »
Поскольку цель этой серии - изучить как можно больше аспектов веб-разработки, поиграть с новыми технологиями и узнать что-то новое и интересное. И сделаем это, создав текстовую браузерную игру.
Если вы не читали предыдущие посты - вот они:
- [Итерация 0] Идея: https://medium.com/@dmitrykmita/how-to-build-browser-game-iteration-0-idea-c0865d125148
- [Итерация 1] Домены: https://medium.com/@dmitrykmita/how-to-build-browser-game-iteration-1-domains-a62fbd9d9fba
Пришло время графического интерфейса. К тому же я ужасно рисую, я никогда не был большим игроком, поэтому было действительно сложно придумать графический интерфейс для этой игры. Я просмотрел несколько игр, много погуглил, проверил ресурсы на сайте разработчиков игр и сделал каркас, используя отличный инструмент https://wireframe.cc/
Затем я использовал другой отличный инструмент https://coolors.co/ для создания цветовой схемы.
Теперь о ReactJS. Это будет базовое руководство по созданию пользовательского интерфейса на ReactJS.
Я использую webpack для сборки и babel для компиляции JS.
Итак, чтобы создать интерфейс с реакцией, вы можете либо использовать службу response-create-app, либо просто инициализировать проект npm в своей общедоступной папке (./web в случае фреймворка Symfony).
После этого добавьте несколько зависимостей:
npm install --save babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom webpack
Теперь у нас есть webpack для сборки, babel для компиляции и реакции на кодирование.
Далее необходимо указать, какие пресеты JS вы будете использовать, поэтому создайте файл .babelrc и добавьте те, которые вы будете использовать (в моем случае это es2015 и реакция jsx):
{ "presets": [ "react", "es2015" ] }
Отлично, теперь babel знает, как скомпилировать наше приложение.
Пришло время создать код пользовательского интерфейса, поэтому я создал еще одну папку (я назвал ее library, не знаю почему, вы можете назвать ее src) и добавил новый файл под названием Game.js
Game.js будет моей точкой входа в интерфейс.
Чтобы веб-пакет работал, я создал файл webpack.config.js в корневой папке npm и добавил общий код:
const path = require('path'); module.exports = { entry: './libraries/Game.js', output: { path: path.join(__dirname, 'dist'), filename: 'game.js' }, module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' } ] } };
Я использую библиотеку NodeJS path, чтобы упростить путь к файлу ввода. Код внутри этого модуля довольно прост: вы устанавливаете входной файл, устанавливаете выходной файл и способы загрузки модулей.
Я ненавижу запускать webpack через папку node_modules, поэтому добавляю этот скрипт в package.json:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack -wd" },
Это запустит webpack в режиме разработки и начнет следить за любыми изменениями, поэтому теперь я могу просто запустить:
npm run webpack
И он должен скомпилировать ваш пакет. О, но он пуст, потому что наш Game.js пуст. Давайте создадим наш новый компонент!
Как вы знаете, ReactJS основан на компонентах, которые очень дружелюбно наследуют, взаимодействуют и просто живут вместе.
Чтобы создать Компонент, вам нужно использовать этот синтаксис (есть несколько способов, но он называется наиболее правильным):
import React from "react"; import ReactDOM from "react-dom"; class Game extends React.Component { render() { return ( <div> <div className="container-fluid"> <div className="row"> <div className="col-md-12"> <CharacterWidget /> <HeaderWidget /> </div> </div> </div> <div className="container-fluid"> <div className="row"> <div className="col-md-6 text-center"> <Map /> </div> <div className="col-md-6"> <MapDetails /> </div> </div> </div> <Inventory /> </div> ) } }; var mountNode = document.getElementById("game"); if (mountNode) { ReactDOM.render(<Game />, mountNode); }
Компоненту React требуется только 1 метод с именем render (), который возвращает шаблон этого конкретного компонента. Как видите, я создал какой-то тупой шаблон с помощью bootstrap. Поскольку мы используем JSX, вместо class мы должны использовать className. Но что это за ‹CharacterWidget /› ‹HeaderWidget /› ‹Map /› ‹MapDetails /›? Ну, это другие компоненты, которые мы создадим. На данный момент я создал все эти компоненты и все, что у них есть, как шаблон:
<div className=”c-character-widget”>Character Widget</div> <div className=”c-header-widget”>Header Widget</div> <div className=”c-map”>Map</div> <div className=”c-map-details”>Map Details</div>
Как я уже упоминал о классе c-%, давайте немного поговорим о css.
Я использую SASS для этого приложения, и есть 2 способа связать наш css. Либо я создаю все файлы scss отдельно для каждого компонента, добавляю файл scss к каждому компоненту в качестве зависимости, а затем просто добавляю sass-compiler в webpack (что является лучшим решением для веб-сайтов, потому что тогда webpack будет генерировать стили по мере необходимости для каждой страницы в зависимости от на странице компонентов с помощью) или создайте main.scss, куда вы добавите все файлы scss компонентов и создадите 1 пакет css (более предпочтительно для моего случая, потому что в игре не так много страниц).
Я использую «своего рода» методологию БЭМ для определения моих классов css. Вот пример блока с дополнительными функциями, которые предоставляет конкретный элемент:
.c-extra { margin-top: 10px; .c-extra--title { font-size: 13px; color: $color-gold-primary; text-shadow: 0 0 5px #000; } .c-extra--item { font-size: 12px; color: $color-gray-light-primary; &.c-extra--item__required { color: $color-red-primary; } .c-extra--item-stat { display: inline-block; margin-right: 5px; } .c-extra--item-value { display: inline-block; } } }
А вот и шаблон jsx:
<div className="c-extra"> <div className="c-extra--title">Extras</div> {this.props.item.extras.map((item, i) => { return ( <div key={i} className="c-extra--item"> <div className="c-extra--item-stat"> {item.attribute} </div> <div className="c-extra--item-value"> {item.value} </div> </div> ) })} </div>
Как вы можете видеть, даже несмотря на то, что эти классы стали немного длиннее - очень легко понять структуру компонента + его многоразовое использование, и если вы переместите его в любое другое место приложения - он должен остаться прежним. Идея компонента - быть без состояния, независимо от того, где вы его включаете - он должен выглядеть одинаково.
Это пример очень уродливого, но работающего виджета персонажа:
Я пока не касался данных, потому что все, что нам нужно, - это просто создать пользовательский интерфейс, а затем мы будем двигаться вперед.
Я думаю, что на данный момент этого достаточно, если у вас есть какие-либо вопросы относительно компонентов ReactJS, sass, webpack или чего-либо еще, о чем говорится в этой статье - не стесняйтесь спрашивать в разделе комментариев ниже.
Что я могу сказать: я изменил свое отношение к ReactJS, потому что нашел его очень тонким и мощным инструментом для создания пользовательских интерфейсов на основе компонентов и использования всей мощи VirtualDOM.
В следующей статье мы поговорим о Map!