Dark Rift 2 Spawning Players Коннект игроков.

Поделиться.

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

Давайте сделаем шаг назад и запланируем, какую информацию о игроке мы будем хранить:

1) X, Y позиция в мире.
2) Размер  (радиус) персонажа.
3) Цвет.
Кроме того, в будущем мы можем захотеть сохранить имя.


Идем дальше и создаем новый класс Player на сервере, он будет хранить нашу информацию. Если вы ленитесь, как я, скопируйте и вставьте этот код :
class Player
{
    public ushort ID { get; set; }
    public float X { get; set; }
    public float Y { get; set; }
    public float Radius { get; set; }
    public byte ColorR { get; set; }
    public byte ColorG { get; set; }
    public byte ColorB { get; set; }

    public Player(ushort ID, float x, float y, float radius, byte colorR, byte colorG, byte colorB)
    {
        this.ID = ID;
        this.X = x;
        this.Y = y;
        this.Radius = radius;
        this.ColorR = colorR;
        this.ColorG = colorG;
        this.ColorB = colorB;
    }
}

Я использую byte, а не float для цвета, потому что это оптимизирует код.  Выполнение таких небольших шагов может значительно повысить пропускную способность при многократном отправке сообщения между Сервером и Клиентам.

Чтобы создать игроков, мы должны сообщить DarkRift, чтобы сообщил нам, когда игрок подключается. для этого в конструкторе AgarPlayerManager добавьте строку:

ClientManager.ClientConnected += ClientConnected;

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

И добавьте новый метод:

void ClientConnected(object sender, ClientConnectedEventArgs e)
{

}

Давайте дадим нашему новоиспеченному игроку собственный экземпляр класса Player. В нашем методе ClientConnected добавьте код, чтобы создать новые настройки игрока со случайными значениями:

Random r = new Random();
Player newPlayer = new Player(
    e.Client.ID,
    (float)r.NextDouble() * MAP_WIDTH - MAP_WIDTH / 2,
    (float)r.NextDouble() * MAP_WIDTH - MAP_WIDTH / 2,
    1f,
    (byte)r.Next(0, 200),
    (byte)r.Next(0, 200),
    (byte)r.Next(0, 200)
);

И в верхней части класса добавьте константу MAP_WIDTH:

const float MAP_WIDTH = 20;

Чтобы мы могли обращаться к игроку, когда он находится на стороне Unity, нам нужно иметь идентификатор, так как у клиента будет только один объект, который они контролируют, достаточно просто использовать идентификатор сервера, который выдается клиентом DarkRift при его подключении ,

Я использую значение от 0 до 200 для каждого цветового канала, потому что, если он слишком близко к 255, 255, 255, мы не сможем увидеть клиента на фоне!

Затем давайте отправим наш новый объект Player всем другим клиентам, чтобы они могли создать нового игрока в своей копии игры. Добавьте далее этот код:

using (DarkRiftWriter newPlayerWriter = DarkRiftWriter.Create())
{
    newPlayerWriter.Write(newPlayer.ID);
    newPlayerWriter.Write(newPlayer.X);
    newPlayerWriter.Write(newPlayer.Y);
    newPlayerWriter.Write(newPlayer.Radius);
    newPlayerWriter.Write(newPlayer.ColorR);
    newPlayerWriter.Write(newPlayer.ColorG);
    newPlayerWriter.Write(newPlayer.ColorB);

    using (Message newPlayerMessage = Message.Create(0, newPlayerWriter))
    {
        foreach (IClient client in ClientManager.GetAllClients().Where(x => x != e.Client))
            client.SendMessage(newPlayerMessage, SendMode.Reliable);
    }
}

Ого! Это сложнее чем все то что я писал ранее, не так ли!

Давайте рассмотрим каждую часть. Нам нужно преобразовать свойства нашего игрока в то, что мы можем отправить по сети, и в настоящее время это переменные, которые несовместимы с Интернетом. DarkRiftWriter и DarkRiftReader — это объекты, предоставленные вам для облегчения преобразования, мы записываем все наши свойства в писателя, а на стороне Unity мы будем читать их в том же порядке с читателем.

Затем мы создаем сообщение. Сообщения по существу обертывают данные (все эти значения, которые мы вносили в DarkRiftWriter) и даем им некоторые простые значения заголовков, которые определяют, что действительно содержит данные. Первый аргумент, тег, представляет собой единое заявление, которое определяет, что такое сообщение (подумайте, перемещайте плеер, используйте объект инвентаря, отправляйте сообщение чата и т. Д.), А второй — наши данные. На данный момент мы можем просто использовать 0 в качестве нашего тега.

Наконец, мы получаем все клиенты, которые в настоящее время подключены к серверу с помощью ClientManager.GetAllClients (), используют небольшое LINQ для удаления только что подключенного клиента (мы поговорим с ним позже), а затем отправим сообщение каждому клиенту. Не забывайте, что когда вы вызываете GetAllClients (), клиент, который только что подключен, уже добавлен!

Видеть? Легко! О чем вы беспокоились?

Несмотря на всю серьезность, это почти 80% всего, что нужно для отправки сообщений в DarkRift, как клиента, так и сервера: создайте запись для данных, сообщение, чтобы обернуть данные и затем отправить.

Однако одна вещь, которую я сделал, — это SendMode. Это указывает, как мы должны отправить сообщение клиенту:

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

Надежность. Гарантируется, что сообщение будет доставлено, а большие сообщения будут разделены на более мелкие сообщения, чтобы их можно было отправить.

Следующее, что нам нужно сделать быстро, — это породить всех других игроков на нашем вновь подключенном игроке; поскольку мы хотим свести к минимуму используемую здесь пропускную способность, мы собираем все это в одно сообщение и отправим его. Добавьте следующую строку вверху вашего AgarPlayerManager:

tionary<IClient, Player> players = new Dictionary<IClient, Player>();

А затем добавьте это в конец вашего метода ClientConnected:

players.Add(e.Client, newPlayer);

using (DarkRiftWriter playerWriter = DarkRiftWriter.Create())
{
    foreach (Player player in players.Values)
    {
        playerWriter.Write(player.ID);
        playerWriter.Write(player.X);
        playerWriter.Write(player.Y);
        playerWriter.Write(player.Radius);
        playerWriter.Write(player.ColorR);
        playerWriter.Write(player.ColorG);
        playerWriter.Write(player.ColorB);
    }

    using (Message playerMessage = Message.Create(0, playerWriter))
        e.Client.SendMessage(playerMessage, SendMode.Reliable);
}

Надеюсь, это должно быть достаточно понятно из того, что мы уже сделали, единственное отличие состоит в том, что мы продолжаем писать в одного писателя, чтобы все было в одном сообщении. Обратите внимание, что мы добавляем нового игрока в словарь, прежде чем перечислить его, чтобы он включался в то, что мы отправляем игроку (иначе они не появлялись бы самим игроком!)

Другое дело, что мы пропустили, было использование заявления. Сообщения, DarkRiftWriters и DarkRiftReaders реализуют IDisposable не потому, что они содержат ресурсы, которые нуждаются в утилизации, а потому, что, когда мы вызываем Dispose, они возвращаются в пул объектов, так что в следующий раз, когда вы вызываете Create, их не нужно перераспределять. Технически используемые операторы являются полностью необязательными, но вы получите гораздо лучшую производительность с оставленными в них! Мы рассмотрим переработку более подробно в разделе «Дополнительно».

Наконец, это хорошая идея для определения констант для тегов и тем, чтобы вы могли легко обращаться к ним и изменять их без побочных эффектов. Добавьте новый файл с именем Tags.cs и введите статический класс для сбора всех наших тегов:

static class Tags
{
    public static readonly ushort SpawnPlayerTag = 0;
}

И измените параметр тега в обоих сообщениях Message.Create на Tags.SpawnPlayerTag.

На самом деле нерестные игроки

Теперь, когда мы написали код сервера для нерестующих игроков, давайте добавим код на стороне клиента. Создайте новый файл PlayerSpawner.cs в проекте Unity и добавьте следующий код:

