Search
Write a publication
Pull to refresh

Сетевой чат в Unity: Часть 3 — Звуки и продвинутые функции

Level of difficultyMedium
Reading time8 min
Views251

Третья часть из серии статей на тему создания сетевого чата в Unity с использованием Netcode for GameObjects.

Привет!

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

Представь, что мы превращаем твою простую рацию из первой части в настоящий командный центр космического корабля!


Что нужно знать перед началом

Важно: Эта часть требует завершенный код из первой части и второй части!

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


Звуковые уведомления: Слышим новые сообщения

Когда приходит новое сообщение, хорошо бы услышать звуковой сигнал. Это как звонок телефона - ты сразу понимаешь, что кто-то написал тебе.

Добавляем звуки к существующему ChatSystem

В твоем скрипте ChatSystem.cs из предыдущих частей добавь эти переменные после всех существующих переменных из первой и второй части:

Напоминание: К этому моменту у тебя уже должны быть все переменные из первых двух частей:

  • Из 1-й части: messageInput, sendButton, chatScrollRect, messagePrefab, messageContainer, maxMessages, messageObjects

  • Из 2-й части: все переменные для команд и функции ProcessEmojis, ProcessFormatting, CreateSystemMessage

[Header("Звуки")]
public AudioSource audioSource;
public AudioClip messageSound;
public AudioClip systemSound;
public AudioClip privateMessageSound;

Теперь создай функцию для воспроизведения звуков:

private void PlayMessageSound(bool isSystemMessage = false, bool isPrivateMessage = false)
{
    if (audioSource != null)
    {
        AudioClip clipToPlay = null;
        
        if (isPrivateMessage)
        {
            clipToPlay = privateMessageSound;
        }
        else if (isSystemMessage)
        {
            clipToPlay = systemSound;
        }
        else
        {
            clipToPlay = messageSound;
        }
        
        if (clipToPlay != null)
        {
            audioSource.PlayOneShot(clipToPlay);
        }
    }
}

Что происходит в этом коде?

  1. AudioSource - компонент Unity для воспроизведения звуков

  2. messageSound - звук для обычных сообщений

  3. systemSound - звук для системных сообщений

  4. privateMessageSound - звук для приватных сообщений

  5. PlayMessageSound - функция, которая выбирает правильный звук и проигрывает его


Приватные сообщения: Личные разговоры

Иногда игроки хотят поговорить наедине, не мешая остальным. Для этого создадим систему приватных сообщений.

Добавляем приватные сообщения

private void ProcessPrivateMessage(string message)
{
    // Формат: /w Игрок1 Привет!
    string[] parts = message.Split(' ');
    
    if (parts.Length < 3)
    {
        CreateSystemMessage("Использование: /w [игрок] [сообщение]");
        return;
    }
    
    string targetPlayerName = parts[1];
    string privateMessage = string.Join(" ", parts, 2, parts.Length - 2);
    
    // Находим ID целевого игрока
    ulong targetPlayerId = FindPlayerByName(targetPlayerName);
    
    if (targetPlayerId != ulong.MaxValue)
    {
        // Обрабатываем эмодзи и форматирование как в обычных сообщениях (из 2-й части)
        privateMessage = ProcessEmojis(privateMessage);
        privateMessage = ProcessFormatting(privateMessage);
        
        SendPrivateMessageServerRpc(privateMessage, targetPlayerId);
    }
    else
    {
        CreateSystemMessage($"Игрок '{targetPlayerName}' не найден!");
    }
}

private ulong FindPlayerByName(string playerName)
{
    // Проверяем, что NetworkManager существует
    if (NetworkManager.Singleton == null)
    {
        return ulong.MaxValue;
    }

    foreach (var client in NetworkManager.Singleton.ConnectedClients)
    {
        string currentPlayerName = client.Key == NetworkManager.Singleton.LocalClientId ? "Ты" : $"Игрок {client.Key}";
        if (currentPlayerName.ToLower() == playerName.ToLower())
        {
            return client.Key;
        }
    }
    return ulong.MaxValue; // Игрок не найден
}

