-
What do you want to achieve?
I’m trying to create a custom character controller without using vector forces or Roblox’s physics. My character controller uses math to calculate the character’s position. I used this video to help me implement collision detection.
-
What is the issue?
The issue is that the collision detection is very buggy. You can go through walls and the floor. Whenever you move on the ground, you just clip into the floor. You can stand on the ground though.
robloxapp-20240317-1401039.wmv (1.6 MB)
-
What solutions have you tried so far?
I looked all over the devforum but haven’t found anything. I tried looking into chickynoid’s code, but couldn’t understand anything.
local Velocity = Vector3.new()
local Gravity = Vector3.new(0, -1 * Delta, 0)
if Jump then
Gravity = Vector3.new(0, 16 * Delta, 0)
end
--The problem lies somewhere here
local MoveAmount = MovementSystem.CollideAndSlide(Character, PlayerMovementParams, Total, false, ReplicationRaycastParams)
MoveAmount += MovementSystem.CollideAndSlide(Character, PlayerMovementParams, Gravity, true, ReplicationRaycastParams)
--
Character.Position += MoveAmount
--MovementSystem Module
local function dot(vec, other)
return vec:Dot(other)
end
local function projectOnPlane(v,n)
return v - (((v:Dot(n))/(n.Magnitude)^2)*n)
end
local function projectAndScale(vec: Vector3, normal: Vector3)
return projectOnPlane(vec, normal).Unit * vec.Magnitude
end
local function isGrounded(char: BasePart, params: RaycastParams)
local Origin = char.Position
local Direction = Vector3.new(0, (char.Size.Y/-2) -0.725, 0)
local GroundRaycast = workspace:Raycast(Origin, Direction, params)
if GroundRaycast then
return true
end
return false
end
local function collideAndSlide(char: BasePart, collisionParams: RaycastParams, moveParams, vel: Vector3, pos: Vector3, depth: number, gravityPass: boolean, isGrounded: boolean, velInit: Vector3)
if depth >= 5.0 then
return Vector3.zero
end
local skinWidth = 0.015
local dist = vel.Magnitude + skinWidth
dist = math.clamp(dist, -1000, 1000)
if dist ~= dist then
dist = skinWidth
end
local size = char.Size - (char.Size.Unit * skinWidth * 2)
local dir = vel.Unit * dist
if dir ~= dir then
dir = Vector3.zero
end
local hit = workspace:Blockcast(CFrame.new(pos, dir), char.Size, dir, collisionParams)
--hitbox.Size = size
--hitbox.Position = pos
if hit then
--isHit.Position = hit.Position
else
--isHit.Position = pos + dir
end
if hit then
local snapToSurface = vel.Unit * (hit.Distance - skinWidth)
local leftOver = vel - snapToSurface
local angle = Vector3.yAxis:Angle(hit.Normal)
if snapToSurface.Magnitude <= skinWidth then
snapToSurface = Vector3.zero
end
if angle <= moveParams.MaxSlopeAngle then
--Normal Ground
if gravityPass then
return snapToSurface
end
leftOver = projectAndScale(leftOver, hit.Normal)
else
--Slope
local scale = 1 - dot(
Vector3.new(hit.Normal.X, 0, hit.Normal.Z).Unit,
-Vector3.new(velInit.X, 0, velInit.Z).Unit
)
if isGrounded and gravityPass then
leftOver = projectAndScale(
Vector3.new(leftOver.X, 0, leftOver.Z),
Vector3.new(hit.Normal.X, 0, hit.Normal.Z)
).Unit
leftOver *= Vector3.new(scale, 0, scale)
else
leftOver = projectAndScale(leftOver, hit.Normal) * scale
end
end
if leftOver ~= leftOver then
leftOver = Vector3.one*skinWidth
end
return snapToSurface + collideAndSlide(char, collisionParams, moveParams, leftOver, pos + snapToSurface, depth + 1, gravityPass, isGrounded, velInit)
end
if vel ~= vel then
vel = Vector3.zero
end
return vel
end
local MovementSystem = {}
function MovementSystem.CollideAndSlide(Character: BasePart, MovementParams: MovementParams, TotalVelocity: Vector3, GravityPass: boolean, CollisionParams: RaycastParams)
local IsGrounded = isGrounded(Character, CollisionParams)
local Velocity = TotalVelocity
if TotalVelocity ~= TotalVelocity then
Velocity = Vector3.zero
end
return collideAndSlide(Character, CollisionParams, MovementParams, TotalVelocity, Character.Position, 0, GravityPass, IsGrounded, TotalVelocity)
end