[open source] sekiro grapple hook

I take this that you will be creating and open sourcing more of projects? If so, what kind of game mechanics are you looking to make and are the streams for this on a schedule?

Will def. Use this for my upcoming Vestaria like game :eyes:

2 Likes

Wow!

I do not know how people even make with these sort of things.

Awesome.

3 Likes

This is one of the coolest things I have seen! Thank you for making it open source!

1 Like

Wow, this seems like a great resource to learn from, thanks for open sourcing it!

1 Like

Amazing! Ive been trying to make this but failed badly.

1 Like

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! :+1:

3 Likes

This was my literal dream as a little kid on roblox. I always wanted to make one of these!!! THANK YOU :star_struck:

Make a batman game? :thinking:
“Thats amazing.”

1 Like

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. :ok_hand:

1 Like

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! :slightly_smiling_face:

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:
image

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.

3 Likes