Redux что это в играх
Metro Redux: возвращение под землю
На весь внушительный набор плюсов серии шутеров «Метро», и в первую очередь неповторимую атмосферу — такую густую, что хоть ложкой ешь, — имеется один жирный минус.
Но робкая надежда все-таки теплилась, особенно учитывая, что разработчики, не скромничая, заявляли, что вслед за графикой подтянут в обновленной версии и игровой процесс.
Тушенка и гитара
Только при скрытном прохождении заметен кое-какой прогресс. Раньше местные обитатели больше походили на роботов: через полкарты видели главного героя, Артема, в кромешной темноте и отыскивали его даже в самых укромных уголках. В таких условиях скрытно проходить игру решались разве что самые страстные любители Снейка и Сэма Фишера.
В новой версии враги все-таки лишились своего сверхъестественного зрения. Правда, в темноте героя различают все так же хорошо, научились, видать, от жизни под землей. Зато теперь от них хотя бы можно спрятаться — сквозь ящики обитатели подземки больше не видят.
Так и не доведенные до ума перестрелки огорчают, но понять разработчиков, сосредоточившихся на доведении до ума именно стелсовой составляющей, можно. Аптечки и патроны в постапокалиптической Москве — весьма редкие штуки, так что игра и раньше подталкивала игроков к скрытному прохождению. А теперь, благодаря режиму «выживание», оно должно и вовсе стать основной геймплейной механикой.
Но основное достоинство нового режима в другом. Он выводит на передний план главное достоинство игры — феноменальную проработку окружения. Кипящую на станциях жизнь и раньше было не пропустить, но вот сотни мелких штрихов на «боевых» уровнях в пылу сражений ускользали от внимания только так.
Теперь же, когда мы вынуждены таиться в тенях, все детали как на ладони. Проработанные до мельчайших деталей интерьеры, случайные разговоры противников и даже совершенно новые комнаты и целые отрезки уровней отлично освежают впечатления от игры, даже если она уже пройдена.
Старый друг лучше новых двух
Впрочем, 4A Games вряд ли целились в старых поклонников. Главными покупателями игры должны стать владельцы новых консолей, с игрой, скорее всего, вовсе не знакомые. Именно поэтому игру так тщательно подтягивали графически. Текстуры высокого разрешения, переработанная система освещения и новые модельки заметно преображают «Метро 2033», по качеству изображения вплотную приближая ее ко второй части.
Некоторые, правда, уже недовольны: дескать, с новым освещением все стало ярче и красочнее, а атмосфера безысходности и уныния улетучилась. Не верьте! Новое освещение лишь придает окружению естественности, с атмосферой по-прежнему полный порядок.
Вот в Last Light картинка почти не изменилась. Но то по сравнению с максимальными настройками в ПК-версии. А если вы проходили оригинал на PS3 или Xbox 360, разницу почувствуете еще как.
Но на фоне похорошевшего окружения еще сильнее бросаются в глаза до жути криво анимированные персонажи. Сложно сказать, почему разработчики оставили это без внимания, но герои двигаются, говорят и выглядят немногим лучше, чем в шутерах середины прошлого десятилетия. Во время диалоговых сцен снова приходится стыдливо отворачиваться и разглядывать банку консервов на полке. Она посимпатичнее будет.
Главное достоинство Redux — это сразу две игры в одной коробке. Две просто хорошие игры, вполне себе складывающиеся в одну отличную. По-настоящему серьезных изменений нет, и если туннели постапокалиптического московского метро вами давно исхожены вдоль и поперек, то бежать сломя голову за сборником едва ли имеет смысл.
Но если вы в свое время пропустили серию или начали знакомство с ней со второй части, хватайте не задумываясь, настолько выгодных предложений мы не видели, кажется, со времен The Orange Box!
Метро Redux или Редух. Или где нас обманули.
Первым делом хочу сказать, что я познакомился с серией через первую игру. Но спустя пару лет после её выхода. И я вообще ожидал открытого мира и чего-то вроде Сталкера. Но я немного ошибся. Хотя и не пожалел.
Первая часть была шедевральной. Атмосфера павшего мира и сильных людей, которые несмотря на окружающий их… швах выживают, радуются, торгуют и любят. Ну и заботятся о детях. И страдают. Да. Это было сделано хорошо. Каждый разговор, каждый детский рисунок на стене, каждый пиксель дышал и выдавал нам прекрасную атмосферу. Я проходил её неоднократно, но так и не смог найти всех её секретов. Хотя любимые нычки у меня были. В основном это были неплохие револьверы, дробовик, да снаряжение. Я ожидал, что это будет и в её переиздании. Но об этом позже.
Ладно. Это был геймплей. Теперь осмотрим мир и атмосферу. Эм… Не то, чтобы плохо, но и не торт. Пирог — да. Но не торт. Вкусный, с прослоечкой пирог с ягодами. Но, мать его, не торт. Почему? Ну… не могу объяснить это ничем, кроме геймплея. Извините. Хотя может сюжет портит? Нет, в первой части он был. В первой части было спасение мира через спасние родной хаты. Был длинный путь, были преграды, был «злодей» в лице Чёрных. Но вторая игра делает упор на то, что «Война не меняется, люди не меняются, ничего не меняется». Из-за жажды власти мрут обычные люди, показывается факт использования эболы страшной болезни, и последующего спасения от неё США Красными. Потом выясняется, что это США Красные и вызвали болезнь, что они — Зло, да и вообще Люди — Зло. Хэпи Энд, блин.
Но в общих чертах — всё хорошо. Я доволен. Но геймплей… Эх…
Затем был Redux. Второй части он дал немного подкраситься, а первой передал весь свой геймплей. На момент написания этого текста я НЕ играл в переиздание второй части. Так что про неё говорить НЕ буду. А вот про первую я поговорить хочу. Очень, мать вашу, хочу.
Итог. Для некстгена это хоть что-то. Но на ПК стоит играть в оригинал.
Спасибо за внимание. Ваш Н.
Что такое Redux простыми словами?
Чтобы понять как работает Redux тебе нужно норм вкуривать React.
Хотя бы для того, чтобы не пугаться props.
Есть у тебя React. Это все просто JS объекты.
— так позволяет писать движок jsx, который и React его использует.
Так как структура компонентная, ты должен думать как обновить компоненты в в другой части страницы.
Что делает Redux:
Он не призывает отказываться от state.
То если mapStateToProps вернет
В данном случае разное мышление (проектирование).
В вашем случае:
Логика идет от компонентов.
Допустим на странице пользователя есть label online/offline
Откуда брать значение, если нужно заблокировать окно чата в тот момент когда пользователь offline?
Что если таких действий не одно, а допустим штук 10?
Что если таких действий не одно, а допустим штук 10 и еще на разных страницах?
Куда вынести функцию, которая проверит статус пользователя?
Как её назвать чтобы не было конфликта имен?
Redux для меня в первую очередь подход.
1. Проектируется состояние (база данных фронта)
2. действие checkUserStatus (ajax/socket)
3. сколько угодно компонентов (которые не знают друг о друге ничего и имеют любое поведение, в зависимости от статуса пользователя)
компоненты могут ничего не уметь кроме как рендерить этот статус (label), или, могут быть более сложными и иметь логику + обработчики (кнопки, пр)
jQuery «видит» весь DOM целиком. И манипулирует DOM-ом, доступ к которому предоставляет браузер. Но можно и по-другому.
Давайте рассмотрим такую архитектуру, в которой все компоненты связаны между собой древовидно. То есть, отдельно взятый компонент может общаться со своими дочерними и со своим родительским. Если нужно, чтобы между собой пообщались два компонента в разных ветках, то в конце концов их общение происходит через общего предка. Такой подход вполне согласуется с древовидной структурой HTML.
Для того, чтобы это всё отобразить, не обязательно в ходе вычисления следующего состояния (или представления) сразу манипулировать DOM-ом. Можно построить из состояния и его представления фрагмент DOM-а, каким он должен получиться. А потом вычислить diff между этим построенным фрагментом (виртуальным DOM-ом) и реальным браузерным. И этот diff применить к реальному DOMу. Один раз. Получится быстро.
Кроме упомянутых особенностей эта архитектура также позволяет выполнять так называемую time-travelling отладку. Поскольку каждое состояние и каждое событие по пути от исходного до текущего можно залогировать и воспроизвести (ведь функция, вычисляющая следующее состояние у нас чистая).
С точки зрения функционального программирования, при таком подходе текущее состояние является результатом функции leftFold, применённой к исходному состоянию, последовательности событий (действий), приводящих к текущему и функции, умеющей вычислять следующее состояние.
Обзор Metro Redux: осторожно, двери закрываются
Ищем причину потратить 750 рублей на немного улучшенную дилогию Metro. Частично удается: Metro 2033 Redux стала намного лучше, а вот смысл в покупке Last Light Redux найдут лишь самые заядлые фанаты.
Дмитрий Глуховский написал не шибко интересный роман «Метро 2033» и однажды проснулся знаменитым. Выкладывая в интернет главы и попутно обивая пороги издательств, в один день начинающему писателю и опытному журналисту улыбнулась удача: книгу взялись выпустить на бумаге. С тех пор (2005 год) вышло продолжение «Метро 2034» и скоро ожидается релиз «Метро 2035». Сеттинг франшизы отлично подходит под игровую адаптацию, поэтому появление шутера не заставило себя долго ждать. Обе части, Metro 2033 и Metro: Last Light, хорошо встречены общественностью. Перед началом работы над новым проектом компания 4A Games решила выпустить что-то вроде Director’s Cut своих Metro.
Зато с Metro 2033 Redux ситуация совсем другая. Игра вышла в 2010 году и на тот момент выглядела очень недурно: передовые технологии, четкие текстуры, хорошая работа с освещением и при этом относительно неплохая оптимизация. Но время идет, и сейчас шутер четырехлетней давности выглядит не столь впечатляюще. Поэтому 4AGamesперенесла Metro 2033 на последнюю версию движка 4AEngine. Результат заметен невооруженным взглядом. Совершенно иного уровня освещение, эффекты в разы красивее, объемный туман и дым, которые хочется развеять рукой. При этом игра избавилась от большинства болячек, вроде беспричинных просадок кадров в секунду, графических багов и других огорчающих моментов из оригинальной версии. Только вот трупы по-прежнему могут проваливаться сквозь объекты: то нога монстра будет торчать из бетонной плиты, то полтуловища уйдет в стену. Также никуда не делись бесконечные орды противников — будут переть, пока не выполните задание. К счастью, таких эпизодов мало на протяжении шести-семичасовой кампании.
Разработчики явно хотели подчеркнуть нелегкую жизнь в постапокалиптическом мире и даже поделили игру на два стиля: «Спартанец» и «Выживание». Последний говорит сам за себя: акцент на тихое прохождение, мало патронов, враги меткие и чуткие, настоятельно рекомендуется играть только в режиме «стелса». «Спартанец» больше подходит для любителей шумной пальбы и взрывов: боеприпасов хватает, аптечки тоже найти не проблема. Хотя ситуации, когда остаешься практически без патронов, очень даже реальны. К тому же не забываем, что один тип пуль считается здесь валютой, и лучше не сорить деньгами в прямом смысле слова. Эти патроны в Metro 2033 Redux теперь зажигательные, как это реализовано в Last Light.
В остатке имеем добротно переделанную Metro 2033, которая все так же интересна, и парочку изменений в Last Light сугубо для фанатов. Приобретать обе части есть смысл, только если до этого не играли в Last Light. Обновлять сиквел до Redux-версии резона нет. Это что касается РС-версий. А на консолях нового поколения Metro Redux Bundle— единственная возможность поиграть в Metro на Xbox One и Playstation 4, тем более при честных Full HD и 60 кадрах в секунду. На Xbox One разрешение, правда, 912p, но на глаз это и не заметишь. Теперь ждем космический шутер в открытом мире, который 4A Games обещает скоро анонсировать.
Введение в Redux & React-redux
Оглавление
Введение
Вот вы прочитали мою статью про React (если нет, то настоятельно рекомендую вам сделать это) и начали разрабатывать приложения на нём. Но что это? Вы замечаете, как с расширением вашего приложения становится всё сложнее следить за текущим состоянием, сложно следить за тем, когда и какие компоненты рендарятся, когда они не рендарятся и почему они не рендарятся, сложно следить за потоком изменяющихся данных. Для этого и есть библиотека Redux. Сам React хоть и лёгкий, но для комфортной разработки на нем нужно много чего изучить.
И сегодня мы разберём 2 библиотеки: Redux и React-redux. Для использования Redux’а вам не нужно скачивать дополнительных библиотек, но, если использовать его в связке с библиотекой React-redux разработка становится ещё удобнее и проще.
Все примеры из этой статьи вы можете найти в этом репозитории на Github. Там находится полностью настроенное приложение React с использованием Redux и React-redux. Вы можете использовать его как начальную точку для вашего проекта. Изменяйте названия файлов и добавляйте новые в этот репозитории для создания собственного приложения. Смотрите во вкладку релизы для того что бы найти разные версии приложения. Первая содержит приложение только с использованием Redux, второе с использованием Redux и React-redux.
Мотивация использования Redux
Механизм локального хранилища компонента, который поставляется вместе с базовой библиотекой (React) неудобен тем, что такое хранилище изолировано. К примеру, если вы хотите, чтобы разные независимые компоненты реагировали на какое-либо событие, вам придётся либо передавать локальное состояние в виде пропсов дочерним компонентам, либо поднимать его вверх до ближайшего родительского компонента. В обоих случаях делать это не удобно. Код становится более грязным, трудночитаемым, а компоненты зависимыми от их вложенности. Redux снимает эту проблему так как всё состояние доступно всем компонентом без особых трудностей.
Redux является универсальным средством разработки и может быть использован в связке с различными библиотеками и фреймворками. В этой же статье будет рассматривается использование Redux в React приложениях.
1. Установка Redux и начало работы
Используете ли вы Yarn или Npm, выполните одну из этих команд для установки Redux:
Скорее всего вы используете папку src в которой хранится ваша кодовая база. Файлы, связанные с redux принято хранить в отдельной папке. Для этого я использую папку /src/store в которой хранится всё то, что связано с Redux и хранилищем приложения. Вы можете назвать ее по другому или поместить в другое место.
Создайте базовую структуру для хранилища. Она должна выглядит примерно следующим образом:
.store
├── actionCreators
│ ├── action_1.js
│ └── action_2.js
├── actions
│ ├── action_1.js
│ └── action_2.js
├── reducers
│ ├── reducer_1.js
│ ├── reducer_2.js
│ └── rootReducer.js
├── initialState.js
└── store.js
Конечно здесь я использовал примитивные названия для файлов, это сделано для наглядности. В настоящем проекте так называть файлы не стоит.
2. Redux
2.1 createStore
Когда вы создали базовую структуру для работы с хранилищем Redux пришло время понять то как вы можете взаимодействовать с ним.
Глобальное хранилище приложения создаётся в отдельном файле, который как правило называется store.js:
2.2 reducer()
reducer — чистая функция которая будет отвечать за обновление состояния. Здесь реализовывается логика в соответствие с которой будет происходить обновление полей store.
Так выглядит базовая функция reducer:
Функция принимает значение текущего состояния и обьект события (action). Обьект события содержит два свойства — это тип события (action.type) и значение события (action.value).
К примеру если нужно обработать событие onChange для поля ввода то объект события может выглядеть так:
Некоторые события могут не нуждаться в передаче каких-либо значении. К примеру, обрабатывая событие onClick мы можем сигнализировать о том, что событие произошло, более никаких данных не требуется, а как на него реагировать будет описывать логика, заложенная непосредственно в сам компонент которой должен на него реагировать и частично в reducer. Но во всех случаях необходимо определять тип события. Редьюсер как бы спрашивает: что произошло? actio.type равен «ACTION_1» ага значит произошло событие номер 1. Дальше его нужно как то обработать и обновить состояние. То, что вернёт редьюсер и будет новым состоянием.
ACTION_1 и ACTION_2 это константы событий. По-другому Actions. Про них мы поговорим далее 2.5 Actions.
Как вы уже догадались store может хранить сложную структуру данных состоящих из набора независимых свойств. Обновление одного свойства оставит нетронутым другие свойства. Так из примера выше, когда происходит событие номер один (ACTION_1) обновляется поле номер один (value_1) в store при этом поле номер два (value_2) остаётся нетронутым. В общем механизм схож с методом this.setState().
2.3 dispatch()
Что бы обновить store необходимо вызвать метод dispatch(). Он вызывается у объекта store который вы создаёте в store.js. Этот объект принято называть store поэтому обновление состояния в моём случае выглядит так:
ACTION_1 это константа события о которой речь пойдет дальше (см. Actions).
Эта функция вызовет функцию reducer который обработает событие и обновит соответствующие поля хранилища.
2.4 actionCreator()
На самом деле передавать объект события напрямую в dispatch() является признаком плохого тона. Для этого нужно использовать функцию под названием actionCreator. Она делает ровно то что и ожидается. Создаёт событие! Вызов этой функции нужно передавать как аргумент в dispatch а в actionCreator передавать необходимое значение (value). Базовый actionCreator выглядит следующим образом:
Таким образом вызов dispatch должен выглядеть так:
С использованием actionCreator код становится более чистым.
2.5 Actions
actions это константы, описывающие событие. Обычно это просто строка с названием описывающее событие. К примеру константа описывающее событие номер один будет выглядеть следующем образом:
Опять же в проекте вам стоит называть константы в соответствии с событием, которое она описывает: onClick, createUserSesion, deleteItem, addItem и т.д. Главное, чтобы было понятно. Замете что я нигде не писал import поэтому не забудьте импортировать ваши константы перед их использованием. Потому что константы тоже принято разбивать на отдельные файлы храня их в специальной папке. Хотя некоторые хранят их в одном файле под названием actionTypes.js. Такое решение нельзя назвать не правильным, но и не идеальным.
2.6 getState()
С помощью dispatch() обновили, а как теперь посмотреть новое значение store? Ничего изобретать не нужно, есть метод getState(). Он также, как и метод dispatch вызывается на экземпляре объекта store. Поэтому для моего примера вызов
вернёт значение полей хранилища. К примеру что бы посмотреть значение поля value_1 необходимо будет вызвать
2.7 subscribe()
А как же узнать, когда состояние обновилось? Для этого есть метод subscribe(). Он также вызывается на экземпляре store. Данный метод принимает функцию, которая будет вызывается каждый раз после обновления store. Он как бы «подписывает» функцию, переданную ему на обновление. К примеру следующий код при каждом обновлении (при каждом вызове dispatch()) будет выводить новое значение store в консоль.
Этот метод возвращает функцию unsubscribe(). Которая позволяет «отписаться от обновления». К примеру если компонент удаляется из DOM стоит отписать его методы от обновления в componentWillUnmount(). Этот метод жизненного цикла вызывается при размонтировании компонента и это именно то место где стоит отписываться от обновления. Проще говоря в деструкторе.
2.8 combineReducers()
combineReducers() позволяет объединить несколько редьюсеров в один.
Если логика обновления компонентов довольно сложна и\или необходимо обрабатывать большое количество различных типов событий, то корневой reducer может стать слишком громоздким. Лучшим решением будет разбить его на несколько отдельных редьюсеров каждый из которых отвечает за обработку только одного типа событий и обновления определённого поля.
Когда вы разбиваете базовый редьюсер на несколько, то название каждого из них должно соответствовать полю которое он обновляет в store.
К примеру если редьюсер обновляет поле номер один, то он может выглядеть так:
Название редьюсера (value_1) показывает какое свойство он будет обновлять в store. Если переименуете его в value_2 то он станет обновлять value_2. Поэтому учтите это!
Когда используется единый редьюсер мы показываем какое поле хотим обновить:
Но когда вы разделили ваши редьюсеры вам нужно просто вернуть новое значение:
Поскольку здесь не требуется указывать которое из полей обновляет редьюсер ибо его название и есть поле которое он обновляет.
2.9 initialState
initialState — объект, представляющий начальное состояние хранилища. Он является вторым не обязательным аргументом метода createStore(). С созданием хранилища можно сразу объявить начальное состояние для его полей. Этот объект желательно создавать, даже в тех случаях, когда объявления начального состояния не требуется. Потому что этот объект помогает посмотреть на структуру хранилища и название его полей. Обычный объект initialState выглядит следующим образом:
В некоторых случаях (когда компонент сразу использует значение из store), его объявление может стать обязательным иначе вы получите ошибку: TypeError: Cannot read property ‘value_1’ of undefined.
Также редьюсеры всегда должны возвращать по дефолту текущее состояние. К примеру, если используется единый reducer то последнее значение в switch должно выглядеть так:
Если же вы разделяете редьюсеры на независимые функции, то он должен возвращать значение того свойства за которое он отвечает:
Также если вы не передаёте объект initialState в createStore вы можете вернуть его из редьюсера. В обоих случаях будет инициализировано начальное состояние для store.
3. React-redux
Казалось бы, у нас есть всё что бы использовать Redux. Но на деле использование его без пакета React-redux в React приложениях выглядит не очень красиво.
3.1 Provider
Для использование store в компоненте вам необходимо передавать его в пропсы:
И после использовать в компоненте: this.props.state. Для этого react-redux предостовляет метод Provider:
Также можно передать store напрямую в компонент, не оборачивая его в Provider и это будет работать. Но лучше всё-таки используйте Provider.
3.2 mapStateToProps()
Этот метод вызывается всякий раз, когда происходит обновление store и именно он передаёт необходимые свойства из store в компонент. К примеру компонент, должен реагировать и обновлять UI каждый раз, когда поле номер один (value_1) обновилось. На обновление других полей ему реагировать не нужно. Если вы не используете React-redux вам бы пришлось использовать метод subscribe() что бы узнавать об обновлении и далее каким то образом проверять обновилось ли поле номер один или нет. В общем несложно понять, что такой код будет выглядеть слишком грязным и избыточным. С помощью mapStateToProps() можно чётко определить какие поля интересуют компонент. И на какие поля он должен реагировать.
Возвращаясь к примеру выше, если компоненту один нужно получать поле номер один (value_1) то mapStateToProps для него будет выглядеть следующим образом:
После внутри компонента мы можем обращается к полю value_1 через this.props.value_1. И каждый раз когда это поле будет обновляется компонент будет рендерится заново.
Вы можете создать отдельную папку в /src/store для хранения файлов каждый из которых будет содержать функцию mapStateToProps для всех ваших компонентов. Либо (как сделал это я) использовать единую функцию возвращающую функцию mapStateToProps для каждого компонента. Лично мне нравится такой подход. Такая функция выглядит следующим образом:
Эта функция в качестве аргумента принимает строку с названием компонента и возвращает функцию mapStateToProps которая возвращает объект со свойством из store необходимом для данного компонента. Эту функцию можно назвать mapStateToPropsGenerator().
3.3 mapDispatchToProps()
Эта функция передаёт в компонент методы для обновления необходимого поля store. Что бы не вызывать dispatch напрямую из компонента вы будете использовать данный метод для того что бы передавать в props метод вызов которого приведёт к вызову dispatch и обновлению соответствующего поля. Просто теперь это будет выглядеть более элегантно, а код более понятным и чистым.
К примеру компонент, номер один должен иметь возможность обновлять поле номер один из store. Тогда mapDispatchToProps для него будет выглядеть следующим образом:
Теперь для обновления свойства value_1 вы будете вызывать changeValue_1() через this.props.changeValue_1(value). Не вызывая dispatch напрямую через this.props.store.dispatch(action_1(value)).
bindActionCreators следует импортировать из redux. Он позволяет оборачивать функцию dispatch и actionCreator в единый объект. Вы можете не использовать bindActionCreators но тогда код будет выглядеть избыточным. Вы должны старятся реализовать какую-либо функциональность так, чтобы код выгладил просто и миниатюрно. Поэтому ничего лишнего писать не следует.
Только чистый и понятный код. Метод bindActionCreators(actionCreator, dispatch) принимает два обязательных параметра это функцию actionCreator о которой мы уже говорили и dispatch. Возвращая метод для изменения полей store.
Также как и для mapStateToProps я использую функцию генератор возвращающую функцию mapDispatchToProps для каждого компонента:
3.4 connect()
Ну и теперь кульминация! То без чего всё это не будет работать. Это функция connect.
Именно она связывает mapStateToProps и mapDispatchToProps с компонентом и передает необходимые поля и методы в него. Возвращает она новый компонент-обёртку для вашего компонента. Как правильно именовать такой компонент я не знаю, ибо в самой документации React-redux это не описывается. Лично я добавляю окончание _w для компонентов оберток. Как бы _w = wrap Component. Подключение компонента в этм случае выглядит так:
И теперь в ReactDOM.render() вы передаёте не ваш компонент, а тот что возвращает функция connect.
Если же у компонента нет необходимости в передаче ему mapStateToProps или mapDispatchToProps передавайте undefined или null в него.