Roblox · Обби · урок 7

Общий: вся полоса целиком

Итог курса на одной странице: полная структура обби и финальные версии всех скриптов. Удобно сверить свою игру или собрать полосу заново.

Структура

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

Workspace
├─ SpawnLocation (SpawnLocation)
├─ Obby (Model) — вся полоса
│  ├─ Start, Platform… (Part) — платформы, Anchored
│  ├─ Lava (Part) — лава под полосой — KillScript
│  ├─ KillBrick (Part) — KillScript
│  ├─ Step (Part) — DisappearScript
│  ├─ MovingPlatform (Part) — MoveScript
│  ├─ SpeedBrick, NormalBrick (Part) — SpeedScript, NormalSpeedScript
│  ├─ Conveyor (Part) — ConveyorScript
│  ├─ SpinBase → Motor, SpinBar (Part) — вращение на HingeConstraint
│  └─ PendulumTop → Rope, Ball (Part) — маятник; в Ball — ResetScript
├─ Checkpoints (Folder)
│  └─ 0, 1, 2, 3, 4, 5 (Part)
├─ Teleport1 (Model) — Glass → TeleportScript
└─ Teleport2 (Model) — Glass — финиш наверху
ServerScriptService
└─ Checkpoints (Script)

Шаги

  1. Сверяй сверху вниз: сначала Explorer со «Структурой», потом каждый скрипт целиком.
  2. Все платформы — Anchored вкл. Исключения: MovingPlatform (скрипт сам отпускает её), SpinBar, Ball и Pad пружины.
  3. Чекпоинты — папка Workspace.Checkpoints, имена строго числами 0, 1, 2… без пропусков.
  4. Передние грани решают направление: у MovingPlatform — куда ехать, у Conveyor — куда тянуть, у Glass второго портала — куда выйти.
  5. Одинаковые скрипты (KillScript, DisappearScript) копируются вместе с деталью — Ctrl+D.

Скрипты

Checkpoints

Где: ServerScriptService · Script
Зачем: Этапы и появление на чекпоинте.

-- сервис игроков
local Players = game:GetService("Players")
-- папка с чекпоинтами 0, 1, 2…
local checkpoints = workspace:WaitForChild("Checkpoints")

-- игрок зашёл
Players.PlayerAdded:Connect(function(player)
    -- таблица по Tab
    local leaderstats = Instance.new("Folder")
    -- имя строго такое
    leaderstats.Name = "leaderstats"
    -- в игрока
    leaderstats.Parent = player
    -- номер этапа
    local stage = Instance.new("IntValue")
    -- столбец «Stage»
    stage.Name = "Stage"
    -- в таблицу
    stage.Parent = leaderstats

    -- персонаж появился (и после каждой смерти)
    player.CharacterAdded:Connect(function(character)
        -- Humanoid персонажа
        local humanoid = character:WaitForChild("Humanoid")
        -- ждём, пока персонаж соберётся
        character:WaitForChild("HumanoidRootPart")
        -- чекпоинт текущего этапа
        local point = checkpoints:FindFirstChild(tostring(stage.Value))
        -- нашёлся
        if point then
            -- даём Roblox закончить появление на SpawnLocation
            task.wait(0.1)
            -- ставим над чекпоинтом
            character:PivotTo(point.CFrame + Vector3.new(0, 3, 0))
        -- конец проверки
        end
        -- персонаж чего-то коснулся
        humanoid.Touched:Connect(function(hit)
            -- это следующий по номеру чекпоинт
            if hit.Parent == checkpoints and tonumber(hit.Name) == stage.Value + 1 then
                -- этап пройден
                stage.Value += 1
            -- конец проверки
            end
        -- конец функции
        end)
    -- конец функции
    end)
-- конец функции
end)

KillScript

Где: в каждом KillBrick и в Lava · Script
Зачем: Убивает при касании.

-- деталь, в которой лежит скрипт
local part = script.Parent

-- когда что-то касается детали
part.Touched:Connect(function(hit)
    -- Humanoid есть только у персонажей
    local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
    -- коснулся персонаж
    if humanoid then
        -- здоровье в ноль — игрок погибает
        humanoid.Health = 0
    -- конец проверки
    end
-- конец функции
end)

SpeedScript

Где: SpeedBrick · Script
Зачем: Скорость 25.

-- деталь со скриптом
local part = script.Parent
-- новая скорость; обычная — 16
local SPEED = 25

-- касание
part.Touched:Connect(function(hit)
    -- ищем Humanoid
    local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
    -- это персонаж
    if humanoid then
        -- меняем скорость ходьбы
        humanoid.WalkSpeed = SPEED
    -- конец проверки
    end
-- конец функции
end)

NormalSpeedScript

Где: NormalBrick · Script
Зачем: Скорость 16.

-- деталь со скриптом
local part = script.Parent
-- обычная скорость Roblox
local SPEED = 16

-- касание
part.Touched:Connect(function(hit)
    -- ищем Humanoid
    local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
    -- это персонаж
    if humanoid then
        -- меняем скорость ходьбы
        humanoid.WalkSpeed = SPEED
    -- конец проверки
    end
-- конец функции
end)

DisappearScript

Где: в каждой Step · Script
Зачем: Исчезающая ступень.

-- ступенька
local part = script.Parent
-- ступень уже исчезает — повторные касания не считаем
local busy = false

-- касание
part.Touched:Connect(function(hit)
    -- уже исчезает — выходим
    if busy then return end
    -- исчезает только от игрока
    if not hit.Parent:FindFirstChildOfClass("Humanoid") then return end
    -- занято
    busy = true
    -- 20 шагов
    for i = 1, 20 do
        -- с каждым шагом прозрачнее
        part.Transparency = i / 20
        -- всего за 1 секунду
        task.wait(0.05)
    -- конец цикла
    end
    -- сквозь ступень можно упасть
    part.CanCollide = false
    -- 2 секунды её нет
    task.wait(2)
    -- снова видна
    part.Transparency = 0
    -- снова твёрдая
    part.CanCollide = true
    -- готова к следующему игроку
    busy = false
-- конец функции
end)

MoveScript

Где: MovingPlatform · Script
Зачем: Движущаяся платформа.

-- платформа
local platform = script.Parent
-- сколько studs проезжает
local DISTANCE = 14
-- за сколько секунд
local TIME = 3

-- откуда
local start = platform.Position
-- куда: вперёд по передней грани (Front)
local finish = start + platform.CFrame.LookVector * DISTANCE

-- точка, за которую тянем
local attachment = Instance.new("Attachment")
-- в центре платформы
attachment.Parent = platform
-- сила, которая тянет деталь в точку
local align = Instance.new("AlignPosition")
-- тянем к координате, а не к другой детали
align.Mode = Enum.PositionAlignmentMode.OneAttachment
-- кого тянем
align.Attachment0 = attachment
-- сила без предела — игрок её не остановит
align.MaxForce = math.huge
-- скорость: путь за TIME секунд
align.MaxVelocity = DISTANCE / TIME
-- быстро реагирует
align.Responsiveness = 50
-- пока стоим на месте
align.Position = start
-- в платформу
align.Parent = platform
-- держит платформу ровно
local turn = Instance.new("AlignOrientation")
-- к заданному повороту
turn.Mode = Enum.OrientationAlignmentMode.OneAttachment
-- кого держим
turn.Attachment0 = attachment
-- жёстко, без раскачки
turn.RigidityEnabled = true
-- поворот как сейчас
turn.CFrame = platform.CFrame.Rotation
-- в платформу
turn.Parent = platform

-- теперь её двигает физика — игрок едет вместе с ней
platform.Anchored = false
-- физику платформы считает сервер — без рывков
platform:SetNetworkOwner(nil)

-- бесконечно туда-обратно
while true do
    -- едем вперёд
    align.Position = finish
    -- доехали и стоим секунду
    task.wait(TIME + 1)
    -- едем обратно
    align.Position = start
    -- доехали и стоим
    task.wait(TIME + 1)
-- конец цикла
end

ConveyorScript

Где: Conveyor · Script
Зачем: Лента.

-- лента (Anchored вкл.)
local part = script.Parent
-- скорость ленты; игрок ходит со скоростью 16
local SPEED = 12

-- закреплённая деталь не едет, но везёт всех, кто на ней
part.AssemblyLinearVelocity = part.CFrame.LookVector * SPEED

ResetScript

Где: Ball (и Pad пружины) · Script
Зачем: Возврат на место раз в 30 секунд.

-- маятник или площадка пружины
local part = script.Parent
-- раз в сколько секунд возвращаем на место
local RESET_TIME = 30
-- место и поворот в начале игры
local start = part.CFrame

-- бесконечно
while true do
    -- ждём
    task.wait(RESET_TIME)
    -- гасим скорость
    part.AssemblyLinearVelocity = Vector3.zero
    -- гасим вращение
    part.AssemblyAngularVelocity = Vector3.zero
    -- ставим на старое место — маятник снова раскачивается
    part.CFrame = start
-- конец цикла
end

TeleportScript

Где: Teleport1 → Glass · Script
Зачем: Портал к финишу.

-- стекло портала
local glass = script.Parent
-- куда ведёт: имя второго портала в Workspace
local TARGET = "Teleport2"
-- стекло второго портала
local target = workspace:WaitForChild(TARGET):WaitForChild("Glass")

-- касание
glass.Touched:Connect(function(hit)
    -- модель персонажа
    local character = hit.Parent
    -- это не персонаж — выходим
    if not character:FindFirstChildOfClass("Humanoid") then return end
    -- когда телепортировался в прошлый раз
    local last = character:GetAttribute("Teleported") or 0
    -- меньше 2 секунд назад — не бросаем обратно
    if os.clock() - last < 2 then return end
    -- запоминаем время
    character:SetAttribute("Teleported", os.clock())
    -- ставим в 5 studs перед вторым порталом
    character:PivotTo(target.CFrame + target.CFrame.LookVector * 5)
-- конец функции
end)

Проверка

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

Собрать всю полосу одной командой