I am interested in creating a ledge climbing system similar to Assassins Creed and/or Hitman series. The player can jump onto ledges and then from the ledge can only jump to another ledge if one is found. Is multiple ledges are found, the player must combine a+spacebar to jump towards the ledge closest to the left, and vice versa for the right. I have drawn a rough sketch to try express what I am talking about
I know I will need to use raycasting and runservice for this but I have little experience with using both services and wondering how I should go about doing this. Code examples would be appreciated, I tend to learn better being shown than being lectured.
I’d also go about it by creating some kind of movement loop (while wait() do or something) for the client, and when the user inputs a+jump, the script checks to see if there is a second ledge (and if there is), a certain animation plays and the player’s rig follows a certain path while the animation plays.
There’s not really another way for me to put this: this is an ambitious project. Make sure you know what you’re doing in terms of programming. This won’t be the main hurdle in this project you are taking on.
This is the general structure of the script. Do you think this might work? I know its in a local script and preferably I would like to play anims and stuff on the server so other players can see the user jumping to and from ledges.
Local script:
Checks for ledges on heartbeat
If ledge found AND player userinputservice keycode == jump+a then
Play jump left animation and move move character to the ledge (what can i use to make the character actually jump to the ledge and follow a path?)
Elseif no ledge was found then strafe character to the left
You can use pathfinding and/or raycasting to make this work I believe. Or, you can do character.humanoid:Jump() and use :Move() to move forward. I believe pathfinding will be your best bet though.
Great, thank you. I’ll attach what I’ve got so far, all seems to be working except I’m not sure what I should use to actually attach the player to the ledge. I was originally going to use welds but problem with welds is I dont know how to make it weld the player to the ledge on an offset (e.g 1 stud off the part so the body isnt inside the ledge) or how to weld the player to the ledge facing the direction that they jumped onto it.
Code in local script:
--[[ local hitbox
local success, err = pcall(function()
hitbox = workspace:FindFirstChild("testLedge"):FindFirstChild("Hitbox")
end)
if not success then
print("Failed to locate HITBOX: ", err)
end
if not (hitbox == nil) then
hitbox.Touched:Connect(function(hit)
if hit:IsA("Part") or hit:IsA("Accessory") and hit.Parent:FindFirstChild("Humanoid") then
--if ledge found and value "CanUse" == true then weld to ledge
--player UIS "jump+a" then raycast for ledges on left
-- if NO ledges found, move character to the left X studs (X represents however many studs animation moves)
-- if ledge IS FOUND then jump character to ledge using cframe or vector3 + 1 stud (smooth so might have to be +0.1 we will see) to simulate jumping + jump animation
-- if NO ledges found AND cannot move left then return (nil) end
--player UIS "a"
-- move character X studs to left
-- if no room on left to move return (nil) end
--REPEAT EVERYTHING FOR UIS "W, A, S, D" (excluding a if already done above ^)
end
end)
end
--]]
wait(2)
local char = game.Players.LocalPlayer.Character
local humanoid = char:FindFirstChild("Humanoid")
local root = char:WaitForChild("HumanoidRootPart")
--SERVICES
local UIS = game:GetService("UserInputService")
local runService = game:GetService("RunService")
--raycast for ledges on heartbeat
runService.Heartbeat:Connect(function(dt)
--Create weld
local weld = Instance.new("Weld")
weld.Parent = root
weld.Part1 = root
if weld.Enabled then
weld.Enabled = false
weld.Part0 = nil
end
--Set raycast properties
local rayOrigin = root.Position
local rayDirection = root.CFrame.LookVector
local raycastParams = RaycastParams.new()
local raydist = 100
local rootCF = root.CFrame
raycastParams.FilterDescendantsInstances = {game.Players.LocalPlayer.Character:GetDescendants(), workspace.Essentials:GetDescendants(), workspace.Excludes:GetDescendants()}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.IgnoreWater = true
local result = workspace:Raycast(rayOrigin, rayDirection * raydist, raycastParams)
if result then
print("Result! ", tostring(result))
--[[ local distance = (rayOrigin - result.Position).Magnitude
local p = Instance.new("Part")
local folder = workspace.Excludes
p.Parent = folder
p.Anchored = true
p.CanCollide = false
p.Size = Vector3.new(0.1, 0.1, distance)
p.CFrame = CFrame.lookAt(rayOrigin, rayDirection)*CFrame.new(0, 0, -distance/2)
--]]
--if ledge found and value "CanUse" == true then weld to ledge
if result and result.Instance then
if result.Instance.Name == "Hitbox" then
warn("Hitbox found")
local hitbox = result.Instance
local ledge = hitbox.Parent
if (root.Position - ledge.Position).Magnitude <= 3 then
if ledge:FindFirstChild("CanUse").Value == true then
weld.Enabled = true
local move = humanoid.MoveDirection:Dot(rootCF.LookVector)
weld.C0 = result.Instance.CFrame:ToObjectSpace(rootCF) * CFrame.new(0, dt*move*5, -dt*move)
weld.Part0 = ledge
--Weld part that I might change ^^
end
end
end
end
else
print("No result!")
end
end)
Only part left after I figure out the welding is the userinputservice and jumping bit.