Studio

NPC и диалоги: персонаж, с которым можно поговорить

Как поставить в мир живого на вид персонажа, повесить на него подсказку «нажми E», показать реплику и выдать награду так, чтобы её нельзя было выпросить у сервера из консоли.

Средне 22 минуты 6 шагов проверен 1 месяц назад

Что понадобится

Roblox Studio, понимание разницы между Script и LocalScript. Код в гайде готовый.

Текст гайда «NPC и диалоги: персонаж, с которым можно поговорить»

Содержание

NPC оживляет карту сильнее, чем любой декор: пустой город с домами выглядит заброшенным, а тот же город с тремя стоящими персонажами — обитаемым. Хорошая новость в том, что первый говорящий NPC делается без единой строки искусственного интеллекта.

Из чего состоит NPC#

NPC — это обычная модель персонажа: части тела, Humanoid и HumanoidRootPart. Ставится так же, как риг для анимаций: вкладка Avatar → Rig Builder. Дальше остаётся закрепить его на месте и убрать лишнее.

  • выдели все части модели и включи Anchored — иначе игроки растолкают NPC по карте
  • у Humanoid поставь DisplayDistanceType в None, если имя над головой не нужно
  • HealthDisplayType — тоже None: полоска здоровья у продавца выглядит странно
  • переименуй модель по-человечески: NPC_Shopkeeper, а не Dummy

Оденься в редакторе

Внешность NPC меняется так же, как своя: перетащи в модель объекты одежды и аксессуаров из библиотеки. Пять минут работы отличают «манекен из урока» от персонажа, которого запоминают.

ProximityPrompt вместо клика#

Раньше взаимодействие делали через ClickDetector, и на телефоне это работало плохо. Сейчас правильный способ один — ProximityPrompt: он сам рисует подсказку, сам понимает, что игрок подошёл достаточно близко, и сам работает и на клавиатуре, и на пальце.

Добавь ProximityPrompt внутрь HumanoidRootPart и настрой его свойства:

Свойства подсказки
СвойствоЧто задаёт
ActionTextЧто произойдёт: «Поговорить», «Купить», «Взять»
ObjectTextС кем или с чем: имя NPC
KeyboardKeyCodeКлавиша на компьютере, обычно E
MaxActivationDistanceС какого расстояния видно подсказку
RequiresLineOfSightПрятать ли подсказку, когда NPC за стеной — почти всегда стоит включить

Реплика#

Самый простой диалог — облачко над головой. Его делают через BillboardGui: он всегда развёрнут к камере и не требует возни с позиционированием.

Luau
local npc = script.Parent
local root = npc:WaitForChild("HumanoidRootPart")
local prompt = root:WaitForChild("ProximityPrompt")
local bubble = root:WaitForChild("Bubble")       -- BillboardGui
local label = bubble:WaitForChild("Text")        -- TextLabel внутри него

local lines = {
    "Привет. Давно тут никого не было.",
    "За мостом сейчас лучше не ходить.",
    "Если найдёшь ключ — возвращайся.",
}

local index = 0
local speaking = false

bubble.Enabled = false

prompt.Triggered:Connect(function(player)
    if speaking then
        return -- пока фраза висит, повторные нажатия игнорируем
    end

    speaking = true
    index = index % #lines + 1
    label.Text = lines[index]
    bubble.Enabled = true

    task.delay(4, function()
        bubble.Enabled = false
        speaking = false
    end)
end)

Реплики держи списком

Один и тот же текст при каждом подходе превращает NPC в табличку. Список из трёх-четырёх фраз по кругу стоит десяти минут и читается совсем иначе. Хранить их лучше в ModuleScript: тогда тексты правит человек, который не боится сломать код.

Награда: где всё ломается#

Как только NPC начинает что-то выдавать — монеты, предмет, доступ, — появляется вопрос безопасности. Клиенту доверять нельзя: игрок может вызвать событие сам, сколько угодно раз и откуда угодно.

  1. выдачу делает сервер, и только он: Script, а не LocalScript
  2. ProximityPrompt.Triggered уже приходит на сервер и приносит игрока — этого достаточно, отдельный RemoteEvent тут не нужен
  3. сервер сам проверяет расстояние: подсказка ограничивает дистанцию на клиенте, но клиент — не доказательство
  4. сервер сам проверяет условие: выполнил ли игрок задание, хватает ли у него монет
  5. состояние (выдано / не выдано) хранится на сервере, а не в интерфейсе
Luau
local MAX_DISTANCE = 12

prompt.Triggered:Connect(function(player)
    local character = player.Character
    local playerRoot = character and character:FindFirstChild("HumanoidRootPart")

    if not playerRoot then
        return
    end

    -- Клиент показывает подсказку только вблизи, но верить в это нельзя:
    -- расстояние пересчитывается на сервере ещё раз.
    if (playerRoot.Position - root.Position).Magnitude > MAX_DISTANCE then
        return
    end

    if given[player.UserId] then
        return -- награда одноразовая
    end

    given[player.UserId] = true
    giveReward(player)
end)

Не храни счётчики в интерфейсе

Если количество монет живёт в TextLabel, а сервер верит тому, что там написано, — игру сломают в первый же день. Интерфейс только показывает число, которое посчитал сервер. Это правило не имеет исключений.

Чтобы NPC не выглядел статуей#

  • повесь на него зацикленную анимацию стойки — даже лёгкое покачивание меняет впечатление
  • разверни лицом к тому, откуда приходит игрок, а не в стену
  • поставь рядом что-то, объясняющее, почему он тут стоит: прилавок, костёр, ящики
  • добавь короткий звук на срабатывание подсказки — он делает нажатие «ощутимым»

Пошагово

6 шагов
  1. 1

    Поставь персонажа

    Вкладка Avatar → Rig Builder, выбери тип скелета, поставь модель на место и включи Anchored у всех частей.

  2. 2

    Убери лишнее

    У Humanoid отключи показ имени и полоски здоровья, переименуй модель понятно.

  3. 3

    Добавь подсказку

    Внутрь HumanoidRootPart вложи ProximityPrompt, задай ActionText «Поговорить» и включи RequiresLineOfSight.

  4. 4

    Сделай облачко

    Туда же добавь BillboardGui с именем Bubble, внутрь — TextLabel с именем Text. Поставь Enabled в false: показывать его будет скрипт.

    Подними StudsOffset по Y, чтобы облачко висело над головой, а не в груди.

  5. 5

    Вставь скрипт

    Положи Script из гайда внутрь модели NPC и замени фразы на свои.

  6. 6

    Проверь вдвоём

    Запусти тест на двух игроков (Test → Players: 2) и убедись, что реплика показывается тому, кто нажал, а награда выдаётся один раз.

Реклама · игра в Telegram Moki Blast — шарики, боссы и дуэли в Telegram Надуй щёки Моки и собери три шара одного цвета Шесть миров: от руин до Золотого города Король обезьян ждёт на 120-м уровне Дуэли с живыми игроками и турниры Играть в Telegram

Обсуждение

Пиши по делу и без личных данных. Ссылки на сторонние сайты, обмен и «бесплатные робуксы» мы скрываем — почему.

Комментарии пишут те, кто вошёл — так спокойнее всем.

Войти

Пока никто ничего не написал. Будь первым.