You can set a debounce on clicking! Sorry by this late response… But i hope that helps you!
Will this be mobile compatible as well? Also epic resource for new devs!
This was my literal dream as a little kid on roblox. I always wanted to make one of these!!! THANK YOU
Make a batman game?
“Thats amazing.”
Since grapple_client do most of the work, is this expensive for the server to handle?
YOOOO This is actually REALLY dopeee!!! Thank you for sharing this with the community, Will def. have to check this out ;D
Really enjoyed this this mechanic, really hope to see more of your work.
What method is used to move the player towards its target?
It’s official… this is the COOLEST thing I’ve ever seen on this platform.
I’d Prefer an easier way to select grappling points, but besides that, this is amazing!
Why do I get sent to 0, -340282346638528859811704183484516925440, 0 when I use it twice in a short period of time?
I’m having the same problem… how do I add a debounce to it?
Really cool source! What you do for the community is really good.
Keep up the great work!
Actually, my theory was wrong, there’s already a debounce line, besides that it is not related with the bug, it would be an issue related with the distance, i tried to fix it from checking the distance between the player and the place you wanna grapple, and if the distance is not long enough to grapple, then just return nothing, and it worked.
This is the script related to the bug:
And here’s the code with the bug fixed:
-- setup
local MinDistanceRequired = 2 --If you still have the bug, try to lightly increment this value
local import = require(game.ReplicatedStorage:WaitForChild("import"))
-- constants
local Services = import("shared/services")
local Raycast = import("shared/raycast")
local Grapple = import("client/grapple")
local GrappleEffect = import("effects/grapple")
local GrappleRemote = import("remotes/grapple_hook")
local Player = Services.Players.LocalPlayer
local Camera = Services.Workspace.CurrentCamera
local GrapplePoints = Services.Workspace:WaitForChild("GrapplePoints")
-- variables
local character, humanoid, root
local animations = {}
local lastUpdate = 0
local grappleCooldown = 0
local grappling = false
-- functions
local function HandleCharacter(newCharacter) -- set up variables for new characters
character, humanoid, root = nil, nil, nil
animations = {}
if not newCharacter then
return
end
-- load animations
humanoid = newCharacter:WaitForChild("Humanoid")
local animFolder = newCharacter:WaitForChild("GrappleAnimations")
animations = {
Cast = humanoid:LoadAnimation(animFolder:WaitForChild("Cast"))
}
-- wait for the root
root = newCharacter:WaitForChild("HumanoidRootPart")
character = newCharacter
end
local function GrappleToPosition(position)
-- make sure the character exists
if not character then
return
end
-- dont allow grappling while already grappling
if grappling then
return
end
local direction = (position - root.Position)
local travelCFrame = CFrame.new(position + Vector3.new(0, 4, 0), position + Vector3.new(direction.X, 4, direction.Z))
if (root.Position - travelCFrame.Position).Magnitude < MinDistanceRequired then --//Here we check if the distance between our character's root and the grapple position is lower than the minimum distance required.
return
end
-- fire remote so the server can replicate the effect
GrappleRemote:FireServer(position)
grappling = true
-- play the grapple effect on the client
GrappleEffect:Play(character, position, Grapple.CastTime, Grapple.TravelTime)
-- ez camera tween for more impact
local cameraInfo = TweenInfo.new((Grapple.CastTime + Grapple.TravelTime) / 2, Enum.EasingStyle.Quad, Enum.EasingDirection.In, 0, true)
local cameraTween = Services.TweenService:Create(Camera, cameraInfo, {FieldOfView = Camera.FieldOfView + 30})
cameraTween:Play()
-- cast
-- play the cast animation
animations.Cast:Play(0.1, 1, 1/(Grapple.CastTime * 2))
-- tween the character into position, keeping them floating in the air
local castInfo = TweenInfo.new(Grapple.CastTime, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local castTween = Services.TweenService:Create(root, castInfo, {CFrame = CFrame.new(root.Position, position)})
castTween:Play()
castTween.Completed:Wait()
-- travel
-- calculate the landing position and tween to it
local travelInfo = TweenInfo.new(Grapple.TravelTime, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local travelTween = Services.TweenService:Create(root, travelInfo, {CFrame = travelCFrame})
travelTween:Play()
travelTween.Completed:Wait()
-- land
-- switch to getting up in case we tripped somehow
humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
grappling = false
end
-- events
GrappleRemote.OnClientEvent:Connect(function(...) -- play the grapple effect when the server says to
GrappleEffect:Play(...)
end)
Services.UserInputService.InputBegan:Connect(function(inputObject, processed) -- basic key input
if processed then
return
end
if inputObject.KeyCode == Grapple.Hotkey then
if not Grapple.Target then
return
end
GrappleToPosition(Grapple.Target.Position)
end
end)
Player.CharacterAdded:Connect(HandleCharacter)
-- main
Services.RunService.Stepped:Connect(function() -- main targeting logic
if not character then
return
end
-- if we're grappling, set the root velocity to 0,0,0 to avoid weird physics glitches
if grappling then
root.Velocity = Vector3.new()
end
-- only update the target if enough time has elapsed
if tick() - lastUpdate < Grapple.TargetUpdateTime then
return
end
lastUpdate = tick()
local targets = {}
for _, point in pairs(GrapplePoints:GetChildren()) do
local offset = point.Position - root.Position
local distance = offset.Magnitude
local dotted = Camera.CFrame.LookVector:Dot(offset)
-- make sure the point is in front of us and within double range
if dotted > 0 and distance < Grapple.MaxRange * 2 then
-- check that the grapple point is visible, ignoring characters
local ignore = {}
for _, player in pairs(Services.Players:GetPlayers()) do
if player.Character and player.Character.Parent then
table.insert(ignore, player.Character)
end
end
local hit = Raycast:CastIgnore(root.Position, offset, ignore)
if hit and hit == point then
-- add the target to our dictionary, with info about angle from center and distance
local angle = math.acos(dotted / offset.Magnitude)
targets[point] = {Angle = angle, Distance = distance}
end
end
end
-- select the target closest to the middle of the screen
local newTarget, angle = nil, 10000
for target, info in pairs(targets) do
if info.Angle < angle then
newTarget = target
angle = info.Angle
end
end
-- set closest target
Grapple.ClosestTarget = newTarget
-- if the closest target is out of range, don't set the primary target
if newTarget then
local info = targets[newTarget]
Grapple.ClosestRange = info.Distance
if info.Distance > Grapple.MaxRange then
newTarget = nil
end
end
Grapple.Target = newTarget
end)
-- init
HandleCharacter(Player.Character)
Remember you can tell me if there’s any issue with it or something. Thanks for reading.
Where Would I put the Animation?
it might be just me but I don’t think the stream link works anymore
Awesome!! This is actually one of the best grapple hooks I’ve ever seen!
It’s not I tested the link and it does not work for me either
Would it be possible to butcher this in a way?
you want to change it into R6 but you don’t know how? follow my lead
first, you gotta change it into R6 and then go to ReplicatedStorage.effects.grapple
and open the module then go to line 20
local hand = character:FindFirstChild("RightHand")
change the “RightHand” into “Right Arm”
this should look like this:
local hand = character:FindFirstChild("Right Arm")
and we’re done!
don’t forget to change the animation into R6
it’s R6 compatible but it just doesn’t show the chain and effect from the grapple