public class PlayerSpawner : MonoBehaviour
{
    const byte SPAWN_TAG = 0;

    [SerializeField]
    [Tooltip("The DarkRift client to communicate on.")]
    UnityClient client;

    [SerializeField]
    [Tooltip("The controllable player prefab.")]
    GameObject controllablePrefab;

    [SerializeField]
    [Tooltip("The network controllable player prefab.")]
    GameObject networkPrefab;

    void Awake()
    {
        if (client == null)
        {
            Debug.LogError("Client unassigned in PlayerSpawner.");
            Application.Quit();
        }

        if (controllablePrefab == null)
        {
            Debug.LogError("Controllable Prefab unassigned in PlayerSpawner.");
            Application.Quit();
        }

        if (networkPrefab == null)
        {
            Debug.LogError("Network Prefab unassigned in PlayerSpawner.");
            Application.Quit();
        }

        client.MessageReceived += SpawnPlayer;
    }
}

Вам нужно будет добавить ссылку на DarkRift.Client.Unity.

В этом вы заметите, что мы определяем ссылку на объект UnityClient, который мы будем заполнять от инспектора. Это компонент, который обрабатывает наше соединение с сервером, мы добавили его в объект «Сеть» на нашей ранней странице Unity, помните?

Событие MessageReceived, на которое мы подписываемся, вызывается всякий раз, когда клиент получает сообщение от сервера. Вы можете подписаться на такое количество разных обработчиков, как вам нравится, но меньше всего легче поддерживать и немного быстрее.

Еще одна важная вещь, которую следует отметить, это то, что мы называем это от Пробуждения. Когда у вас установлен UnityClient для подключения в рутине «Пуск», важно убедиться, что подписались на все начальные сообщения от Awake, потому что в противном случае вы можете пропустить сообщения, если он подключится, прежде чем подписываться!

Добавьте следующий код для декодирования наших пакетов spawn:

void SpawnPlayer(object sender, MessageReceivedEventArgs e)
{
    using (Message message = e.GetMessage())
    using (DarkRiftReader reader = message.GetReader())
    {
        if (message.Tag == Tags.SpawnPlayerTag)
        {
            if (reader.Length % 17 != 0)
            {
                Debug.LogWarning("Received malformed spawn packet.");
                return;
            }

            while (reader.Position < reader.Length)
            {
                ushort id = reader.ReadUInt16();
                Vector3 position = new Vector3(reader.ReadSingle(), reader.ReadSingle());
                float radius = reader.ReadSingle();
                Color32 color = new Color32(
                    reader.ReadByte(), 
                    reader.ReadByte(), 
                    reader.ReadByte(),
                    255
                );

                GameObject obj;
                if (id == client.ID)
                {
                    obj = Instantiate(controllablePrefab, position, Quaternion.identity) as GameObject;
                }
                else
                {
                    obj = Instantiate(networkPrefab, position, Quaternion.identity) as GameObject;
                }

                AgarObject agarObj = obj.GetComponent<AgarObject>();

                agarObj.SetRadius(radius);
                agarObj.SetColor(color);
            }
        }
    }
}

Если вы внимательно просмотрите код, вы увидите, что мы просто отменили процесс упаковки, который мы делали раньше: мы получаем читателя из сообщения, содержащего данные сообщения, и просто просматриваем данные в том же порядке, в каком мы писали это (обратите внимание, порядок имеет значение здесь!), пока не осталось больше данных. Когда мы читаем, мы создаем необходимые объекты в зависимости от того, является ли ID объекта игрока нашим идентификатором (если это то, что должно быть объектом, которым мы управляем).

Добавьте компонент PlayerSpawner к объекту Network в сцене и перетащите на него клиент и оба сборника в соответствующие места.

Наконец, скопируйте файл tags.cs из плагина, чтобы у нас была идентичная копия в нашем проекте Unity.

Теперь вы должны быть в состоянии проверить и увидеть игрока на клиенте. Следующий шаг, движение!


Поделиться.

Добавить комментарий

Войти с помощью: 

Ваш e-mail не будет опубликован. Обязательные поля помечены *