Need Help Fixing Custom Collisions for a Custom Physics Controller

Hi devs,

I tried making a platformer framework for future games where I wanted smooth, realistic movement via a physics hitbox. It was going well until I needed to create custom collisions (it would be jittery with default roblox collisions). :sob:

The problem is that the hitbox usually gets stuck on objects and has really unaccurate collisions. Here’s a vid:

https://streamable.com/7vx34y?src=player-page-share - (video of me showing how buggy it is)

(The jittering is from me leaving hitboxes collidable with default collisions, but the issues are pretty much the same, except that without collisions it can sometimes phase through objects or get shorter with feet beneath the ground.)

Anyways, I think the code is short enough to the point where I can just paste it in and explain what it does:

local PLAYER = game:GetService("Players").LocalPlayer
local CHARACTER = PLAYER.Character
local ROOT_PART = CHARACTER:FindFirstChild("HumanoidRootPart")
local HUMANOID = CHARACTER:FindFirstChildWhichIsA("Humanoid")

local RUN_SERVICE = game:GetService("RunService")

local HITBOX = Instance.new("Part", workspace)
local HITBOX_ATTACHMENT = Instance.new("Attachment", HITBOX)
local LINEAR_VELOCITY = Instance.new("LinearVelocity", HITBOX)
local ALIGN_ORIENT = Instance.new("AlignOrientation", HITBOX)
local WELD_CONSTRAINT = Instance.new("WeldConstraint", HITBOX)
local OVERLAP_PARAMS = OverlapParams.new()
local RAYCAST_PARAMS = RaycastParams.new()
local RESTITUTION = 0.6 -- change per material later

local velocities = {
	gravity = Vector3.new(0, -90, 0)
}
local accumulatedVelocity = Vector3.zero
local lastMoveDirection = Vector3.new(0, 0, 1)

local function summateVelocities (velocityList)
	local sum = Vector3.zero
	for i, velocity:Vector3 in velocityList do
		sum += velocity
	end
	return sum
end
local function averageVelocities(velocityList)
	local sum = Vector3.zero
	local count = 0
	for _, velocity: Vector3 in velocityList do
		sum += velocity
		count += 1
	end
	if count > 0 then
		return sum / count
	end
	return Vector3.zero
end

local function handleVelocityCollisions (vel)
	local partsInHitbox = workspace:GetPartsInPart(HITBOX, OVERLAP_PARAMS)
	if #partsInHitbox > 0 then
		local collisionVelocities = {}
		for i, part in ipairs(partsInHitbox) do
			local direction = part.Position - HITBOX.Position
			local raycastResult = workspace:Raycast(HITBOX.Position, direction, RAYCAST_PARAMS)

			if raycastResult then
				local normal = raycastResult.Normal
				local dot = vel:Dot(normal)
				dot = if (math.abs(dot) == 0) then 1 else dot 

				local reflection = vel - (1 + RESTITUTION) * dot * normal
				collisionVelocities[i] = reflection-- * vel.Magnitude
			end
		end
		return averageVelocities(collisionVelocities)
	end
	return vel
end

HITBOX.Size = Vector3.new(4, 6, 1)
HITBOX.CFrame = CHARACTER:GetPivot()
HITBOX.Transparency = 0.9
HITBOX.Color = Color3.new(1, 0, 0)
HITBOX.Material = Enum.Material.Neon
HITBOX.Name = PLAYER.Name .. "'s hitbox"
--HITBOX.CanCollide = false
LINEAR_VELOCITY.Attachment0 = HITBOX_ATTACHMENT
LINEAR_VELOCITY.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector
LINEAR_VELOCITY.MaxForce = math.huge 
LINEAR_VELOCITY.RelativeTo = Enum.ActuatorRelativeTo.World
ALIGN_ORIENT.Attachment0 = HITBOX_ATTACHMENT
ALIGN_ORIENT.Mode = Enum.OrientationAlignmentMode.OneAttachment
ALIGN_ORIENT.CFrame = CFrame.new()
ALIGN_ORIENT.Responsiveness = math.huge
ALIGN_ORIENT.MaxTorque = math.huge
RAYCAST_PARAMS.FilterDescendantsInstances = {HITBOX, CHARACTER}
OVERLAP_PARAMS.FilterDescendantsInstances = {HITBOX, CHARACTER}
WELD_CONSTRAINT.Part0 = HITBOX
WELD_CONSTRAINT.Part1 = CHARACTER.PrimaryPart

RUN_SERVICE.PostSimulation:Connect(function(dt)
	
	velocities.movement = HUMANOID.MoveDirection * 40
	
	accumulatedVelocity += summateVelocities(velocities) * dt
	accumulatedVelocity = handleVelocityCollisions(accumulatedVelocity)
	
	LINEAR_VELOCITY.VectorVelocity = accumulatedVelocity
	
	if HUMANOID.MoveDirection.Magnitude > 0 then lastMoveDirection = HUMANOID.MoveDirection end
	ALIGN_ORIENT.CFrame = CFrame.lookAlong(Vector3.zero, lastMoveDirection)
end)
Summary
  1. Creates a hitbox, a central attachment, an align orientation constraint, a linear velocity constraint, and a weld constraint.
  2. It welds the HRP and the hitbox together.
  3. It constantly summates a velocities array per frame.
  4. It then applies collisions to that and accumulates (+=) that with the final velocity.
    [spoiler]Yes, I know about about character collisions and how the primary part is not the HRP. I modified those in a server script. [/spoiler]

Also, I don’t know if this will help, but I used some modified code from this post here:

Please help!

May I know what is stopping you with using the default roblox collision?

In the code, I have a custom physics handler that overrides default controller behaviour. It sets a velocity per frame. If I use default collisions, it would be jittery (I’m overriding default physics), and it would not be realistic because the velocities added would not be stopped or cancelled in the code, it would just be stopped or cancelled by the world (this would make something like gravity keep accumulating). I can fix this by detecting collisions to cancel out the velocity, but this is practically the same as making your own collisions system (which is what I plan on doing). Either way, using default collisions would not help me! I wish it could lol