I am currently working on a non physics based charater controller.
I am using raycast for collusion.
The ground check works without problem, however the side collusion has a weird offset issue. I want the character to stop once it touches the wall, but the it always ends up stoping with a gap between.
I’ve been trying to fix this for hours with no success!
Thanks for taking your time.
Also please point out possible performace issues too.
my code (cm for character motor)
local cm = {}
cm.__index = cm
--variable
cm.targetPos = Vector3.zero
cm.input = Vector3.zero
cm.velocity = Vector3.zero
cm.falling = false
--setting
cm.walkSpeed = 20
cm.gravity = 2
--internal_variables
cm._minMove = 0.01
cm._rayMultiplier = 1.5
cm._hitboxRadius = 1
cm._hitboxHeightHalf = 1
cm._hitboxSideRays = {}
function cm:update(dt)
local currentPos = self.hitbox.Position
local diff = self.targetPos - currentPos
local diffXZ = diff * Vector3.new(1, 0, 1)
local diffXZMag = diffXZ.Magnitude
local velocity = Vector3.zero
local prevVelocity = self.velocity
--calc walk
if diffXZMag > self._minMove then
if diffXZMag > self.walkSpeed * dt then
velocity = diffXZ.Unit * self.walkSpeed * dt
else
velocity = diffXZ
end
end
--gravity
if self.falling then
velocity += Vector3.yAxis * ( prevVelocity.Y - (self.gravity * dt) )
end
--check ground collusion
local groundCastResult = workspace:Raycast(
currentPos + velocity,
Vector3.yAxis * -1 * self._rayMultiplier * self._hitboxHeightHalf,
self._rayPara
)
if groundCastResult then
local dist = groundCastResult.Distance
local diff = dist - self._hitboxHeightHalf
if diff > self._minMove then
self.falling = true
elseif diff < self._minMove then
self.falling = false
velocity -= Vector3.yAxis * diff
else
self.falling = false
end
else
self.falling = true
end
--check side collusion
local sidePush = Vector3.zero
for i, vec in ipairs(self._hitboxSideRays) do
local result = workspace:Raycast(currentPos + velocity, vec * self._hitboxRadius * self._rayMultiplier, self._rayPara)
if result then
local dist = result.Distance - self._hitboxRadius
if dist < 0 then
sidePush += vec * dist
end
end
end
velocity += sidePush
--apply min move
local xor = Vector3.yAxis
if math.abs(velocity.X) > self._minMove then
xor += Vector3.xAxis
end
if math.abs(velocity.Z) > self._minMove then
xor += Vector3.zAxis
end
velocity = velocity * xor
self.velocity = velocity
self.model:TranslateBy(velocity)
end
function cm.new(m)
local new = {
model = m,
hitbox = m.PrimaryPart,
_hitboxHeightHalf = m.PrimaryPart.Size.Y / 2,
_hitboxRadius = math.max(m.PrimaryPart.Size.X, m.PrimaryPart.Size.Z),
targetPoint = m.PrimaryPart.Position
}
setmetatable(new ,cm)
new._hitboxSideRays = {
Vector3.new(1, 0, 0),
Vector3.new(-1, 0, 0),
Vector3.new(0, 0, 1),
Vector3.new(0, 0, -1)
}
local rayPara = RaycastParams.new()
rayPara.IgnoreWater = true
rayPara.FilterDescendantsInstances = {m}
new._rayPara = rayPara
return new
end
return cm