Roblox · Doors · урок 1

Комнаты и генерация

Doors — хоррор, где игрок идёт через бесконечную цепочку комнат, открывает двери, ищет ключи и прячется от монстра. Сегодня: набор комнат и скрипт, который при каждом запуске выстраивает их в новом случайном порядке.

Структура

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

ServerStorage — образцы — не видны на карте
└─ RoomSamples (Folder) — образцы комнат
   ├─ SmallRoom (Model) — PrimaryPart = Enter
   │  ├─ Build (Model) — пол, стены, потолок, декор
   │  ├─ Enter (Part) — вход: прозрачный, CanCollide выкл., Anchored
   │  └─ Exit (Part) — выход в дверном проёме, смотрит в ту же сторону, что Enter
   └─ LargeRoom, LeftCorner, RightCorner, … (Model) — так же: Build + Enter + Exit
Workspace
├─ StartRoom (Model) — стартовая: есть Exit, нет Enter
└─ GeneratedRooms (Folder) — сюда скрипт ставит комнаты
ServerScriptService
└─ MainScript (Script)
   └─ Room (ModuleScript) — выбор и установка комнат
      └─ RoomInfo (ModuleScript) — редкость и правила

Шаги

  1. Механики Doors: случайная генерация комнат, двери с номерами, ключи и замки, монстры, прятки в шкафах.
  2. Новое место из шаблона Baseplate. Маленькая комната: пол, две боковые стены, стена с дверным проёмом (ширина 6, высота 8), потолок. Четвёртая сторона — открытая: это вход.
  3. В дверной проём — деталь «Exit» размером с проём, на открытую сторону — такая же «Enter». Обе: Transparency 1, CanCollide выкл., CanQuery выкл., Anchored.
  4. Главное правило: Enter и Exit смотрят в одну сторону — передняя грань (Orientation Indicator) Enter наружу из комнаты, Exit — внутрь. Иначе комнаты встанут друг в друга.
  5. Стены, пол, потолок и декор — в модель «Build». Build + Enter + Exit — в модель «SmallRoom».
  6. Так же сделай другие комнаты: большую, повороты налево и направо, лестницы, зигзаги. Высота входа и выхода у всех одинаковая — иначе пол «прыгает».
  7. ServerStorage → «+» → Folder «RoomSamples», все комнаты — туда. В ServerStorage их не видно на карте, а скрипт берёт копии.
  8. Workspace → Folder «GeneratedRooms». Стартовая комната «StartRoom» — в Workspace: стена вместо входа, Exit есть, SpawnLocation внутри.
  9. ServerScriptService → Script «MainScript», в него ModuleScript «Room», в Room — ModuleScript «RoomInfo».
  10. RoomInfo: имя комнаты → Rarity (вес: чем больше, тем чаще), Direction у поворотов, Stairs у лестниц. Комнаты, которых у тебя нет, скрипт просто пропустит.
  11. Правила выбора: две одинаковые подряд нельзя, два поворота в одну сторону нельзя (коридор замкнётся в кольцо), две лестницы подряд нельзя.

Скрипты

RoomInfo

Где: ServerScriptService → MainScript → Room · ModuleScript
Зачем: Редкость и особенности каждой комнаты.

-- таблица: имя комнаты → её настройки
local roomInfo = {
    -- маленькая; Rarity — вес: чем больше, тем чаще
    ["SmallRoom"] = {Rarity = 4},
    -- большая
    ["LargeRoom"] = {Rarity = 4},
    -- лестница вверх
    ["Upstairs"] = {Stairs = true, Rarity = 2},
    -- лестница вниз
    ["Downstairs"] = {Stairs = true, Rarity = 2},
    -- поворот налево
    ["LeftCorner"] = {Direction = "Left", Rarity = 6},
    -- поворот направо
    ["RightCorner"] = {Direction = "Right", Rarity = 6},
    -- зигзаг влево
    ["LeftTwist"] = {Rarity = 5},
    -- зигзаг вправо
    ["RightTwist"] = {Rarity = 5},
-- конец таблицы
}

-- отдаём таблицу модулю Room
return roomInfo

Room

Где: ServerScriptService → MainScript · ModuleScript
Зачем: Выбирает случайную комнату по правилам и ставит за предыдущей.

-- здесь лежат образцы комнат
local SS = game:GetService("ServerStorage")

-- таблица модуля
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)
    -- копия случайной комнаты
    local newRoom = room.GetRandom(prevRoom):Clone()
    -- двигаем комнату за вход
    newRoom.PrimaryPart = newRoom.Enter
    -- вход новой — точно на выход предыдущей
    newRoom:PivotTo(prevRoom.Exit.CFrame)
    -- показываем комнату
    newRoom.Parent = workspace.GeneratedRooms
    -- она станет «предыдущей» для следующей
    return newRoom
-- конец функции
end

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

MainScript

Где: ServerScriptService · Script
Зачем: Строит цепочку из 10 комнат.

-- модуль комнат
local room = require(script.Room)
-- сколько комнат вместе со стартовой
local ROOMS = 10

-- первая «предыдущая» — стартовая
local prevRoom = workspace.StartRoom
-- комнаты 2…10
for i = 2, ROOMS do
    -- новая комната за предыдущей
    prevRoom = room.Generate(prevRoom)
-- конец цикла
end

Проверка

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