Бег с выносливостью через контроллер
Shift или экранная кнопка тратит энергию; после отдыха можно снова ускоряться.
Ускорение тратит энергию, восстанавливается во время отдыха и не сбрасывает другие эффекты.
1. Подготовьте объект
Где разместить: ServerScriptService: StatsController, StaminaServer; StarterPlayerScripts: StaminaClient; RemoteEvent создаётся сервером.
ServerScriptService
StatsController (ModuleScript из практики 91)
StaminaServer (Script)
StarterPlayer
StarterPlayerScripts
StaminaClient (LocalScript)
ReplicatedStorage
IgroStaminaSprint (RemoteEvent, создаётся сервером)- Сначала установите StatsController из практики 91.
- В ServerScriptService создайте Script StaminaServer, вставьте первый блок.
- В StarterPlayer → StarterPlayerScripts создайте LocalScript StaminaClient, вставьте второй блок.
- Удалите или отключите SprintServer/SprintClient из практики 92: оба скрипта бега не должны работать одновременно.
- Нажмите Play. Удерживайте Shift (или экранную кнопку «Бег» на телефоне), чтобы расходовать энергию.
- При нулевой энергии ускорение выключается, а в покое запас восстанавливается.
- Проверьте гибель и возрождение, двух игроков и совместную работу с SpeedPad.
Это пути для скриптов из блоков кода ниже. Остальные объекты создайте по инструкции.
├── ServerScriptService
│ └── StaminaServer (Script)
└── StarterPlayer
└── StarterPlayerScripts
└── StaminaClient (LocalScript)2. Вставьте код
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Controller = require(script.Parent:WaitForChild("StatsController"))
local remote = ReplicatedStorage:FindFirstChild("IgroStaminaSprint")
if not remote then
remote = Instance.new("RemoteEvent")
remote.Name = "IgroStaminaSprint"
remote.Parent = ReplicatedStorage
end
local MAX = 100
local DRAIN = 22
local RECOVER = 14
local LEASE = 1.2 -- сервер выключит бег, если клиент перестал подтверждать удержание
local states = {}
local function setupPlayer(player)
local state = {energy = MAX, requested = false, active = false, lastCall = 0, lastKeepalive = 0, exhausted = false}
states[player] = state
player:SetAttribute("IgroStamina", MAX)
player.CharacterAdded:Connect(function()
state.energy = MAX
state.requested = false
state.active = false
state.exhausted = false
state.lastKeepalive = 0
player:SetAttribute("IgroStamina", MAX)
end)
end
Players.PlayerAdded:Connect(setupPlayer)
for _, player in ipairs(Players:GetPlayers()) do setupPlayer(player) end
Players.PlayerRemoving:Connect(function(player) states[player] = nil end)
remote.OnServerEvent:Connect(function(player, wantsSprint)
if typeof(wantsSprint) ~= "boolean" then return end
local state = states[player]
if not state then return end
local now = os.clock()
if wantsSprint and now - state.lastCall < 0.08 then return end
state.lastCall = now
state.requested = wantsSprint
if wantsSprint then
state.lastKeepalive = now
else
-- После полного истощения требуется отпустить и нажать кнопку заново.
state.exhausted = false
end
end)
local elapsed = 0
RunService.Heartbeat:Connect(function(dt)
elapsed += dt
if elapsed < 0.15 then return end
local step = math.min(elapsed, 0.5)
elapsed = 0
for player, state in pairs(states) do
local character = player.Character
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
local alive = humanoid and humanoid.Health > 0
local leaseValid = os.clock() - state.lastKeepalive <= LEASE
local wants = alive and state.requested and leaseValid and not state.exhausted and state.energy > 0
if wants then
state.energy = math.max(0, state.energy - DRAIN * step)
else
state.energy = math.min(MAX, state.energy + RECOVER * step)
end
local shouldRun = wants and state.energy > 0
if shouldRun and not state.active then
state.active = Controller.Apply(player, "StaminaSprint", {speed = 1.5})
elseif not shouldRun and state.active then
Controller.Remove(player, "StaminaSprint")
state.active = false
end
if state.energy <= 0 then
state.requested = false
state.exhausted = true
elseif not leaseValid then
state.requested = false
end
player:SetAttribute("IgroStamina", math.floor(state.energy + 0.5))
end
end)local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local remote = ReplicatedStorage:WaitForChild("IgroStaminaSprint")
local player = Players.LocalPlayer
local pressed = false
local function setPressed(value)
if pressed == value then return end
pressed = value
remote:FireServer(value)
end
local function sprintAction(_, inputState)
if inputState == Enum.UserInputState.Begin then
setPressed(true)
elseif inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
setPressed(false)
end
return Enum.ContextActionResult.Pass
end
ContextActionService:BindAction("IgroStaminaSprint", sprintAction, true,
Enum.KeyCode.LeftShift, Enum.KeyCode.ButtonL3)
ContextActionService:SetTitle("IgroStaminaSprint", "Бег")
ContextActionService:SetPosition("IgroStaminaSprint", UDim2.new(1, -100, 1, -190))
player.CharacterAdded:Connect(function()
pressed = false
remote:FireServer(false)
end)
UserInputService.WindowFocusReleased:Connect(function()
setPressed(false)
end)
-- Пока кнопка удерживается, обновляем запрос. Без обновлений сервер
-- отключает ускорение через 1,2 секунды: бег не застрянет из-за потери события.
task.spawn(function()
while true do
task.wait(0.35)
if pressed then remote:FireServer(true) end
end
end)
-- Простой индикатор энергии: создаётся только для локального игрока.
local gui = Instance.new("ScreenGui")
gui.Name = "IgroStaminaHud"
gui.ResetOnSpawn = false
gui.Parent = player:WaitForChild("PlayerGui")
local background = Instance.new("Frame")
background.Name = "Meter"
background.AnchorPoint = Vector2.new(0.5, 1)
background.Position = UDim2.new(0.5, 0, 1, -24)
background.Size = UDim2.new(0, 240, 0, 24)
background.BackgroundColor3 = Color3.fromRGB(38, 45, 62)
background.BorderSizePixel = 0
background.Parent = gui
local fill = Instance.new("Frame")
fill.Name = "Fill"
fill.Size = UDim2.fromScale(1, 1)
fill.BackgroundColor3 = Color3.fromRGB(80, 210, 150)
fill.BorderSizePixel = 0
fill.Parent = background
local label = Instance.new("TextLabel")
label.Name = "Value"
label.BackgroundTransparency = 1
label.Size = UDim2.fromScale(1, 1)
label.TextColor3 = Color3.new(1, 1, 1)
label.TextScaled = true
label.Font = Enum.Font.GothamBold
label.Parent = background
local function updateHud()
local energy = math.clamp(tonumber(player:GetAttribute("IgroStamina")) or 100, 0, 100)
fill.Size = UDim2.fromScale(energy / 100, 1)
label.Text = string.format("Энергия: %d%%", energy)
end
player:GetAttributeChangedSignal("IgroStamina"):Connect(updateHud)
updateHud()3. Проверьте в игре
Нажмите Play в Roblox Studio и проверьте действие механики. Если механика не работает, откройте окно Output и проверьте тип скрипта, его расположение и названия объектов.
Когда проверите механику в Roblox Studio, отметьте практику как выполненную. Отметка хранится только в этом браузере, не в личном кабинете.
Добавляйте механики по одной и проверяйте окно Output. Не создавайте несколько одинаковых систем монет или интерфейсов; скрипты, меняющие WalkSpeed, прыжок или освещение, могут влиять друг на друга.
Поменяйте расход энергии с 22 на 15 единиц в секунду.
Добавьте индикатор энергии на экран, используя атрибут IgroStamina.
Создайте гонку с зонами отдыха и ограниченным запасом сил.
4. Проверьте себя
Почему энергия расходуется в серверном Script?
Отключите предыдущий SprintServer/SprintClient из практики 92. Если бег не включается, проверьте, что StatsController уже инициализировал персонажа. При нулевой энергии нужно отпустить и снова нажать кнопку после восстановления.