Roblox · 2D Раннер · урок 3
Сервер считает, сколько метров пробежал игрок от старта, и запоминает рекорд. Метры и рекорд видны на экране и в таблице игроков по Tab — у каждого свои.
Синим — что добавляем на этом уроке.
ReplicatedStorage └─ Events (Folder) — все события └─ GameStateEvent (RemoteEvent) ServerScriptService └─ ScoreServer (Script) StarterPlayer └─ StarterPlayerScripts └─ ControlScript (LocalScript) — дополняем StarterGui └─ ScoreGui (ScreenGui) ├─ Score (TextLabel) ├─ Highscore (TextLabel) └─ ScoreLocal (LocalScript)
Где: StarterPlayer → StarterPlayerScripts · LocalScript
Зачем: Как в прошлом уроке, плюс сообщения серверу о старте и конце забега.
-- игрок на этом компьютере
local player = game.Players.LocalPlayer
-- события каждого кадра
local RunService = game:GetService("RunService")
-- клавиатура и экран
local UIS = game:GetService("UserInputService")
-- стандартные скрипты управления
local PlayerModule = require(player:WaitForChild("PlayerScripts"):WaitForChild("PlayerModule"))
-- событие «забег начался / закончился» для сервера
local GameStateEvent = game.ReplicatedStorage:WaitForChild("Events"):WaitForChild("GameStateEvent")
-- персонаж, которым управляем
local humanoid = nil
-- идёт ли забег
local gameStarted = false
-- выключаем WASD и стандартный прыжок — управляем сами
PlayerModule:GetControls():Disable()
-- забег окончен
local function StopRun()
-- больше не бежим
gameStarted = false
-- сообщаем серверу: стоп
GameStateEvent:FireServer(false)
-- перестаём толкать персонажа вперёд
RunService:UnbindFromRenderStep("Move")
-- конец функции
end
-- новый персонаж
local function Setup(character)
-- запоминаем его
humanoid = character:WaitForChild("Humanoid")
-- стоит на месте, пока не нажали старт
character:WaitForChild("HumanoidRootPart").Anchored = true
-- погиб — забег окончен
humanoid.Died:Connect(StopRun)
-- конец функции
end
-- каждый новый персонаж
player.CharacterAdded:Connect(Setup)
-- персонаж уже появился до запуска скрипта
if player.Character then Setup(player.Character) end
-- нажали клавишу или тапнули — подписка одна на всю игру
UIS.InputBegan:Connect(function(input, gp)
-- нажатие ушло в чат или на кнопку интерфейса
if gp then return end
-- пробел или тап по экрану телефона
local pressed = input.KeyCode == Enum.KeyCode.Space or input.UserInputType == Enum.UserInputType.Touch
-- другая клавиша
if not pressed then return end
-- персонажа нет или он погиб
if not humanoid or humanoid.Health <= 0 then return end
-- уже бежим
if gameStarted then
-- прыжок
humanoid.Jump = true
-- ещё стоим
else
-- старт
gameStarted = true
-- сообщаем серверу: побежали
GameStateEvent:FireServer(true)
-- отпускаем персонажа
humanoid.Parent.HumanoidRootPart.Anchored = false
-- каждый кадр
RunService:BindToRenderStep("Move", Enum.RenderPriority.Character.Value, function()
-- бежим по оси X в сторону минуса
humanoid:Move(Vector3.new(-1, 0, 0), false)
-- конец функции кадра
end)
-- конец проверки
end
-- конец обработчика
end)Где: ServerScriptService · Script
Зачем: Считает метры каждого игрока и обновляет рекорд.
-- старт и стоп от игроков
local GameStateEvent = game.ReplicatedStorage:WaitForChild("Events"):WaitForChild("GameStateEvent")
-- текущий забег каждого игрока
local runs = {}
-- игрок зашёл
game.Players.PlayerAdded:Connect(function(player)
-- его таблица очков
local leaderstats = Instance.new("Folder")
-- имя, которое понимает Roblox — видно по Tab
leaderstats.Name = "leaderstats"
-- в игрока
leaderstats.Parent = player
-- метры в этом забеге
local score = Instance.new("IntValue")
-- имя
score.Name = "Score"
-- в таблицу
score.Parent = leaderstats
-- лучший результат
local highscore = Instance.new("IntValue")
-- имя
highscore.Name = "Highscore"
-- в таблицу
highscore.Parent = leaderstats
-- конец обработчика
end)
-- игрок вышел
game.Players.PlayerRemoving:Connect(function(player)
-- его забег окончен
runs[player] = nil
-- конец обработчика
end)
-- игрок прислал старт (true) или стоп (false)
GameStateEvent.OnServerEvent:Connect(function(player, started)
-- стоп
if not started then
-- забег окончен — цикл ниже остановится
runs[player] = nil
-- больше ничего не делаем
return
-- конец проверки
end
-- уже бежит — второй подсчёт не нужен
if runs[player] then return end
-- центр персонажа
local hrp = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
-- персонажа нет
if not hrp then return end
-- его метры
local score = player.leaderstats.Score
-- его рекорд
local highscore = player.leaderstats.Highscore
-- метка этого забега
local run = {}
-- запоминаем
runs[player] = run
-- откуда стартовали
local startX = hrp.Position.X
-- новый забег — с нуля
score.Value = 0
-- пока идёт именно этот забег
while runs[player] == run do
-- сколько пробежали от старта (бежим в сторону минуса X)
score.Value = math.max(0, math.floor(startX - hrp.Position.X))
-- дальше рекорда
if score.Value > highscore.Value then
-- новый рекорд
highscore.Value = score.Value
-- конец проверки
end
-- 10 раз в секунду
task.wait(0.1)
-- конец цикла
end
-- конец обработчика
end)Где: StarterGui → ScoreGui · LocalScript
Зачем: Показывает метры и рекорд на экране.
-- игрок на этом компьютере
local player = game.Players.LocalPlayer
-- его таблица (создаёт ScoreServer)
local leaderstats = player:WaitForChild("leaderstats")
-- метры
local score = leaderstats:WaitForChild("Score")
-- рекорд
local highscore = leaderstats:WaitForChild("Highscore")
-- надпись с метрами
local scoreText = script.Parent:WaitForChild("Score")
-- надпись с рекордом
local highscoreText = script.Parent:WaitForChild("Highscore")
-- обновить обе надписи
local function Update()
-- метры
scoreText.Text = score.Value .. " м"
-- рекорд
highscoreText.Text = "Рекорд: " .. highscore.Value .. " м"
-- конец функции
end
-- показываем сразу, не дожидаясь первого изменения
Update()
-- метры поменялись — обновляем
score.Changed:Connect(Update)
-- рекорд поменялся — обновляем
highscore.Changed:Connect(Update)Запусти Play (или Run) и убедись: