Running up Walls

Hey guys, I’ve been trying to create sonic movement inside studio and I’m really close to it

[TURN DOWN YOUR VOLUME :sweat_smile:]

Example 1

Example 2

One thing that’s been holding me back is that you’re unable to run up straight walls

Problem

This is the function that handles the movement and the alignment

local function HandleMovement(delta)
	local rayDirection = rootpart.CFrame.UpVector * -5
	local ray = game.Workspace:Raycast(rootpart.Position, rayDirection, raycastParams)

	if ray then
		local position, normal = ray.Position, ray.Normal

		local moveDirection = humanoid.MoveDirection
		local projectedMoveDirection = ProjectVectorOntoNormal(moveDirection, normal)

		local surfaceRight = projectedMoveDirection:Cross(normal)
		local slopeRotation = CFrame.fromMatrix(position, surfaceRight, normal)

		local horizontalVelocity = projectedMoveDirection.Unit * walkSpeedValue.Value
		local verticalVelocity = Vector3.new(0, -10, 0)

		moveVelocity.VectorVelocity = (moveDirection.Magnitude > 0.5 and ((horizontalVelocity + verticalVelocity))) or Vector3.zero
		lookGyro.CFrame = slopeRotation
	else
		moveVelocity.VectorVelocity = Vector3.new(0, -game.Workspace.Gravity, 0)
		lookGyro.CFrame = CFrame.identity
	end
end

One thing that I noticed is that the MoveVelocity's VectorVelocity's X changes to 0 which makes it so I can’t run up and down, but only left and right

And I wondering if any of you guys can help me since I’ve be struggling with this since the past day

I’m not familiar with coding movement, but I think you need to check if the wall is vertical using something like
if math.abs(normal.Y) < 0.1 then
and do different movement if it is, so something like this?

local function HandleMovement(delta)
    local rayDirection = rootpart.CFrame.UpVector * -5
    local ray = game.Workspace:Raycast(rootpart.Position, rayDirection, raycastParams)

    if ray then
        local position, normal = ray.Position, ray.Normal

        local moveDirection = humanoid.MoveDirection

        -- check if the wall is vertical
        if math.abs(normal.Y) < 0.1 then -- condition to detect vertical walls
           -- the wall is vertical, move directly towards it
            local wallDirection = normal.Unit
            local horizontalVelocity = moveDirection.Unit * walkSpeedValue.Value
            local verticalVelocity = Vector3.new(0, -10, 0)
            
            moveVelocity.VectorVelocity = (moveDirection.Magnitude > 0.5 and (horizontalVelocity + verticalVelocity)) or Vector3.zero

            local surfaceRight = wallDirection:Cross(Vector3.new(0, 1, 0)).Unit 
            local slopeRotation = CFrame.fromMatrix(position, surfaceRight, wallDirection)
            lookGyro.CFrame = slopeRotation
        else
            -- the wall isnt vertical
            local projectedMoveDirection = ProjectVectorOntoNormal(moveDirection, normal)
            
            local surfaceRight = projectedMoveDirection:Cross(normal)
            local slopeRotation = CFrame.fromMatrix(position, surfaceRight, normal)

            local horizontalVelocity = projectedMoveDirection.Unit * walkSpeedValue.Value
            local verticalVelocity = Vector3.new(0, -10, 0)

            moveVelocity.VectorVelocity = (moveDirection.Magnitude > 0.5 and (horizontalVelocity + verticalVelocity)) or Vector3.zero
            lookGyro.CFrame = slopeRotation
        end
    else
        moveVelocity.VectorVelocity = Vector3.new(0, -game.Workspace.Gravity, 0)
        lookGyro.CFrame = CFrame.identity
    end
end

and probably add prints aswell so like when they run over slopes print(“running on slope”) and when they run over vertical walls print(“running on vertical wall”) so you can atleast know if its checking stuff correctly. again, sorry if my code doesnt work :sweat_smile:

1 Like

Super close!


One issue is that I can’t turn anymore since I’m using LookVector now

local function ProjectVectorOntoNormal(v: Vector3, n: Vector3) : Vector3
	return v - n * v:Dot(n)
end

local function GetNormalAngle(normal)
	return math.deg(math.acos(normal:Dot(Vector3.yAxis)))
end

local function HandleMovement(delta)
	local rayDirection = rootpart.CFrame.UpVector * -5
	local ray = game.Workspace:Raycast(rootpart.Position, rayDirection, raycastParams)

	if ray then
		local position, normal = ray.Position, ray.Normal
		
		local moveDirection = humanoid.MoveDirection
		local lookVector = rootpart.CFrame.LookVector
		
		local projectedMoveDirection = ProjectVectorOntoNormal(moveDirection, normal)
		
		local angle = GetNormalAngle(normal)

		if angle >= 90 then
			local surfaceRight = lookVector:Cross(normal)
			local slopeRotation = CFrame.fromMatrix(position, surfaceRight, normal)

			local horizontalVelocity = lookVector.Unit * walkSpeedValue.Value
			local verticalVelocity = Vector3.new(0, -10, 0)

			moveVelocity.VectorVelocity = (moveDirection.Magnitude > 0.5 and (horizontalVelocity + verticalVelocity)) or Vector3.zero
			lookGyro.CFrame = slopeRotation
		else
			local projectedMoveDirection = ProjectVectorOntoNormal(moveDirection, normal)

			local surfaceRight = projectedMoveDirection:Cross(normal)
			local slopeRotation = CFrame.fromMatrix(position, surfaceRight, normal)

			local horizontalVelocity = projectedMoveDirection.Unit * walkSpeedValue.Value
			local verticalVelocity = Vector3.new(0, -10, 0)

			moveVelocity.VectorVelocity = (moveDirection.Magnitude > 0.5 and (horizontalVelocity + verticalVelocity)) or Vector3.zero
			lookGyro.CFrame = slopeRotation
		end
	else
		moveVelocity.VectorVelocity = Vector3.new(0, -game.Workspace.Gravity, 0)
		lookGyro.CFrame = CFrame.identity
	end
	
	print(`MoveVelocity {moveVelocity.VectorVelocity}`)
end

Bump, super close to figuring this out

local function GetNormalAngle(normal)
	return math.deg(math.acos(normal:Dot(Vector3.yAxis)))
end

function IsUpsideDown(rootPart)
	return rootPart.CFrame.UpVector.Y < 0.3
end

local function HandleMovement(delta)
	local rayDirection = rootpart.CFrame.UpVector * -5
	local ray = game.Workspace:Raycast(rootpart.Position, rayDirection, raycastParams)
	
	if ray then
		local position, normal = ray.Position, ray.Normal

		local moveDirection = humanoid.MoveDirection
		local projectedMoveDirection = ProjectVectorOntoNormal(moveDirection, normal)
		
		local lookVector = rootpart.CFrame.LookVector

		local angle = GetNormalAngle(normal)

		if angle >= 90 and IsUpsideDown(rootpart) then
			local surfaceRight = lookVector:Cross(normal)
			local slopeRotation = CFrame.fromMatrix(position, surfaceRight, normal)

			local horizontalVelocity = lookVector * walkSpeedValue.Value
			local verticalVelocity = Vector3.new(0, -10, 0)

			moveVelocity.VectorVelocity = (moveDirection.Magnitude > 0.5 and (horizontalVelocity + verticalVelocity)) or Vector3.zero
			lookGyro.CFrame = slopeRotation

All I need now is to somehow make the character be able to turn if they pressed A or D while using LookVector

Bump again :slightly_frowning_face: Really close to solving this

Fixed!

A bit of chat gpt magic helped

local function AlignMoveDirection(moveDirection : Vector3, surfaceNormal : Vector3) : Vector3
	surfaceNormal = surfaceNormal.Unit

	if surfaceNormal.Magnitude == 0 then
		return moveDirection
	end

	local upVector = Vector3.yAxis
	local axis = upVector:Cross(surfaceNormal)

	if axis.Magnitude == 0 then
		return moveDirection
	end

	axis = axis.Unit

	local dotProduct = upVector:Dot(surfaceNormal)
	dotProduct = math.clamp(dotProduct, -1, 1)

	local angle = math.acos(dotProduct)

	local rotation = CFrame.fromAxisAngle(axis, angle)
	local alignedMoveDirection = rotation:VectorToWorldSpace(moveDirection)

	return alignedMoveDirection
end

local function GetNormalAngle(normal : Vector3) : number
	return math.deg(math.acos(Vector3.yAxis:Dot(normal)))
end

local function IsUpsideDown() : boolean
	return rootpart.CFrame.UpVector.Y < 0
end

local function HandleMovement(delta : number) : any
	local rayDirection = rootpart.CFrame.UpVector * -5
	local ray = game.Workspace:Raycast(rootpart.Position + rootpart.CFrame.LookVector * 1.1, rayDirection, raycastParams)
	
	local moveDirection = humanoid.MoveDirection
	local walkSpeed = walkSpeedValue.Value
	
	if ray then
		local hit, position, normal = ray.Instance, ray.Position, ray.Normal
		local alignedMoveDirection = AlignMoveDirection(moveDirection, normal)

		local surfaceAngle = GetNormalAngle(normal)
		local isUpsideDown = IsUpsideDown()
		
		if surfaceAngle > 170 and isUpsideDown then
			alignedMoveDirection = alignedMoveDirection.Unit
		end

		local surfaceRight = alignedMoveDirection:Cross(normal)
		local slopeRotation = CFrame.fromMatrix(position, surfaceRight, normal)

		local horizontalVelocity = alignedMoveDirection.Unit * walkSpeed
		local verticalVelocity = rootpart.CFrame.UpVector * -10

		moveVelocity.VectorVelocity = (moveDirection.Magnitude > 0.5 and (horizontalVelocity + verticalVelocity)) or Vector3.zero
		lookGyro.CFrame = slopeRotation
	else
		moveVelocity.VectorVelocity = Vector3.new(moveDirection.X * walkSpeed, -(game.Workspace.Gravity), moveDirection.Z * walkSpeed)
		lookGyro.CFrame = (moveDirection.Magnitude > 0.5 and CFrame.new(rootpart.Position, rootpart.Position + moveDirection)) or CFrame.identity
	end
end
2 Likes

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