Roblox · Дополнительное
Житель: подходишь, E «Поговорить» — внизу окно с его именем, реплика печатается по буквам, под ней варианты ответа. Ответ ведёт к следующей реплике или заканчивает разговор. Все диалоги — в одной таблице.
Синим — что нужно создать.
Workspace └─ Villager (Model) — NPC: HumanoidRootPart Anchored └─ HumanoidRootPart → TalkPrompt (ProximityPrompt) — имя важно ReplicatedStorage └─ Dialogs (ModuleScript) — реплики всех NPC StarterGui └─ DialogGui (ScreenGui) — ResetOnSpawn выкл. └─ Frame (Frame) — Visible выкл.: NameLabel, TextLabel, Options (UIListLayout), Close StarterPlayer → StarterPlayerScripts └─ DialogLocal (LocalScript)
Где: ReplicatedStorage · ModuleScript
Зачем: Реплики и варианты ответов.
-- все диалоги: имя NPC → его реплики
local dialogs = {
-- имя — как у модели NPC
["Villager"] = {
-- первая реплика всегда start
start = {
-- что говорит NPC
Text = "Привет, путник! Ты здесь впервые?",
-- варианты ответа
Options = {
-- Next — к какой реплике перейти
{Text = "Да, что тут у вас?", Next = "about"},
-- без Next — разговор окончен
{Text = "Нет, пока!"},
-- конец вариантов
},
-- конец реплики
},
-- реплика about
about = {
-- текст
Text = "Наша деревня. За холмом живут монстры — будь осторожен.",
-- варианты
Options = {
-- дальше
{Text = "А как их победить?", Next = "tip"},
-- конец
{Text = "Спасибо!"},
-- конец вариантов
},
-- конец реплики
},
-- реплика tip
tip = {
-- текст
Text = "Возьми меч в магазине и прячься за укрытиями.",
-- один вариант
Options = {{Text = "Понял, спасибо!"}},
-- конец реплики
},
-- конец диалога
},
-- конец таблицы
}
-- отдаём таблицу
return dialogsГде: StarterPlayer → StarterPlayerScripts · LocalScript
Зачем: Окно диалога и переходы.
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- все надписи ProximityPrompt
local PPS = game:GetService("ProximityPromptService")
-- реплики всех NPC
local dialogs = require(RS:WaitForChild("Dialogs"))
-- этот игрок
local player = game.Players.LocalPlayer
-- окно диалога
local gui = player:WaitForChild("PlayerGui"):WaitForChild("DialogGui")
-- окно
local frame = gui:WaitForChild("Frame")
-- имя NPC
local nameLabel = frame:WaitForChild("NameLabel")
-- реплика
local textLabel = frame:WaitForChild("TextLabel")
-- кнопки ответов
local options = frame:WaitForChild("Options")
-- с каким диалогом говорим
local talking = nil
-- номер реплики — чтобы старая печать не мешала новой
local version = 0
-- закончить разговор
local function Close()
-- ни с кем
talking = nil
-- окно прячем
frame.Visible = false
-- конец функции
end
-- показать реплику
local function ShowNode(nodeName)
-- реплика из таблицы
local node = talking and talking[nodeName]
-- нет такой — конец
if not node then return Close() end
-- новая реплика
version += 1
-- запоминаем свою
local my = version
-- старые кнопки
for _, child in ipairs(options:GetChildren()) do
-- убираем
if child:IsA("TextButton") then child:Destroy() end
-- конец перебора
end
-- весь текст
textLabel.Text = node.Text
-- но пока не видно ни буквы
textLabel.MaxVisibleGraphemes = 0
-- по одной букве (русские буквы считаем правильно)
for i = 1, utf8.len(node.Text) do
-- уже показали другую реплику
if my ~= version then return end
-- показываем i букв — эффект печати
textLabel.MaxVisibleGraphemes = i
-- скорость печати
task.wait(0.02)
-- конец цикла
end
-- весь текст
textLabel.MaxVisibleGraphemes = -1
-- каждый вариант ответа
for i, option in ipairs(node.Options or {}) do
-- кнопка
local button = Instance.new("TextButton")
-- во всю ширину
button.Size = UDim2.new(1, 0, 0, 34)
-- фон
button.BackgroundColor3 = Color3.fromRGB(60, 60, 80)
-- белый текст
button.TextColor3 = Color3.new(1, 1, 1)
-- по размеру
button.TextScaled = true
-- жирный
button.Font = Enum.Font.GothamBold
-- текст ответа
button.Text = option.Text
-- порядок
button.LayoutOrder = i
-- в список
button.Parent = options
-- выбрали ответ
button.MouseButton1Click:Connect(function()
-- дальше или конец
if option.Next then ShowNode(option.Next) else Close() end
-- конец обработчика
end)
-- конец перебора
end
-- вариантов нет
if not node.Options or #node.Options == 0 then
-- пусть дочитает
task.wait(2)
-- и закрываем
if my == version then Close() end
-- конец проверки
end
-- конец функции
end
-- нажал E у надписи
PPS.PromptTriggered:Connect(function(prompt)
-- это не разговор
if prompt.Name ~= "TalkPrompt" then return end
-- NPC, к которому подошли
local npc = prompt:FindFirstAncestorOfClass("Model")
-- для него нет диалога
if not npc or not dialogs[npc.Name] then return end
-- его реплики
talking = dialogs[npc.Name]
-- имя
nameLabel.Text = npc.Name
-- открываем окно
frame.Visible = true
-- первая реплика
ShowNode("start")
-- конец обработчика
end)
-- крестик — закончить разговор
frame:WaitForChild("Close").MouseButton1Click:Connect(Close)Запусти Play (или Run) и убедись: