В этот раз я расскажу как удалось реализовать открытый бесшовный мир в горизонтально масштабируемой 2D онлайн игре.
О проекте
Для тех кто первый раз читает мои статьи — я разрабатываю авторитарный сервер игровой с управлением по API и прикладным ПО для редактирования карт, игровых механик , управления догружаемым контентом (музыки, анимаций, диалогов, переводов) и рассказываю об этом в серии статей. Это будет единое игровое пространство (не комнаты), где NPC будут под управление искусственного интеллекта.
Это Российская разработка — аналог западных сервисов таких как Firebase, Photon, PlayFab, Mirror, RakNet, AWS GameLift (во всяком случае так мне ответила нейросеть ChatGPT).
![](https://habrastorage.org/getpro/habr/upload_files/a02/ebe/2bd/a02ebe2bd589d0079af77d2c9a361962.png)
![](https://habrastorage.org/getpro/habr/upload_files/a36/0a0/010/a360a00102e6591416e71f88b1cc88c3.png)
Открытый мир
Общий принцип открытого мира основана на том, что игра становится не линейной — мы можем отправиться куда нам хочется и обычно это сопряжено с побочными игровыми заданиями. Однако если мир огромен то для его полной загрузки и обработки потребуется очень много ресурсов компьютера. Эту проблему решают разбивая мир на отдельные локации переход между которыми сопряжен с загрузкой игры (во время которой обычно играет какая то анимация).
![край локации игры Fallout справа внизу край локации игры Fallout справа внизу](https://habrastorage.org/getpro/habr/upload_files/a5e/629/b51/a5e629b51943138d14136bd6c11a557a.png)
Бесшовный мир
![мы видим соседние загруженные игровые локации мы видим соседние загруженные игровые локации](https://habrastorage.org/getpro/habr/upload_files/f35/747/e06/f35747e06c60f322428939d9f560a8a1.png)
С бесшовным миром все смотрится куда интереснее — находясь в определенной локации в память компьютера загружаются те локации которые вокруг нас при условии, что мы подходим к их границам. Часто используется туман в области, где локации не загружены (например мы далеко отошли от них и они удалены из оперативной памяти до момента пока мы не подойдем к ним ближе). В основном это используется для 3D игр.
Моя реализация для 2D онлайн игры
Я уже заранее позаботился что мир будет открытым и масштабируемым (где каждая локация может быть отдельно на физической машине), осталось придумать как сделать его бесшовным.
![](https://habrastorage.org/getpro/habr/upload_files/063/d87/733/063d8773334a23830d143c87e3db9e94.png)
Первое что пришло на ум — соединятся по websoсket каналу не только с сервером (локацией) где мы играем, но и со смежными. Однако от этой идеи пришлось сразу отказаться ведь не только клиенту (игроку), но и серверу (локации) где клиент играет нужно знать что происходит на соседних (например механика взрыва снаряда на границе локаций должна задеть и тех существ что будут на соседней локации возле границы). В итоге наш сервер‑локация всегда соединяется по websocket со смежными локациями (то есть сервер становится еще и клиентом).
Сразу появилась проблемы — сервер после перезагрузки подымается только если на нем кто то играет (пришлось реализовать внутреннее api для самих серверов который проверяет статус другого, перезагружает, останавливает и запускает выбранную сервер‑локацию), а так же проблема согласованности кто с кем должен соединятся тк соседние локации так же хотят соединится с нами образуя некую сеть (временным решением стало то, что тот сервер у которого сумма цифр в IP выше — тот и должен инициализировать соединение, а в случае если на одном физическом сервере несколько локаций — в дополнении проверим порт тк локации работают на разных портах в рамках одного физического сервера).
![Приоритетный сервер подымает локацию и инициирует соединение websocket с ней Приоритетный сервер подымает локацию и инициирует соединение websocket с ней](https://habrastorage.org/getpro/habr/upload_files/7e6/63a/4f9/7e663a4f95e404acc1b2bab9836533bc.png)
![Соседняя локация на которой никого нет поднялась и приняла запрос на соединение с приоритетным сервером (где авторизовался игрок) Соседняя локация на которой никого нет поднялась и приняла запрос на соединение с приоритетным сервером (где авторизовался игрок)](https://habrastorage.org/getpro/habr/upload_files/357/596/01f/35759601fba8a5b12aa5d833c8f3ba96.png)
Наш сервер где мы играем получает пакеты от соседних локаций что там происходит (тем сам обладает пониманием кто там есть) и уже ретранслирует всем клиентам игрокам, а так же рассылает пакеты соседним локациям что происходит у него.
![Граница нескольких сервер-локаций Граница нескольких сервер-локаций](https://habrastorage.org/getpro/habr/upload_files/f19/f73/a2b/f19f73a2bb42eba583a6988154517dab.png)
Осталось реализовать функционал перехода между серверами (они могут быть физически разными машинами). Идея следующая:
Игрок отправляет на сервер команду на выход
Сервер сохраняет игрока в бд
Сервер убирает из массива существ находящихся на нем игрока, рассылаем всем команду что игрок удаляется с сервера
Клиенты получают команду что игрок отключается, ждут пару секунд и убирают со сцены данный «прафаб» (в Unity игровые сущности называются префабами)
Если за те пару секунд что клиенты ждут удаление префаба он появился в другой сервер локации — мы анимируем плавный переход границы
В самой панели разработчика клиентской части (в настоящий момент использую Unity) локации и находящиеся в них существа (префабы) выглядят следующим образом.
![Группа слоев Map — содержит графику текущей (center) локации и соседней (left) Группа слоев Map — содержит графику текущей (center) локации и соседней (left)](https://habrastorage.org/getpro/habr/upload_files/dbb/4e7/7a9/dbb4e77a9118bae05eba228767a02120.png)
![Группа слоев World — содержит прафабы текущей (center) локации и соседней (left) Группа слоев World — содержит прафабы текущей (center) локации и соседней (left)](https://habrastorage.org/getpro/habr/upload_files/c17/6b5/39e/c176b539e3332886d0d8621fdf98fa20.png)
По итогу первых экспериментов переход между локациями получился не очень плавный, но в следующих статьях я расскажу как это исправить и как обрабатывать только тех существ которые видны игрокам. Демонстрация того что вышло на видео ниже:
Если вам интересно следить за реализацией проекта (адрес mmogick.ru) — вы можете подписаться на меня и быть в курсе выхода новых статей.
История:
Выбор технологий, протокола и архитектурный шаблон Entity Component System
Открытый бесшовный мир в 2D игре
FPS, Ping, паузы между командами, интерполяция и экстраполяция
Event-driven паттерн, JSON-RPC и почему не сервисная (SOA) архитектура
Сетевая карта и задержка кадра (Latency frame) по RFC 2544 (1242)