Русский
Русский
English
Статистика
Реклама

Мультиплеер

Как я разработал мобильную игру на Android с использованием React.js и выложил её в Google Play Store

30.12.2020 18:06:06 | Автор: admin

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

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

Скриншот готовой игрыСкриншот готовой игры

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

Предыстория

Добрый день, уважаемые читатели. Конечно же мы с Вами не знакомы, однако если бы были, то Вы бы знали, что ещё с раннего детства после приобретения отцом персонального компьютера у меня была мечта стать разработчиком игр, что в дошкольном возрасте выражалось в "создании" платформеров мелом на асфальте для ребят во дворе, а в школьном - отдельными играми в тетрадках, в том числе и во время уроков.

Разработчиком игр (пока что) так я и не стал, но стал веб-разработчиком, имея на данный момент 8+ лет коммерческого опыта. Однажды на глаза попалась весьма интересная библиотека-фреймворк для создания пошаговых игр, документация которой была незамедлительно изучена. Предлагалось создавать такие игры, как крестики-нолики или шахматы, однако это показалось скучным и мне захотелось пойти дальше - сделать нечто посложнее и похожее на то, во что я играл ещё в детстве. Вероятно, старички заметят некоторое сходство финального результата с той игрой, на которую делался акцент.

Построение и прорисовка мира

Сперва было принято решение создать изометрический мир, который мог бы поддерживать бесконечную прокрутку по горизонтали. После тщательного изучения статей на тему "Как создавать изометрические миры" и им подобных, а также просмотра библиотек для JavaScript и React, которые якобы должны помогать выполнять данную задачу, дело перешло к практике.

Времени на это было потрачено немало и готовых подходящих решений так и не было найдено. Ну что ж, напишем своё. По сути весь наш мир это набор квадратиков-тайлов, которые находятся рядом друг с другом и на соседний квадратик можно перейти в восьми направлениях.

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

Сперва отрисуем ячейки мира построчно. Пускай они будут размером 64x64 пикселя. Далее развернём наш контейнер таким образом, чтобы он выглядел изометрично:

.rotate {  transform: rotateX(60deg) rotateZ(45deg);  transform-origin: left top;}

При имплементации данного подхода к отрисовке можно наблюдать, что необходимые нам "строки" мира на самом деле идут не прямо, а зигзагом, так как мы развернули нашу карту. Таким образом, каждую из ячеек необходимо позиционировать абсолютно с учетом текущего индекса строки, а также индекса колонки:

const cellOffsets = {};export function getCellOffset(n) {  if (n === 0) {    return 0;  }  if (cellOffsets[n]) {    return cellOffsets[n];  }  const result = 64 * (Math.floor(n / 2));  cellOffsets[n] = result;  return result;}

Использование:

import { getCellOffset } from 'libs/civilizations/helpers';// ...const offset = getCellOffset(columnIndex);// ...style={{  transform: `translateX(${(64 * rowIndex) + (64 * columnIndex) - offset}px) translateY(${(64 * rowIndex) - offset}px)`,}}

Для увеличения производительности нам необходимо перерисовывать только те ячейки карты, которые сейчас являются видимыми на экране. Для этого был использован компонент FixedSizeGrid из модифицированной версии библиотеки react-window с учетом нашего поворота и расположений ячеек, код которого здесь приводить не буду. Из того, что не получилось - это сделать бесконечную прокрутку мира. После изучений исходного различных библиотек для бесконечного скролла / слайдеров и тп. подходящего решения найдено не было. Что ж, значит наш мир будет с границами по всем бокам.

Графика

Для отображения поворотов наших юнитов, а также анимации атаки, используются png-спрайты. Поиск графических элементов занял очень большой промежуток времени, было очень сложно найти хотя бы какие-то картинки возможных юнитов для нашей игры. Изначально ещё до поиска игра выглядела вот так:

Игра до поиска графических элементовИгра до поиска графических элементов

Вообще сам я не дизайнер и попросить кого-либо нарисовать тоже возможности нет, потому каждый кадр спрайта пришлось ручками размещать в необходимую позицию. Для примера, финальный спрайт вертолёта выглядит вот так:

Спрайт вертолётаСпрайт вертолёта

Локализация

Игра поддерживает 4 языка и, если честно, мне непонятно, зачем в несложных приложениях разработчики подключают массивные библиотеки типа react-i18next. Давайте напишем похожее кастомное решение, которое уместится в чуть более чем 100 строк с учетом красивой разметки кода, а также будет поддерживать определение языка девайса пользователя, переключение языков в реальном времени и сохранение последнего выбора пользователя. Здесь используется redux, однако данный код можно адаптировать и под другие реактивные хранилища. Да, здесь нет некоторых фишек больших библиотек типа поддержки переменных в строках, однако в таком проекте нам это и не нужно. И да, эту библиотеку можно использовать как легковесную замену react-i18next (или подобным) в уже существующем проекте.

import React, { Component } from 'react';import PropTypes from 'prop-types';import { connect } from 'react-redux';import get from 'lodash/get';import set from 'lodash/set';import size from 'lodash/size';import { emptyObj, EN, LANG, PROPS, langs } from 'defaults';import { getLang } from 'reducers/global/selectors';import en from './en';export function getDetectedLang() {  if (!global.navigator) {    return EN;  }  let detected;  if (size(navigator.languages)) {    detected = navigator.languages[0];  } else {    detected = navigator.language;  }  if (detected) {    detected = detected.substring(0, 2);    if (langs.indexOf(detected) !== -1) {      return detected;    }  }  return EN;}const options = {  lang: global.localStorage ?    (localStorage.getItem(LANG) || getDetectedLang()) :    getDetectedLang(),};const { lang: currentLang } = options;const translations = {  en,};if (!translations[currentLang]) {  try {    translations[currentLang] = require(`./${currentLang}`).default;  } catch (err) {} // eslint-disable-line}export function setLang(lang = EN) {  if (langs.indexOf(lang) === -1) {    return;  }  if (global.localStorage) {    localStorage.setItem(LANG, lang);  }  set(options, [LANG], lang);  if (!translations[lang]) {    try {      translations[lang] = require(`./${lang}`).default;    } catch (err) {} // eslint-disable-line  }}const mapStateToProps = (state) => {  return {    lang: getLang(state),  };};export function t(path) {  const { lang = get(options, [LANG], EN) } = get(this, [PROPS], emptyObj);  if (!translations[lang]) {    try {      translations[lang] = require(`./${lang}`).default;    } catch (err) {} // eslint-disable-line  }  return get(translations[lang], path) || get(translations[EN], path, path);}function i18n(Comp) {  class I18N extends Component {    static propTypes = {      lang: PropTypes.string,    }    static defaultProps = {      lang: EN,    }    constructor(props) {      super(props);      this.t = t.bind(this);    }    componentWillUnmount() {      this.unmounted = true;    }    render() {      return (        <Comp          {...this.props}          t={this.t}        />      );    }  }  return connect(mapStateToProps)(I18N);}export default i18n;

Использование:

import i18n from 'libs/i18n';// ...static propTypes = {  t: PropTypes.func,}// ...const { t } = this.props;// ...{t(['path', 'to', 'key'])}// ...или тоже самое, но слегка медленнее{t('path.to.key')}// ...export default i18n(Comp);

Мультиплеер

Игра поддерживает мультиплеер в реальном времени для устройств с Android 9 или выше (возможно, будет работать и на 8-м, однако данное предположение не проверялось) с рейтингом и таблицей лидеров.

Сам движок не поддерживает ходы в реальном времени, потому многопользовательский режим построен таким образом, что все события происходят в один и тот же ход, в это же время существуют кулдауны на определенные действия, которые реализованы через requestAnimationFrame. На Android 7 и ранее такой подход почему-то просто-напросто не работает.

Кастомный код библиотеки, которая регистрирует requestAnimationFrame, для интересующихся (не забывайте также, что после регистрации колбека его нужно и отрегистрировать, что обычно происходит по завершении колбека или на анмаут компонента):

import isFunction from 'lodash/isFunction';let lastTime = 0;const vendors = ['ms', 'moz', 'webkit', 'o'];for (let x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {  window.requestAnimationFrame = window[`${vendors[x]}RequestAnimationFrame`];  window.cancelAnimationFrame = window[`${vendors[x]}CancelAnimationFrame`] || window[`${vendors[x]}CancelRequestAnimationFrame`];}if (!window.requestAnimationFrame) {  window.requestAnimationFrame = (callback) => {    const currTime = new Date().getTime();    const timeToCall = Math.max(0, 16 - (currTime - lastTime));    const id = window.setTimeout(() => { callback(currTime + timeToCall); },      timeToCall);    lastTime = currTime + timeToCall;    return id;  };}if (!window.cancelAnimationFrame) {  window.cancelAnimationFrame = (id) => {    clearTimeout(id);  };}let lastFrame = null;let raf = null;const callbacks = [];const loop = (now) => {  raf = requestAnimationFrame(loop);  const deltaT = now - lastFrame;  // do not render frame when deltaT is too high  if (deltaT < 160) {    let callbacksLength = callbacks.length;    while (callbacksLength-- > 0) {      callbacks[callbacksLength](now);    }  }  lastFrame = now;};export function registerRafCallback(callback) {  if (!isFunction(callback)) {    return;  }  const index = callbacks.indexOf(callback);  // remove already existing the same callback  if (index !== -1) {    callbacks.splice(index, 1);  }  callbacks.push(callback);  if (!raf) {    raf = requestAnimationFrame(loop);  }}export function unregisterRafCallback(callback) {  const index = callbacks.indexOf(callback);  if (index !== -1) {    callbacks.splice(index, 1);  }  if (callbacks.length === 0 && raf) {    cancelAnimationFrame(raf);    raf = null;  }}

Использование:

import { registerRafCallback, unregisterRafCallback } from 'client/libs/raf';// ...registerRafCallback(this.cooldown);// ...componentWillUnmount() {  unregisterRafCallback(this.cooldown);}

Стандартная имплементация Lobby из библиотеки движка мне не подходила, так как она открывала ещё одно новое websocket-подключение на каждый инстанс игры, но мне также нужно было передавать данные пользователя и таблицу лидеров по своему уже существующему websocket-подключению, потому, чтобы не плодить подключения, здесь снова было использовано собственное решение на основе библиотеки primus. На стороне клиента подключение хендлится сбилдженной библиотекой от примуса, которое также выложил на npm с именем primus-client. Вы можете сами сбилдить себе подобную клиентскую библиотеку для определенной версии примуса через функцию save на стороне сервера.

Видео геймплея многопользовательского режима можно наблюдать ниже:

Звук и музыка

В игре присутствует несколько звуков - старта игры, атаки и победы. Для проигрывания звука - также кастомная библиотека (для музыки - схожий подход):

import { SOUND_VOLUME } from 'defaults';const Sound = {  audio: null,  volume: localStorage.getItem(SOUND_VOLUME) || 0.8,  play(path) {    const audio = new Audio(path);    audio.volume = Sound.volume;    if (Sound.audio) {      Sound.audio.pause();    }    audio.play();    Sound.audio = audio;  },};export function getVolume() {  return Sound.volume;}export function setVolume(volume) {  Sound.volume = volume;  localStorage.setItem(SOUND_VOLUME, volume);}export default Sound;

Использование:

import Sound from 'client/libs/sound';// ...Sound.play('/mp3/win.mp3');
Окно настроек игрыОкно настроек игры

Сборка проекта

Сборка web-части осуществляется вебпаком. Однако тут нужно учитывать особенности путей к файлам, ведь в процессе разработке на локалхосте или на сервере в продакшене они являются относительными корня домена, а для приложения в Cordova наши файлы будут размещены по протоколу file:// и потому после сборки нам необходимо провести некоторые преобразования, а именно:

const replace = require('replace-in-file');const path = require('path');const options = {  files: [    path.resolve(__dirname, './app/*.css'),    path.resolve(__dirname, './app/*.js'),    path.resolve(__dirname, './app/index.html'),  ],  from: [/url\(\/img/g, /href="\//g, /src="\//g, /"\/mp3/g],  to: ['url(./img', 'href="./', 'src="./', '"./mp3'],};replace(options)  .then((results) => {    console.log('Replacement results:', results);  })  .catch((error) => {    console.error('Error occurred:', error);  });

Итоги

Приложение разрабатывалось в течении года, находится в Google Play Store с середины сентября, а значит уже прошло три месяца. Общее количество установок - 46, из которых ещё непонятно, сколько там на самом деле настоящих людей. Если коротко, то это провал. Однако был приобретен первичный опыт как разработки игр, так и мобильных приложений.

Из того, что было задумано, но не получилось:

  1. Более сложный геймплей

  2. Бесконечная прокрутка карты по горизонтали

  3. Продвинутый ИИ компьютера

  4. Поддержка мультиплеера на всех устройствах

Дальнейшие планы

Сейчас понятно, что подобные игры мало кому интересны, так что в прогрессе изучение Unity, и возможно через некоторое время появится ещё одна игра в жанре tactical rts.

Можно потыкать?

Можно. Для интересующихся - ссылка на приложение на Google Play Store.

P.S. Отдельное спасибо музыканту Anton Zvarych за предоставленную фоновую музыку.

Подробнее..

Перевод Лаги, джиттер и потеря пакетов откуда берутся проблемы с неткодом и как их решать

28.05.2021 12:18:53 | Автор: admin

Если вы когда-либо играли в онлайн-игры, то наверняка знаете о лагах не понаслышке. Пропущенный выстрел, который должен был попасть в цель; мяч, резко меняющий направление прямо в воздухе; вы вдруг снова оказываетесь на несколько шагов назад в том месте, где находились секундой ранее. В этом нет ничего сверхъестественного: это обычные артефакты систем, обеспечивающих совместную игру с людьми из других стран почти так же эффективно, как и с соседом по комнате. По крайней мере, большую часть времени.

В этой статье речь пойдет о самых базовых вещах, почему вообще возникает сетевая задержка, с какими еще проблемами неткода можно столкнуться, а также с тем, что можно сделать со своей стороны, чтобы улучшить ситуацию. Также разберем остальную часть конвейера между ПК и игровым сервером, который может повлиять на получение лучшего игрового опыта без задержек и прочих неприятностей.

Итак, почему вообще возникают лаги? Почему в 2021 году это все еще является проблемой с мощностью современных компьютеров, повсеместным использованием широкополосного Интернета и спустя десятилетия попыток разработчиков решить эту проблему?

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

Состояние игры должно быть идеально синхронизировано с минимальной задержкой независимо от того, имеете ли вы дело с игроками в одной локальной сети или с разных континентов. Поэтому вам придется придумать способ компенсации задержки игроков. При этом лучше избегать необходимости ожидания игроками, пока сервер сообщит им результаты их же собственных действий, ведь динамике это на пользу не пойдет. Для этого вам, вероятно, потребуется показать эффекты ввода каждого игрока сразу же, а затем уже сглаживать любые различия между ПК игрока и сервером таким образом, чтобы это было внешне неочевидно.

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

С двумя-то игроками это реализовать достаточно сложно, а теперь представьте, каково организовать подобное для игры с 10, 20 или даже сотнями игроков на одном сервере. При разработке игры жанра battle royale со всеми ее особенностями реализация хорошего мультиплеера является одной из самых сложных частей разработки даже для опытной команды, поэтому неудивительно, что временами в игре могут возникать сетевые проблемы. Конечно, это не делает такие сбои менее раздражающими, и чем быстрее скорость и выше конкуренция в игре, тем больше такой опыт может помешать вам получить от нее удовольствие.

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

Так что же вызывает все эти проблемы?

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

Первое из них задержка. Она возникает тогда, когда пакеты с данными слишком долго передаются серверу и затем возвращаются к игроку. Проще говоря, задержка это скорость реакции вашего интернет-соединения, то есть время, необходимое для передачи данных с вашего устройства на сервер. Важно отметить, что задержка зависит от качества вашего интернет-соединения, а не от скорости.

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

Джиттер это колебания задержки, означающие, что пакеты отправляются и принимаются с разной скоростью. Это похоже на плохой frame pacing: то ваш пинг меняется с 20 миллисекунд до секунды, то с секунды до 90 миллисекунд, а затем возвращается к 30 миллисекундам, которые были когда-то уже давно.

Потеря пакетов это когда пакеты вообще не достигают места назначения. Она вызывает необходимость повторной отправки одних и тех же данных и и потенциально странное поведение игры.

Конечно, наличие даже одной из этих проблем может довольно сильно раздражать. С точки зрения игрока, если вы не чувствуете, что контролируете игру, если ваша производительность страдает от проблем с сетью, велика вероятность, что вы просто закроете игру и больше в нее не зайдете. Для разработчиков и издателей это будет означать, что аудитория станет меньше, а если у вас free-to-play тайтл, это поставит под угрозу само его существование. Особенно важно это для соревновательных игр, поскольку любые повторяющиеся проблемы могут заставить игроков и организаторов турниров отказаться от их проведения до тех пор, пока проблемы не разрешатся.

Так почему же возникают подобные сбои?

Существуют три основных типа проблем с соединением:

  • проблемы первой мили, вызванные домашней сетью вашего ПК и подключением к Интернету;

  • проблемы средней мили, обусловленные перемещением данных по маршруту между вашим интернет-провайдером и игровым сервером;

  • проблемы последней мили, связанные непосредственно с игровым сервером.

Обычно первая и последняя миля вызывают наименьшую задержку в нашем примере это около одной миллисекунды. Средняя же миля имеет наибольшую задержку, ведь именно на ней пакеты преодолевают все географическое расстояние между вашим компьютером и сервером.

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

Точно так же, как производительность игры ограничивается компонентами ПК, такими как центральный процессор или видеокарта, сетевое соединение тоже зависит от многих элементов. На то, как будут отправляться и приниматься пакеты данных, могут влиять настройки игры, сетевое оборудование вашего ПК и его драйверы, ваша операционная система и ее настройки. Но есть и физические аспекты например, то, как ваш компьютер подключен к роутеру, какой у вас роутер и как он настроен и, конечно же, подключение к Интернету, предоставляемое провайдером.

Пожалуй, наиболее важным для игр является переключение с беспроводного на проводной Интернет, что может значительно снизить задержки, джиттер и потерю пакетов. Помимо этого, возможно, стоит изучить роутеры с более гибко настраиваемой прошивкой например, отдать предпочтение роутерам OpenWRT или pfSense или просто повысить скорость вашего интернет-соединения особенно в домашних условиях, где оно используется несколькими людьми, которые могут смотреть потоковые видео, загружать файлы из Интернета и выполнять другие задачи с высокой пропускной способностью одновременно. В идеале роутер должен иметь возможность отдавать приоритет критическим с точки зрения задачам, таким как онлайн-игры и видеоконференции, по сравнению с такими вещами, как воспроизведение видео с YouTube.

Наконец, полезно будет убедиться, что драйвера обновлены, вы отключили все приложения, которые используют значительный объем ЦП или пропускной способности сети в фоновом режиме, а также что сетевые настройки игры установлены правильно.

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

В первую очередь ваш пакет должен обработать местный интернет-провайдер. Но совсем скоро он перейдет к оптоволоконным магистралям, которые соединяют города и страны друг с другом. Здесь маршрут, по которому идет пакет, не обязательно окажется самым быстрым, и нет никакой гарантии, что пакет вообще доберется до конечного пункта назначения. Помните, что предшественник Интернета был разработан министерством обороны США для работы в условиях ядерной войны. Таким образом, доставляемость для него важнее скорости.

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

Так что же могут сделать разработчики игр для оптимизации на этом участке? Географическое расстояние критически влияет на пинг, поэтому можно открывать новые игровые серверы, которые находились бы в недостаточно обслуживаемых регионах базы игроков. Это уменьшит задержку для этих игроков и вероятность того, что какое-то соединение между игроком и сервером замедлится или полностью выйдет из строя. Но, конечно, запускать серверы повсеместно может оказаться непросто, особенно для небольшой компании с ограниченными ресурсами да и это не решит всех проблем.

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

Такой контроль над маршрутизацией также открывает некоторые интересные возможности для киберспорта, где отборочные и другие важные матчи проводятся онлайн. Можно настроить маршрутизацию и выбрать расположение сервера так, чтобы у обеих команд был одинаковый пинг, даже если одна из них находится географически ближе к серверу, и тем самым обеспечить участникам более справедливую игру.

Наконец, перейдем к последней миле в цепочке игровым серверам.

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

Client hosting это когда ПК одного из игроков сам по себе выступает в качестве сервера. Это хороший способ для разработчиков игр минимизировать затраты, но опыт каждого участника матча будет зависеть от качества соединения игрока-хоста. Таким образом, если такие игроки подключаются к сети через Wi-Fi или вовсе испытывают проблемы с подключением, другие игроки тоже столкнутся с лагами, джиттером и потерей пакетов.

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

Другой вариант одноранговая (peer-to-peer) сеть, где игроки напрямую подключается друг к другу. Как правило, в таком случае тоже существует некий хост, который номинально отвечает за обработку новых соединений, поэтому проблема с миграцией хоста в данном случае сохраняется.

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

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

Некоторые игры работают с переменным весом тиков например, королевские битвы, в которых скорость тиков повышается по мере выбывания игроков, или Counter-Strike, где сторонние и киберспортивные матчи проводятся со скоростью 128 тиков в секунду по сравнению со встроенным в игру матчмейкингом, работающим на 64 тиках.

Матчмейкинг наиболее важная часть мультиплеера. Чтобы свести игроков в одном матче, вам, как разработчику игры, понадобится учитывать такие факторы, как пинг, уровень навыков, количество игроков в группе и выбранные каждым игроком карты и режимы. Каждый новый фактор существенно уменьшает размер пула для подбора игроков что, в свою очередь, увеличивает время ожидания игры в среднем.

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

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

Так называемое предсказание на стороне клиента часто используют в шутерах от первого лица. Его можно разделить на предсказание ввода и расчет траектории (dead reckoning): первое будет скрывать задержку действий самого игрока, в то время как второй других игроков.

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

Dead reckoning это, по сути, алгоритм оценки положения объекта в виртуальном мире на основе его предыдущего положения, направления движения, скорости, ускорения и других параметров. Получив первый блок данных протокола состояния (protocol data unit, PDU) для объекта (например, персонажа другого игрока), каждый клиент начинает перемещение этого объекта, применяя согласованный алгоритм dead reckoning. Его движение обновляется при получении последующих PDU. Если для пакетов, несущих PDU, возникнет увеличенная задержка или вовсе их потеря, каждая копия виртуального мира продолжит показывать движение объектов в соответствии с алгоритмом до тех пор, пока не получит следующее обновление. Кроме того, при несоответствиях между статусом сервера и предсказанным клиентом некоторые игры могут сделать переход к новому статусу менее резким, используя алгоритмы сглаживания.

В дополнение к предсказанию клиента сервер может прибегать к методу компенсации сетевой задержки, чтобы правильно объединить виртуальные реальности, которые из-за проблем с неткодом испытывают рассинхронизацию. В таком случае сервер хранит историю последних позиций игроков (так, серверы, на которых работает движок Valve Source, сохраняют позиции игроков в течение 1 секунды), и когда ему нужно вычислить новое состояние, он сначала оценивает момент, когда действие было выполнено в клиентской версии состояния игрового мира. Другими словами, сервер перематывает время в соответствии с задержкой конкретного клиента, вычисляя выполнение введенной им команды (например, удалось ли выстрелу игрока поразить цель). Для этого используется следующая формула:

Время выполнения команды = Текущее время сервера Задержка пакета Интерполяция представления клиента

Иллюстрация выше представляет собой пример скриншота, сделанного на сервере сразу после того, как он подтвердил обращение. Красный хитбокс показывает позицию цели на клиенте, как это было некоторое время назад. Пока команда игрока (то есть, выстрел по цели) добиралась до сервера, цель продолжала двигаться влево. После того, как команда прибыла на сервер, для расчета факта попадания сервер восстанавливает позицию цели (синий хитбокс) на основе предполагаемого времени выполнения команды. Так сервер отслеживает траекторию выстрела и подтверждает или не подтверждает попадание.

Впрочем, иногда такой алгоритм приводит к ошибкам попадания, когда цель уже скрылась за укрытием. Эту проблему можно было бы решить, если бы клиент мог послать сообщение попадание вместо сообщения выстрел. Однако серверы не могут разрешить клиентам сообщать о попаданиях по соображениям безопасности: некоторые клиенты могут этим воспользоваться в собственных целях и прибегнуть к читерству.


Мы рассмотрели основные причины различных задержек и других нежелательных аспектов производительности сети. Как у игрока, у вас есть немало опций, которые могут значительно улучшить качество вашего игрового опыта. Надежное сетевое соединение может сделать игру намного стабильнее и интереснее. Также мы рассмотрели варианты, что со своей стороны могут сделать разработчики и как они могут повлиять на игровой процесс.

Для получения дополнительной информации о лагах и о том, что вы можете с ними сделать, можно ознакомиться с каналом Battle(non)sense: там разбираются тесты сетевых проблем в разных играх и то, как разные технологии на них влияют. Прилагаем также и другие ссылки на видео и статьи о сетевом коде ниже:

Подробнее..

В играх детям нравятся атрибуты взрослой жизни как мы разрабатывали обучающую игру для младших школьников

14.07.2020 18:11:56 | Автор: admin
Лето время каникул и низкий сезон для образовательных проектов. За эти три месяца дети могут забыть до четверти знаний, полученных в классе и дома. Для того чтобы вовлечь детей в повторение математики на каникулах, мы делаем образовательные игры.

Сегодня я расскажу о нашем опыте в такой лернификации мультиплеерных игровых механик и адаптации их для детей от семи до одиннадцати лет на примере новой онлайн-игры Формула 1+1. За несколько месяцев работы в тестовом режиме (игра доступна только 10% пользователей платформы) в ней приняли участие 95 тыс. игроков, а самые упорные провели в ней больше 100 часов, то есть более 6 тыс. игр. Я постараюсь показать, как мы реализуем лернификацию процесс, когда мы не пытаемся сделать интересной учебу, а напротив добавляем элемент обучения в игру.


Карта гонок в игре Формула 1+1

Игра Формула 1+1 мультиплеерные гонки, где на скорость автомобиля влияет решение примеров устного счета: чем быстрее и больше правильных ответов будет давать игрок, тем быстрее он будет ехать. За победу в заезде игрок получает опыт (поднимается в рейтинге) и монеты, на которые можно покупать новые, более мощные на вид машины.

Какие цели мы ставили


Когда игра задумывалась, мы хотели, чтобы она была по-настоящему интересна детям и в то же время развивала навыки из школьной программы 14 классов.

Мы стремились:

Учесть особенности детского восприятия


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

Показать ценность игры взрослым


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

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

Безопасность с точки зрения авторизации обеспечивает платформа Учи.ру. В игре также нет рекламы, механик лутбоксов все улучшения доступны только за счет внутриигровой валюты.

Игровая механика


Ядро геймплея


За основу мы взяли механику игры, популярной у детей на нашей платформе: решение арифметических примеров на скорость. Добавили генерацию арифметических примеров каждый раз игрок видит новое задание. Это делает игру намного увлекательнее и повышает реиграбельность, а значит, игроки улучшают навыки счета.

В пользу мультиплеера выступает наш удачный опыт проведения различных соревнований на портале и обилие успешных мультиплеерных мобильных игр на рынке: Brawl Stars, Clash of Clans, Clash Royale и подобные. Так у нас получились гонки, где в онлайн-заезде два ученика соревнуются в том, кто быстрее доедет до финиша.

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

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


Выбирая правильный ответ, игрок продвигает свой автомобиль к финишной черте. Циферблат слева показывает номер текущего вопроса, справа таймер, отсчитывающий выделенное на ответ время.

Второстепенные механики


Трудно повлиять на арифметические способности ребенка, если он быстро потеряет интерес к игре, станет неохотно в нее играть или вообще забросит. Мы должны удерживать его в игре достаточно долго и мотивировать улучшать результаты. Для этого в игре предусмотрено несколько инструментов.

Чтобы добавить гонке вариабельности (читай реиграбельности), мы придумали бонус за быстрые ответы. Если игрок два раза подряд дает правильные ответы быстрее соперника, его автомобиль еще чуть-чуть продвигается вперед.

Заезды можно проводить на разных трассах. Всего их пять. На каждой свой тип арифметических примеров: сложение, вычитание, умножение, деление и микс, где встречаются все примеры.

В каждой категории вопросы генерируются во время игры. В отличие от заранее прописанных примеров, этот способ не только сильно расширяет количество заданий, которые может встретить ученик, но и дает нам возможность регулировать их сложность в зависимости от класса. Мы выделили четыре уровня сложности, которые соответствуют нормам 1, 2, 3 и 4 (и выше) класса.

Например, ученики 1 класса умеют считать только в пределах 20, а в 4-м дети решают примеры с трехзначными слагаемыми и переходом через один десяток. С заданиями последнего уровня без калькулятора справится не каждый взрослый на один пример отведено всего десять секунд. Попробуйте прямо сейчас быстро выполнить задание на сложение с двумя переходами через десятки: 574 + 349.



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

Хотя у портала и огромная аудитория в 8 млн учеников, игрокам может не хватать соперников в онлайне. Чтобы не заставлять детей долго ждать подходящего партнера по игре, мы ввели в игру ботов. Бот-соперник выглядит как обычный игрок. На основе рейтинга игрока боту присваивается соотношение правильных и ошибочных ответов: сильные игроки получают сильного бота, слабые слабого.

Чтобы избежать договорных матчей, мы не даем вознаграждений за заезды с друзьями.

Персонаж: детали и нюансы


Три составляющие персонажа игрока


Дети любят подражать родителям, им нравятся атрибуты взрослой жизни. Мы постарались это учесть, поэтому при первом входе в игру каждый ребенок получает игровые автомобильные права. Заполняя их, игрок создает своего персонажа: выбирает аватарку, игровое имя и автомобильный номер для внутриигровой идентификации.

Имя можно выбрать из выпадающего списка прилагательных и существительных. Это было сделано неслучайно. Во-первых, потому что отображать в игре настоящее имя и фамилию ребенка было бы некорректно игровое имя позволяет сохранить анонимность. А во-вторых, представьте, что будет, если дать детям блеснуть чувством юмора и самим придумать игровое имя. Как минимум нам пришлось бы вводить модерацию имен, что значительно осложнило бы процесс для всех.

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



Авторизация 0+


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

Мотивация


1. Награда за сыгранную партию


В конце заезда ученик всегда получает награды очки опыта и монеты. Очки опыта нужны, чтобы повысить свой уровень. Это самый простой инструмент мотивации. Чем больше ты играешь, тем выше у тебя уровень, и не важно, хорошо ты играешь или нет. За монеты можно купить новые автомобили, более крутые и красивые.

Кстати, когда мы тестировали разные модели авто с детьми, самыми крутыми они называли не суперкары и не новинки автопрома, а модели, которые видели в семье.



2. Прокачка в нескольких направлениях


Улучшение личного автопарка не ограничивается покупкой новой машины. Выполнив определенные задачи, игрок может перекрасить кузов или добавить другие визуальные эффекты. Одни задачи поощряют количество правильных ответов или серию побед, другие мотивируют регулярно заходить в игру, проводить больше заездов в день. Таким образом машина становится витриной достижений ученика, которую постоянно видят другие игроки.

3. Личный рейтинг


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

Для расчета рейтинга игрока мы используем коэффициент Эло, часто применяемый в спорте (шахматы, футбол, теннис). Способности в счете у детей сильно различаются не только от года к году, но и внутри класса. Кто-то научился считать еще до школы, а кто-то в 1 классе только знакомится с цифрами. Поэтому мы должны подбирать соперников, учитывая не только класс игрока, но и его способности. Иначе слабому ребенку будет грустно играть с сильными игроками, а сильному быстро надоест постоянно выигрывать.

Рейтинг Эло метод расчета относительной силы игроков в парных играх. Все новые игроки получают стартовое значение рейтинга. После гонки с соперником рейтинг обоих изменяется в зависимости от победителя и значений рейтингов обоих игроков. Если ребенок выиграл у партнера с более высоким местом в рейтинге, его рейтинг вырастет больше, чем если бы он выиграл у соперника на более низком месте. Таким образом сильный игрок, постоянно выигрывая, будет увеличивать свой рейтинг, пока не дойдет до равных ему, а шанс на победу не составит 50/50.


Игроки с меньшим уровнем могут оказаться выше в рейтинге.

4. Рейтинг класса


Как показывает наш опыт, дети с удовольствием соревнуются целыми классами, поэтому мы добавили в игру и рейтинг класса. Чтобы решить проблему разной численности классов (от нескольких учеников до 35) и чрезмерного рвения к первым местам, мы разработали отдельную систему расчета классного рейтинга.
  1. Очки для рейтинга класса игрок получает только за первые пять заездов в день в режиме Микс.
  2. Количество получаемых очков зависит от численности класса. Если каждый ребенок в каждом классе будет участвовать в пяти гонках в день и выигрывать, то эти классы наберут одинаковую (максимальную) сумму очков класса вне зависимости от количества учеников. Эта сумма становится основой для расчета баллов рейтинга для всех классов в игре.
  3. Эта максимальная сумма должна хорошо (без остатка или с наименьшим остатком) делиться на количество дней в месяце, на пять гонок, на различное количество детей в классе и на три (за проигрыш полагается от выигрыша). Это важно, потому что ученики 14 классов еще не знают дробей.
  4. Для подбора наилучшего значения этой максимальной суммы мы написали скрипт, который перебрал несколько сот тысяч значений и выбрал наиболее подходящее число 890 100. Сумма вознаграждения за заезд получается в виде целого числа, при этом не громоздкой. Например, в классе из 30 человек за победу в заезде игрок получит 198 очков класса.


Матрица расчета баллов

Вывод


У нас получилась игра, очень похожая на обычную онлайн-игру, но с реальным образовательным эффектом, при этом адаптированная для детей 711 лет. Главная механика игры служит улучшению навыка счета у ребенка. Первые результаты показывают, что дети готовы проводить в ней огромное количество времени: на момент написания этой статьи мы насчитали более 95 тыс. активных игроков (при том, что игра все еще находится в тестовом режиме и доступна только 10% учеников Учи.ру). Реакция родителей на Формулу 1+1 тоже положительная.

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

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru