Roblox · RPG · урок 7
Лук: поза с луком в руках, вид из-за плеча, стрела летит дугой туда, где центр экрана. Навык — высокий прыжок на E.
Синим — что добавляем на этом уроке.
ServerStorage ├─ Weapons (Folder) │ └─ Bow (Tool) │ ├─ Handle (Part) — лук; Fire, Hit (Sound) │ ├─ Arrow (Part) — стрела на тетиве, приварена к Handle │ ├─ Bow (RemoteEvent) │ ├─ YouAreNotGui (ScreenGui) — «Ты не лучник» │ ├─ BowLocal (LocalScript) — Animation — поза (Movement, Looped) │ └─ BowServer (Script) — Animation — выстрел (Action) └─ Classes → Archer (Folder) ├─ ArcherCamera (LocalScript) — вид из-за плеча └─ Jump (LocalScript) — Animation — прыжок (Action)
Где: ServerStorage → Weapons → Bow · LocalScript
Зачем: Поза и клик.
-- игроки
local Players = game:GetService("Players")
-- лук
local tool = script.Parent
-- сигнал «выстрел» на сервер
local bowEvent = tool:WaitForChild("Bow")
-- «Ты не лучник»
local notGui = tool:WaitForChild("YouAreNotGui")
-- поза с луком (Movement, зациклена)
local holdAnim = script:WaitForChild("Animation")
-- этот игрок
local player = Players.LocalPlayer
-- куда целимся
local mouse = player:GetMouse()
-- чей это лук
local CLASS = "Archer"
-- дорожка позы
local holdTrack = nil
-- взяли лук
local function OnEquipped()
-- Humanoid
local humanoid = tool.Parent:FindFirstChildOfClass("Humanoid")
-- анимации
local animator = humanoid and humanoid:FindFirstChildOfClass("Animator")
-- поза задана
if animator and holdAnim.AnimationId ~= "" then
-- загружаем
holdTrack = animator:LoadAnimation(holdAnim)
-- по кругу
holdTrack.Looped = true
-- держим лук
holdTrack:Play()
-- конец проверки
end
-- конец функции
end
-- каждый раз, когда берут
tool.Equipped:Connect(OnEquipped)
-- подобрали с земли — уже в руках
if tool.Parent and tool.Parent:FindFirstChildOfClass("Humanoid") then OnEquipped() end
-- убрали лук
tool.Unequipped:Connect(function()
-- обычная поза
if holdTrack then holdTrack:Stop() end
-- дорожки нет
holdTrack = nil
-- конец функции
end)
-- клик — подписка одна, не внутри Equipped
tool.Activated:Connect(function()
-- чужой класс
if player:GetAttribute("Class") ~= CLASS then
-- надпись ещё не показана
if notGui.Parent == tool then
-- «Ты не …» на экран
notGui.Parent = player.PlayerGui
-- 2 секунды
task.wait(2)
-- прячем обратно в оружие
notGui.Parent = tool
-- конец проверки
end
-- не стреляем
return
-- конец проверки
end
-- стреляем туда, где центр экрана
bowEvent:FireServer(mouse.Hit.Position)
-- конец функции
end)Где: ServerStorage → Weapons → Bow · Script
Зачем: Выстрел стрелой.
-- игроки
local Players = game:GetService("Players")
-- удаляет стрелы
local Debris = game:GetService("Debris")
-- лук
local tool = script.Parent
-- сам лук
local handle = tool:WaitForChild("Handle")
-- стрела на тетиве
local arrow = tool:WaitForChild("Arrow")
-- выстрел от клиента
local bowEvent = tool:WaitForChild("Bow")
-- звук выстрела
local fireSound = handle:WaitForChild("Fire")
-- звук попадания
local hitSound = handle:WaitForChild("Hit")
-- анимация выстрела (Action)
local anim = script:WaitForChild("Animation")
-- кто может стрелять
local CLASS = "Archer"
-- перезарядка
local COOLDOWN = 1.5
-- скорость стрелы
local SPEED = 200
-- перезаряжаемся
local busy = false
-- клиент просит выстрелить
bowEvent.OnServerEvent:Connect(function(player, target)
-- кто держит
local character = tool.Parent
-- его Humanoid
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
-- лук не в его руках или мусор вместо точки
if busy or Players:GetPlayerFromCharacter(character) ~= player or typeof(target) ~= "Vector3" then return end
-- погиб или не лучник
if not humanoid or humanoid.Health <= 0 or player:GetAttribute("Class") ~= CLASS then return end
-- перезарядка
busy = true
-- натягиваем тетиву
if anim.AnimationId ~= "" then humanoid:FindFirstChildOfClass("Animator"):LoadAnimation(anim):Play() end
-- момент выстрела
task.wait(0.3)
-- звук
fireSound:Play()
-- летящая стрела
local shot = arrow:Clone()
-- всё внутри копии
for _, obj in ipairs(shot:GetChildren()) do
-- сварка с луком — иначе стрела тянет лук за собой
if obj:IsA("WeldConstraint") or obj:IsA("JointInstance") then obj:Destroy() end
-- конец перебора
end
-- летит
shot.Anchored = false
-- не отскакивает
shot.CanCollide = false
-- у стрелы есть вес — падает дугой
shot.Massless = false
-- у лука, остриём к цели
shot.CFrame = CFrame.new(handle.Position, target)
-- в мир
shot.Parent = workspace
-- полёт считает сервер
shot:SetNetworkOwner(nil)
-- вылетает
shot.AssemblyLinearVelocity = shot.CFrame.LookVector * SPEED
-- на тетиве стрелы нет
arrow.Transparency = 1
-- стрела ещё не попала
local done = false
-- стрела коснулась
shot.Touched:Connect(function(hit)
-- уже попала или задела стрелка
if done or hit:IsDescendantOf(character) then return end
-- чья деталь
local model = hit:FindFirstAncestorOfClass("Model")
-- Humanoid цели
local target = model and model:FindFirstChildOfClass("Humanoid")
-- живой моб
if target and target.Health > 0 and not Players:GetPlayerFromCharacter(model) then
-- попала
done = true
-- прошлая метка «кто ударил»
local old = target:FindFirstChild("creator")
-- убираем
if old then old:Destroy() end
-- новая метка
local tag = Instance.new("ObjectValue")
-- стандартное имя метки в Roblox
tag.Name = "creator"
-- кто ударил — получит опыт за победу
tag.Value = player
-- в Humanoid цели
tag.Parent = target
-- через 3 секунды метка исчезнет
Debris:AddItem(tag, 3)
-- урон
target:TakeDamage(20)
-- звук попадания
hitSound:Play()
-- стрела исчезает
shot:Destroy()
-- врезалась в стену или землю
elseif hit.CanCollide then
-- дальше не летит
done = true
-- торчит, где попала
shot.Anchored = true
-- конец проверки
end
-- конец функции
end)
-- через 5 секунд стрела исчезнет
Debris:AddItem(shot, 5)
-- перезарядка
task.wait(COOLDOWN)
-- новая стрела на тетиве
arrow.Transparency = 0
-- можно снова
busy = false
-- конец функции
end)Где: ServerStorage → Classes → Archer · LocalScript
Зачем: Вид из-за плеча с луком.
-- мышь
local UIS = game:GetService("UserInputService")
-- каждый кадр
local RunService = game:GetService("RunService")
-- персонаж: скрипт → Archer → персонаж
local character = script.Parent.Parent
-- Humanoid
local humanoid = character:WaitForChild("Humanoid")
-- центр
local root = character:WaitForChild("HumanoidRootPart")
-- камера
local camera = workspace.CurrentCamera
-- сейчас лук в руках?
local aiming = false
-- каждый кадр
RunService.RenderStepped:Connect(function()
-- что в руках
local tool = character:FindFirstChildOfClass("Tool")
-- лук
if tool and tool.Name == "Bow" then
-- целимся
aiming = true
-- курсор в центре экрана — камера крутится мышью
UIS.MouseBehavior = Enum.MouseBehavior.LockCenter
-- камера чуть правее — вид из-за плеча
humanoid.CameraOffset = Vector3.new(2, 0, 0)
-- персонаж не разворачивается сам
humanoid.AutoRotate = false
-- куда смотрит камера
local look = camera.CFrame.LookVector
-- то же направление по горизонтали
local flat = Vector3.new(look.X, 0, look.Z)
-- камера не смотрит строго вверх или вниз
if flat.Magnitude > 0.01 then
-- персонаж смотрит туда же — не стреляет себе за спину
root.CFrame = CFrame.new(root.Position, root.Position + flat)
-- конец проверки
end
-- лук убрали
elseif aiming then
-- больше не целимся
aiming = false
-- обычный курсор
UIS.MouseBehavior = Enum.MouseBehavior.Default
-- камера на место
humanoid.CameraOffset = Vector3.zero
-- персонаж снова поворачивается сам
humanoid.AutoRotate = true
-- конец проверки
end
-- конец функции
end)Где: ServerStorage → Classes → Archer · LocalScript
Зачем: Высокий прыжок на E.
-- клавиатура
local UIS = game:GetService("UserInputService")
-- игроки
local Players = game:GetService("Players")
-- удаляет толчок
local Debris = game:GetService("Debris")
-- персонаж
local character = script.Parent.Parent
-- Humanoid
local humanoid = character:WaitForChild("Humanoid")
-- центр
local root = character:WaitForChild("HumanoidRootPart")
-- анимация прыжка
local anim = script:WaitForChild("Animation")
-- навык класса — на E
local KEY = Enum.KeyCode.E
-- перезарядка
local COOLDOWN = 5
-- сила прыжка
local POWER = 60
-- можно прыгать
local ready = true
-- нажали клавишу
UIS.InputBegan:Connect(function(input, processed)
-- не та клавиша, перезарядка или погиб
if processed or input.KeyCode ~= KEY or not ready or humanoid.Health <= 0 then return end
-- перезарядка
ready = false
-- анимация
if anim.AnimationId ~= "" then humanoid:FindFirstChildOfClass("Animator"):LoadAnimation(anim):Play() end
-- точка приложения силы
local attachment = root:FindFirstChild("RootAttachment") or Instance.new("Attachment", root)
-- толчок
local push = Instance.new("LinearVelocity")
-- за центр
push.Attachment0 = attachment
-- только вдоль одной линии
push.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
-- вверх — по сторонам можно двигаться
push.LineDirection = Vector3.new(0, 1, 0)
-- скорость вверх
push.LineVelocity = POWER
-- сильный
push.MaxForce = 100000
-- своего персонажа игрок двигает сам
push.Parent = root
-- толкает 0.3 секунды
Debris:AddItem(push, 0.3)
-- перезарядка
task.wait(COOLDOWN)
-- можно снова
ready = true
-- конец функции
end)Запусти Play (или Run) и убедись: