Третья часть из серии статей на тему создания сетевого чата в 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);
}
}
}
Что происходит в этом коде?
AudioSource
- компонент Unity для воспроизведения звуковmessageSound
- звук для обычных сообщенийsystemSound
- звук для системных сообщенийprivateMessageSound
- звук для приватных сообщений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);
}
Что происходит в этом коде?
ProcessPrivateMessage
- обрабатывает команду/w игрок сообщение
FindPlayerByName
- находит ID игрока по имени (ищет "Ты" или "Игрок X")SendPrivateMessageServerRpc
- отправляет приватное сообщение только нужным клиентамReceivePrivateMessageClientRpc
- получает приватное сообщение с правильной адресацией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
Создай AudioSource:
Добавь компонент
AudioSource
к объекту сChatSystem
Перетащи его в поле
audioSource
Добавь звуковые файлы:
Импортируй звуковые файлы (.wav, .mp3) в папку Assets
Перетащи их в соответствующие поля:
messageSound
,systemSound
,privateMessageSound
Настрой громкость:
В компоненте
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: Звуки, приватные сообщения, дополнительные функции