Custom collision detection: Spherecast jittering issue

I’m writing my own collision response system. I want the player position to be set a certain distance away from the wall after colliding with it.

I’m projecting a spherecast a slight distance in front of the player (velocity * dt), and then setting the root.CFrame using the returned data.

The issue is on seemingly random frames, the player position is set closer to the wall than intended. Video:


I’ve also included a .rbxl file.

I know for sure this isn’t a result of the spherecast not being cast/not returning anything, because valid data can be returned and the jitter still occurs.
I know for sure it’s not linked the my use of dt, because even when the code is frame dependant (meaning * dt isn’t used at all) the jitter occurs.

This is the relevant code:

local function collide(vel: Vector2, pos: Vector3, dt: number)
	-- convert velocity to vec3 so it can be used to spherecast
	local velVec3 = Vector3.new(vel.X, 0, vel.Y)
	
	-- speed moved per frame, so sphere can be cast in front of player (where you're about to be)
	local extension = velVec3.Magnitude * dt
	
	-- startPos is behind player because whatever is inside where the sphere is initially cast
	-- isn't returned by the spherecast
	local startPos = pos - velVec3.Unit * radius
	local dir = velVec3.Unit * (radius + extension)
	
	local raycastResult = workspace:Spherecast(startPos, radius, dir, raycastParams)
	vis:SphereCast(startPos, radius, dir, raycastParams)
	
	if raycastResult ~= nil then
		local oldCFrame = root.CFrame
		local newPosition = raycastResult.Position + (raycastResult.Normal.Unit * radius)
		
		-- this SHOULD place the player at the edge of the sphere radius
		root.CFrame = CFrame.new(newPosition) * oldCFrame.Rotation
	else
		print("nothing returned")
	end
end


function update(_, dt)	
	local origin = root.Position

	local moveDirection = Vector2.new(humanoid.MoveDirection.X, humanoid.MoveDirection.Z)

	local targetVelocity
	local deltaVelocity
	local accelerationRate

	targetVelocity = Vector2.new(moveDirection.X, moveDirection.Y) * 32
	accelerationRate = if moveDirection.Magnitude > 0 then 10 else 4

	deltaVelocity = targetVelocity - linearVelocity.PlaneVelocity
	linearVelocity.PlaneVelocity += deltaVelocity * accelerationRate * dt

	local velocity = Vector2.new(linearVelocity.PlaneVelocity.X, linearVelocity.PlaneVelocity.Y)

	-- collisions
	local getParts = workspace:GetPartBoundsInRadius(origin, radius, overlapParams)
	if #getParts > 0 then
		collide(velocity, origin, dt)
	end
end

RunService.Stepped:Connect(update)

[jitter.rbxl|attachment](upload://hOignGjTKNqAxfVF6S08et8RrN6.rbxl) (59.6 KB)
1 Like

Try doing the linear velocity stuff on stepped.

But this time do the collision CFraming on heartbeat. Otherwise the jitter is caused by the physics update. So you should have both stepped and hearbeat.

Stepped (update) → physics update → heartbeat (new pos value not updated) → render(jitter here)

2 Likes

here’s what i would try:

  1. try removing(or inverting?) the character’s velocity along the collision normal

  2. try using BindToRenderStep(mess around w/ render prioritiy) instead of .Stepped(but I think .Stepped should be fine for this)

1 Like

Doing exactly what you suggested fixed it, thanks!

2 Likes

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