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)

Шаги

  1. Tool «Bow»: лук — «Handle», стрела — «Arrow» (стоит на тетиве, длинной стороной вперёд). WeldConstraint: сначала лук, потом стрела.
  2. Звуки в Handle: «Fire» (Bow Fire) и «Hit» (Arrow Hit). В Bow — Script «BowServer», LocalScript «BowLocal», RemoteEvent «Bow».
  3. Поза с луком — один кадр, Looped, приоритет Movement (иначе ходьба перебьёт). ID — в Animation внутри BowLocal.
  4. Анимация выстрела — приоритет Action. ID — в Animation внутри BowServer.
  5. YouAreNotGui — копия из меча с текстом «Ты не лучник».
  6. ArcherCamera в папке Archer: пока лук в руках, курсор в центре экрана, камера чуть правее, персонаж смотрит туда же, куда камера.
  7. В методичке подписка на выстрел создавалась при каждом взятии лука — выстрелов становилось больше; стрела оставалась приваренной к луку. Здесь подписка одна, сварка у летящей стрелы удаляется.
  8. Высокий прыжок: анимация (Action), LocalScript «Jump» с Animation в папке Archer. Толчок вверх LinearVelocity на 0.3 секунды.

Скрипты

BowLocal

Где: 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)

BowServer

Где: 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)

ArcherCamera

Где: 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)

Jump

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