Roblox · Королевская битва · урок 7
Падая с высоты, жми пробел — раскроется парашют (забудешь — раскроется сам). Следы в воздухе от рук и ног. Зиплайн: E у верхней точки троса — и съезжаешь вниз.
Синим — что добавляем на этом уроке.
ServerStorage ├─ Parachute (Model) — MainPart (PrimaryPart) + купол; Anchored и CanCollide выкл., Massless └─ FallTrail (Trail) — след в воздухе ReplicatedStorage └─ ParachuteEvent (RemoteEvent) ServerScriptService └─ ParachuteServer (Script) StarterPlayer → StarterCharacterScripts └─ ParachuteLocal (LocalScript) — Animation — поза падения (ID можно не вставлять) Workspace └─ Zip (Model) ├─ Start (Part) — наверху; Attachment, Beam, ProximityPrompt → ZipScript └─ End (Part) — внизу; Attachment
Где: StarterPlayer → StarterCharacterScripts · LocalScript
Зачем: Падение, пробел, авто.
-- клавиатура
local UIS = game:GetService("UserInputService")
-- события
local RS = game:GetService("ReplicatedStorage")
-- каждый кадр
local RunService = game:GetService("RunService")
-- персонаж
local character = script.Parent
-- Humanoid
local humanoid = character:WaitForChild("Humanoid")
-- центр
local root = character:WaitForChild("HumanoidRootPart")
-- просим сервер: следы, парашют, приземлились
local event = RS:WaitForChild("ParachuteEvent")
-- поза падения (ID можно не вставлять)
local anim = script:WaitForChild("Animation")
-- загружаем, если ID есть
local track = anim.AnimationId ~= "" and humanoid:WaitForChild("Animator"):LoadAnimation(anim) or nil
-- ниже этой высоты парашют раскроется сам
local AUTO_HEIGHT = 60
-- ниже — открывать поздно
local MIN_HEIGHT = 20
-- когда начали падать
local fallStart = nil
-- парашют уже открыт
local opened = false
-- следы уже включены
local trails = false
-- настройки луча
local params = RaycastParams.new()
-- исключаем…
params.FilterType = Enum.RaycastFilterType.Exclude
-- …самого себя
params.FilterDescendantsInstances = {character}
-- высота над землёй
local function Height()
-- луч строго вниз (в методичке летел не туда)
local result = workspace:Raycast(root.Position, Vector3.new(0, -2000, 0), params)
-- до земли или «очень высоко»
return result and root.Position.Y - result.Position.Y or math.huge
-- конец функции
end
-- раскрыть парашют
local function Open()
-- уже
if opened then return end
-- раскрыт
opened = true
-- сервер вешает парашют и замедляет падение
event:FireServer("Open")
-- конец функции
end
-- состояние персонажа сменилось
humanoid.StateChanged:Connect(function(_, new)
-- начал падать
if new == Enum.HumanoidStateType.Freefall then
-- запоминаем время
fallStart = os.clock()
-- падение закончилось
elseif fallStart then
-- не падаем
fallStart = nil
-- поза падения выключается
if track then track:Stop() end
-- убрать парашют и следы
if opened or trails then event:FireServer("Landed") end
-- к следующему падению всё сначала
opened, trails = false, false
-- конец проверки
end
-- конец функции
end)
-- нажали клавишу
UIS.InputBegan:Connect(function(input, processed)
-- пробел во время падения
if processed or input.KeyCode ~= Enum.KeyCode.Space or not fallStart then return end
-- падаем давно и ещё высоко
if os.clock() - fallStart > 0.7 and Height() > MIN_HEIGHT then Open() end
-- конец функции
end)
-- каждый кадр
RunService.Heartbeat:Connect(function()
-- обычный прыжок короче секунды — не трогаем
if not fallStart or os.clock() - fallStart < 1 then return end
-- падаем долго — значит с высоты
if not trails then
-- один раз
trails = true
-- поза падения
if track then track:Play() end
-- сервер добавит следы-Trail на руки и ноги
event:FireServer("Fall")
-- конец проверки
end
-- забыл раскрыть — раскроется сам
if not opened and Height() < AUTO_HEIGHT then Open() end
-- конец функции
end)Где: ServerScriptService · Script
Зачем: Парашют и следы.
-- события
local RS = game:GetService("ReplicatedStorage")
-- образцы
local SS = game:GetService("ServerStorage")
-- просьбы клиента
local event = RS:WaitForChild("ParachuteEvent")
-- модель парашюта (PrimaryPart — MainPart)
local parachuteSample = SS:WaitForChild("Parachute")
-- след в воздухе
local trailSample = SS:WaitForChild("FallTrail")
-- скорость спуска на парашюте
local FALL_SPEED = 20
-- где рисуем следы (R15)
local TRAIL_PARTS = {"LeftHand", "RightHand", "LeftFoot", "RightFoot"}
-- удалить всё с этим именем
local function Remove(character, name)
-- все части персонажа
for _, item in ipairs(character:GetDescendants()) do
-- нашли — удаляем
if item.Name == name then item:Destroy() end
-- конец перебора
end
-- конец функции
end
-- клиент попросил
event.OnServerEvent:Connect(function(player, action)
-- персонаж
local character = player.Character
-- Humanoid
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
-- центр
local root = character and character:FindFirstChild("HumanoidRootPart")
-- погиб
if not humanoid or not root or humanoid.Health <= 0 then return end
-- долгое падение — следы
if action == "Fall" then
-- старые убираем
Remove(character, "FallTrail")
-- руки и ноги
for _, partName in ipairs(TRAIL_PARTS) do
-- часть тела
local part = character:FindFirstChild(partName)
-- есть (у R6 таких нет)
if part then
-- начало следа
local a0 = Instance.new("Attachment")
-- имя — чтобы потом удалить
a0.Name = "FallTrail"
-- с одной стороны
a0.Position = Vector3.new(0.4, 0, 0)
-- в часть тела
a0.Parent = part
-- конец следа
local a1 = a0:Clone()
-- с другой стороны
a1.Position = Vector3.new(-0.4, 0, 0)
-- в часть тела
a1.Parent = part
-- след
local trail = trailSample:Clone()
-- имя
trail.Name = "FallTrail"
-- между двумя точками
trail.Attachment0 = a0
-- получается лента
trail.Attachment1 = a1
-- в часть тела
trail.Parent = part
-- конец проверки
end
-- конец перебора
end
-- раскрыть парашют
elseif action == "Open" then
-- уже раскрыт
if character:FindFirstChild("Parachute") then return end
-- туловище R15 или R6
local torso = character:FindFirstChild("UpperTorso") or character:FindFirstChild("Torso")
-- нет туловища
if not torso then return end
-- парашют
local chute = parachuteSample:Clone()
-- имя
chute.Name = "Parachute"
-- над головой
chute:PivotTo(torso.CFrame * CFrame.new(0, 6, 0))
-- приварим к туловищу
local weld = Instance.new("WeldConstraint")
-- туловище
weld.Part0 = torso
-- MainPart парашюта
weld.Part1 = chute.PrimaryPart
-- в парашют
weld.Parent = chute.PrimaryPart
-- в персонажа
chute.Parent = character
-- точка для силы
local attachment = Instance.new("Attachment")
-- имя
attachment.Name = "ParachuteForce"
-- в центр
attachment.Parent = root
-- держит скорость падения
local slow = Instance.new("LinearVelocity")
-- имя
slow.Name = "ParachuteForce"
-- за центр
slow.Attachment0 = attachment
-- только по одной оси…
slow.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
-- …вертикальной: по сторонам игрок управляет сам
slow.LineDirection = Vector3.new(0, 1, 0)
-- вниз 20 studs/с
slow.LineVelocity = -FALL_SPEED
-- достаточно, чтобы затормозить
slow.MaxForce = 100000
-- в центр
slow.Parent = root
-- приземлился
elseif action == "Landed" then
-- парашют
Remove(character, "Parachute")
-- торможение
Remove(character, "ParachuteForce")
-- следы
Remove(character, "FallTrail")
-- конец проверки
end
-- конец функции
end)Где: Workspace → Zip → Start → ProximityPrompt · Script
Зачем: Спуск по тросу.
-- каждый кадр
local RunService = game:GetService("RunService")
-- ProximityPrompt «Спуститься»
local prompt = script.Parent
-- верхняя точка троса
local zipStart = prompt.Parent
-- нижняя точка
local zipEnd = zipStart.Parent:WaitForChild("End")
-- скорость спуска, studs/с
local SPEED = 40
-- персонаж висит под тросом
local OFFSET = Vector3.new(0, -3.5, 0)
-- игрок → true: кто едет
local riding = {}
-- нажал E
prompt.Triggered:Connect(function(player)
-- персонаж
local character = player.Character
-- Humanoid
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
-- центр
local root = character and character:FindFirstChild("HumanoidRootPart")
-- уже едет или погиб
if riding[player] or not humanoid or not root or humanoid.Health <= 0 then return end
-- едет (другие тоже могут — кнопка не блокируется)
riding[player] = true
-- откуда
local from = zipStart.Position + OFFSET
-- куда
local to = zipEnd.Position + OFFSET
-- смотрит вдоль троса, а не боком
local facing = CFrame.lookAt(from, Vector3.new(to.X, from.Y, to.Z)).Rotation
-- время спуска — одинаковая скорость на любом тросе
local duration = (to - from).Magnitude / SPEED
-- едет по тросу, а не падает
root.Anchored = true
-- сколько едем
local t = 0
-- пока не доехали
while t < duration and root.Parent and humanoid.Health > 0 do
-- прошёл кадр
t += RunService.Heartbeat:Wait()
-- точка на тросе — от начала к концу
root.CFrame = facing + from:Lerp(to, math.min(t / duration, 1))
-- конец спуска
end
-- отпускаем
if root.Parent then root.Anchored = false end
-- приехал
riding[player] = nil
-- конец функции
end)Запусти Play (или Run) и убедись: