Roblox · Doors · урок 2

Двери с номерами

В каждом проёме — дверь с табличкой 01, 02, 03… Подходишь — дверь сама распахивается со скрипом.

Структура

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

ServerStorage
└─ Door (Model) — PrimaryPart = Primary
   ├─ Primary (Part) — копия Exit: прозрачная, CanCollide и CanQuery выкл.
   ├─ Spinner (Part) — петля сбоку, Anchored
   │  └─ WeldConstraint (WeldConstraint) — Part0 = Spinner, Part1 = Door
   ├─ Door (Part) — полотно; Anchored выкл.
   │  ├─ WeldConstraint (WeldConstraint) — Part0 = Door, Part1 = Label
   │  └─ Open (Sound) — скрип
   ├─ Label (Part) — табличка; Anchored выкл.
   │  └─ NumberGui (SurfaceGui) — Face — передняя грань
   │     └─ Number (TextLabel)
   └─ Trigger (Part) — перед дверью: прозрачный, CanCollide выкл., Anchored
ServerScriptService
└─ MainScript (Script)
   └─ Room (ModuleScript)
      └─ Door (ModuleScript)

Шаги

  1. Дверь удобно строить прямо в проёме комнаты. Полотно — «Door» (Anchored выкл.), сбоку тонкая петля «Spinner» (Anchored вкл.).
  2. Табличка «Label» на полотне (Anchored выкл.), в ней SurfaceGui «NumberGui» → TextLabel «Number».
  3. Скопируй Exit комнаты в дверь и назови «Primary» — по нему дверь встаёт в любой проём. У модели Door PrimaryPart = Primary.
  4. Передние грани Door, Label и Primary смотрят в одну сторону.
  5. Перед дверью — большой «Trigger» (прозрачный, CanCollide выкл., CanQuery выкл., Anchored): коснулся — дверь открывается.
  6. WeldConstraint: в Door (Part0 Door, Part1 Label) и в Spinner (Part0 Spinner, Part1 Door). Поворачиваем петлю — полотно с табличкой едут следом.
  7. Звук «Door open» из Toolbox → в Door, имя «Open».
  8. Готовую модель Door перенеси в ServerStorage.
  9. В Room — ModuleScript «Door». В Room и MainScript добавились строки с дверями: тексты ниже целиком.
  10. Дверь открывается в другую сторону — поменяй 90 на −90 в door.Open.

Скрипты

Door

Где: ServerScriptService → MainScript → Room · ModuleScript
Зачем: Ставит дверь с номером и открывает её при касании.

-- плавный поворот двери
local TS = game:GetService("TweenService")
-- здесь лежит образец двери
local SS = game:GetService("ServerStorage")

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

-- открывает дверь
function door.Open(doorModel)
    -- отмечаем: открыта
    doorModel:SetAttribute("Open", true)
    -- петля повёрнута на 90°
    local cframe = doorModel.Spinner.CFrame * CFrame.Angles(0, math.rad(90), 0)
    -- за полсекунды
    local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Linear)
    -- поворачиваем петлю — дверь приварена и едет следом
    TS:Create(doorModel.Spinner, tweenInfo, {CFrame = cframe}):Play()
    -- скрип двери
    doorModel.Door.Open:Play()
-- конец функции
end

-- ставит дверь на выход комнаты
function door.New(roomModel, number)
    -- копия образца
    local doorModel = SS.Door:Clone()
    -- в дверной проём
    doorModel:PivotTo(roomModel.Exit.CFrame)
    -- закрыта
    doorModel:SetAttribute("Open", false)
    -- номер на табличке: 01, 02…
    doorModel.Label.NumberGui.Number.Text = string.format("%02d", number)
    -- кто-то подошёл к двери
    doorModel.Trigger.Touched:Connect(function(otherPart)
        -- модель, которой принадлежит деталь
        local character = otherPart.Parent
        -- это персонаж?
        local humanoid = character:FindFirstChildOfClass("Humanoid")
        -- не персонаж или уже открыта
        if not humanoid or doorModel:GetAttribute("Open") then return end
        -- открываем
        door.Open(doorModel)
    -- конец обработчика
    end)
    -- дверь — часть комнаты
    doorModel.Parent = roomModel
    -- отдаём дверь
    return doorModel
-- конец функции
end

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

Room

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

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

-- таблица модуля
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)
    -- дверь на выходе с номером
    door.New(newRoom, number)
    -- показываем комнату
    newRoom.Parent = workspace.GeneratedRooms
    -- она станет «предыдущей» для следующей
    return newRoom
-- конец функции
end

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

MainScript

Где: ServerScriptService · Script
Зачем: Дверь 01 в стартовой комнате и номера комнат.

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

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

Проверка

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