ИИ-NPC в Roblox можно собрать из трёх независимых частей: триггера игрока, TextGenerator для реплики и AudioTextToSpeech для голоса. Между генерацией и показом текста обязательна фильтрация Roblox. Мы сделаем гида с ProximityPrompt, короткой репликой на экране, 3D-голосом и безопасным запасным ответом.
Такой NPC может учитывать цель игрока, но не должен управлять наградами и прогрессом. Модель формулирует фразу, игровые правила остаются в серверном коде. Первая версия отвечает на фиксированный вопрос о кристаллах: prompt запускает генерацию, фильтр, BillboardGui и голос у головы NPC.
ProximityPrompt передаёт серверу конкретного игрока и запускает одну реплику.
TextGenerator создаёт черновик, TextService готовит безопасный текст для показа.
AudioTextToSpeech соединяется Wire с AudioEmitter у головы NPC.
Как устроена цепочка ИИ-диалога и голоса
Создайте GuideNPC с Humanoid и Head. В Head добавьте ProximityPrompt, BillboardGui/TextLabel, AudioTextToSpeech, AudioEmitter и Wire. Свяжите Wire от TTS к Emitter. Включите нужные AI beta features, если они ещё не общедоступны. Скрипт в ServerScriptService получает объекты по точным путям. Фиксированный первый вопрос облегчает проверку длины и содержания.
| Объект | Где находится | За что отвечает |
|---|---|---|
| ProximityPrompt | GuideNPC/Head | Явное действие игрока |
| TextGenerator | GuideNPC | Короткая динамическая реплика |
| TextService | Сервис Roblox | Фильтрация перед показом |
| BillboardGui/TextLabel | GuideNPC/Head | Субтитр над NPC |
| AudioTextToSpeech | GuideNPC/Head | Преобразование текста в речь |
| AudioEmitter + Wire | GuideNPC/Head | 3D-источник звука |
Каждый объект имеет одну работу. Если голос молчит, можно отдельно проверить TTS и Wire. Если ответ не генерируется, запасная реплика всё равно покажет, что prompt, интерфейс и звук подключены правильно.

Текст становится видимым только после фильтра. Один отфильтрованный результат идёт в оба канала
Собираем NPC и серверный Luau-скрипт
Код ожидает объекты из таблицы. TextGenerator уже добавлен в GuideNPC. SystemPrompt ограничивает роль, MaxTokens сокращает ответ, а строка обрезается до лимита TTS. GenerateTextAsync и фильтрация работают в pcall. При любом сбое возвращается безопасная подсказка.
local TextService = game:GetService("TextService")
local npc = workspace:WaitForChild("GuideNPC")
local head = npc:WaitForChild("Head")
local prompt = head.ProximityPrompt
local label = head.BillboardGui.TextLabel
local voice = head.AudioTextToSpeech
local generator = npc.TextGenerator
local FALLBACK = "Ищи три кристалла у водопада."
local busy = false
generator.SystemPrompt = "Ты добрый проводник. Ответь одной фразой до 25 слов."
local function safeReply(player)
local ok, response = pcall(function()
return generator:GenerateTextAsync({
UserPrompt = "Где искать кристаллы?", MaxTokens = 50})
end)
if not ok or not response.GeneratedText then return FALLBACK end
local filteredOk, filtered = pcall(function()
return TextService:FilterStringAsync(
response.GeneratedText, player.UserId
):GetNonChatStringForBroadcastAsync()
end)
if not filteredOk or filtered == "" then return FALLBACK end
return string.sub(filtered, 1, 300)
end
prompt.Triggered:Connect(function(player)
if busy then return end
busy = true
prompt.Enabled = false
label.Text = "Думаю..."
local reply = safeReply(player)
label.Text = reply
voice.Text = reply
voice.TimePosition = 0
voice:Play()
voice.Ended:Wait()
prompt.Enabled = true
busy = false
end)- Нажатие prompt показывает состояние «Думаю...», затем безопасную короткую реплику.
- Тот же отфильтрованный текст передаётся в AudioTextToSpeech и звучит от головы NPC.
- Повторное нажатие во время речи не создаёт второй параллельный запрос.
- При сбое TextGenerator или фильтрации используется FALLBACK, и сценарий не ломается.

Модели подходит только одна сторона. Текст не является командой игровому серверу
Почему текст нужно фильтровать до показа и речи
TextGenerator возвращает GeneratedText, который нельзя сразу показывать или озвучивать. Roblox требует TextService:FilterStringAsync. Для общей реплики берём GetNonChatStringForBroadcastAsync. Только этот результат идёт в TextLabel и AudioTextToSpeech.Text. Строка ограничена 300 символами, busy и отключение prompt защищают от параллельных запросов.
| Этап | Если всё хорошо | Если произошёл сбой |
|---|---|---|
| GenerateTextAsync | Есть GeneratedText | Лог и FALLBACK |
| FilterStringAsync | Получен безопасный broadcast text | Лог и FALLBACK |
| TextLabel | Показывает субтитр | Остаётся понятная подсказка |
| AudioTextToSpeech | Проигрывает ту же строку | Текст всё равно виден |
| Ended | Возвращает prompt | Нужен таймаут в развитой версии |
AudioTextToSpeech ограничивает запросы и длину Text до 300 символов. Не запускайте генерацию на каждом Touched. Prompt, cooldown и кэш сокращают обращения. Храните несколько fallback-фраз и добавьте таймаут ожидания Ended. Генеративный текст отвечает за атмосферу, а число кристаллов, награды и условия квеста берёт сервер.
Проверяем задержку, повторные запросы и запасной ответ
Сначала отключите генерацию и верните FALLBACK напрямую. Проверьте prompt, BillboardGui, TTS и повторное нажатие. Затем включите TextGenerator и временно распечатайте только статус этапов, не полный пользовательский текст. Смоделируйте ошибку неверным именем NPC, отключённой beta feature и пустым GeneratedText. После восстановления запустите тест с двумя клиентами. Текущая версия использует общий busy, поэтому второй игрок ждёт окончания реплики. Это осознанное ограничение первого примера. Для персональных диалогов понадобится отдельное состояние по Player и решение, кому именно транслировать звук и субтитры.
- Создайте структуру GuideNPC и вручную проиграйте статический TTS-текст.
- Подключите Script с временным return FALLBACK и проверьте интерфейс.
- Включите генерацию и убедитесь, что ответ проходит TextService до показа.
- Нажмите prompt дважды во время речи. Второй запрос не должен стартовать.
- Нарушьте путь к TextGenerator и получите запасную реплику без падения сценария.
- Проведите тест с двумя клиентами и зафиксируйте выбранное поведение общего busy.
- Статический TTS работает независимо от модели.
- Сгенерированный текст всегда фильтруется.
- Субтитр и голос получают одинаковую безопасную строку.
- Сбой генерации приводит к FALLBACK, а не к сломанному NPC.

Проверяем каждый слой отдельно. Сбой AI не ломает квест
Развиваем персонажа без передачи ему игровых правил
Дальше передавайте небольшой контекст: задание, найденные предметы и разрешённую подсказку. SystemPrompt запрещает менять правила, сервер фильтрует ответ. Кэшируйте одинаковые состояния, добавьте таймаут и оставьте субтитры. Фраза NPC «дам 100 монет» не является серверной командой.
Как изучить тему в Кодике
В Кодике сначала изучите обычного NPC: Model, Head, ProximityPrompt, события и свойства. Затем разберите pcall и серверные таблицы состояния. После этого подключите отдельно TTS и отдельно TextGenerator. Только когда оба компонента работают по одному, соединяйте их фильтрованной строкой.
| Шаг | Что изучить | Проверяемый результат |
|---|---|---|
| 1 | Instances и пути Explorer | Найти все шесть объектов NPC |
| 2 | События Luau | Показать статическую реплику по prompt |
| 3 | pcall и fallback | Пережить искусственную ошибку |
| 4 | TextService и безопасность | Показать только фильтрованный текст |
| 5 | AudioTextToSpeech | Озвучить тот же субтитр |
В курсе Lua / Luau повторите функции, условия и события, затем соберите статического гида без генеративного API. В Кодике полезно нарисовать цепочку от Triggered до Play и подписать, на каком шаге появляется пользовательский контент и где он фильтруется.
Любой сгенерированный текст должен пройти обязательную фильтрацию Roblox.
Используйте один результат после TextService для обоих каналов.
Запросы дублируются, расходуют лимит и перекрывают звук.
Игровые правила и прогресс остаются в проверяемом серверном коде.
Что получится в итоге

NPC показывает отфильтрованную подсказку над головой и произносит ту же строку в 3D-пространстве, сохраняя рабочий fallback при сбое AI.
Можно ли сразу показать GeneratedText?
Нет. Roblox требует обработать сгенерированный текст через TextService:FilterStringAsync.
Зачем нужен FALLBACK?
Генерация, фильтрация или TTS могут быть недоступны. Основной игровой сценарий должен продолжаться.
Должна ли модель решать, сколько монет выдать?
Нет. Она формулирует реплику, а правила и награды определяет серверный код.
Как понять, что проект действительно работает?
Проверьте основной сценарий, ошибочный ввод, повторный запуск и один граничный случай. Затем объясните вслух, где появляются данные, кто принимает решение и где хранится состояние.
Можно ли начать с готового кода из статьи?
Да. После первого запуска измените один параметр, добавьте одну проверку и повторите проект без подсказки. Так пример превращается в собственный навык.
События, функции и серверную логику изучите в курсе Lua / Luau. Для первого персонажа полезны границы клиента и сервера и работа с Output.
Если хотите ускорить сборку объектов, сначала прочитайте как подключить Studio MCP. В Кодике повторите статический prompt и TTS, затем добавьте генерацию последним шагом.