Турель Аннигилятор
  • Игровой backend: из каких модулей он должен состоять?
  • Расчет параметров персонажа: виртуальные методы или сложение массивов?
  • Логика поведения: на каком уровне она должна находится?
  • Перемещение персонажей: кто этим должен управлять?

        Сегодня мы продолжим знакомиться с разработкой и проектированием он-лайн игры на примере космической ММО RPG «Звездные Призраки». В этой статье речь пойдет о backend'е на С++ и она будет насквозь техническая.

        В тексте будет много отсылок к функционалу «Звездных Призраков», но я постараюсь излагать материал так, чтобы вам не было нужды вникать (и играть) в наш продукт. Однако, для лучшего понимания материала желательно потратить пару минут и посмотреть, как это все выглядит.

        В статье мы сосредоточимся именно на архитектурных решениях применительно к backend'у MMO RPG в реальном времени. Исходного кода будет не много и он точно не будет содержать таких специфических для С++ вещей как множественное наследование или шаблоны. Задача данной статьи помочь в проектировании игрового сервера и ознакомить всех желающих со спецификой игрового backend'а.

        Описываемые решения достаточно универсальны и вполне подойдут для многих RPG. В качестве иллюстрации в конце статьи я приведу пример использования описанной архитектуры в игре «про эльфов».

        Выбор технологии


        Чтобы реализовать задуманный нами геймплей, был необходим сервер с постоянным сокетным соединением и достаточно малым временем отклика на действие любого пользователя — не более 50мс, не считая пинга. Выбор технологий, которые позволяли удовлетворить такие требования, не так уж и велик. На тот момент у нашей кампании уже был опыт реализации backend'а на С++ для неигрового проекта, и поэтому выбор был сделан в пользу именно С++: у нас были и люди, и опыт в этой технологии.

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

        Я затрудняюсь ответить каким бы вышел сервер на Java (или какой-то другой технологии), но на С++ мы получили именно то, что нам требовалось, к тому же за вменяемые сроки.

        Общая схема сервера


        Сервер состоит из следующих модулей (см. рис.1).

Общая схема сервера

  • Ship содержит данные об устройствах и текущих параметрах корабля, а так же занимается расчетом этих параметров в соответствии с установленными устройствами. Это самый нижний уровень, на который опираются все остальные модули сервера.
  • Space – это модуль описания мира и объектов в мире. На этом уровне у объектов появляются их координаты, текущие векторы движения, реализовано взаимодействие объектов (обработка выстрелов, нанесения урона и прочее).
  • AI – это модуль, реализующий AI мобов, а так же специфические навыки NPC.
  • Quests – реализация квестовой системы.
  • Main – содержит весь функционал, отвечающий за взаимодействие с пользователем (сокеты, потоки и пр.), а так же специфический для персонажа функционал (навыки, бафы, достижения, крафт и прочее).
  • Packets – автогенерируемый модуль, содержащий обертки для пакетов и реализующий RPC клиент<->сервер.

        В этой части статьи мы рассмотрим архитектуру модулей Ship и Space. Архитектура остальных модулей будет рассмотрена в следующей части этой статьи.

        Модуль Ship


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

Диаграмма классов модуля Ship

        На рис. 2 представлена диаграмма классов (диаграмма упрощена для большей наглядности). Вы видите разделение классов на две части: внизу прототипы предметов, а вверху – сами предметы. Прототипы полностью статичны и безлики – они загружаются из БД, не могут изменяться и никому не принадлежат. А объекты предметов (все потомки от ICargo), наоборот, могут быть модифицированы и содержат в себе уникальный ID, который позволяет идентифицировать конкретный предмет и определить в каком месте он находится (трюм, склад, контейнер в космосе, магазин и т.д.). Такой подход добавляет гибкости и позволяет модифицировать функционал предметов, не затрагивая другие классы.

        В нашем решении большинство потомков ICargo (вернее все, кроме TDevice и TShip) являются просто проксями для своих прототипов. Тогда возникает вопрос: а так ли они были нужны? Ведь проще создавать потомков прототипов, с добавлением уникального ID для идентификации, да и дело с концом? Нет, не проще. Но при таком подходе, во-первых, нам все равно потребовалось бы два класса на предмет (прототип и потомок), а во-вторых, у нас бы смешивались динамические данные со статическими (ведь прототипы неизменны). Вдобавок ко всему, конечно же, увеличился бы расход памяти и время создания предмета, потому что необходимо было бы клонировать прототип со всеми его полями. В подтверждение сказанному приведу такой пример: изначально у нас в игре не было чипов, и когда они появились, то все изменения свелись к добавлению пары классов TMicromodule/TMicromoduleProto с добавлением функционала по учету установленных чипов в TDevice. Класс TShip, как и все прочие классы, не был затронут вообще.

        Расчет параметров корабля и обрудования
        В «Звездных Призраках» есть много различных типов устройств (турели, ракетницы, радар, система маскировки, защитное поле, усилители урона и прочее). Казалось бы, для каждого из них необходимо делать класс-поток от TDevice и реализовывать там специфичный функционал для этого устройства. Но давайте еще раз взглянем на общую схему сервера и описание модуля Ship: этот модуль, в основном, просто предоставляет итоговые расчетные параметры корабля более верхнему уровню, при этом сам функции предметов не выполняет. Поясню на примере. Класс TShip содержит параметр ScanningRange – радиус работы радара, – но фактическую фильтрацию объектов по дальности он не делает. И, что самое главное, на уровне модуля Ship сделать эту фильтрацию не получится, так как у объектов нет координат в пространстве. Самое время спросить себя: есть ли смысл создавать пару классов TRadarPrototype (как потомка от TProtoBase) и TRadar (как потомка от TDevice), отдельную таблицу в БД для этого класса и страницу в админке только ради одного поля ScanningRange? Ответ очевиден: смысл всех этих строк кода и классов весьма сомнителен. Именно поэтому мы создали один класс TStaticParams, содержащий в себе все параметры, которые могут быть у любого устройства в игре, а также класс TPrototypeMod, который может загружать из БД TStaticParams.

        Конечно это излишество, но не очень большое: на данный момент класс TStaticParams содержит всего 34 поля типа int. А вот взамен мы получили несколько отличных плюшек. Во-первых, простоту модификации. Теперь можно создавать новые типы устройств и параметров без создания новых классов. Во-вторых, простоту подсчета параметров. Достаточно просто сложить все одноименные поля всех TStaticParams в корабле, чтобы получить итоговые параметры! Никаких виртуальных вызовов или downcast'ов – простая операция «+=» в цикле. В-третьих, мы получили геймдизайнерскую гибкость. Например, у нас в игре есть чип, который может быть установлен в любое устройство, и дает он НР. Такой механизм позволяет геймдизайнерам резвиться так как им захочется, при этом абсолютно не дергая программистов по каждой мелочи типа «ребзя, допишите мне тут капарик, чтобы я мог задавать в устройстве маскировки бонус к уклонению».

        И это еще не все. Так как у нас один класс с параметрами для любого устройства, нам очень легко удалось реализовать рандомизацию параметров и заточку. TStaticParams – это массив, поэтому в админке геймдизайнер при создании устройства может указать до трех параметров (индексов в массиве), и процент разброса в этих параметров. При создании предмета, TDevice в первую очередь копирует данные из TPrototypeMod.TStaticParams в свой экземпляр TStaticParams. Потом он просматривает индексы разброса и если они установлены, бросает кубик и рандомизирует параметры. Значение кубика сохраняется в полях TDevice, чтобы после загрузки из БД параметры не изменились. Заточка выполняется аналогично: в админке геймдизайнер указывает MainParam для устройства. То есть устройство знает индекс параметра, который необходимо увеличить на +10% за каждую успешную заточку.
        Но есть один нюанс при расчете параметров оружия: их нельзя просто суммировать с параметрами остальных устройств. Простое суммирование приведет к тому, что если у вас установлено больше одного оружия, то вы сложите, в том числе и такие параметры, как WeaponRange всех пушек на борту, хотя так быть не должно. С другой стороны, если это артефакт, который увеличивает радиус действия оружия, то мы должны прибавить его к WeaponRange оружия. Мы решили эту проблемы следующим образом: во-первых, TStaticParams содержит два массива – общие параметры, которые всегда можно складывать безопасно (например, НР, ScanningRange и т.д.) и так называемые WeaponParams, которые в общем случае складывать нельзя. И только если устройство не является оружием, его параметры необходимо прибавить к параметрам оружия. Выглядит это все так:

void TShip::Recalc() {
      m_xStatic.Set(0);
      TDevice* dev = NULL;

      for(unsigned i=0;i<m_vSlots.size();i++) {
            dev = m_vSlots[i].Device();
            if( !dev || !dev->IsOnline() ) continue;
            if( dev->IsWeapon() ) {
                  m_xStatic.AddDevice( dev->Static() );//типа HP там прибавать
            } else {
                  m_xStatic.Add( dev->Static() );
            }
      }//for i

      if(m_pStaticModifier) m_xStatic.Add( *m_pStaticModifier );// прибавим навыки пилота, бафы и прочее, что приходит сверху

      // и вот тут ещё момент - нужно прибавить ко всему оружию параметры, которые висят на корпусе
      for(unsigned i=0;i<m_vSlots.size();i++) {
            dev = m_vSlots[i].Device();
            if( !dev || !dev->IsOnline() || !dev->IsWeapon() ) continue;
            dev->SetWeapon( &m_xStatic );
      }//for i
}

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

        Расчет выстрела
        Кроме расчета параметров устройств и проверки на возможность установки в слот, класс TShip выполняет еще одну функцию – расчет параметров выстрела. Делает это метод SFireResult TShip::Fire(NSlotPlace slot). Этот метод проверяет возможность выстрела (оружие ли это вообще, закончился ли кулдаун устройства, есть ли патроны для выстрела), рассчитывает наносимый урон, количество произведенных выстрелов, а также бросает кубик на допустимые флаги выстрела (такие, как нанесение критического урона). Все параметры записываются в структуру SFireResult, устройство ставится в кул-даун, списываются боеприпасы, результат выстрела возвращается. При этом TShip не может проверить ни дальность, ни параметры объекта, в который стреляют (например, если у объекта есть защита и урон нужно снизить). Это делает верхний уровень Space, который располагает всеми необходимыми данными.

        Остальные классы модуля Ship
        Класс TProtoBase содержит в себе общие данные для любого предмета, такие как ImageID, Name, Level и т.д.

       ICargo содержит в себе указатель на TProtoBase и проксирует его данные наружу, а также предоставляет Factory для создания предметов. В этом ему помогает класс-синглтон TDeviceHandbook, который загружает из БД все протитипы и содержит указатели на них.

        Класс TCargoBay – это хранилище объектов типа ICargo. Он умеет сохранять свое состояние в БД и предоставляет ряд сервисных функций таких как: поиск ближайшего свободного слота, поиск совместимого стакабельного предмета (например, патронов, чтобы объединить с другими патронами) и т.п. Потомки от этого класса накладывают ограничения на типы хранимых предметов (например, в ангаре можно хранить только корабли, а на складе – все кроме кораблей), и, по необходимости, ограничения на количество доступных ячеек для хранения.
        Класс IShipNotifyReciver является интерфейсным и обеспечивает связь с более высоким уровнем. Например, передачу на уровень Main сообщения о начале регенерации, чтобы можно было отправить соответствующий пакет клиенту.

        Модуль Space


       Этот модуль оперирует космическими объектами (КО), такими как космические корабли, астероиды, планеты и т.д. У всех КО есть текущие координаты в космосе и вектор их движения. Диаграмма классов представлена на рис.3 (для большей наглядности диаграмма несколько упрощена).

