Roblox · Doors · урок 6
После каждой третьей двери из прошлой комнаты вылетает монстр и проносится через комнаты вперёд. Пока он только пугает — убивать научим на следующем уроке.
Синим — что добавляем на этом уроке.
ServerStorage ├─ EnterEvent (BindableEvent) — Door → MainScript ├─ Enemies (Folder) │ └─ RedMonster (Model) — PrimaryPart = Monster │ ├─ Monster (Part) — размером с Enter: полупрозрачный, Anchored, CanCollide выкл. │ │ └─ Decal (Decal) — картинка монстра │ ├─ Emitter (Part) — зона на всю большую комнату: прозрачная, CanCollide выкл. │ │ └─ ParticleEmitter (ParticleEmitter) — дым, искры │ └─ WeldConstraint (WeldConstraint) — Monster + Emitter └─ RoomSamples → извитые комнаты (Model) └─ Waypoints (Folder) — точки 1, 2, 3… в углах — монстр не режет углы ServerScriptService └─ MainScript (Script) └─ Monster (ModuleScript)
Где: ServerScriptService → MainScript · ModuleScript
Зачем: Плавно проводит монстра через две комнаты.
-- события каждого кадра
local RunService = game:GetService("RunService")
-- здесь лежит образец монстра
local SS = game:GetService("ServerStorage")
-- таблица модуля
local monster = {}
-- скорость: studs в секунду
monster.speed = 20
-- плавно ведёт монстра к детали target
function monster.LerpTo(model, target)
-- откуда
local start = model:GetPivot()
-- сколько лететь
local distance = (start.Position - target.Position).Magnitude
-- сколько секунд (не ноль)
local duration = math.max(distance / monster.speed, 0.05)
-- доля пути: 0 — начало, 1 — конец
local alpha = 0
-- пока не долетел
while alpha < 1 do
-- прибавляем долю за прошедший кадр
alpha = math.min(alpha + RunService.Heartbeat:Wait() / duration, 1)
-- точка между началом и целью
model:PivotTo(start:Lerp(target.CFrame, alpha))
-- конец цикла
end
-- конец функции
end
-- проводит монстра через комнаты
function monster.Navigate(model, fromNumber, toNumber, rooms)
-- каждая комната по порядку
for i = fromNumber, toNumber do
-- комната
local roomModel = rooms[i]
-- ко входу
if roomModel:FindFirstChild("Enter") then monster.LerpTo(model, roomModel.Enter) end
-- точки поворота в извитых комнатах
local waypoints = roomModel:FindFirstChild("Waypoints")
-- есть
if waypoints then
-- по номерам 1, 2, 3…
for w = 1, #waypoints:GetChildren() do
-- точка с этим номером
local point = waypoints:FindFirstChild(tostring(w))
-- летим к ней
if point then monster.LerpTo(model, point) end
-- конец перебора
end
-- конец проверки
end
-- к выходу
monster.LerpTo(model, roomModel.Exit)
-- конец перебора
end
-- конец функции
end
-- монстр проходит комнату number и следующую
function monster.New(number, rooms)
-- дальше последней комнаты не идёт
local toNumber = math.min(number + 1, #rooms)
-- откуда начинает
local startRoom = rooms[number]
-- копия монстра
local model = SS.Enemies.RedMonster:Clone()
-- ставим на вход комнаты
model:PivotTo((startRoom:FindFirstChild("Enter") or startRoom.Exit).CFrame)
-- появляется
model.Parent = workspace
-- проходит комнаты
monster.Navigate(model, number, toNumber, rooms)
-- исчезает
model:Destroy()
-- конец функции
end
-- отдаём таблицу модуля
return monsterГде: ServerScriptService → MainScript → Room · ModuleScript
Зачем: При открытии сообщает номер двери.
-- плавный поворот двери
local TS = game:GetService("TweenService")
-- здесь лежит образец двери
local SS = game:GetService("ServerStorage")
-- сообщает главному скрипту номер открытой двери
local enterEvent = SS:WaitForChild("EnterEvent")
-- таблица модуля
local door = {}
-- открывает дверь
function door.Open(doorModel, number)
-- отмечаем: открыта
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()
-- игрок вошёл в комнату number + 1
enterEvent:Fire(number)
-- конец функции
end
-- ключ в руках или в рюкзаке
local function FindKey(character)
-- чей это персонаж
local player = game.Players:GetPlayerFromCharacter(character)
-- Tool «Key» или nil
return character:FindFirstChild("Key") or (player and player.Backpack:FindFirstChild("Key"))
-- конец функции
end
-- ставит дверь на выход комнаты
function door.New(roomModel, number, locked)
-- копия образца
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.Lock.Transparency = locked and 0 or 1
-- кто-то подошёл к двери
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
-- замка нет
if not locked then
-- просто открываем
door.Open(doorModel, number)
-- готово
return
-- конец проверки
end
-- есть ли у игрока ключ
local key = FindKey(character)
-- есть
if key then
-- дверь больше не заперта
locked = false
-- ключ тратится
key:Destroy()
-- замок исчезает
doorModel.Lock:Destroy()
-- щелчок замка
doorModel.Door.Unlock:Play()
-- открываем
door.Open(doorModel, number)
-- конец проверки
end
-- конец обработчика
end)
-- дверь — часть комнаты
doorModel.Parent = roomModel
-- отдаём дверь
return doorModel
-- конец функции
end
-- отдаём таблицу модуля
return doorГде: ServerScriptService · Script
Зачем: Таблица комнат по номерам; монстр после 3-й, 6-й, 9-й двери.
-- модуль комнат
local room = require(script.Room)
-- модуль дверей
local door = require(script.Room.Door)
-- модуль монстра
local monster = require(script.Monster)
-- номер открытой двери
local enterEvent = game.ServerStorage:WaitForChild("EnterEvent")
-- сколько комнат вместе со стартовой
local ROOMS = 10
-- монстр после каждой 3-й двери
local MONSTER_EVERY = 3
-- первая «предыдущая» — стартовая
local prevRoom = workspace.StartRoom
-- все комнаты по номерам: 1 — стартовая
local generatedRooms = {prevRoom}
-- дверь 01 в стартовой комнате
door.New(prevRoom, 1, false)
-- комнаты 2…10
for i = 2, ROOMS do
-- новая комната с дверью номер i
prevRoom = room.Generate(prevRoom, i)
-- запоминаем под номером
generatedRooms[i] = prevRoom
-- конец цикла
end
-- открыта дверь number
enterEvent.Event:Connect(function(number)
-- номер делится на 3 и комната не последняя
if number % MONSTER_EVERY == 0 and number < #generatedRooms then
-- монстр пролетает комнаты
monster.New(number, generatedRooms)
-- конец проверки
end
-- конец обработчика
end)Запусти Play (или Run) и убедись: