Custom Character Controller Collision Response Algorithm

Hey bois.

I’ve been working on a character controller for the past few weeks and found an algorithm to handle horizontal collision response but it’s not working properly as it allows the player to go through walls when walking towards it at certain angles, here’s a preview of what it looks like in action:

I’m not sure if it has to do with the way I project the velocity vector or perhaps even with floating point errors accumilating, here is the code that adjusts the horizontal velocity based on collision:

local function projectOnPlane(vector: Vector3, planeNormal: Vector3)
	local dotProduct = vector:Dot(planeNormal)
	return (vector - (dotProduct * planeNormal))
end

local function projectAndScale(vector: Vector3, planeNormal: Vector3)
	local projectedVector = projectOnPlane(vector, planeNormal).Unit
	return projectedVector * vector.Magnitude
end

local colliderRadius = 1
local wallOffset = 0.015

local function collideAndSlide(position: Vector3, velocity: Vector3, depth: number): Vector3
	if depth > 3 then return Vector3.zero end
	
	local direction = velocity.Unit * (velocity.Magnitude + wallOffset)
	local hitResult = workspace:Spherecast(position, colliderRadius, velocity)
	
	if hitResult then
		local snapToSurface = velocity.Unit * (hitResult.Distance - wallOffset)
		local leftover = velocity - snapToSurface
		
		if snapToSurface.Magnitude <= wallOffset then
			snapToSurface = Vector3.zero
		end

		leftover = projectAndScale(leftover, hitResult.Normal)
		return snapToSurface + collideAndSlide(position + snapToSurface, leftover, depth+1)
	end
	
	return velocity
end

Increasing the wallOffset to something like 0.25 make it harder to go through walls, but you still end up going through walls if you try hard enough with different camera angles.
This problem is driving me nuts, any help is really appreciated.

2 Likes

I’m also working on something similar and I’ve had the exact same issue, have you found any solutions to yours yet?

Nope, tried everything, looked at and implemented the research papers made on this topic using the built in Ray and Shape casting api’s, couldn’t get the collisions to be 100% reliable.

The only collision that I could get to be 100% accurate is the ground collision, with walls though, the sliding mechanic is really complex to implement.

Maybe along using spherecast make a collider that is slightly smaller that the sphere cast.

1 Like

Try casting the sphere with the colliderRadius, minus twice the wallOffset

workspace:Spherecast(position, colliderRadius - 2 * wallOffset, velocity)