Roblox · RPG · урок 5
Ролевая система: опыт и уровни с полосой на экране и выбор класса — мечник, лучник или маг. Класс даёт свои навыки и оружие и сохраняется после смерти.
Синим — что добавляем на этом уроке.
ServerStorage ├─ Classes (Folder) │ ├─ Swordsman (Folder) — навыки мечника │ ├─ Archer (Folder) — навыки лучника │ └─ Mage (Folder) — навыки мага └─ Weapons (Folder) — классовое оружие (следующие уроки) ReplicatedStorage └─ ClassEvent (RemoteEvent) ServerScriptService ├─ PlayerData (Script) — + Lvl, Exp, RequiredExp └─ ClassServer (Script) StarterGui ├─ ClassGui (ScreenGui) — ResetOnSpawn выкл. │ └─ Frame (Frame) — Swordsman, Archer, Mage (TextButton) │ └─ ClassLocal (LocalScript) └─ ExpGui (ScreenGui) — ResetOnSpawn выкл. └─ ExpFrame (Frame) — ExpBar, ExpText, LevelText └─ ExpLocal (LocalScript)
Где: 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)
-- уровень
local lvl = Value("IntValue", "Lvl", 1, player)
-- опыт
local exp = Value("IntValue", "Exp", 0, player)
-- сколько опыта до следующего уровня
local required = Value("IntValue", "RequiredExp", 100, player)
-- опыт изменился
exp.Changed:Connect(function()
-- хватает на уровень (и даже на несколько)
while exp.Value >= required.Value do
-- лишний опыт не пропадает
exp.Value -= required.Value
-- новый уровень
lvl.Value += 1
-- следующий порог выше
required.Value = lvl.Value * 100
-- конец цикла
end
-- конец функции
end)
-- конец функции
end
-- новые игроки
Players.PlayerAdded:Connect(Setup)
-- кто зашёл раньше скрипта
for _, player in ipairs(Players:GetPlayers()) do task.spawn(Setup, player) endГде: StarterGui → ExpGui → ExpFrame · LocalScript
Зачем: Полоса опыта.
-- игроки
local Players = game:GetService("Players")
-- этот игрок
local player = Players.LocalPlayer
-- опыт
local exp = player:WaitForChild("Exp")
-- уровень
local lvl = player:WaitForChild("Lvl")
-- порог уровня
local required = player:WaitForChild("RequiredExp")
-- ExpFrame — подложка полосы
local frame = script.Parent
-- полоса опыта
local bar = frame:WaitForChild("ExpBar")
-- «40 / 100»
local expText = frame:WaitForChild("ExpText")
-- «Ур. 2»
local lvlText = frame:WaitForChild("LevelText")
-- обновить полосу и надписи
local function Update()
-- доля до следующего уровня
local k = math.clamp(exp.Value / required.Value, 0, 1)
-- плавно меняем длину
bar:TweenSize(UDim2.fromScale(k, 1), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.3, true)
-- числа
expText.Text = exp.Value .. " / " .. required.Value
-- уровень
lvlText.Text = "Ур. " .. lvl.Value
-- конец функции
end
-- опыт изменился
exp.Changed:Connect(Update)
-- новый уровень
lvl.Changed:Connect(Update)
-- новый порог
required.Changed:Connect(Update)
-- сразу при входе
Update()Где: StarterGui → ClassGui → Frame · LocalScript
Зачем: Окно выбора класса.
-- игроки
local Players = game:GetService("Players")
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- этот игрок
local player = Players.LocalPlayer
-- окно выбора класса
local frame = script.Parent
-- сообщить серверу выбор
local classEvent = RS:WaitForChild("ClassEvent")
-- показать окно, только если класса ещё нет
local function Update()
-- класс выбран (или загружен) — окно не нужно
frame.Visible = player:GetAttribute("Class") == nil
-- конец функции
end
-- все объекты окна
for _, button in ipairs(frame:GetChildren()) do
-- кнопки классов
if button:IsA("TextButton") then
-- нажали
button.MouseButton1Click:Connect(function()
-- прячем окно
frame.Visible = false
-- имя кнопки = имя класса
classEvent:FireServer(button.Name)
-- конец функции
end)
-- конец проверки
end
-- конец перебора
end
-- класс появился — окно прячется
player:GetAttributeChangedSignal("Class"):Connect(Update)
-- сразу
Update()Где: ServerScriptService · Script
Зачем: Класс, навыки и оружие.
-- игроки
local Players = game:GetService("Players")
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- образцы классов и оружия
local SS = game:GetService("ServerStorage")
-- выбор класса
local classEvent = RS:WaitForChild("ClassEvent")
-- папки навыков: Swordsman, Archer, Mage
local classes = SS:WaitForChild("Classes")
-- классовое оружие
local weapons = SS:WaitForChild("Weapons")
-- класс → его оружие
local WEAPON = {Swordsman = "Sword", Archer = "Bow", Mage = "Staff"}
-- папка навыков в персонажа
local function GiveSkills(player)
-- класс игрока
local className = player:GetAttribute("Class")
-- персонаж
local character = player.Character
-- нет класса, персонажа или уже выдано
if not className or not character or character:FindFirstChild(className) then return end
-- папка класса
local folder = classes:FindFirstChild(className)
-- навыки (локальные скрипты) работают в персонаже
if folder then folder:Clone().Parent = character end
-- конец функции
end
-- классовое оружие
local function GiveWeapon(player)
-- образец
local weapon = weapons:FindFirstChild(WEAPON[player:GetAttribute("Class")] or "")
-- предметы, которые Roblox выдаёт при каждом появлении
local gear = player:FindFirstChild("StarterGear")
-- нет оружия или уже выдано
if not weapon or not gear or gear:FindFirstChild(weapon.Name) then return end
-- будет у игрока после каждой смерти
weapon:Clone().Parent = gear
-- рюкзак — прямо сейчас
local backpack = player:FindFirstChildOfClass("Backpack")
-- в рюкзак
if backpack then weapon:Clone().Parent = backpack end
-- конец функции
end
-- игрок выбрал класс
classEvent.OnServerEvent:Connect(function(player, className)
-- класс выбирается один раз
if player:GetAttribute("Class") then return end
-- нет такого класса
if typeof(className) ~= "string" or not classes:FindFirstChild(className) then return end
-- запоминаем — дальше сработает подписка ниже
player:SetAttribute("Class", className)
-- конец функции
end)
-- игрок зашёл
local function Setup(player)
-- класс выбран или загружен из сохранения
player:GetAttributeChangedSignal("Class"):Connect(function()
-- навыки
GiveSkills(player)
-- оружие
GiveWeapon(player)
-- конец функции
end)
-- каждое появление
player.CharacterAdded:Connect(function(character)
-- ждём, пока персонаж соберётся
character:WaitForChild("Humanoid")
-- навыки снова (старые удалились вместе с персонажем)
GiveSkills(player)
-- конец функции
end)
-- класс загрузился раньше, чем мы подписались
if player:GetAttribute("Class") then
-- навыки
GiveSkills(player)
-- оружие
GiveWeapon(player)
-- конец проверки
end
-- конец функции
end
-- новые игроки
Players.PlayerAdded:Connect(Setup)
-- кто зашёл раньше скрипта
for _, player in ipairs(Players:GetPlayers()) do Setup(player) endЗапусти Play (или Run) и убедись: