Roblox · Гонки · урок 4

Таймер и победитель

Таймер сверху экрана считает время гонки. Кто первым проехал последний чекпоинт, тот победил: в конце всем показывается имя победителя и его время. Если время вышло, итог показывается всё равно.

Структура

Синим — что добавляем на этом уроке.

ReplicatedStorage
├─ Values (Folder)
│  ├─ Minutes (IntValue) — 0
│  ├─ Seconds (IntValue) — 0
│  └─ MaxMinutes (IntValue) — сколько минут длится гонка, например 5
└─ WinEvent (RemoteEvent)
ServerScriptService
├─ TimerServer (Script)
└─ WinServer (Script)
StarterGui
├─ TimerGui (ScreenGui)
│  └─ Frame (Frame) — вверху по центру
│     └─ TextLabel (TextLabel)
│        └─ TimerLocal (LocalScript)
└─ WinGui (ScreenGui)
   └─ Frame (Frame) — по центру экрана
      ├─ Player (TextLabel)
      ├─ Time (TextLabel)
      └─ WinLocal (LocalScript)

Шаги

  1. ReplicatedStorage → «+» → Folder «Values», внутри три IntValue: Minutes = 0, Seconds = 0, MaxMinutes = 5 (длительность гонки).
  2. ReplicatedStorage → «+» → RemoteEvent «WinEvent».
  3. StarterGui → «+» → ScreenGui «TimerGui» → Frame вверху по центру → TextLabel (крупный шрифт). В TextLabel «+» → LocalScript «TimerLocal».
  4. StarterGui → «+» → ScreenGui «WinGui» → Frame по центру → два TextLabel: «Player» и «Time». В Frame «+» → LocalScript «WinLocal».
  5. ServerScriptService → «+» → Script «TimerServer» и Script «WinServer». Тексты всех скриптов — в «Скриптах».
  6. Как работает: TimerServer прибавляет секунды в Values, TimerLocal показывает их на экране. WinServer ждёт, когда счётчик чекпоинтов игрока станет равен числу чекпоинтов (финиш), запоминает время и сравнивает с лучшим. Доехали все или вышло время — WinEvent показывает итог всем.
  7. Время хранится двумя способами: текстом «01 : 05» — чтобы показать, и числом секунд — чтобы сравнивать, кто быстрее.

Скрипты

TimerServer

Где: ServerScriptService · Script
Зачем: Считает время гонки и останавливается, когда оно вышло.

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- прошло минут
local minutes = RS.Values:WaitForChild("Minutes")
-- прошло секунд
local seconds = RS.Values:WaitForChild("Seconds")
-- сколько минут длится гонка
local maxMinutes = RS.Values:WaitForChild("MaxMinutes")

-- каждую секунду
while task.wait(1) do
    -- плюс секунда
    seconds.Value += 1
    -- набралась минута
    if seconds.Value > 59 then
        -- секунды с нуля
        seconds.Value = 0
        -- плюс минута
        minutes.Value += 1
        -- время гонки вышло — таймер стоп
        if minutes.Value >= maxMinutes.Value then break end
    -- конец проверки
    end
-- конец цикла
end

TimerLocal

Где: StarterGui → TimerGui → Frame → TextLabel · LocalScript
Зачем: Показывает время на экране.

-- надпись таймера
local text = script.Parent
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- минуты
local minutes = RS.Values:WaitForChild("Minutes")
-- секунды
local seconds = RS.Values:WaitForChild("Seconds")

-- обновить надпись
local function Update()
    -- всегда две цифры: 01 : 05
    text.Text = string.format("%02d : %02d", minutes.Value, seconds.Value)
-- конец функции
end

-- показываем сразу
Update()
-- минуты поменялись — обновляем
minutes.Changed:Connect(Update)
-- секунды поменялись — обновляем
seconds.Changed:Connect(Update)

WinServer

Где: ServerScriptService · Script
Зачем: Записывает время финиша, находит лучшее и показывает итог всем.

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- минуты таймера
local minutes = RS.Values:WaitForChild("Minutes")
-- секунды таймера
local seconds = RS.Values:WaitForChild("Seconds")
-- длительность гонки
local maxMinutes = RS.Values:WaitForChild("MaxMinutes")
-- событие «гонка окончена»
local winEvent = RS:WaitForChild("WinEvent")
-- последний чекпоинт = финиш
local amountOfCheckpoints = #workspace.Checkpoints:GetChildren()

-- лучшее время в секундах (пока — всё время гонки)
local bestSeconds = maxMinutes.Value * 60
-- лучшее время текстом
local bestTime = "—"
-- кто победил
local bestPlayer = "никто"
-- сколько игроков в гонке
local racers = 0
-- сколько доехали
local finished = 0
-- итог уже показан?
local over = false

-- показать победителя всем
local function ShowResult()
    -- уже показали
    if over then return end
    -- больше не показываем
    over = true
    -- имя и время — всем игрокам
    winEvent:FireAllClients(bestPlayer, bestTime)
-- конец функции
end

-- игрок зашёл
game.Players.PlayerAdded:Connect(function(player)
    -- ещё один участник
    racers += 1
    -- таблица игроков
    local leaderstats = Instance.new("Folder")
    -- имя, которое понимает Roblox
    leaderstats.Name = "leaderstats"
    -- в игрока
    leaderstats.Parent = player
    -- время игрока текстом
    local timeValue = Instance.new("StringValue")
    -- колонка в таблице
    timeValue.Name = "Best Time"
    -- ещё не финишировал
    timeValue.Value = "—"
    -- в таблицу
    timeValue.Parent = leaderstats
    -- счётчик чекпоинтов (создаёт CheckpointsServer)
    local index = player:WaitForChild("Index")
    -- игрок прошёл чекпоинт
    index.Changed:Connect(function(newValue)
        -- ещё не финиш
        if newValue ~= amountOfCheckpoints then return end
        -- доехал
        finished += 1
        -- его время в секундах — так удобно сравнивать
        local total = minutes.Value * 60 + seconds.Value
        -- его время текстом
        timeValue.Value = string.format("%02d : %02d", minutes.Value, seconds.Value)
        -- быстрее всех
        if total <= bestSeconds then
            -- новый рекорд
            bestSeconds = total
            -- лидер
            bestPlayer = player.Name
            -- время лидера
            bestTime = timeValue.Value
        -- конец проверки
        end
        -- доехали все — итог
        if finished >= racers then ShowResult() end
    -- конец обработчика
    end)
-- конец обработчика
end)

-- игрок вышел из игры
game.Players.PlayerRemoving:Connect(function()
    -- участников меньше
    racers -= 1
    -- все оставшиеся уже доехали — итог
    if racers > 0 and finished >= racers then ShowResult() end
-- конец обработчика
end)

-- прошла минута
minutes.Changed:Connect(function(newValue)
    -- время вышло — итог для всех
    if newValue >= maxMinutes.Value then ShowResult() end
-- конец обработчика
end)

WinLocal

Где: StarterGui → WinGui → Frame · LocalScript
Зачем: Показывает окно с победителем и временем.

-- окно с итогом
local frame = script.Parent
-- надпись с именем
local playerText = frame:WaitForChild("Player")
-- надпись со временем
local timeText = frame:WaitForChild("Time")
-- событие «гонка окончена»
local winEvent = game.ReplicatedStorage:WaitForChild("WinEvent")

-- до финиша окно скрыто
frame.Visible = false

-- сервер прислал итог
winEvent.OnClientEvent:Connect(function(playerName, bestTime)
    -- показываем окно
    frame.Visible = true
    -- кто победил
    playerText.Text = "Победитель: " .. playerName
    -- за сколько
    timeText.Text = "Время: " .. bestTime
-- конец обработчика
end)

Проверка

Запусти Play (или Run) и убедись: