Best way to handle holding the player in the air?

I’m working on a climbing system where one of the mechanics involves the player hooking into a wall, during this time I want the player to not fall, but still be able to move around.

Methods I’ve considered so far:

  • Setting a linear velocity with the vector being the players position - Saw this in a post once, however it just causes the player to fly upwards into the air

  • Anchor the player - Not viable in this situation

  • Alignposition to an anchor point - This is the most workable for this however it would stop the player from moving left and right, which I want to allow.

(Sorry for poor formatting im typing this on mobile)

Ok the linear velocity thing works with a 0, 0, 0 velocity somehow? Though this doesn’t entirely solve it

Try setting your LinearVelocity.ForceLimitMode to PerAxis, then set the MaxAxesForce to 0, inf, 0, (or set the Z axis to inf as well if you want to keep the player from moving back and forth)

I wasn’t aware PerAxis was a thing, this basically does the thing I wanted to do, big thanks for that. (Though unfortunately locking the other axis with it doesn’t work much in this situation so I’ll have to do a separate solution for those)

I did find that using “inf” didn’t actually work though, instead I just set an extremely high number in it’s place to achieve the same thing.

Try setting LinearVelocity.RelativeTo to be Enum.RelativeTo.Attachment0 and that should give you your intended result

Yea if you’re doing it via script you’d have to use math.huge in place of inf, but a high number should work as well

I already have the relativeto set, it’s just a found if I did the unlocked other axis, the player could infinitely fly off in a direction by turning around and holding a or d (For context the player can freelook during a climb, to jump in a certain direction)

Ok interesting, I’ve gotta go but I’ll make a prototype for you tomorrow that should address these issues, I’ve bookmarked this post so I should get a notif at that time

Never mind I had some time to be able to do it.

Here’s what I came up with, I’ve added comments and such + where to enable/disable moving along the entire wall’s surface or just the top edge:

local userInputService = game:GetService('UserInputService')

local humanoid: Humanoid = script.Parent:WaitForChild('Humanoid') -- setup humanoid for wall climbing
humanoid.WalkSpeed = 0
humanoid.PlatformStand = true

local root: BasePart = script.Parent:WaitForChild('HumanoidRootPart')

local rootConstraintAttachment = Instance.new('Attachment')
rootConstraintAttachment.CFrame = CFrame.Angles(0, 0.5 * math.pi, 0)
rootConstraintAttachment.Parent = root

local moveVector = Vector3.new(0, 0, 0)

local binds = {
	[Enum.KeyCode.W] = Vector3.new(0, 1, 0); -- up
	[Enum.KeyCode.A] = Vector3.new(1, 0, 0); -- left
	[Enum.KeyCode.S] = Vector3.new(0, -1, 0); -- down
	[Enum.KeyCode.D] = Vector3.new(-1, 0, 0); -- right
}

local wall = workspace:WaitForChild('Part')

local wallCFrame = wall.CFrame
local frontFace = wall.Position - (wallCFrame.LookVector * 0.5 * wall.Size.Z) -- for "locking" to the top edge + 0.5 * wall.Size.Y * wallCFrame.UpVector

root.CFrame = CFrame.lookAt(frontFace - wallCFrame.LookVector * 0.5 * root.Size.Z, frontFace)

local wallAttachment = Instance.new('Attachment') -- attachment in the wall for the PlaneConstraint
wallAttachment.CFrame = CFrame.new(0, 0, (wall.Size.Z * 0.5 + root.Size.Z * 0.501)) * CFrame.Angles(0, 0.5 * math.pi, 0)
wallAttachment.Parent = wall

-- PlaneConstraint itself, will make the root be a certain distance from the wall's front surface
local planeConstraint = Instance.new('PlaneConstraint')
planeConstraint.Attachment0 = wallAttachment
planeConstraint.Attachment1 = rootConstraintAttachment
planeConstraint.Parent = root

-- this will be what moves us around the wall
local linearVelocity = Instance.new('LinearVelocity')
linearVelocity.Attachment0 = rootConstraintAttachment
linearVelocity.ForceLimitMode = Enum.ForceLimitMode.PerAxis
linearVelocity.MaxAxesForce = Vector3.new(math.huge, math.huge, math.huge)
linearVelocity.VectorVelocity = Vector3.new(0, 0, 0)
linearVelocity.Parent = root

-- for ensuring the root keeps an appropriate orientation
local orientation = Instance.new('AlignOrientation')
orientation.RigidityEnabled = true
orientation.CFrame = rootConstraintAttachment.WorldCFrame
orientation.Mode = Enum.OrientationAlignmentMode.OneAttachment
orientation.Attachment0 = rootConstraintAttachment
orientation.Parent = root

local function update()
	if moveVector.Magnitude < 0.001 then -- no movement, we have to check magnitude for 0 because we will get nan if we try to use .Unit on it
		linearVelocity.VectorVelocity = Vector3.zero

		return
	end

	linearVelocity.VectorVelocity = moveVector.Unit * 5 -- set the LinearVelocity's VectorVelocity; .Unit * 5 ensures they only move 5 studs per second
end

game:GetService('RunService').PostSimulation:Connect(function(dt)
	moveVector = Vector3.zero

	-- step 1: get their move vector

	for key, vec in binds do
		if userInputService:IsKeyDown(key) then
			moveVector += vec * Vector3.new(math.sign(workspace.CurrentCamera.CFrame.LookVector:Dot(root.CFrame.LookVector)), 1, 0) -- small ux thing to swap A and D if the player's camera is facing the front of their character
		end
	end
	
	-- step 2: clamp the root's position to be within the bounds of `wall`

	local cfRelative = wall.CFrame:ToObjectSpace(root.CFrame)
	local x = cfRelative.X
	local y = cfRelative.Y

	local newPosRelative = cfRelative.Position

	local clampX = math.clamp(x, -0.5 * wall.Size.X, 0.5 * wall.Size.X)
	local clampY = math.clamp(y, -0.5 * wall.Size.Y, 0.5 * wall.Size.Y)

	-- fix X if not equal
	if clampX ~= x then
		moveVector = Vector3.new(0, moveVector.Y, 0)

		newPosRelative = Vector3.new(clampX, newPosRelative.Y, newPosRelative.Z)
	end

	-- fix Y if not equal; you can remove this if you don't want to allow them to move up and down since their Y axis will remain the same
	if clampY ~= y then
		moveVector = Vector3.new(moveVector.X, 0, 0)

		newPosRelative = Vector3.new(newPosRelative.X, clampY, newPosRelative.Z)
	end
	
	-- fix their CFrame if need be
	if cfRelative.Position ~= newPosRelative then
		root.CFrame = wall.CFrame:ToWorldSpace(CFrame.new(newPosRelative) * cfRelative.Rotation)
	end
	
	update()
end)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.