Roblox · Doors · урок 3

Мебель с ящиками

В комнатах появляется мебель: тумбочки и столы с выдвижными ящиками, шкафы-гардеробы с дверцей. Ящики открываются по E. Где стоит мебель — решают невидимые локаторы в комнатах.

Структура

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

ServerStorage
├─ FurnitureSamples (Folder)
│  ├─ Bedside (Model) — тумбочка; PrimaryPart = Primary
│  │  ├─ Primary (Part) — покрывает всю тумбочку: прозрачная, CanCollide и CanQuery выкл.
│  │  ├─ Frame… (Part) — каркас, Anchored
│  │  ├─ Bottoms (Folder)
│  │  │  └─ Bottom (Part) — дно ящика: Anchored; передняя грань — к ручке
│  │  │     ├─ Location (Attachment) — центр ящика
│  │  │     └─ Handle (Attachment) — на ручке
│  │  └─ Drawer… (Part) — стенки и ручка ящика: приварены к Bottom, Anchored выкл.
│  ├─ Table (Model) — стол — как тумбочка
│  └─ Wardrobe (Model) — гардероб с дверцей
│     ├─ Spinner (Part) — петля дверцы, Anchored
│     └─ Bottoms → Bottom (Part) — дно с Location и Handle (Handle — на ручке дверцы)
└─ RoomSamples (Folder)
   └─ SmallRoom (Model)
      └─ Furniture (Folder) — локаторы
         └─ Bedside, Table, Wardrobe (Part) — имя = имя мебели; передняя грань — в комнату
ServerScriptService
└─ MainScript → Room (ModuleScript)
   └─ Furniture (ModuleScript)

Шаги

  1. Тумбочка: сначала деталь-локатор «Bedside» (CanCollide выкл., Anchored), внутри неё строй каркас.
  2. Ящик: дно, стенки, перед с ручкой; внутри — пустое место. Выдели детали ящика → Model → Weld: они сварятся.
  3. Дно ящика — «Bottom», Anchored вкл.; остальные детали ящика — Anchored выкл. Передняя грань Bottom — к ручке.
  4. В Bottom два Attachment: «Location» в центре ящика (сюда ляжет ключ) и «Handle» на ручке (здесь будет «Открыть»).
  5. Второй ящик — копия первого. Прозрачная деталь «Primary» покрывает всю тумбочку: CanCollide и CanQuery выкл., передняя грань — наружу. Всё в модель «Bedside», PrimaryPart = Primary, донышки — в папку «Bottoms».
  6. Стол «Table» — копия тумбочки с длинной столешницей и ножкой (Primary растяни).
  7. Гардероб «Wardrobe»: каркас, дверца с ручкой, петля «Spinner» (Anchored); дверца и ручка приварены к Spinner, Anchored выкл. Дно «Bottom» в папке Bottoms, на нём Location и Handle (Handle — на ручке).
  8. Все модели — в ServerStorage → Folder «FurnitureSamples».
  9. В комнатах-образцах: копии локаторов (имя — как у мебели) вдоль стен, передней гранью в комнату. Локаторы одной комнаты — в её папку «Furniture».
  10. В Room — ModuleScript «Furniture». Ящик выдвигается на SLIDE studs — поменяй под размер своих ящиков.

Скрипты

Furniture

Где: ServerScriptService → MainScript → Room · ModuleScript
Зачем: Ставит мебель на места локаторов и открывает ящики и дверцы.

-- плавное движение ящиков
local TS = game:GetService("TweenService")
-- здесь лежат образцы мебели
local SS = game:GetService("ServerStorage")
-- на сколько 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 bottoms = model:FindFirstChild("Bottoms")
    -- ящики есть
    if bottoms then
        -- каждый ящик
        for _, bottom in ipairs(bottoms:GetChildren()) do
            -- закрыт
            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 model
-- конец функции
end

-- расставляет всю мебель комнаты
function furniture.Generate(roomModel)
    -- папка с локаторами
    local folder = roomModel:FindFirstChild("Furniture")
    -- мебели в комнате нет
    if not folder then return nil end
    -- каждый локатор
    for _, sample in ipairs(folder:GetChildren()) do
        -- ставим мебель
        furniture.New(sample)
    -- конец перебора
    end
-- конец функции
end

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

Room

Где: ServerScriptService → MainScript · ModuleScript
Зачем: Как раньше + мебель в каждой комнате.

-- здесь лежат образцы комнат
local SS = game:GetService("ServerStorage")
-- модуль дверей
local door = require(script.Door)
-- модуль мебели
local furniture = require(script.Furniture)

-- таблица модуля
local room = {}
-- редкость и правила комнат
room.info = require(script.RoomInfo)
-- генератор случайных чисел
room.random = Random.new()
-- куда был последний поворот
room.lastTurnDirection = nil

-- можно ли поставить эту комнату следующей
local function Allowed(name, info, prevRoom)
    -- настройки предыдущей (у стартовой их нет)
    local prevInfo = room.info[prevRoom.Name] or {}
    -- образца нет — пропускаем
    if not SS.RoomSamples:FindFirstChild(name) then return false end
    -- две одинаковые подряд — скучно
    if name == prevRoom.Name then return false end
    -- два поворота в одну сторону — коридор замкнётся
    if info.Direction and info.Direction == room.lastTurnDirection then return false end
    -- две лестницы подряд — нельзя
    if info.Stairs and prevInfo.Stairs then return false end
    -- подходит
    return true
-- конец функции
end

-- случайная комната с учётом редкости
function room.GetRandom(prevRoom)
    -- сумма весов подходящих комнат
    local totalRarity = 0
    -- все комнаты из RoomInfo
    for name, info in pairs(room.info) do
        -- подходящие складываем
        if Allowed(name, info, prevRoom) then totalRarity += info.Rarity end
    -- конец перебора
    end
    -- понятная ошибка вместо зависания
    if totalRarity == 0 then error("Нет подходящих комнат в ServerStorage.RoomSamples") end
    -- случайное число от 1 до суммы
    local randomRarity = room.random:NextInteger(1, totalRarity)
    -- накопленный вес
    local currentRarity = 0
    -- снова по комнатам (порядок тот же)
    for name, info in pairs(room.info) do
        -- только подходящие
        if Allowed(name, info, prevRoom) then
            -- прибавляем вес
            currentRarity += info.Rarity
            -- число попало на эту комнату
            if randomRarity <= currentRarity then
                -- запоминаем поворот
                if info.Direction then room.lastTurnDirection = info.Direction end
                -- отдаём образец
                return SS.RoomSamples[name]
            -- конец проверки
            end
        -- конец проверки
        end
    -- конец перебора
    end
-- конец функции
end

-- ставит новую комнату за предыдущей
function room.Generate(prevRoom, number)
    -- копия случайной комнаты
    local newRoom = room.GetRandom(prevRoom):Clone()
    -- двигаем комнату за вход
    newRoom.PrimaryPart = newRoom.Enter
    -- вход новой — точно на выход предыдущей
    newRoom:PivotTo(prevRoom.Exit.CFrame)
    -- расставляем мебель по локаторам
    furniture.Generate(newRoom)
    -- дверь на выходе с номером
    door.New(newRoom, number)
    -- показываем комнату
    newRoom.Parent = workspace.GeneratedRooms
    -- она станет «предыдущей» для следующей
    return newRoom
-- конец функции
end

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

Проверка

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