Roblox · Интерфейсы
Окно «Настройки»: громкость музыки, громкость звуков, угол обзора камеры. Кнопки «−» и «+», всё применяется сразу и сохраняется до следующего захода.
Синим — что нужно создать.
ReplicatedStorage └─ SettingsEvent (RemoteEvent) SoundService └─ Music, Effects (SoundGroup) — группы звуков ServerScriptService └─ SettingsServer (Script) StarterGui └─ SettingsGui (ScreenGui) — ResetOnSpawn выкл. ├─ OpenButton (TextButton) — справа вверху ├─ Window (Frame) — Visible выкл. │ ├─ Music, Effects, FOV (Frame) — Caption, Minus, Value, Plus │ └─ CloseButton (TextButton) └─ SettingsLocal (LocalScript)
Где: ServerScriptService · Script
Зачем: Загрузка, проверка и сохранение настроек.
-- игроки
local Players = game:GetService("Players")
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- сохранение
local DSS = game:GetService("DataStoreService")
-- игрок меняет настройку
local event = RS:WaitForChild("SettingsEvent")
-- хранилище настроек
local store = DSS:GetDataStore("PlayerSettings")
-- значения по умолчанию
local DEFAULTS = {Music = 5, Effects = 5, FOV = 70}
-- границы: от и до
local LIMITS = {Music = {0, 10}, Effects = {0, 10}, FOV = {60, 100}}
-- у кого загрузка удалась
local loaded = {}
-- игрок зашёл
local function Setup(player)
-- загружаем
local ok, data = pcall(function() return store:GetAsync("settings_" .. player.UserId) end)
-- ничего нет — пусто
if type(data) ~= "table" then data = {} end
-- каждая настройка
for name, value in pairs(DEFAULTS) do
-- сохранённое значение
local saved = data[name]
-- атрибут игрока — его видит и клиент
player:SetAttribute(name, type(saved) == "number" and saved or value)
-- конец перебора
end
-- можно сохранять, только если загрузилось
loaded[player] = ok
-- конец функции
end
-- сохранить
local function Save(player)
-- не загрузилось — не затираем
if not loaded[player] then return end
-- что сохраняем
local data = {}
-- все настройки
for name in pairs(DEFAULTS) do data[name] = player:GetAttribute(name) end
-- сохраняем
pcall(function() store:SetAsync("settings_" .. player.UserId, data) end)
-- конец функции
end
-- игрок нажал «+» или «−»
event.OnServerEvent:Connect(function(player, name, value)
-- есть такая настройка?
local limit = LIMITS[name]
-- чужое имя, не число или NaN
if not limit or type(value) ~= "number" or value ~= value then return end
-- в границах, целое
player:SetAttribute(name, math.clamp(math.floor(value + 0.5), limit[1], limit[2]))
-- конец функции
end)
-- новые игроки
Players.PlayerAdded:Connect(Setup)
-- кто зашёл раньше скрипта
for _, player in ipairs(Players:GetPlayers()) do task.spawn(Setup, player) end
-- игрок вышел
Players.PlayerRemoving:Connect(function(player)
-- сохраняем
Save(player)
-- забываем
loaded[player] = nil
-- конец функции
end)
-- сервер закрывается
game:BindToClose(function()
-- сохраняем всех
for _, player in ipairs(Players:GetPlayers()) do Save(player) end
-- конец функции
end)Где: StarterGui → SettingsGui · LocalScript
Зачем: Окно настроек.
-- игроки
local Players = game:GetService("Players")
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- группы звуков
local SoundService = game:GetService("SoundService")
-- этот игрок
local player = Players.LocalPlayer
-- сообщить серверу
local event = RS:WaitForChild("SettingsEvent")
-- SettingsGui
local gui = script.Parent
-- окно настроек
local window = gui:WaitForChild("Window")
-- от, до, шаг кнопки
local STEPS = {Music = {0, 10, 1}, Effects = {0, 10, 1}, FOV = {60, 100, 5}}
-- применить настройки
local function Apply()
-- группа музыки
local music = SoundService:FindFirstChild("Music")
-- группа эффектов
local effects = SoundService:FindFirstChild("Effects")
-- громкость музыки 0…1 — только у этого игрока
if music then music.Volume = (player:GetAttribute("Music") or 5) / 10 end
-- громкость звуков
if effects then effects.Volume = (player:GetAttribute("Effects") or 5) / 10 end
-- угол обзора
workspace.CurrentCamera.FieldOfView = player:GetAttribute("FOV") or 70
-- каждая строка окна
for name in pairs(STEPS) do
-- число в строке
window[name].Value.Text = tostring(player:GetAttribute(name) or "")
-- конец перебора
end
-- конец функции
end
-- каждая настройка
for name, step in pairs(STEPS) do
-- её строка
local row = window:WaitForChild(name)
-- нажали «−» или «+»
local function Change(direction)
-- сейчас
local current = player:GetAttribute(name) or step[1]
-- новое значение — серверу
event:FireServer(name, math.clamp(current + direction * step[3], step[1], step[2]))
-- конец функции
end
-- «−»
row:WaitForChild("Minus").Activated:Connect(function() Change(-1) end)
-- «+»
row:WaitForChild("Plus").Activated:Connect(function() Change(1) end)
-- сервер принял — применяем
player:GetAttributeChangedSignal(name):Connect(Apply)
-- конец перебора
end
-- закрыть
window:WaitForChild("CloseButton").Activated:Connect(function() window.Visible = false end)
-- кнопка настроек
gui:WaitForChild("OpenButton").Activated:Connect(function() window.Visible = not window.Visible end)
-- сразу при входе
Apply()Запусти Play (или Run) и убедись: