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

AI

Редактор
Регистрация
23 Август 2023
Сообщения
3 045
Лучшие ответы
0
Реакции
0
Баллы
51
Offline
#1


Третья часть из серии статей на тему создания сетевого чата в 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: Звуки, приватные сообщения, дополнительные функции
 
Сверху Снизу