Roblox · Движение

Крюк-кошка

Предмет-крюк: кликаешь по стене, крыше или дереву — к точке тянется верёвка, и персонаж летит туда. Дальность 120 studs.

Структура

Синим — что нужно создать.

StarterPack
└─ Grapple (Tool)
   ├─ Handle (Part) — крюк; CanCollide выкл.
   └─ GrappleLocal (LocalScript)

Шаги

  1. Tool «Grapple» с деталью Handle (CanCollide выкл., Massless вкл.) → StarterPack.
  2. В Tool — LocalScript «GrappleLocal». Всё на клиенте: персонажа двигает его компьютер, остальные видят полёт.
  3. Как работает: луч из камеры через курсор (mouse.UnitRay). Попал не дальше RANGE — в точке появляется невидимый зацеп, Beam рисует верёвку, LinearVelocity тянет персонажа к зацепу (не дольше 3 секунд), в конце — подскок, чтобы забраться на край.
  4. Верёвку видит только сам игрок — её рисует его компьютер.

Скрипты

GrappleLocal

Где: StarterPack → Grapple · LocalScript
Зачем: Луч, верёвка и полёт к точке.

-- крюк-кошка (Tool)
local tool = script.Parent
-- события каждого кадра
local RunService = game:GetService("RunService")
-- этот игрок
local player = game.Players.LocalPlayer
-- мышь — куда целимся
local mouse = player:GetMouse()
-- дальность крюка
local RANGE = 120
-- скорость притягивания
local PULL_SPEED = 80
-- уже летим
local busy = false

-- клик (тап) с крюком в руках
tool.Activated:Connect(function()
    -- уже летим
    if busy then return end
    -- персонаж
    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
    -- настройки луча
    local params = RaycastParams.new()
    -- не замечать список
    params.FilterType = Enum.RaycastFilterType.Exclude
    -- свой персонаж
    params.FilterDescendantsInstances = {character}
    -- луч из камеры через курсор
    local result = workspace:Raycast(mouse.UnitRay.Origin, mouse.UnitRay.Direction * 1000, params)
    -- никуда не попал или слишком далеко
    if not result or (result.Position - root.Position).Magnitude > RANGE then return end
    -- летим
    busy = true
    -- точка зацепа
    local hook = Instance.new("Part")
    -- не падает
    hook.Anchored = true
    -- не мешает
    hook.CanCollide = false
    -- не ловит лучи
    hook.CanQuery = false
    -- невидимая
    hook.Transparency = 1
    -- маленькая
    hook.Size = Vector3.new(0.2, 0.2, 0.2)
    -- где зацепились
    hook.Position = result.Position
    -- в мир (только у этого игрока)
    hook.Parent = workspace
    -- начало верёвки
    local a0 = Instance.new("Attachment")
    -- в крюке в руке
    a0.Parent = tool.Handle
    -- конец верёвки
    local a1 = Instance.new("Attachment")
    -- в точке зацепа
    a1.Parent = hook
    -- верёвка — линия между точками
    local rope = Instance.new("Beam")
    -- от руки
    rope.Attachment0 = a0
    -- до зацепа
    rope.Attachment1 = a1
    -- толщина
    rope.Width0 = 0.15
    -- толщина
    rope.Width1 = 0.15
    -- всегда видна
    rope.FaceCamera = true
    -- коричневая
    rope.Color = ColorSequence.new(Color3.fromRGB(70, 50, 35))
    -- показываем
    rope.Parent = hook
    -- точка тяги
    local ra = Instance.new("Attachment")
    -- в центр персонажа
    ra.Parent = root
    -- тяга к зацепу
    local pull = Instance.new("LinearVelocity")
    -- тянем персонажа
    pull.Attachment0 = ra
    -- без ограничения силы
    pull.MaxForce = math.huge
    -- включаем
    pull.Parent = root
    -- время начала
    local start = os.clock()
    -- пока не долетели (не дольше 3 с)
    while (hook.Position - root.Position).Magnitude > 4 and os.clock() - start < 3 and humanoid.Health > 0 and tool.Parent == character do
        -- летим прямо к зацепу
        pull.VectorVelocity = (hook.Position - root.Position).Unit * PULL_SPEED
        -- кадр
        RunService.Heartbeat:Wait()
    -- конец цикла
    end
    -- тяга кончилась
    pull:Destroy()
    -- точка тоже
    ra:Destroy()
    -- зацеп и верёвка
    hook:Destroy()
    -- начало верёвки
    a0:Destroy()
    -- небольшой подскок — забраться на край
    root.AssemblyLinearVelocity = Vector3.new(0, 25, 0)
    -- можно снова
    busy = false
-- конец обработчика
end)

Проверка

Запусти Play (или Run) и убедись:

Собрать крюк одной командой