Введение в многопользовательские игры с Unity и Photon
Узнайте, как создать собственную многопользовательскую игру на Unity с использованием библиотеки Photon Unity Networking (PUN).
Если вы какое-то время создавали игры для Unity, вы знаете, что для создания игры требуется много тяжелой работы. Такие вещи, как дизайн уровней, игровая механика и прогресс, требуют много проб и ошибок и тщательного проектирования, чтобы добиться хороших результатов. Не смотря на это, только ограниченное количество людей может создать рабочую игру.
По сравнению с этим многопользовательские игры, такие как Fortnite и PUBG, покорили мир. Эти игры, которые легко осваивать, но трудно создавать. Они становятся все более популярными среди рекордного числа людей.
Некоторые люди даже делают карьеру из этих игр в качестве стримеров Twitch, что очень трудно сделать в одиночных играх.
В этом руководстве вы узнаете, как создать собственную многопользовательскую игру на Unity с использованием библиотеки Photon Unity Networking (для краткости PUN).
В этом уроке, вы узнаете:
- Ключевые различия между Unity Networking и PUN.
- Как создать сцену в лобби, чтобы игроки могли присоединяться
- Как загрузить игровую сцену и синхронизировать значения трансформации игроков.
Начнём
Photon или Unity — что выбрать?
Прежде чем приступить к обучению, давайте рассмотрим основные различия между Unity Networking и Photon Unity Networking.
Изучение архитектуры
Движок Unity, как и PUN имеют похожие низкоуровневые API. Но архитектура, необходимая для этих библиотек, чтобы использовать эти API, является ключевым дифференцирующим фактором между ними.
Можете ознакомиться с этим видео:
Приведенная выше диаграмма показывает, как сообщения передаются между узлами в сети в Unity и в PUN.
Unity Networking поддерживает архитектуру сервер / клиент.
Все сообщения должны проходить через хост-клиент и не могут быть отправлены напрямую между узлами.
Например, на основе приведенной выше схемы, сообщения передаются от клиента B к клиенту C по следующему пути: Клиент B ▸ Сервер ретрансляции ▸ Хост A Сервер ретрансляции ▸ Клиент C.
Как видите, сообщение от источника до места назначения занимает всего 4 шага. В дополнение к этому, если Хост отключается от сети, игра останавливается.
PUN имеет аналогичную архитектуру сервер / клиент, но также поддерживает одноранговую отправку сообщений.
Например, на основе приведенной выше схемы сообщения передаются от клиента B к клиенту C по следующему пути: клиент B сервер ретрансляции ▸ клиент C.
Это всего 2 шага по сравнению с 4 в Unity для одной и той же передачи сообщений между двумя узлами.
В дополнение к этому, PUN потенциально может полностью обойти сервер ретрансляции, и клиент B может напрямую связываться с клиентом C, тем самым уменьшая количество переходов до 1.
Исходя из этой информации, PUN быстрее, чем Unity.
Цена
Другое ключевое отличие между Unity и PUN — модель стоимость.
Unity предлагает бесплатное количество параллельных пользователей (CCU) для каждой лицензии.
- Personal: 20 одновременных пользователей
- Plus: 50 одновременных пользователей
- Professional: 200 одновременных пользователей
Если вам нужно увеличить количество CCU, которые поддерживает ваша игра, вам придется заплатить за дополнительную полосу пропускания, которую вы используете. С вас будет взиматься $ 0,49 / за 1 ГБ трафика, который проходит через инфраструктуру Unity (Matchmaker и Relay Server).
Источник – https://support.unity3d.com/hc/en-us/articles/209604483-How-much-does-Unity-Multiplayer-cost-
PUN также предоставляет до 20 CCU, 8000 ежемесячных активов и 500 сообщений на номер бесплатно. В дополнение к бесплатному плану, он предлагает очень хороший вариант однократной оплаты в размере $ 95 на 60 месяцев, который включает в себя 100 CCU, 40 000 ежемесячных активных и 500 сообщений на номер. Этот вариант отлично подходит для небольших независимых разработчиков, которые имеют ограниченный бюджет.
Комбинация сравнительно более быстрой производительности, хорошо написанных учебников и документации, а также здоровый выбор ценовых планов делают PUN очень хорошим вариантом для разработчиков для создания многопользовательских игр.
Купить или попробовать бесплатно можете по ссылке – https://www.photonengine.com/en-US/PUN/pricing
Начинаем работу
Вам потребуется Unity версии 2018.3 или более новой версии для успешного запуска начального проекта. Если вы не установили его в своей системе, вы можете загрузить его с unity3d.com.
Примечание. Это руководство предназначено для пользователей, обладающих базовыми знаниями в области сценариев и которые знакомы с редактором. Если вы новичок в Unity, вы можете ознакомиться с нашими уроками для начинающих.
Как только вы все настроите, загрузите стартовый проект, и откройте Photon Starter с Unity.Project Overview
Посмотрите на структуру папок в окне проекта:
Вот что содержит каждая папка:
- LogViewer: Файлы, необходимые для актива LogViewer.
- Materials: Материалы, необходимые для этого урока.
- Models: Модели, необходимые для этого урока.
- Photon: Файлы, необходимые для Фотонной библиотеки
- PhysicsMaterial: Это физические материалы, которые будут использоваться в проекте.
- Prefabs: Готовые префабы для туториала.
- Resources: Сборные, которые должны быть синхронизированы Photon.
- Scenes: Главное меню игры и сцены Арены.
- Scripts: Скрипты, необходимые для проекта.
Откройте сцену запуска из Активов / RW / Сцены.
Если вы раньше играли в многопользовательские игры, вы должны понимать, что прежде чем вы и ваши друзья начнете играть в эту игру вместе, вам сначала нужно создать или присоединиться к лобби (или игровой комнате), где вы все подключитесь, а затем лидер лобби начинает игру, либо она стартует автоматически.
В этой сцене вы создадите лобби с использованием Photon Unity Networking. Вы создадите комнату с определенным именем, и тогда ваш друг сможет присоединиться к вашему лобби, введя то же имя комнаты в своем экземпляре игры.
Как только вы и ваш друг присоединились к одной комнате, лидер лобби может загрузить сцену MainArena, где вы оба можете играть в игру вместе.
Создаём аккаунт в Photon
Прежде чем приступить к созданию лобби, вам необходимо создать учетную запись на официальном сайте Photon Engine, перейдя по адресу https://dashboard.photonengine.com/en-us/account/SignUp.
- После успешной регистрации вы будете перенаправлены на панель управления учетной записью.
- На той же странице нажмите кнопку «Создать новое приложение». Введите имя для вашего приложения, например «SampleApp», и нажмите кнопку «Создать» в нижней части формы.
- Наконец, на странице Dashboard вы увидите окно с информацией о вашем «SampleApp». Скопируйте AppId и сохраните его где-нибудь, так как вы будете использовать его позже в учебнике для тестирования.
Примечание: Библиотека Photon Unity Networking уже присутствует в начальном проекте, который вы загрузили в начале этого учебного пособия, но вы также можете использовать ее в своих существующих проектах, загрузив пакет Unity Pack из хранилища активов.
Продолжайте работу, вернувшись в Редактор Unity, откройте Мастер PUN, выбрав Окно ▸ Фотон Unity сети ▸ Мастер PUN.
В окне PUN Wizard нажмите Setup Project и введите AppId, который вы сохранили при настройке учетной записи движка Photom в предыдущем разделе. Нажмите кнопку «Настройка проекта».
Теперь, когда у вас есть Photon, давайте приступим к созданию лобби.
Создание Лобби
Вот обзор того, что делает скрипт Launcher.cs, по порядку:
- Лаунчер подключается к сети Photon Network.
- После подключения примите два вводных значения от пользователя: имя игрока, которое он хочет использовать, и имя комнаты, которое они хотят создать или присоединиться.
- Если комнаты с введенным именем не существует, создайте комнату с таким именем и сделайте текущего игрока лидером лобби. Если Комната существует, игрок присоединится к Комнате.
- Как только оба игрока подключились к одной комнате, лидер лобби сможет загрузить сцену MainArena.
Откройте скрипт Launcher.cs в Assets / RW / Scripts.
Добавьте следующие строки кода в Launcher.cs после комментария // Start Method.
Не беспокойтесь о промежуточных ошибках при добавлении кода. Весь необходимый код будет объяснен в разделах ниже.
// Start Method
void Start()
{
//1
PlayerPrefs.DeleteAll();
Debug.Log("Connecting to Photon Network");
//2
roomJoinUI.SetActive(false);
buttonLoadArena.SetActive(false);
//3
ConnectToPhoton();
}
void Awake()
{
//4
PhotonNetwork.AutomaticallySyncScene = true;
}
Вот краткое объяснение кода.
- При подключении к серверу PUN пропингует все доступные серверы и сохраняет IP-адрес сервера с самым низким пингом в виде пары ключ-значение PlayerPrefs. Это может привести к неожиданному поведению на этапе подключения. Чтобы избежать каких-либо аномалий, вызывается DeleteAllis при запуске сцены Launcher. Элементы пользовательского интерфейса по умолчанию скрыты и активируются после установления соединения с сервером Photon.
- ConnectToPhotonвызывается для подключения к сети Photon. Значение AutomaticsSyncScene имеет значение true. Это используется для синхронизации сцены между всеми подключенными игроками в комнате.
Загрузка сцены MainArena
Чтобы получить входные данные из элементов пользовательского интерфейса TextField, вам нужен открытый метод для сохранения значения в TextField. Добавьте следующий код после комментария // Tutorial Methods:
// Helper Methods
public void SetPlayerName(string name)
{
playerName = name;
}
public void SetRoomName(string name)
{
roomName = name;
}
Затем добавьте следующие методы после комментария // Tutorial Methods:
// Tutorial Methods
void ConnectToPhoton()
{
connectionStatus.text = "Connecting...";
PhotonNetwork.GameVersion = gameVersion; //1
PhotonNetwork.ConnectUsingSettings(); //2
}
Вот как работает этот код:
- Параметр GameVersion установлен. Это строка версии для вашей сборки и может использоваться для разделения несовместимых клиентов. Для этого урока его значение будет равно 1 (устанавливается при объявлении поля gameVersion).
- ConnectUsingSettingsвызывается, когда нужно подключиться к Photon, в редакторе уже всё настроено. Вы можете прочитать больше в документации.
Затем добавьте следующие строки кода:
public void JoinRoom()
{
if (PhotonNetwork.IsConnected)
{
PhotonNetwork.LocalPlayer.NickName = playerName; //1
Debug.Log("PhotonNetwork.IsConnected! | Trying to Create/Join Room " +
roomNameField.text);
RoomOptions roomOptions = new RoomOptions(); //2
TypedLobby typedLobby = new TypedLobby(roomName, LobbyType.Default); //3
PhotonNetwork.JoinOrCreateRoom(roomName, roomOptions, typedLobby); //4
}
}
public void LoadArena()
{
// 5
if (PhotonNetwork.CurrentRoom.PlayerCount > 1)
{
PhotonNetwork.LoadLevel("MainArena");
}
else
{
playerStatus.text = "Minimum 2 Players required to Load Arena!";
}
}
Смотрите внимательно за каждой строкой кода:
- Параметр NickName LocalPlayer устанавливается из закрытой переменной playerName. Это имя будет доступно всем, с кем вы играете по сети Photon, и используется в качестве уникального идентификатора.
- Объект класса RoomOptions объявлен. Это оборачивает свойства общей комнаты, необходимые при создании комнаты. Он может использоваться для предоставления пользователю контроля над различными характеристиками комнаты, такими как максимальное количество игроков, которые могут присоединиться, PlayerTtl (Player Time To Live) и т. д. (Документация)
- Объявлен объект класса TypedLobby. Он относится к конкретному типу лобби на сервере Photon. Название и тип лобби используются в качестве уникального идентификатора. Имя комнаты задается из закрытой переменной roomName, а тип лобби устанавливается по умолчанию. (Документация)
- Наконец, метод JoinOrCreateRoom класса PhotonNetwork вызывается с аргументами — roomName, roomOptions и typedLobby, которые были установлены ранее. Если метод вызывается новым пользователем с новым именем комнаты, которое еще не существует, комната создается, и пользователь автоматически назначается лидером лобби. В противном случае другие игроки просто присоединяются к комнате.
- После того, как лидер лобби создал комнату и присоединился к ней, кнопка LoadArena станет активной. Перед загрузкой Arena устанавливается проверка, чтобы убедиться, что сцена MainArena загружена, только если оба игрока присоединились к комнате.
Методы обратного вызова PUN
Теперь, когда вы добавили основные строительные блоки присоединения и создания комнаты, осталось только добавить методы обратного вызова PUN, которые позаботятся об обработке исключений.
Добавьте следующий код после комментария // Photon Methods:
// Photon Methods
public override void OnConnected()
{
// 1
base.OnConnected();
// 2
connectionStatus.text = "Connected to Photon!";
connectionStatus.color = Color.green;
roomJoinUI.SetActive(true);
buttonLoadArena.SetActive(false);
}
public override void OnDisconnected(DisconnectCause cause)
{
// 3
isConnecting = false;
controlPanel.SetActive(true);
Debug.LogError("Disconnected. Please check your Internet connection.");
}
public override void OnJoinedRoom()
{
// 4
if (PhotonNetwork.IsMasterClient)
{
buttonLoadArena.SetActive(true);
buttonJoinRoom.SetActive(false);
playerStatus.text = "You are Lobby Leader";
}
else
{
playerStatus.text = "Connected to Lobby";
}
}
Давайте посмотрим, что делает каждый фрагмент кода:
- Как следует из названия, OnConnected вызывается, когда пользователь подключается к сети Photon. Этот метод вызывает другой базовый метод onConnected (). Любой дополнительный код, который должен быть выполнен, должен быть написан после вызова этого метода.
- Эти методы обеспечивают обратную связь с пользователем. Когда пользователь успешно подключается к сети Photon, устанавливается текстовое соединение UI Text connectionStatus, а для roomJoinUI GameObject устанавливается значение visible.
- OnDisconnectedвызывается, если пользователь отключается от сети Photon. В этом случае controlPanel GameObject имеет значение false, а сообщение об ошибке типа регистрируется в Unity.
- Наконец, OnJoinedRoom вызывается, когда пользователь входит в комнату. Здесь он проверяет, является ли пользователь главным клиентом (первым, кто присоединился к комнате). Если это так, пользователь устанавливается в качестве лидера лобби и отображается сообщение об этом. Лидер лобби может загрузить сцену MainArena, которая является обычным способом создания комнаты в самых популярных многопользовательских играх. В противном случае, если пользователь не первый, кто присоединился к комнате, отображается сообщение, сообщающее этому пользователю, что он успешно присоединился к комнате.
Сохраните сценарий Launcher.cs и вернитесь к сцене Launcher и нажмите «Play».
Как вы можете видеть, когда сцена запускается, вызывается ConnectToPhoton и в тексте интерфейса состояния соединения отображается «Connecting…». После успешного подключения текст меняется на «Connected», и видимость roomJoinUI GameObject устанавливается в значение true.
Затем пользователь может ввести свое имя и имя комнаты, которую он хочет создать или присоединиться, нажав кнопку «Присоединиться к комнате».
Наконец, если пользователь является первым клиентом, текст playerStatus имеет значение «Вы теперь являетесь лидером лобби!», И кнопка «Загрузить арену» будет активна. В противном случае отображается сообщение об успешном соединении в лобби.
На этом этапе вы можете протестировать шаг присоединение к комнате, создав исполняемый файл проекта для вашей операционной системы, выбрав Файл ▸ Построить и запустить. Вы должны быть в состоянии загрузить сцену MainArena, используя только что построенный исполняемый файл и редактор Unity, соединенные в одной комнате.
Тем не менее, вы можете увидеть только пустую арену без игроков. В следующем разделе вы узнаете, как добавить префабы игроков и мячей на сцену для каждого отдельного клиента. Вы также будете синхронизировать их положение, вращение и многое другое в сети фотонов.
Использование компонента преобразования просмотра — Photon View
После того, как код комнаты объединен, следующая важная концепция Photon Unity Networking. Также важная функция, которую вам нужно знать, — это компонент Photon View.
PUN позволяет очень просто создать префаб, свойства которого (положение, вращение и т. д.) Должны синхронизироваться по сети во время многопользовательской игры с использованием компонента Photon View.
При использовании PUN важно понимать, что Prefab, который должен создаваться по сети, должен находиться внутри папки с именем Resources.
Важным побочным эффектом наличия Prefabs внутри папок Resources, является необходимость просмотра их имен. У вас не должно быть двух префабов под путями ресурсов ваших активов, названных одинаково, потому что Unity просто выберет первый, который найдет.
После этого давайте начнем создавать Game Manager.
Откройте сценарий GameManager.cs в разделе «Ресурсы / RW / Scripts».
Вот краткий обзор того, что собирается делать скрипт GameManager.cs:
- Когда сцена MainArena загружена, проверьте, является ли клиент главным в лобби или нет. Если это так, создайте игровой объект Car GameObject с помощью Instantiate, измените имя игрока 1 GameObject, а также создайте шарик GameObject. В противном случае просто создайте player2 GameObject и измените его имя.
- Добавьте метод обратного вызова PUN, который позаботится о различных случаях использования в зависимости от условий и событий сети.
- Вспомогательные методы отключат пользовательский интерфейс, комнату выхода и т. д.
Добавьте следующий код после комментария // Start Method:
// Start Method
void Start()
{
if (!PhotonNetwork.IsConnected) // 1
{
SceneManager.LoadScene("Launcher");
return;
}
if (PlayerManager.LocalPlayerInstance == null)
{
if (PhotonNetwork.IsMasterClient) // 2
{
Debug.Log("Instantiating Player 1");
// 3
player1 = PhotonNetwork.Instantiate("Car",
player1SpawnPosition.transform.position,
player1SpawnPosition.transform.rotation, 0);
// 4
ball = PhotonNetwork.Instantiate("Ball",
ballSpawnTransform.transform.position,
ballSpawnTransform.transform.rotation, 0);
ball.name = "Ball";
}
else // 5
{
player2 = PhotonNetwork.Instantiate("Car",
player2SpawnPosition.transform.position,
player2SpawnPosition.transform.rotation, 0);
}
}
}
Вот как это работает
- Проверьте, подключен ли клиент к сети Photon или нет. В случае возникновения проблем с сетью, сцена Launcher должна быть загружена, чтобы клиент мог попытаться подключиться снова.
- Получить ссылку на локального игрока (игрока, который контролирует клиента) и проверить, является ли он главным клиентом.
- Если это так, создайте экземпляр GameObject игрока из папки «Ресурсы» (вы этим займетесь на следующем шаге) с помощью Instantiate и сохраните ссылку на него в player1 GameObject.
- Аналогично, создайте объект Ball GameObject таким образом, чтобы это был тот же Ball GameObject, который загружен на всех клиентах, подключенных к текущей комнате.
- Если клиент не является Мастером, загрузите PlayerObject игрока из папки «Ресурсы» и сохраните ссылку на него в GameObject player2.
Сохраните файл и вернитесь в редактор.
Теперь, когда у вас есть готовая логика для создания объектов Player и Ball GameObject, следующим шагом будет добавление необходимых компонентов, чтобы их можно было создать с помощью метода PhotonNetwork.Instantiate.
В окне «Project» дважды щелкните «Car prefab» в «Assets / RW / Prefabs», чтобы открыть его в режиме «Prefab Editing».
В Инспекторе вы должны увидеть, что некоторые базовые компоненты для Car GameObject (Rigidbody, Collider, Movements и т. д.) Уже являются частью префаба.
Поскольку по сети необходимо синхронизировать только свойства Transform объекта GameObject, добавьте компонент Photon Transform View в Car GameObject.
Вы заметите, что компонент Photon View также добавлен, так как компонент Photon Transform View уже наследует от него множество свойств.
В дополнение к синхронизации положения, поворота и масштаба GameObject, Photon Transform View дает вам много разных вариантов, чтобы синхронизированные значения выглядели гладкими, даже когда данные принимаются только пару раз в секунду.
В Инспекторе установите для параметра «Наблюдение» значение «Ненадежный» при изменении в компоненте Photon View. Это обеспечит плавный переход между значениями Car Transform.
Кроме того, добавьте префаб «Автомобиль» или «Car» в список «Наблюдаемые компоненты» в компоненте «Photon View», чтобы синхронизировать его выбранные свойства преобразования (которые отображаются как выбранные в компоненте «Photon View Component»).
Сохраните готовый префаб Car
Затем откройте префаб Ball из Assets / RW / Prefabs в режиме Prefab Editing и повторите вышеуказанные шаги:
- Добавьте компонент «Photon View»
- Установите для параметра «Наблюдение» значение «Ненадежный при изменении».
- Добавьте префаб Ball в список наблюдаемых компонентов в компоненте Photon View.
Наконец, переместите префабы Car и Ball из папки Assets / RW / Prefabs в директорию Assets / RW / Resources, чтобы их можно было загрузить методом PhotonNetwork.Instantiate.
Время тестировать нашу игру!
Выберите File ▸ Build and Run, чтобы создать исполняемый файл для вашей операционной системы.
На этот раз:
- Введите другое имя игрока в обоих клиентах.
- Введите то же имя комнаты.
- Нажмите кнопку «Загрузить арену», Load Arena в главном клиенте.
Вы должны увидеть, что сцена MainArena загружена двумя игроками (машинами) и мячом. Вы можете использовать кнопки WASD или клавиши со стрелками на клавиатуре, чтобы перемещать машину по арене.
Обратите внимание, что на обоих клиентах движется только 1 автомобиль (автомобиль, принадлежащий клиенту).
Вы также можете видеть, что положение игроков и мяч синхронизированы у обоих клиентов.
Вся работа выполняется с помощью компонента Photon Transform View, который вы добавили в префабы Car и Ball.
Однако если вы закроете один из клиентов, то другой клиент останется нестабильным. Для обработки подобных случаев вы добавите методы обратного вызова, чтобы выполнить необходимое действие в зависимости от текущей ситуации клиента (например, потеря сети или другой оставшийся игрок).).
Добавление методов обратного вызова PUN
Добавьте следующий код в скрипт GameManager.cs после комментария // Update Method:
// Update Method
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape)) //1
{
Application.Quit();
}
}
Это довольно просто.
В любой момент игры, если нажата кнопка Escape, будет вызвано событие Application.Quit.
Затем добавьте следующий код после комментария // Photon Methods:
// Photon Methods
public override void OnPlayerLeftRoom(Player other)
{
Debug.Log("OnPlayerLeftRoom() " + other.NickName); // seen when other disconnects
if (PhotonNetwork.IsMasterClient)
{
PhotonNetwork.LoadLevel("Launcher");
}
}
OnPlayerLeftRoom является методом обратного вызова PUN, который вызывается всякий раз, когда игрок покидает комнату, либо закрывая клиента, либо отключается от сети.
Наконец, добавьте следующий код после комментария // Вспомогательные методы: // Helper Method
// Helper Methods
public void QuitRoom()
{
Application.Quit();
}
При нажатии на Canvas / Top Menu Panel / Quit Room Button будет вызван данный метод.
Теперь вам предстоит установить событие On Click () и кнопки Quit Room на GameManager.QuitRoom
И это все!
Вы можете создать финальный бинарный файл для вашей операционной системы и начать играть прямо сейчас.
Что делать дальше?
Целью этого руководства было дать вам представление об основных принципах построения многопользовательской игры. Вы можете использовать эти же принципы, чтобы превратить существующую однопользовательскую игру в многопользовательскую игру!
Вы можете узнать больше о библиотеке Photon Unity Networking на официальном сайте
Если у вас есть какие-либо вопросы или комментарии, или вы просто хотите показать, над чем вы экспериментировали в этом уроке, присоединяйтесь к обсуждению ниже.
PUN не может обойти сервер ретрансляции, Photon Bolt может. Это другой продукт. Пожалуйста, не вводите в заблуждение читателей.
А как же время синхронизации?
Афтар Алень