[ServerRpc(RequireOwnership = false)]
private void SendPrivateMessageServerRpc(string message, ulong targetPlayerId, ServerRpcParams serverRpcParams = default)
{
    var senderId = serverRpcParams.Receive.SenderClientId;
    
    // Отправляем сообщение только отправителю
    ClientRpcParams senderParams = new ClientRpcParams
    {
        Send = new ClientRpcSendParams
        {
            TargetClientIds = new ulong[] { senderId }
        }
    };
    ReceivePrivateMessageClientRpc(message, senderId, targetPlayerId, true, senderParams);
    
    // Отправляем сообщение только получателю (если это не сам отправитель)
    if (targetPlayerId != senderId)
    {
        ClientRpcParams targetParams = new ClientRpcParams
        {
            Send = new ClientRpcSendParams
            {
                TargetClientIds = new ulong[] { targetPlayerId }
            }
        };
        ReceivePrivateMessageClientRpc(message, senderId, targetPlayerId, false, targetParams);
    }
}

[ClientRpc]
private void ReceivePrivateMessageClientRpc(string message, ulong senderId, ulong targetPlayerId, bool isSender, ClientRpcParams clientRpcParams = default)
{
    CreatePrivateMessage(message, senderId, targetPlayerId, isSender);
}

private void CreatePrivateMessage(string message, ulong senderId, ulong targetPlayerId, bool isSender)
{
    // Проверяем, что шаблон сообщения существует
    if (messagePrefab == null)
    {
        Debug.LogError("MessagePrefab не назначен!");
        return;
    }
    
    // Проверяем, что контейнер существует
    if (messageContainer == null)
    {
        Debug.LogError("MessageContainer не назначен!");
        return;
    }
    
    GameObject messageObj = Instantiate(messagePrefab, messageContainer);
    TMP_Text messageText = messageObj.GetComponentInChildren<TMP_Text>();
    
    // Проверяем, что текстовый компонент найден
    if (messageText == null)
    {
        Debug.LogError("TMP_Text компонент не найден в MessagePrefab!");
        Destroy(messageObj);
        return;
    }
    
    // Проверяем, что NetworkManager существует
    if (NetworkManager.Singleton == null)
    {
        Debug.LogError("NetworkManager.Singleton is null!");
        Destroy(messageObj);
        return;
    }
    
    string senderName = senderId == NetworkManager.Singleton.LocalClientId ? "Ты" : $"Игрок {senderId}";
    string targetName = targetPlayerId == NetworkManager.Singleton.LocalClientId ? "тебе" : $"Игроку {targetPlayerId}";
    
    if (isSender)
    {
        messageText.text = $"💬 [Ты → {targetName}]: {message}";
        messageText.color = Color.cyan; // Сообщения от тебя - голубые
    }
    else
    {
        messageText.text = $"💬 [{senderName} → тебе]: {message}";
        messageText.color = Color.magenta; // Сообщения к тебе - розовые
    }
    
    messageObjects.Add(messageObj);
    
    if (messageObjects.Count > maxMessages)
    {
        GameObject oldMessage = messageObjects[0];
        messageObjects.RemoveAt(0);
        Destroy(oldMessage);
    }
    
    Canvas.ForceUpdateCanvases();
    chatScrollRect.verticalNormalizedPosition = 0f;
    
    // Играем звук приватного сообщения
    PlayMessageSound(false, true);
}

Что происходит в этом коде?

  1. ProcessPrivateMessage - обрабатывает команду /w игрок сообщение

  2. FindPlayerByName - находит ID игрока по имени (ищет "Ты" или "Игрок X")

  3. SendPrivateMessageServerRpc - отправляет приватное сообщение только нужным клиентам

  4. ReceivePrivateMessageClientRpc - получает приватное сообщение с правильной адресацией

  5. CreatePrivateMessage - создает красивое приватное сообщение с разными цветами

Примеры использования:

  • /w Ты Привет! - отправить сообщение самому себе

  • /w Игрок 2 Как дела? - отправить сообщение игроку с ID 2

Приватные сообщения выделяются цветом: голубые - от тебя, розовые - к тебе.


Интеграция звуков с существующими функциями

У тебя уже есть функции ProcessEmojis, ProcessFormatting и CreateSystemMessage из второй части. Теперь нужно просто обновить функцию CreateSystemMessage, чтобы она воспроизводила звук.

Найди свою функцию CreateSystemMessage из второй части и добавь в конце (перед закрывающей скобкой):

// Добавить в конец существующей функции CreateSystemMessage
PlayMessageSound(true, false);

Также обнови свою функцию CreateMessageInChat из первой части, добавив в конце:

// Добавить в конец существующей функции CreateMessageInChat
// Играем звук только если сообщение не от нас самих
if (senderId != NetworkManager.Singleton.LocalClientId)
{
    PlayMessageSound(false, false);
}

Обновляем обработку команд

Теперь нужно добавить обработку приватных сообщений в твою существующую функцию ProcessCommand из второй части.

Найди свою функцию ProcessCommand и добавь новую команду /w перед существующей проверкой if (command.StartsWith("help")):

// Добавить в ProcessCommand перед if (command.StartsWith("help")):
if (command.StartsWith("w ") || command.StartsWith("whisper "))
{
    ProcessPrivateMessage(commandMessage); // Передаем оригинальную команду с "/"
}
else if (command.StartsWith("help")) // не меняй существующий if - просто добавь else
{
    ShowHelpMessage();
}
// ... остальные команды остаются как есть (else if)

И обнови свою существующую функцию ShowHelpMessage из второй части, добавив строку о приватных сообщениях:

// Добавить в твою существующую функцию ShowHelpMessage эту строку:
"/w [игрок] [сообщение] - приватное сообщение\n" +

Просто вставь эту строку в список команд в нужном месте.

Важно: После добавления всех новых команд убедись, что финальная else из второй части остается последней и обрабатывает неизвестные команды.


Важное замечание о существующих функциях

Не нужно ничего менять в функции SendMessage - она уже правильно настроена во второй части.

Во второй части мы обновили SendMessage для обработки команд до отправки на сервер. А функции SendMessageServerRpc, ReceiveMessageClientRpc и CreateMessageInChat из первой части остались без изменений - они просто передают и отображают уже обработанное сообщение.

Важно: Эмодзи и форматирование обрабатываются в SendMessage (во 2-й части), поэтому CreateMessageInChat получает уже готовое сообщение.


Дополнительные продвинутые функции

1. Автоответчик

Добавь эти переменные в начало ChatSystem.cs:

[Header("Автоответчик")]
public bool autoReplyEnabled = false;
public string autoReplyMessage = "Сейчас не могу ответить, играю!";

Создай функцию для переключения автоответчика:

private void ToggleAutoReply()
{
    autoReplyEnabled = !autoReplyEnabled;
    string status = autoReplyEnabled ? "включен" : "выключен";
    CreateSystemMessage($"Автоответчик {status}");
}

И добавь команду в существующую ProcessCommand после всех остальных команд но перед финальной else:

// Добавить в ProcessCommand после команды /joke, но перед финальной else:
else if (command.StartsWith("autoreply"))
{
    ToggleAutoReply();
}

2. Фильтр слов

Добавь переменные для фильтрации:

[Header("Фильтр")]
public bool filterEnabled = true;
public string[] badWords = { "плохое", "слово", "спам" };

Создай функцию фильтрации (можешь использовать ее в SendMessage перед отправкой):

private string FilterMessage(string message)
{
    if (!filterEnabled) return message;
    
    string filteredMessage = message;
    foreach (string badWord in badWords)
    {
        string replacement = new string('*', badWord.Length);
        filteredMessage = filteredMessage.Replace(badWord, replacement);
    }
    
    return filteredMessage;
}

3. Сохранение истории чата

private void SaveChatHistory()
{
    string history = "";
    foreach (GameObject messageObj in messageObjects)
    {
        TMP_Text messageText = messageObj.GetComponentInChildren<TMP_Text>();
        if (messageText != null)
        {
            history += messageText.text + "\n";
        }
    }
    
    try
    {
        string fileName = $"ChatHistory_{System.DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt";
        System.IO.File.WriteAllText(fileName, history);
        CreateSystemMessage($"История чата сохранена в файл: {fileName}");
    }
    catch (System.Exception e)
    {
        CreateSystemMessage($"Ошибка сохранения: {e.Message}");
    }
}

Добавь команду в ProcessCommand после всех остальных команд но перед финальной else:

// Добавить в ProcessCommand после команды /autoreply, но перед финальной else:
else if (command.StartsWith("save"))
{
    SaveChatHistory();
}

Как настроить звуки в Unity

  1. Создай AudioSource:

    • Добавь компонент AudioSource к объекту с ChatSystem

    • Перетащи его в поле audioSource

  2. Добавь звуковые файлы:

    • Импортируй звуковые файлы (.wav, .mp3) в папку Assets

    • Перетащи их в соответствующие поля: messageSound, systemSound, privateMessageSound

  3. Настрой громкость:

    • В компоненте AudioSource установи подходящую громкость

    • Проверь, что звуки не слишком громкие


Полный список функций чата

Теперь твой чат поддерживает:

Основные функции:

  • Отправка и получение сообщений

  • Система команд

  • Эмодзи и форматирование

  • Звуковые уведомления

Команды:

  • /help - справка (из 2-й части)

  • /w [игрок] [сообщение] - приватное сообщение (новая!)

  • /time - время (из 2-й части)

  • /players - список игроков (из 2-й части)

  • /roll - бросить кости (из 2-й части)

  • /joke - шутка (из 2-й части)

  • /color [цвет] - сменить цвет (из 2-й части)

  • /autoreply - автоответчик (новая!)

  • /save - сохранить историю (новая!)

Эмодзи: (из 2-й части)

  • :) 😊 :D 😄 <3 ❤️ :fire: 🔥 и многие другие

Форматирование: (из 2-й части)

  • **жирный** *курсив* __подчеркнутый__


Заключение

Поздравляю! Теперь у тебя есть сетевой чат с множеством функций:

  • Базовый функционал (из 1-й части) - отправка сообщений

  • Система команд (из 2-й части) - интерактивность

  • Эмодзи и форматирование (из 2-й части) - красота

  • Приватные сообщения (новое!) - личное общение

  • Звуковые уведомления (новое!) - удобство

  • Дополнительные функции (новое!) - автоответчик, фильтр, сохранение

Этот чат можно использовать в любых сетевых играх Unity. Игроки смогут общаться, координировать действия, выражать эмоции и даже вести личные разговоры.


Что дальше?

Теперь у тебя есть сетевой чат! Вот несколько идей для дальнейшего развития:

  1. Система ролей - админы, модераторы, обычные игроки

  2. Голосовой чат - добавить поддержку голосовых сообщений

  3. Каналы - разные чаты для разных команд

  4. История сообщений - сохранение между сессиями

  5. Блокировка игроков - система игнора

Главное - твой чат работает, игроки довольны, а ты научился создавать настоящие сетевые системы!

Удачи в создании твоих сетевых игр! Общение между игроками делает любую игру намного интереснее и веселее.


Краткое резюме серии

Часть 1: Основы - базовая отправка и получение сообщений
Часть 2: Команды, эмодзи, форматирование, система смены цветов
Часть 3: Звуки, приватные сообщения, дополнительные функции

Tags:
Hubs:
0
Comments0

Articles