Roblox · RPG · урок 4
Своя система взаимодействия: наводишь курсор на предмет — над ним надпись, кликаешь — монета в кошельке или открывается записка.
Синим — что добавляем на этом уроке.
Workspace └─ MapFolder (Folder) ├─ Coin (Part) — Highlight, CanInteract (BoolValue, вкл.), Coin (IntValue), Sound └─ Note (Part) — Highlight, CanInteract, Text (StringValue), Sound ReplicatedStorage └─ InteractEvent (RemoteEvent) ServerScriptService ├─ PlayerData (Script) — кошелёк «Монеты» └─ InteractServer (Script) StarterPlayer → StarterPlayerScripts └─ InteractSystem (LocalScript) StarterGui ├─ InteractGui (BillboardGui) — AlwaysOnTop, MaxDistance 60 │ └─ Object, Interact (TextLabel) — имя и действие └─ NoteGui (ScreenGui) — ResetOnSpawn выкл. └─ NoteFrame (Frame) — NoteText, CloseButton; Visible выкл.
Где: ServerScriptService · Script
Зачем: Кошелёк игрока.
-- игроки
local Players = game:GetService("Players")
-- создаёт контейнер со значением
local function Value(class, name, value, parent)
-- контейнер
local v = Instance.new(class)
-- имя
v.Name = name
-- значение
v.Value = value
-- на место
v.Parent = parent
-- отдаём
return v
-- конец функции
end
-- игрок зашёл
local function Setup(player)
-- таблица по Tab
local leaderstats = Instance.new("Folder")
-- имя строго такое
leaderstats.Name = "leaderstats"
-- в игрока
leaderstats.Parent = player
-- кошелёк
local coins = Value("IntValue", "Монеты", 0, leaderstats)
-- конец функции
end
-- новые игроки
Players.PlayerAdded:Connect(Setup)
-- кто зашёл раньше скрипта
for _, player in ipairs(Players:GetPlayers()) do task.spawn(Setup, player) endГде: ServerScriptService · Script
Зачем: Монеты и сундуки: начисление и возврат.
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- клик игрока по монете или сундуку
local interactEvent = RS:WaitForChild("InteractEvent")
-- дальше — не засчитываем (защита от читов)
local MAX_DISTANCE = 32
-- через сколько секунд награда вернётся
local RESPAWN = 60
-- спрятать или показать монету/сундук
local function SetVisible(item, visible)
-- всё внутри
local list = item:GetDescendants()
-- и сам объект
table.insert(list, item)
-- каждый
for _, obj in ipairs(list) do
-- деталь
if obj:IsA("BasePart") then
-- запоминаем обычную прозрачность
if obj:GetAttribute("BaseTransparency") == nil then obj:SetAttribute("BaseTransparency", obj.Transparency) end
-- видна или нет
obj.Transparency = visible and obj:GetAttribute("BaseTransparency") or 1
-- подсветка
elseif obj:IsA("Highlight") then
-- вместе с объектом
obj.Enabled = visible
-- конец проверки
end
-- конец перебора
end
-- конец функции
end
-- игрок кликнул
interactEvent.OnServerEvent:Connect(function(player, item)
-- пришло не то — игнорируем
if typeof(item) ~= "Instance" or not item:IsDescendantOf(workspace) then return end
-- метка доступности
local can = item:FindFirstChild("CanInteract")
-- сколько монет внутри
local coin = item:FindFirstChild("Coin")
-- уже взято или это не награда
if not can or not can.Value or not coin then return end
-- где объект
local position = item:IsA("Model") and item:GetPivot().Position or item.Position
-- далеко — не засчитываем
if player:DistanceFromCharacter(position) > MAX_DISTANCE then return end
-- больше не взять
can.Value = false
-- монеты в кошелёк
player.leaderstats["Монеты"].Value += coin.Value
-- звук монеты
local sound = item:FindFirstChild("Sound")
-- звон
if sound then sound:Play() end
-- исчезает
SetVisible(item, false)
-- ждём
task.wait(RESPAWN)
-- появляется снова
SetVisible(item, true)
-- можно взять снова
can.Value = true
-- конец функции
end)Где: StarterPlayer → StarterPlayerScripts · LocalScript
Зачем: Надпись над объектом и клики.
-- игроки
local Players = game:GetService("Players")
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- каждый кадр
local RunService = game:GetService("RunService")
-- этот игрок
local player = Players.LocalPlayer
-- мышь: mouse.Target — деталь под курсором
local mouse = player:GetMouse()
-- интерфейс игрока
local playerGui = player:WaitForChild("PlayerGui")
-- надпись над объектом (BillboardGui)
local interactGui = playerGui:WaitForChild("InteractGui")
-- окно записки
local noteFrame = playerGui:WaitForChild("NoteGui"):WaitForChild("NoteFrame")
-- просим сервер: «взять монету/сундук»
local interactEvent = RS:WaitForChild("InteractEvent")
-- дальше 30 studs не взаимодействуем
local MAX_DISTANCE = 30
-- объект с CanInteract под курсором (деталь или её модель)
local function Target()
-- деталь под курсором
local obj = mouse.Target
-- поднимаемся к родителям
while obj and obj ~= workspace do
-- метка «с этим можно взаимодействовать»
local can = obj:FindFirstChild("CanInteract")
-- нашли, и он сейчас доступен
if can and can.Value then return obj end
-- выше
obj = obj.Parent
-- конец цикла
end
-- ничего нет
return nil
-- конец функции
end
-- где объект
local function PositionOf(item)
-- у модели — центр
if item:IsA("Model") then return item:GetPivot().Position end
-- у детали — её позиция
return item.Position
-- конец функции
end
-- что написать под именем
local function ActionText(item)
-- записка
if item:FindFirstChild("Text") then return "ЛКМ — прочитать" end
-- монета или сундук
return "ЛКМ — взять"
-- конец функции
end
-- каждый кадр
RunService.RenderStepped:Connect(function()
-- что под курсором
local item = Target()
-- есть и не слишком далеко
if item and player:DistanceFromCharacter(PositionOf(item)) < MAX_DISTANCE * 2 then
-- надпись над объектом
interactGui.Adornee = item:IsA("Model") and (item.PrimaryPart or item:FindFirstChildWhichIsA("BasePart")) or item
-- имя объекта
interactGui.Object.Text = item.Name
-- что можно сделать
interactGui.Interact.Text = ActionText(item)
-- показываем
interactGui.Enabled = true
-- ничего нет
else
-- отвязываем
interactGui.Adornee = nil
-- прячем
interactGui.Enabled = false
-- конец проверки
end
-- конец функции
end)
-- клик левой кнопкой
mouse.Button1Down:Connect(function()
-- что под курсором
local item = Target()
-- ничего или далеко
if not item or player:DistanceFromCharacter(PositionOf(item)) > MAX_DISTANCE then return end
-- это записка?
local text = item:FindFirstChild("Text")
-- да
if text then
-- текст записки
noteFrame.NoteText.Text = text.Value
-- показываем окно
noteFrame.Visible = true
-- шелест бумаги
local sound = item:FindFirstChild("Sound")
-- играем у себя
if sound then sound:Play() end
-- монета или сундук
else
-- деньги начисляет только сервер
interactEvent:FireServer(item)
-- конец проверки
end
-- конец функции
end)
-- «Закрыть» записку — подписка одна
noteFrame:WaitForChild("CloseButton").MouseButton1Click:Connect(function()
-- прячем
noteFrame.Visible = false
-- конец функции
end)Запусти Play (или Run) и убедись: