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). ![]()
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
- Creates a hitbox, a central attachment, an align orientation constraint, a linear velocity constraint, and a weld constraint.
- It welds the HRP and the hitbox together.
- It constantly summates a
velocitiesarray per frame. - 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!