Диаграмма классов модуля Space

        Не смотря на алгоритмическую сложность, с архитектурной точки зрения этот модуль достаточно прост. Все объекты в космосе (корабли, астероиды, планеты, контейнеры, звезда) являются потомками TSpaceObject и находятся в объекте типа TSystem. У TSpaceObject есть текущие координаты, размер и два объекта, управляющие его поведением – это FlyCommand (потомок от ISpaceFlyTo) и Action (потомок от ISpaceAction). FlyCommand вычисляет текущие координаты объекта и его текущую скорость (в данный момент времени). Алгоритм расчета зависит от типа команды: для движения по орбите он один, для линейного перемещения другой, для перемещения с плавными поворотами – третий. Action отвечает за более сложные алгоритмы перемещения объекта. Например, TFollowShipAction выполняет преследование указанной цели. Для этого она в каждом вызове Update проверяет, изменились ли координаты цели и если да, то заменяет в Owner команду FlyCommand (с указанными новыми координатами цели). Введение Action позволило существенно упростить создание AI и избежать дублирования кода, так как функционал, реализуемый в Action необходим и кораблям игроков и ботам.

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

        Нанесение урона
        У класса TSpaceObject есть два виртуальных метода – CorrectDamage и ApplyDamage, а у класса TSystem есть метод DoDamage. Когда какой-то объект хочет нанести урон другому объекту (например, астероид попал в другой объект), он сообщает это TSystem. Система вызывает CorrectDamage и, если урон не нулевой (например, планета имунна к любым видам урона), то она отправляет сообщение о нанесенном уроне «наверх» (чтобы передать в клиенты) и вызывает ApplyDamage, чтобы реципиент выполнил специфические действия (например, корабль уменьшает НР и если НР равно нулю, корабль выбрасывает контейнеры в космос).

        Класс TSpaceShip содержит метод FireSlot, который реализует стрельбу спец.абилками. Он проверяет допустимую дистанцию, потом вызывает TShip:: Fire и в зависимости от типа абилки производит дальнейшие действия. Например, для MissileLauncher создает ракеты.

        Остальные классы модуля Space
        Класс ISpaceShipNotifyReciver используется в TSpaceShip для передачи в верхний модуль сообщений типа «меня атаковали», «я убит», «готов к гиперпереходу» и т.д.

        Класс ISpaceSystemNotifyReciver используется в TSystem для передачи наверх сообщений о добавлении/удалении космических объектов, о новых FlyCommands и о нанесении урона.

        Класс TGalaxy является синглтоном и содержит в себе список всех TSystem в Галактике.

        To be continued


        В следующей статье цикла мы рассмотрим модули AI, Quest, Main, а так же некоторые аспекты работы с БД. И, конечно же, обещанную адаптацию для игры «про эльфов».

