Roblox · Doors · урок 5

Прятки в шкафу

Шкаф, в котором можно спрятаться: «Спрятаться» — двери открываются, персонаж внутри, двери закрываются. Сквозь щель видно комнату. «Выйти» — обратно.

Структура

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

ServerStorage
├─ FurnitureSamples (Folder)
│  └─ Closet (Model) — PrimaryPart = Primary
│     ├─ Primary (Part) — покрывает шкаф: прозрачная, CanCollide и CanQuery выкл.
│     ├─ Frame… (Part) — каркас, Anchored
│     ├─ Barrier (Part) — перед дверями: прозрачный, Anchored, CanCollide вкл.
│     ├─ Outside (Part) — перед барьером, лицом к шкафу; прозрачный, CanCollide выкл.
│     ├─ Inside (Part) — внутри, лицом наружу; прозрачный, CanCollide выкл.
│     ├─ LeftSpinner, RightSpinner (Part) — петли, Anchored
│     └─ LeftDoor, RightDoor (Part) — двери, приварены к своим петлям
└─ RoomSamples → комната → Furniture (Folder)
   └─ Closet (Part) — локатор шкафа
ServerScriptService
└─ MainScript → Room → Furniture (ModuleScript)
   └─ Closet (ModuleScript)

Шаги

  1. Каркас шкафа. Перед ним — «Barrier»: прозрачный, Anchored, CanCollide вкл. — не выпускает персонажа и закрывает его от монстра.
  2. «Outside» — перед барьером, передней гранью к шкафу; «Inside» — внутри, развёрнут на 180° (лицом наружу). Обе: прозрачные, CanCollide выкл., Anchored, на высоте центра персонажа (~3 studs над полом).
  3. Две двери как у двери между комнатами: «LeftSpinner» и «RightSpinner» (Anchored), к каждой приварена своя дверь (Anchored выкл.). Между дверями — щель.
  4. Прозрачная «Primary» вокруг шкафа (CanCollide и CanQuery выкл.), модель «Closet», PrimaryPart = Primary → в FurnitureSamples.
  5. Локаторы «Closet» поставь в комнатах (в папки Furniture) — в каждой комнате хотя бы один: от монстра надо успеть спрятаться.
  6. В Furniture — ModuleScript «Closet». Furniture ставит шкаф через свой модуль, остальное — как раньше.
  7. Двери открываются внутрь — поменяй знак у 100 в closet.MoveSpinner.

Скрипты

Closet

Где: ServerScriptService → MainScript → Room → Furniture · ModuleScript
Зачем: Шкаф: войти, выйти, один игрок внутри; погиб — шкаф освобождается.

-- плавное движение дверей
local TS = game:GetService("TweenService")
-- здесь лежит образец шкафа
local SS = game:GetService("ServerStorage")
-- обычная скорость и прыжок персонажа
local SP = game:GetService("StarterPlayer")
-- таблица модуля
local closet = {}

-- поворачивает одну петлю
function closet.MoveSpinner(spinner, direction)
    -- на 100° в нужную сторону
    local cframe = spinner.CFrame * CFrame.Angles(0, math.rad(100 * direction), 0)
    -- за полсекунды
    TS:Create(spinner, TweenInfo.new(0.5), {CFrame = cframe}):Play()
-- конец функции
end

-- обе двери: 1 — открыть, −1 — закрыть
function closet.MoveDoors(model, direction)
    -- левая
    closet.MoveSpinner(model.LeftSpinner, direction)
    -- правая — в зеркальную сторону
    closet.MoveSpinner(model.RightSpinner, -direction)
    -- ждём, пока повернутся
    task.wait(0.5)
-- конец функции
end

-- запретить или вернуть ходьбу и прыжок
local function Freeze(humanoid, frozen)
    -- скорость
    humanoid.WalkSpeed = frozen and 0 or SP.CharacterWalkSpeed
    -- прыжок (если в игре JumpPower)
    humanoid.JumpPower = frozen and 0 or SP.CharacterJumpPower
    -- прыжок (если в игре JumpHeight)
    humanoid.JumpHeight = frozen and 0 or SP.CharacterJumpHeight
-- конец функции
end

-- игрок прячется
function closet.Enter(player, model)
    -- его персонаж
    local character = player.Character
    -- Humanoid
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    -- персонажа нет или погиб
    if not humanoid or humanoid.Health <= 0 then return false end
    -- шкаф занят
    model.HasPlayer.Value = player
    -- стоит на месте
    Freeze(humanoid, true)
    -- открываем двери
    closet.MoveDoors(model, 1)
    -- переносим внутрь
    character:PivotTo(model.Inside.CFrame)
    -- закрываем
    closet.MoveDoors(model, -1)
    -- спрятался
    return true
-- конец функции
end

-- игрок выходит
function closet.Exit(player, model)
    -- его персонаж
    local character = player.Character
    -- Humanoid
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    -- открываем двери
    closet.MoveDoors(model, 1)
    -- персонаж на месте
    if humanoid then
        -- переносим наружу
        character:PivotTo(model.Outside.CFrame)
        -- снова может ходить
        Freeze(humanoid, false)
    -- конец проверки
    end
    -- секунду двери открыты
    task.wait(1)
    -- закрываем
    closet.MoveDoors(model, -1)
    -- шкаф свободен
    model.HasPlayer.Value = nil
-- конец функции
end

-- ставит шкаф на место локатора
function closet.New(sample)
    -- копия образца
    local model = SS.FurnitureSamples.Closet:Clone()
    -- на место локатора
    model:PivotTo(sample.CFrame)
    -- в комнату
    model.Parent = sample.Parent
    -- локатор больше не нужен
    sample:Destroy()
    -- кто сейчас внутри
    local hasPlayer = Instance.new("ObjectValue")
    -- имя
    hasPlayer.Name = "HasPlayer"
    -- в шкаф
    hasPlayer.Parent = model
    -- надпись снаружи
    local outsidePrompt = Instance.new("ProximityPrompt")
    -- текст
    outsidePrompt.ActionText = "Спрятаться"
    -- с 5 studs
    outsidePrompt.MaxActivationDistance = 5
    -- невидимые детали шкафа не мешают
    outsidePrompt.RequiresLineOfSight = false
    -- перед шкафом
    outsidePrompt.Parent = model.Outside
    -- надпись внутри
    local insidePrompt = outsidePrompt:Clone()
    -- текст
    insidePrompt.ActionText = "Выйти"
    -- только изнутри
    insidePrompt.MaxActivationDistance = 3
    -- пока внутри никого
    insidePrompt.Enabled = false
    -- внутри шкафа
    insidePrompt.Parent = model.Inside

    -- освободить шкаф
    local function Free()
        -- внутри никого
        hasPlayer.Value = nil
        -- «Выйти» не нужно
        insidePrompt.Enabled = false
        -- снова можно спрятаться
        outsidePrompt.Enabled = true
    -- конец функции
    end

    -- нажали «Спрятаться»
    outsidePrompt.Triggered:Connect(function(player)
        -- занято (и тот игрок ещё в игре)
        if hasPlayer.Value and hasPlayer.Value.Parent then return end
        -- второй не войдёт
        outsidePrompt.Enabled = false
        -- не получилось
        if not closet.Enter(player, model) then
            -- шкаф снова свободен
            Free()
            -- выходим
            return
        -- конец проверки
        end
        -- можно выйти
        insidePrompt.Enabled = true
        -- погиб внутри
        player.Character.Humanoid.Died:Once(function()
            -- шкаф не останется занятым навсегда
            if hasPlayer.Value == player then Free() end
        -- конец обработчика
        end)
    -- конец обработчика
    end)
    -- нажали «Выйти»
    insidePrompt.Triggered:Connect(function(player)
        -- выйти может только тот, кто внутри
        if hasPlayer.Value ~= player then return end
        -- второй раз не нажать
        insidePrompt.Enabled = false
        -- выходим
        closet.Exit(player, model)
        -- шкаф свободен
        Free()
    -- конец обработчика
    end)
-- конец функции
end

-- отдаём таблицу модуля
return closet

Furniture

Где: ServerScriptService → MainScript → Room · ModuleScript
Зачем: Локатор Closet ставит шкаф для пряток.

-- плавное движение ящиков
local TS = game:GetService("TweenService")
-- здесь лежат образцы мебели
local SS = game:GetService("ServerStorage")
-- шкаф для пряток
local closet = require(script.Closet)
-- на сколько studs выдвигается ящик
local SLIDE = 1.5

-- таблица модуля
local furniture = {}

-- открыть или закрыть ящик / дверцу
function furniture.Open(bottom)
    -- двигается — второе нажатие не примем
    bottom:SetAttribute("Moving", true)
    -- сейчас открыт?
    local opened = bottom:GetAttribute("Open")
    -- открыт — задвигаем (1), закрыт — выдвигаем (−1)
    local direction = opened and 1 or -1
    -- Bottom → папка Bottoms → модель мебели
    local model = bottom.Parent.Parent
    -- анимация
    local tween
    -- у мебели есть петля — это дверца (шкаф-гардероб)
    if model:FindFirstChild("Spinner") then
        -- поворот петли на 90°
        local cframe = model.Spinner.CFrame * CFrame.Angles(0, math.rad(90 * direction), 0)
        -- поворачиваем петлю
        tween = TS:Create(model.Spinner, TweenInfo.new(0.5), {CFrame = cframe})
    -- ящик
    else
        -- сдвиг вперёд или назад
        local cframe = bottom.CFrame * CFrame.new(0, 0, SLIDE * direction)
        -- двигаем дно — стенки приварены
        tween = TS:Create(bottom, TweenInfo.new(0.5), {CFrame = cframe})
    -- конец проверки
    end
    -- запускаем
    tween:Play()
    -- ждём конца
    tween.Completed:Wait()
    -- запоминаем новое положение
    bottom:SetAttribute("Open", not opened)
    -- можно нажимать снова
    bottom:SetAttribute("Moving", false)
-- конец функции
end

-- ставит мебель на место локатора
function furniture.New(sample)
    -- образец с тем же именем
    local template = SS.FurnitureSamples:FindFirstChild(sample.Name)
    -- образца нет
    if not template then
        -- подсказка в Output
        warn("Нет мебели " .. sample.Name .. " в ServerStorage.FurnitureSamples")
        -- ничего не ставим
        return nil
    -- конец проверки
    end
    -- копия мебели
    local model = template:Clone()
    -- на место и с поворотом локатора
    model:PivotTo(sample.CFrame)
    -- места внутри ящиков, куда можно положить ключ
    local locations = {}
    -- папка с донышками ящиков
    local bottoms = model:FindFirstChild("Bottoms")
    -- ящики есть
    if bottoms then
        -- каждый ящик
        for _, bottom in ipairs(bottoms:GetChildren()) do
            -- запоминаем место внутри
            table.insert(locations, bottom.Location)
            -- закрыт
            bottom:SetAttribute("Open", false)
            -- не двигается
            bottom:SetAttribute("Moving", false)
            -- надпись «Открыть» (E / тап / X)
            local prompt = Instance.new("ProximityPrompt")
            -- текст
            prompt.ActionText = "Открыть"
            -- с 5 studs
            prompt.MaxActivationDistance = 5
            -- невидимая деталь Primary не мешает
            prompt.RequiresLineOfSight = false
            -- на ручке ящика
            prompt.Parent = bottom.Handle
            -- нажали
            prompt.Triggered:Connect(function()
                -- ещё едет — ждём
                if bottom:GetAttribute("Moving") then return end
                -- открываем или закрываем
                furniture.Open(bottom)
                -- меняем надпись
                prompt.ActionText = bottom:GetAttribute("Open") and "Закрыть" or "Открыть"
            -- конец обработчика
            end)
        -- конец перебора
        end
    -- конец проверки
    end
    -- мебель в комнату
    model.Parent = sample.Parent
    -- локатор больше не нужен
    sample:Destroy()
    -- отдаём места для ключа
    return locations
-- конец функции
end

-- расставляет всю мебель комнаты
function furniture.Generate(roomModel)
    -- все места для ключа в этой комнате
    local roomLocations = {}
    -- папка с локаторами
    local folder = roomModel:FindFirstChild("Furniture")
    -- мебели в комнате нет
    if not folder then return nil end
    -- каждый локатор
    for _, sample in ipairs(folder:GetChildren()) do
        -- локатор шкафа для пряток
        if sample.Name == "Closet" then
            -- у него свой модуль
            closet.New(sample)
        -- обычная мебель
        else
            -- ставим мебель
            local locations = furniture.New(sample)
            -- поставили
            if locations then
                -- её места
                for _, location in ipairs(locations) do
                    -- добавляем к местам комнаты
                    table.insert(roomLocations, location)
                -- конец перебора
                end
            -- конец проверки
            end
        -- конец проверки
        end
    -- конец перебора
    end
    -- есть куда положить ключ
    if #roomLocations > 0 then return roomLocations end
    -- ящиков нет
    return nil
-- конец функции
end

-- отдаём таблицу модуля
return furniture

Проверка

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