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)
Где: 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Где: 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) и убедись: