My script has a folder named "IKArmTargets " that is in the workspace. Each player has two sets of Inverse kinematic feet or arms, whichever way you look at it, that are used to maneuver. The only downside that I'm trying to figure out is how I can make it so each player can see each other's foot targets left and right? All they can see is the starter character I made for all players, not the IK foot targets. Here's the script: -- Enhanced R6 IK with Wall Walking Physics
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
-- Configuration
local FOOT_HEIGHT_OFFSET = 0.3
local MAX_STEP_HEIGHT = 0.4
local STEP_SPEED = 11
local FOOT_DISTANCE = 1.5
local FOOT_WIDTH = 1.5
local ARM_SWING_AMOUNT = 10
local LEG_LERP_SPEED = 20.0
local ARM_LERP_SPEED = 15.0
local MOVE_SPEED = 18
local TURN_SPEED = 43
local GROUND_CHECK_DISTANCE = 0.5
local REQUIRED_GROUND_ANGLE = math.rad(45)
local CLIMB_CHECK_DISTANCE = 3.0
local JUMP_POWER = 10
local JUMP_BOUNCE = 10
local ANCHOR_DELAY = 0
local ARM_ANCHOR_HEIGHT = 0.5
local CLIMB_GRAB_DISTANCE = 5
local CLIMB_PULL_FORCE = 5
local SPIDER_LEG_SPREAD = 5
-- Wall Walking Physics
local WALL_WALK_GRAVITY = 1
local WALL_JUMP_FORCE = 10
local WALL_STICK_FORCE = 300
local CAMERA_LERP_SPEED = 0
local MAX_WALL_ANGLE = math.rad(45)
local WALL_CHECK_DISTANCE = 5
-- Services
local Players = game:GetService("Players")
local Camera = workspace.CurrentCamera
-- Create shared folders for all players to see
local sharedIKFolder = Instance.new("Folder")
sharedIKFolder.Name = "SharedIKTargets"
sharedIKFolder.Parent = workspace
-- Persistent arm storage
local armTargetsFolder = Instance.new("Folder")
armTargetsFolder.Name = "IKArmTargets"
armTargetsFolder.Parent = workspace
-- Get character
local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")
-- Wall Walking State
local currentWallNormal = Vector3.new(0, 1, 0)
local isWallWalking = false
local lastWallNormal = Vector3.new(0, 1, 0)
-- Function to create or get arm targets with collision
local function ensureArmTargets()
local leftArmTarget = armTargetsFolder:FindFirstChild("LeftArmIKTarget")
local rightArmTarget = armTargetsFolder:FindFirstChild("RightArmIKTarget")
if not leftArmTarget then
leftArmTarget = Instance.new("Part")
leftArmTarget.Name = "LeftArmIKTarget"
leftArmTarget.Anchored = true
leftArmTarget.CanCollide = false
leftArmTarget.Transparency = 0.5
leftArmTarget.Color = Color3.fromRGB(255, 0, 0)
leftArmTarget.Size = Vector3.new(1, 1, 1)
leftArmTarget.Parent = armTargetsFolder
local collision = Instance.new("BallSocketConstraint")
collision.Parent = leftArmTarget
end
if not rightArmTarget then
rightArmTarget = leftArmTarget:Clone()
rightArmTarget.Name = "RightArmIKTarget"
rightArmTarget.Color = Color3.fromRGB(0, 0, 255)
rightArmTarget.Parent = armTargetsFolder
end
return leftArmTarget, rightArmTarget
end
-- Function to setup character parts with shared foot targets
local function setupCharacterParts()
local leftLeg = character:FindFirstChild("Left Leg")
local rightLeg = character:FindFirstChild("Right Leg")
local leftArm = character:FindFirstChild("Left Arm")
local rightArm = character:FindFirstChild("Right Arm")
-- Create foot targets in shared folder if they don't exist
local playerFootFolder = sharedIKFolder:FindFirstChild(player.Name)
if not playerFootFolder then
playerFootFolder = Instance.new("Folder")
playerFootFolder.Name = player.Name
playerFootFolder.Parent = sharedIKFolder
end
if not playerFootFolder:FindFirstChild("LeftFootIKTarget") then
local leftFootTarget = Instance.new("Part")
leftFootTarget.Name = "LeftFootIKTarget"
leftFootTarget.Anchored = true
leftFootTarget.CanCollide = false
leftFootTarget.Transparency = 0
leftFootTarget.Color = Color3.fromRGB(0, 255, 0)
leftFootTarget.Size = Vector3.new(1, 1, 2.45)
leftFootTarget.Parent = playerFootFolder
-- Make foot target visible to all players
local surfaceGui = Instance.new("SurfaceGui")
surfaceGui.Face = Enum.NormalId.Top
surfaceGui.Adornee = leftFootTarget
surfaceGui.Parent = leftFootTarget
local textLabel = Instance.new("TextLabel")
textLabel.Size = UDim2.new(1, 0, 1, 0)
textLabel.Text = "Left Foot"
textLabel.TextColor3 = Color3.new(1, 1, 1)
textLabel.BackgroundTransparency = 1
textLabel.Parent = surfaceGui
end
if not playerFootFolder:FindFirstChild("RightFootIKTarget") then
local rightFootTarget = playerFootFolder.LeftFootIKTarget:Clone()
rightFootTarget.Name = "RightFootIKTarget"
rightFootTarget.Color = Color3.fromRGB(0, 200, 0)
rightFootTarget.Parent = playerFootFolder
-- Update the text label for right foot
local surfaceGui = rightFootTarget:FindFirstChildOfClass("SurfaceGui")
if surfaceGui then
local textLabel = surfaceGui:FindFirstChildOfClass("TextLabel")
if textLabel then
textLabel.Text = "Right Foot"
end
end
end
return leftLeg, rightLeg, leftArm, rightArm, playerFootFolder
end
local leftLeg, rightLeg, leftArm, rightArm, playerFootFolder = setupCharacterParts()
local leftArmTarget, rightArmTarget = ensureArmTargets()
-- Movement state
local moveDir = Vector3.new()
local turnDir = 0
local isMoving = false
local isJumping = false
local isClimbing = false
local lastMoveTime = 0
local lastGroundTime = 0
local anchorFeet = false
local anchorArms = false
-- Limb states
local limbs = {
leftFoot = { currentPos = Vector3.new(), targetPos = Vector3.new(), stepProgress = 0, moving = false },
rightFoot = { currentPos = Vector3.new(), targetPos = Vector3.new(), stepProgress = 0, moving = false },
leftArm = { swingPhase = 0, swingDir = 1, anchorProgress = 0, isGrabbing = false },
rightArm = { swingPhase = math.pi, swingDir = -1, anchorProgress = 0, isGrabbing = false }
}
-- Raycast setup
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {character}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
-- Helper functions
local function lerp(a, b, t) return a + (b - a) * math.clamp(t, 0, 1) end
local function findGround(position)
local origin = position + Vector3.new(0, 2, 0)
local ray = workspace:Raycast(origin, Vector3.new(0, -GROUND_CHECK_DISTANCE, 0), rayParams)
if ray and math.acos(ray.Normal:Dot(Vector3.new(0, 1, 0))) < REQUIRED_GROUND_ANGLE then
lastGroundTime = os.clock()
return ray.Position + Vector3.new(0, FOOT_HEIGHT_OFFSET, 0), ray.Normal
end
return position - Vector3.new(0, GROUND_CHECK_DISTANCE - FOOT_HEIGHT_OFFSET, 0), Vector3.new(0, 1, 0)
end
-- Wall detection similar to referenced asset
local function checkForWalls()
local origin = rootPart.Position
local params = RaycastParams.new()
params.FilterDescendantsInstances = {character}
params.FilterType = Enum.RaycastFilterType.Exclude
-- Check in multiple directions for walls
local directions = {
rootPart.CFrame.LookVector,
-rootPart.CFrame.LookVector,
rootPart.CFrame.RightVector,
-rootPart.CFrame.RightVector
}
for _, dir in ipairs(directions) do
local ray = workspace:Raycast(origin, dir * WALL_CHECK_DISTANCE, params)
if ray and ray.Instance.CanCollide then
local angle = math.acos(ray.Normal:Dot(Vector3.new(0, 1, 0)))
if angle > MAX_WALL_ANGLE then
return ray.Position, ray.Normal
end
end
end
return nil
end
-- Enhanced spider-like climbing detection
local function findClimbSurface(armPosition, direction)
local origin = armPosition
local ray = workspace:Raycast(origin, direction.Unit * CLIMB_GRAB_DISTANCE, rayParams)
if ray and ray.Instance.CanCollide then
local angle = math.acos(ray.Normal:Dot(Vector3.new(0, 1, 0)))
if angle > math.rad(20) and angle < math.rad(160) then
return ray.Position, ray.Normal
end
end
return nil
end
-- Foot movement
local function updateFoot(dt, state, otherState, targetPart, offset)
local rootPos = rootPart.Position
local rootCF = rootPart.CFrame
local desiredPos = rootPos + rootCF:VectorToWorldSpace(offset)
state.targetPos = findGround(desiredPos)
local dist = (state.targetPos - state.currentPos).Magnitude
if dist > 0.5 and not state.moving and not otherState.moving and not anchorFeet then
state.moving = true
state.stepProgress = 0
end
if state.moving then
state.stepProgress = state.stepProgress + dt * STEP_SPEED
if state.stepProgress >= 1 then
state.currentPos = state.targetPos
state.moving = false
else
local t = state.stepProgress
local height = math.sin(t * math.pi) * MAX_STEP_HEIGHT
local mid = (state.currentPos + state.targetPos) * 0.5 + Vector3.new(0, height, 0)
state.currentPos = lerp(lerp(state.currentPos, mid, t), lerp(mid, state.targetPos, t), t)
end
end
targetPart.CFrame = CFrame.new(state.currentPos) * CFrame.Angles(math.rad(-90), 0, 0)
end
-- Enhanced spider-like arm movement with climbing and camera adjustment
local function updateArm(dt, state, side, targetPart, arm)
local rootPos = rootPart.Position
local rootCF = rootPart.CFrame
-- Check for climbable surfaces
if isMoving and not isJumping then
local armOffset = Vector3.new(side * SPIDER_LEG_SPREAD, 0, 0)
local armWorldPos = rootPos + rootCF:VectorToWorldSpace(armOffset)
local lookDir = rootCF.LookVector
local climbPos, climbNormal = findClimbSurface(armWorldPos, lookDir)
if climbPos then
state.isGrabbing = true
state.climbPos = climbPos
state.climbNormal = climbNormal
isClimbing = true
else
state.isGrabbing = false
isClimbing = limbs.leftArm.isGrabbing or limbs.rightArm.isGrabbing
end
else
state.isGrabbing = false
isClimbing = false
end
if state.isGrabbing then
-- Spider climbing behavior
local grabPos = state.climbPos + (state.climbNormal * 0.3)
local armRot = CFrame.lookAt(grabPos, grabPos + state.climbNormal)
targetPart.CFrame = armRot * CFrame.Angles(math.rad(90), 0, 0)
-- Apply climbing pull force
if isMoving then
local alongWall = rootCF.RightVector:Dot(state.climbNormal) * state.climbNormal
local moveDirection = (rootCF.LookVector - alongWall).Unit
rootPart.Velocity = moveDirection * MOVE_SPEED + (state.climbNormal * CLIMB_PULL_FORCE)
end
else
-- Normal arm movement
if not isMoving and not isJumping then
state.anchorProgress = math.min(state.anchorProgress + dt * 2, 1)
else
state.anchorProgress = math.max(state.anchorProgress - dt * 3, 0)
end
if state.anchorProgress > 0 then
-- Anchored arm position
local anchorOffset = Vector3.new(side * 1.5, -ARM_ANCHOR_HEIGHT * state.anchorProgress, 0)
local armPos = rootPos + rootCF:VectorToWorldSpace(anchorOffset)
local armRot = CFrame.Angles(math.rad(-20), 0, math.rad(side * -10))
targetPart.CFrame = CFrame.new(armPos) * armRot
else
-- Normal swinging behavior
if isMoving then
state.swingPhase = state.swingPhase + dt * 5 * state.swingDir
else
state.swingPhase = lerp(state.swingPhase, side == 1 and math.pi or 0, dt * 2)
end
local armOffset = Vector3.new(side * 1.5, 0.5, 0)
local swingOffset = Vector3.new(0, 0, math.sin(state.swingPhase) * ARM_SWING_AMOUNT)
local armPos = rootPos + rootCF:VectorToWorldSpace(armOffset + swingOffset)
local armRot = CFrame.Angles(math.rad(-20), 0, math.rad(side * -10))
targetPart.CFrame = CFrame.new(armPos) * armRot
end
end
-- Apply to actual arm with collision
if arm then
arm.CFrame = arm.CFrame:Lerp(targetPart.CFrame, dt * ARM_LERP_SPEED)
arm.CanCollide = state.isGrabbing
end
end
-- Wall walking physics update
local function updateWallWalking(dt)
local wallPos, wallNormal = checkForWalls()
if wallNormal then
-- Calculate new up vector based on wall normal
local newUp = -wallNormal
local newRight = rootPart.CFrame.RightVector - (rootPart.CFrame.RightVector:Dot(newUp)) * newUp
local newLook = newUp:Cross(newRight).Unit
-- Smoothly transition to wall orientation
currentWallNormal = lerp(currentWallNormal, newUp, dt * 5)
-- Apply wall stick force
rootPart.Velocity = rootPart.Velocity + (currentWallNormal * WALL_STICK_FORCE * dt)
-- Adjust gravity
Workspace.Gravity = WALL_WALK_GRAVITY
-- Rotate character to match wall
rootPart.CFrame = CFrame.new(rootPart.Position, rootPart.Position + newLook) * CFrame.fromMatrix(Vector3.new(), newRight, newUp, newLook)
-- Adjust camera
local camCF = Camera.CFrame
local newCamLook = camCF.LookVector - (camCF.LookVector:Dot(currentWallNormal)) * currentWallNormal
Camera.CFrame = CFrame.new(camCF.Position, camCF.Position + newCamLook)
isWallWalking = true
lastWallNormal = wallNormal
else
-- Return to normal gravity
Workspace.Gravity = 196.2
isWallWalking = false
end
end
-- Movement handling
local function updateMovement(dt)
local now = os.clock()
local wasMoving = isMoving
isMoving = moveDir.Magnitude > 0
if isMoving then
lastMoveTime = now
anchorFeet = false
if turnDir ~= 0 and moveDir.Z == 0 then
humanoid.AutoRotate = false
rootPart.CFrame = rootPart.CFrame * CFrame.Angles(0, turnDir * TURN_SPEED * dt, 0)
else
humanoid.AutoRotate = true
end
local moveVector = rootPart.CFrame:VectorToWorldSpace(moveDir) * MOVE_SPEED
if not isClimbing and not isWallWalking then
rootPart.Velocity = Vector3.new(moveVector.X, rootPart.Velocity.Y, moveVector.Z)
end
else
humanoid.AutoRotate = true
if wasMoving and now - lastMoveTime > ANCHOR_DELAY then
anchorFeet = true
end
if not isJumping and rootPart.Velocity.Y > -5 then
rootPart.Velocity = Vector3.new(0, rootPart.Velocity.Y, 0)
end
end
end
-- Enhanced jump with wall jump
local function handleJump()
if isJumping then return end
if isWallWalking then
-- Wall jump
isJumping = true
local jumpDir = (rootPart.CFrame.LookVector + lastWallNormal).Unit
rootPart.Velocity = jumpDir * WALL_JUMP_FORCE
isWallWalking = false
delay(0.5, function()
isJumping = false
end)
else
-- Normal jump
local groundPos = findGround(rootPart.Position)
if (os.clock() - lastGroundTime) < 0.1 then
isJumping = true
rootPart.Velocity = Vector3.new(rootPart.Velocity.X, JUMP_POWER, rootPart.Velocity.Z)
leftLeg.CFrame = leftLeg.CFrame * CFrame.new(0, -JUMP_BOUNCE, 0)
rightLeg.CFrame = rightLeg.CFrame * CFrame.new(0, -JUMP_BOUNCE, 0)
delay(0.5, function()
isJumping = false
end)
end
end
end
-- Main update
local function update(dt)
if not rootPart or not rootPart.Parent then return end
updateMovement(dt)
updateWallWalking(dt)
local moveOffset = Vector3.new(moveDir.X * FOOT_DISTANCE, 0, moveDir.Z * FOOT_DISTANCE)
if not isJumping then
updateFoot(dt, limbs.leftFoot, limbs.rightFoot, playerFootFolder.LeftFootIKTarget,
Vector3.new(-FOOT_WIDTH, 0, -FOOT_DISTANCE/2) + moveOffset)
updateFoot(dt, limbs.rightFoot, limbs.leftFoot, playerFootFolder.RightFootIKTarget,
Vector3.new(FOOT_WIDTH, 0, -FOOT_DISTANCE/2) + moveOffset)
end
updateArm(dt, limbs.leftArm, -1, leftArmTarget, leftArm)
updateArm(dt, limbs.rightArm, 1, rightArmTarget, rightArm)
if not isJumping then
leftLeg.CFrame = leftLeg.CFrame:Lerp(CFrame.new(limbs.leftFoot.currentPos), dt * LEG_LERP_SPEED)
rightLeg.CFrame = rightLeg.CFrame:Lerp(CFrame.new(limbs.rightFoot.currentPos), dt * LEG_LERP_SPEED)
end
end
-- Input handling
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.W then moveDir = Vector3.new(0, 0, -1)
elseif input.KeyCode == Enum.KeyCode.S then moveDir = Vector3.new(0, 0, 1)
elseif input.KeyCode == Enum.KeyCode.A then
if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then
turnDir = -1
else
moveDir = Vector3.new(-1, 0, 0)
end
elseif input.KeyCode == Enum.KeyCode.D then
if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then
turnDir = 1
else
moveDir = Vector3.new(1, 0, 0)
end
elseif input.KeyCode == Enum.KeyCode.Space then
handleJump()
end
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode == Enum.KeyCode.W or input.KeyCode == Enum.KeyCode.S then
if moveDir.Z ~= 0 then moveDir = Vector3.new(moveDir.X, 0, 0) end
elseif input.KeyCode == Enum.KeyCode.A or input.KeyCode == Enum.KeyCode.D then
if moveDir.X ~= 0 then moveDir = Vector3.new(0, 0, moveDir.Z) end
turnDir = 0
end
end)
-- Initialize
local function init()
-- Re-get character parts in case of reset
leftLeg, rightLeg, leftArm, rightArm, playerFootFolder = setupCharacterParts()
local rootPos = rootPart.Position
local rootCF = rootPart.CFrame
limbs.leftFoot.currentPos = findGround(rootPos + rootCF:VectorToWorldSpace(Vector3.new(-FOOT_WIDTH, 0, 0)))
limbs.rightFoot.currentPos = findGround(rootPos + rootCF:VectorToWorldSpace(Vector3.new(FOOT_WIDTH, 0, 0)))
playerFootFolder.LeftFootIKTarget.CFrame = CFrame.new(limbs.leftFoot.currentPos) * CFrame.Angles(math.rad(-90), 0, 0)
playerFootFolder.RightFootIKTarget.CFrame = CFrame.new(limbs.rightFoot.currentPos) * CFrame.Angles(math.rad(-90), 0, 0)
if leftArm and rightArm then
leftArmTarget.CFrame = CFrame.new(rootPos + rootCF:VectorToWorldSpace(Vector3.new(-1.5, 0.5, 0))) * CFrame.Angles(math.rad(-20), 0, math.rad(10))
rightArmTarget.CFrame = CFrame.new(rootPos + rootCF:VectorToWorldSpace(Vector3.new(1.5, 0.5, 0))) * CFrame.Angles(math.rad(-20), 0, math.rad(-10))
end
end
-- Handle character reset
player.CharacterAdded:Connect(function(newCharacter)
character = newCharacter
humanoid = character:WaitForChild("Humanoid")
rootPart = character:WaitForChild("HumanoidRootPart")
leftLeg, rightLeg, leftArm, rightArm, playerFootFolder = setupCharacterParts()
-- Reinitialize with existing arm targets
init()
end)
init()
RunService.Heartbeat:Connect(update)
print("Enhanced R6 IK with Wall Walking Physics Active")