В процессе создания своей первой игры на Unity3D я осознал необходимость организации сетевого режима, в котором игроки могли бы калечить компьютерных аватаров своих противников. Если вы раньше не занимались созданием клиент-серверных приложений, не поднимали своих игровых серверов, не желаете вкладывать силы и средства в их установку и поддержку, а хотите просто понять принципы организации мультиплеера и за 15-20 минут модернизировать свой проект так, чтобы с чистой совестью добавить к его названию слово Online, то эта статья для вас.
Немного о самом Photon. Это компания специализируется на серверных движках. Удобный сайт, на котором вы можете зарегистрироваться и получить AppID буквально за пару минут. Обширная документация, правда на языке оригинала, куча руководств и демопроектов. В сети есть даже видео уроки на русском, правда всё это относится к Photon Server. Если же вы не хотите заморачиваться с установкой и настройкой сервера, то можно воспользоваться облачным сервисом Photon Cloud, для этого необходимо лишь получить AppID при регистрации, который гарантирует вам лицензию на 20 одновременно подключенных пользователей.
На сайте утверждается, что подключиться к Облаку можно буквально за несколько минут. И это правда… если, конечно, перед этим потратить несколько часов-дней на изучение документации, потому что всё не так очевидно как хотелось бы думать разработчикам.
Итак, что нам нужно для счастья?
1. Подключиться к Облаку.
2. Создать тестовую комнату, либо присоединиться к ней, если она уже существует.
3. Убедиться, что все игроки взаимодействуют друг с другом в одной и той же сцене.
Подключение организуется с помощью встроенного статичного класса PhotonNetwork:
PhotonNetwork.Connect("app.exitgamescloud.com",5055,"здесь мог быть ваш AppID","v 0.1");
Думаю, значения параметров понятны. Последний параметр нужен для того, чтобы отличать версии ваших приложений друг от друга.
Если же в каталоге AssetsPhoton Unity NetworkingResources у вас лежит скрипт PhotonServerSettings, ,
то вы можете использовать процедуру
PhotonNetwork.ConnectUsingSettings(“Версия игры”);
Создание или присоединение к тестовой комнате:
if (PhotonNetwork.countOfRooms==0) {PhotonNetwork.CreateRoom("test",true,true,4); }
else {PhotonNetwork.JoinRoom("test");}
При создании комнаты можно кроме её названия указать, также видима ли она для остальных, открыта она или нет, а также максимальное количество игроков. При присоединении достаточно указать лишь название.
Если вы уже находитесь в комнате, то можете посмотреть её свойства в PhotonNetwork.room
Примечание: При загрузке новой сцены соединение с сервером и комнатой не разрывается.
Теперь осталось синхронизировать сцены и движения игроков на ней. Тут всё зависит от самого проекта. Есть несколько основных вариантов:
1. Вам требуется, чтобы какой-то объект синхронно перемещался для всех игроков. Для этого достаточно добавить к синхронизируемым объектам компонент PhotonView — «Components/Miscellaneous/PhotonView» .
В качестве Observe укажите синхронизируемый объект, а в Serialization синхронизируемые параметры.
Каждый компонент PhotonView имеет View ID.
2. Вам необходимо создавать один и тот же объект во время игры по желанию пользователя. Например, игрок должен кидать гранату. Для этого, нужно создать игровой объект, где вместо Instaniate используется процедура
GameObject grenade = PhotonNetwork.Instantiate ("grenad", Vector3.zero, Quaternion.identity, 0);
В отличие от Instaniate сперва необходимо указать название префаба, а в конце указать его группу (просто любое целое число). Созданный, одним из игроков, такой объект появляется у всех остальных.
Именно с помощью данной процедуры нужно создавать главного героя. Однако, вы столкнётесь с тем, что все персонажи будут реагировать на ваше управление. Чтобы избежать этого, вы должны добавить в скрипт управления персонажем условие определяющее локального игрока. Что-то вроде:
if (photonView.isMine) {/*Основной скрипт*/}
Примечание: Чтобы воспользоваться свойством photonView.isMine необходимо в качестве родительского класса для скрипта указать Photon.MonoBehaviour.
Создавать можно только префабы, находящиеся AssetsPhoton Unity NetworkingResources
3. Для управления объектами вам помогут RCP (Remote Call Procedure), с помощью которых вы сможете выполнять процедуры на удалённых приложениях передавая им необходимые аргументы. Для этого нужно перед объявлением процедуры в скрипте добавить тег [RPC], а к объекту, к которому прикреплён скрипт добавить компонент PhotonView, чтобы потом была возможность к ней обратиться.
Например:
[RPC]
void Chat(string NewMessage)
{
Debug.Log(NewMessage);
}
Соответственно, чтобы удалённо запустить процедуру на клиенте, необходимо определить PhotonView:
PhotonView photonView = PhotonView.Find(view ID);
или
PhotonView photonView = PhotonView.Get(gameobject);
А затем выполнить процедуру
photonView.RPC("Chat ", PhotonTargets.All, “Привет всем”);
где в качестве аргументов указать название вызываемой процедуры, получателей, и аргументы исходной функции.
Примечание: В качестве параметров RPC понимает только основные типы, т.е. int, float, bool, string и т.п.
4. В качестве объекта синхронизации компонент PhotonView, по умолчанию выбирает компонент Transform, отвечающий за положение объекта в пространстве. Если же нам нужно передавать какую-то информацию, то в качестве объекта синхронизации нужно указать скрипт
,
а в самом скрипте вставить процедуру
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
//We own this player: send the others our data
stream.SendNext((int)controllerScript._characterState);
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
}
else
{
//Network player, receive data
controllerScript._characterState = (CharacterState)(int)stream.ReceiveNext();
correctPlayerPos = (Vector3)stream.ReceiveNext();
correctPlayerRot = (Quaternion)stream.ReceiveNext();
}
}
Хотя, по идее эта процедура должна вызываться при каждом случае изменения одного из отсылаемых параметров, в данном случае при изменении позиции, положения или состояния персонажа, но вы можете как и я столкнуться с ситуацией, когда она вызывалась только мастер-клиентом. Для того, чтобы принудительно запустить эту процедуру на обычном клиенте можете воспользоваться данным скриптом:
if (!PhotonNetwork.isMasterClient)
{
PhotonStream PS=new PhotonStream(true, null);
PhotonMessageInfo PI= new PhotonMessageInfo();
OnPhotonSerializeView(PS,PI);
}
Благодарю, за внимание. В следующий раз я планирую подробнее разобрать функционал Photon Cloud.
Автор: malan
Спасибо, кое-что прояснил. А вот не могли бы посоветовать статью о реализации сетевой игры вот в каком плане. Непонятно вот допустим где считать коллизию? Каждый игрок считает ее на своей машине исходя из своих данных? Как быть допустим в стрелялке? Что делать с той же гранатой которая летит у игрока 1, а кинута она игроком 2. Взрывается же она у всех, значит по нескольку раз ударит одного и того же.
Или создавать у других игроков надо “учебную гранат, которая взрывается только визуально?
Спасибо, помогли