Пример реализации сетевой архитектуры в Unity3D с авторитарным сервером
Задача:
Ограничения:
Требуемый результат:
Термины:
Начали.
1) Открываем Untiy3D, добавляем землю, например, Plane. Масштабируем пол до 1000*1*1000.
2) Создаём Material, красим его в цвет RBG=64:80:40. Назначим его как материал пола. Просто, чтобы белый цвет по умолчанию нас не ослеплял. Я его назвал Ground.
3) Добавляем на сцену Cube. К нему добавляем компонент RigidBody, массу которого указываем 30 тонн. Нормально для среднего танка. Переименовываем Cube в Tank. Масштаб Tank выставляем в (3:2:6). Приподнимаем его над полом, указав координату Y, равной 1,1. При этом X и Z советую оставить нулевыми.
4) Добавляем на сцену Cube. Указываем масштаб (1,5:1:2). Называем его Turret. Координаты (0:2,4:1).
5) Добавляем на сцену Cube. Указываем масштаб (0,2:0,2:4) и координаты (0:2,5:4). Назовём Barrel.
6) Добавляем на сцену пустой GameObject, обзовём Gun. Координаты (0:2,4:2), масштаб не трогаем.
7) Вложите объект Turret в объект Tank. Вложите объект Gun в объект Turret. Вложите объект Barrel в объект Gun.
8) Я назначил частям танка материал Khaki с цветом (64:96:32). Просто так.
В результате мы получим некое подобие танка. Если вы спросите, зачем надо вкладывать Barrel в Gun, отвечу – если вращать Barrel по оси X, то ось будет не там. А если вращать Gun по оси X, то… в приемлемом диапазоне картинка не вызывает бешенства. На самом деле вы будете работать с нормальными графическими моделями, у которых центры вращения указаны правильно ещё в графическом редакторе, и таких проблем у вас возникать не должно.
Идём далее.
Мы будем ориентироваться на статью docs.unity3d.com/Manual/UNetConverting.html, но только ориентироваться. Мы будем отклоняться от статьи.
9) Создаём на сцене GameObject и называем его NetworkCommander. К нему добавляем два компонента: NetworkManager и NetworkmanagerHUD. Надо убедиться, что у компонента NetworkManagerHUD включена галочка ShowRuntimeUI. NetworkManager реализует общее управление сетевыми сессиями. Для простоты игроков/разработчиков компонент NetworkManagerHUD рисует на экране простенькое меню, в котором заложены базовые действия с сетью – сервировать игру, хостить игру, присоединиться к игре.
10) К объекту Tank добавьте компонент NetworkIdentity. Однако не включайте галочку LocalPlayerAuthority. Это нам не нужно для авторитарной архитектуры, и всё равно смысла у нас она не имеет.
11) Сделайте из танка Prefab – то есть скопируйте его со сцены в ресурсы. Назовите TankPrefab.
12) В объекте NetworkCommander в разделе SpawInfo вставьте TankPrefab в поле PlayerPrefab.
13) Создайте C# скрипт ArmourDrive.cs. Процедура Start() нам на данный момент не нужна. Нам нужна только Update().
14) Включите пространство имён UnityEngine.Networking. Это позволит нам пользоваться сетевыми функциями uNet.
15) Смените класс ArmourDrive с MonoBehaviour на NetworkBehaviour. Этот класс расширяет обычную логику, добавляя к ней сетевые функции.
16) Создайте в классе ArmourDrive поля:
17) Текущий скрипт будет исполняться сразу в трёх местах: у меня для чтения моих намерений («я»), на сервере для приёма намерений и превращения их в действия («мой дух»), и на компе того парня, для которого действия будут рисоваться («мой аватар»). Различить эти три ипостаси мы можем двумя переменными.
Переменная isServer означает, что эта копия скрипта исполняется на сервере и, в данном случае является «моим духом».
Переменная isClient означает, что эта копия скрипта исполняется на клиенте и, в данном случае является «чьим-то аватаром».
Переменная isLocalPlayer означает, что это именно «мой аватар».
18) Сформируем намерения внутри функции Update:
19) Для того, чтобы послать на сервер мои намерения, программист обязан: а) начать название функции с букв “Cmd”, б) при описании функции пометить её, как исполняемую на сервере по требованию клиента. Вот её текст:
20) Теперь сервер должен авторитарно передвинуть мой танк. Это можно сделать и в упомянутой функции Update(). Однако авторы Unity3D рекомендуют физические расчёты осуществлять внутри FixdUpdate(). У нас не крутая физика, но последуем совету:
21) Обратно функциям типа Cmd исполняются функции Rpc – они вызываются сервером, а исполняются на клиентских машинах. Здесь тоже синтаксические требования: функция должна иметь пометку [ClientRpc], и название должно начинаться на буквы “Rpc”. Сервер нам выслал новые координаты, которые получились из нашего требования скорости, и мы должны переставить свой юнит в новое место.
Здесь можно было бы вставить гладкое приближение к этим координатам во избежание рывков танка на дисплее, но мы не будем на это отвлекаться.
22) Поскольку танк может при линейном движении случайно повернуться (на кочку наехать и т.д.), нам надо бы новую ориентацию танка так же выставить
23) Этот скрипт надо добавить к префабу танка
24) Теперь мы почти готовы стартовать игру. Но есть проблема – обычно удобнее работать с игровым объектом на сцене, конфигурировать, развивать его. Но при старте сетевой игры этого объекта на сцене быть не должно. Обычно на youtube различные учителя перед запуском игры стирают со сцены этот объект. Но мне удобнее его выключать, а не удалять. Выключите объект Tank на сцене или удалите (убедитесь, что префаб существует!)
25) Теперь мы точно готовы запускать игру. Давайте воспользуемся режимом «Хост», чтобы и создать и войти в игру быстро без проблем с запуском дополнительных копий. Стартуйте и нажмите в диалоге, который предоставляет NetworkManagerHud, кнопку LAN (Host).
26) Оп-па. Танк утонул в земле по башню. Это потому, что NetworkManager по умолчанию спавнит игрока на нулевой высоте, так, что днище танка уже находится под землёй. С респавном игроков мы можем разобраться позже, а сейчас в педагогических целях давайте себе позволим один костыль. Потому, что мы разбираем сейчас не респавн, а сетевое движение. Пишем:
27) Запускаем игру снова и опять выбираем режим Host. Всё нормально, танк при нажатии клавиш двигается вперёд и назад.
28) Теперь давайте соберём Standalone, чтобы мы могли запустить несколько копий игры. Вызываем Build Settings и Player options. Обязательно включаем галочку Run in Background – это позволит нам на одном компьютере запустить несколько экземпляров без того, чтобы большинство из них впало в спячку. Так же давайте пока выключим Default is Full Screen и для наглядности выставим мелкое разрешение 400*300. Поскольку больше нам пока не нужно, выключаем Display Resolution Dialog в Disabled. Далее опять запускаем Build и создаём исполняемый файл.
29) Мы можем запустить исполняемые файлы несколько раз.
30) Мы можем включить их в любой комбинации – хост и два клиента, или сервер и два клиента. Вот пример сервера и двух клиентов