Комментарии (29)


  1. aleks_raiden
    15.05.2015 00:16

    Только вчера перечитывал первую статью вашу ) Спасибо!


  1. starni Автор
    15.05.2015 00:19

    Всегда пожалуйста :). Надеюсь, материал и будущих статей будет для Вас интересен.


  1. Semenych
    15.05.2015 00:20
    +2

    Не вполне технический комментарий, скорее про бизнес в целом — сделано достаточно хорошо, потрачено много человеко часов, но неужели не нашлось 5 минут посмотреть в wiki статью про литий? С каких пор Литий ru.wikipedia.org/wiki/%D0%9B%D0%B8%D1%82%D0%B8%D0%B9 — тяжелый метал?
    Сценаристы работают за еду?


    1. starni Автор
      15.05.2015 00:23
      -2

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


      1. Semenych
        15.05.2015 00:33

        Я «старый IT-шник и не знаю слов любви» :-)
        Проект явно хорошо вылизан. не скажу так сразу сколько тут тысяч часов только разработки но явно больше 1000 и наверное меньше 10к.
        Технологически сделано неплохо. Хотя конечно challenge 1000 кораблей в секторе тут присутствует, открытой карты с перетеканием игроков по карте тоже нет. Но все равно хорошо и достойно.
        Но вот такие мелкие казалось бы огрехи низводят все усилия на нет. Игра это прежде всего фан и геймплей. Саркофаг из тяделого металла лития больно бьет по часам потраченным разработчиками. Ну че уже озвучили школота и так схавает.
        На мой взгляд один из компонентов успеха игр Blizzard — внимание к деталям.
        Не так уж и сложно было ПМ-у на стадии разработки до озвучки почитать сценарий — там я думаю всего -то страниц 100 не больше и проверить на констентность. Игра не про эльфов. а как бы с претензией на НФ — ну потратьте пару дней на валидацию.
        Грудь у девушки вышла вполне зачетная, а на background check времени пожалели. Платежеспособная аудитория придирчива и может знать атомный номер лития наизусть


        1. starni Автор
          15.05.2015 00:42

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


          1. Viacheslav01
            15.05.2015 11:14
            +3

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

            Литий даже в воде не тонет, хотя проверять я бы не советовал…


            1. Biga
              15.05.2015 15:23

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


              1. Viacheslav01
                15.05.2015 15:44

                Мы бросали диаметром миллиметров 5 кусочки, в масле и ждали когда вода доберется, когда она добиралась реакция была очень бурная :)


              1. Viacheslav01
                15.05.2015 15:51

                Хотя учитывая, что прошло больше 25 лет, могу предположить, что это был не литий, а натрий :)
                Точно могу сказать, что он плавал и достаточно бурно реагировал с водой, но без взрыва :)


      1. vladvoron
        19.05.2015 20:21

        Сюжет пишут филологи. Потому что они действительно умеют писать.


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

        Сорри, если критика очень уж жесткая получилась. Я прежде всего как потенциальный игрок говорю. Я бы с удовольствием сыграл бы в браузере в РПГ на космическую тематику. Фентези приелась уже как-то. Но «Призраки» не зацепили. Хотя проект, сразу видно, добротный, не на коленке делается. И визуально приятный. Хотя и внешне персонажи тоже без изюминки, очень уж шаблонные — бравый красавчик-капитан, сисястая блондинка-помощница, суровый шеф…


    1. ls1
      15.05.2015 07:11
      +9

      С каких пор Литий — тяжелый метал?
      А те, кому не нравится таскать тяжелый металл люминий литий будет таскать лёгкий металл чугуний (с)


    1. cadmi
      15.05.2015 23:44

      Там и в коде работают за еду. ISpaceSystemNotifyReciver.
      (facepalm)


      1. cadmi
        20.05.2015 12:52
        -1

        Ну и чего минусуем в карму? Обиженки, которым все равно, Reciver или Receiver?

        Так дьявол кроется в мелочах. Как говорил мой преподаватель, отправляя переоформлять лабораторную по оптике на физфаке после единственной помарки-исправления: «какой из вас будет ученый с такой небрежностью? чего вы там наизмеряете, если не в состоянии две страницы написать без поправок?».


        1. aleks_raiden
          20.05.2015 13:02
          +1

          То есть, как минимум, все эти люди — в его глазах полное ничтожество? www.adme.ru/svoboda-psihologiya/17-izvestnyh-lyudej-i-ih-pocherk-612055

          И до кучи, Эйнштейн также не пройдет — www.metronews.ru/novosti/k-100-letie-teorii-otnositel-nosti-razrabotchiki-sozdali-shrift-ejnshtejna/Tpooeo---1DzEchaajDGts


  1. uvelichitel
    15.05.2015 00:51
    +2

    Как, если не секрет, сделан собственно сервер, concurrency model? User-space threads, event-callback, OS threads-kqueue? Сами писали c чистого листа или готовый приспособили?


    1. starni Автор
      15.05.2015 07:17
      +1

      Не секрет. Это будет описано в следующей части статьи


  1. denqxotl
    15.05.2015 09:48

    Спасибо, класно. С нетерпением жду продолжения!


  1. afiskon
    15.05.2015 11:24
    +1

    Обожаю статьи о практическом опыте разработки приложений. Спасибо и с нетерпением жду продолжения! Особенно интересно было бы почитать про архитектуру серверной части (имеется ввиду БД, кэширование, репликация, фейловеры, монитроинг, где хоститесь — вот это все) и как вы делали такой крутой UI. Здесь и тут для примера список вопросов, ответы на которые было бы особенно интересно получить :)


  1. valiorik
    15.05.2015 12:02

    Вы open source? Было бы интересно посмотреть на реализацию, читая описание.

    Офтопик: Как переключиться на английский, подскажите пожалуйста?


    1. starni Автор
      15.05.2015 12:45

      Нет, не open source. Английского пока нет — локализация в процессе.


  1. likerRr
    15.05.2015 16:49

    Спасибо за серию статей, очень интересно почитать чужой опыт. Скажите, пожалуйста, как происходит обработка любого действия, оно проходит подтверждение через сервер? Лежит ли какая-то логика на клиенте? Я имею ввиду например выстрел по врагу (каюсь, не смотрел рабочую версию вашей игры, может там и нет в точности такого кейса, но, думаю, суть вы поймете). Верно ли я понимаю порядок действий:

    • 1) Игрок А скомандовал стрелять по игроку Б
    • 2) На сервер ушла информацию о намерениях игрока А
    • 3) Сервер проверил, соблюдены ли все условия (возможен ли выстрел по игроку Б) и вернул результат
    • 4) Клиент получил одобрение, пошла анимация выстрела
    • 5) В этом пункте я сомневаюсь, что должно происходить: а) снаряд\патрон долетел до игрока Б, сообщаем об этом серверу и он возвращает результат бомбардировки; б) на сервер пошла информация о том, что выстрел совершился и сервер заранее (до того, как анимация завершится) возвращает результат выстрела


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


    1. starni Автор
      15.05.2015 17:13

      Все проверки делаются на сервере, часть из них (для удобства пользователя) дублируется на клиенте.
      Конкретно к вашему вопросу порядок такой
      1. Игрок А скомандовал стрелять по игроку Б
      2. На сервер ушла информацию о намерениях игрока А
      3. Сервер проверил, можно ли атаковать эту цель, если да, то созда Action TFollowShipAction, который будет пытаться подлететь на близкое расстояние к цели, но при этом не «врезаться» в ней. А так же выставил в TSpaceShip::Enemy корабль Б
      4. На сервере, в каждом TSpaceShip::Update проверяется, если есть Enemy и есть турели, которые могут в него стрелять (позволяет расстояние, закончен кул-даун и т.д.), то производится выстрел.
      5. Данные о произведенном выстреле отправляются на клиенты, чтобы он мог создать particle выстрела из лазера и отобразить урон.
      Выстрел из лазера (турель) долетает до противника мгновенно.

      Отдельно ведется огонь из спец.оружия (абилки). Там порядок такой
      1. Игрок жмет на кнопку «выстрел абилкой», пакет уходит на сервер
      2. Сервер проверяет, можно ли произвести выстрел абилкой. При этом не все типы абилки требуют наличие TSpaceShip::Enemy
      3. Если стрелять можно, то сервер производит выстрел. Выстрел может быть двух типов — с созданием объекта (ракеты, плазма, мины, дроны), которые как-то перемещаются и наносят урон, только настигнув цель — или мгновенный урон (призмалуч).
      4. В случае мгновенного урона данные об уроне отправляются на клиенты, чтобы они могли отобразить particle выстрела и урон
      5. В случае создание объекта, этот объект выполняет свою полетную программу, на клиенты уходит пакет о добавлении объекта и данные о его полетных командах. Если объект наносит урон, то так же отправляется пакет на клиенты с типом урона, чтобы клиенты могли отобразить необходимый particle и цифры урона.

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


      1. likerRr
        15.05.2015 17:30

        Спасибо за подробный ответ. Правильно ли я понимаю, если у меня слабое интернет соединение, то вероятнее всего я нажму на выстрел, но сам выстрел (анимация и результат) произойдет только тогда, когда сервер даст добро? В целом все логично, но… мне не дает покоя эта задержка :) Ее можно как-то миновать или в этом нету смысла? К примеру в играх, где выстрелы должны происходить моментально (шутеры).

        Вы так же упомянули об ориентации, которая может не соответстовать рассчетам с сервера. Каждое движение так же согласуется с сервером? Если я беспорядочно буду кликать по области, как это обрабатывает сервер?


        1. starni Автор
          15.05.2015 17:38

          Все-таки вам стоит посмотреть на игру, тогда часть вопросов отпадет :).
          Есть два типа оружия на борту: один полностью управляет сервер, главное сказать кого мочить и подлететь на дистанцию выстрела. И второй тип: им полностью управляет пользователь. И сервер попробует произвести быстрел абилкой как только к нему прийдет пакет на выстрел.

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

          Теперь про ориентацию. Сервер передает коэффициенты уравнения движения на клиент. И клиент уже строит ф-цию движения по ним. Но если произошла задержка в передаче (и, скажем, составила 500мс или больше), то будет небольшая рассинхронизация, т.к. за это время сервер уже новую команду на движение дал, а клиент ещё это не отобразил.


          1. likerRr
            15.05.2015 19:00

            Постараюсь глянуть на досуге. Спасибо.

            И еще хочу спросить) Вы писали, что выбрали mysql в качестве бд. Отсюда два вопроса: как часто сервер сбрасывает игровые данные в бд (каждый ход?)? И не боитесь, что mysql в какой-то момент начнет сдавать в производительности?


            1. starni Автор
              15.05.2015 19:04

              У нас нет такого понятия, как «ход» — игра в реальном времени.
              О работе с БД я напишу в следующей статье ).


              1. likerRr
                15.05.2015 21:30

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

                Буду ждать статью про БД, спасибо еще раз