Hello! I am using a Linear Velocity Constraint to handle motion for a vehicle of mine, specifically a scooter. The scooter itself is really just more of an aesthetics thing on the physics side as collisions are off and it is massless. What’s really interacting with the world is a transparent rectangular prism part with enabled collisions. I will refer to this part as the physics box.
I do not know how to go about this. When going into a collision, the physics box orients rapidly in a jittery motion and even slowly slides up the wall (you will notice that the tangent axes continually change but this is expected behavior in my project and in my tests does not impact the results): Video of scooter (physics box) colliding with wall
How can I achieve the desired behavior of no reorientations or bounces during collisions involving a part using a linear velocity constraint to move?
Just to clarify, every single object in the “Scooter-Vulqilin” model (shown in physics box collision video) is non-collidable with the exception of the physics box.
I tested with rectangular prisms of different sizes as well as a ball just out of curiosity and the result is essentially the same, even when going at a lower speed. It seems the issue arises when the linear velocity keeps applying a constant velocity when already in contact with the wall. Any ideas on how to prevent reorientations/bounces after a collision with the wall? I just want a “hard stop.”
another bump since its now been three; I was thinking of possibly implementing a system such that once the physics box is some small distance away from the wall, it would not be able to move any closer to the wall, but that would be difficult as it may interfere with certain movements and cause the physics box to be unnaturally “stuck.”
I tried creating a function to make the physics box become parallel to the wall that is run at every Heartbeat event and although it does successfully do so when the scooter is tilted enough, the same problem still arises when the scooter is just going straight into the wall: Video of physics box colliding with wall with adjustment
Here’s the relevant code:
local function handleCollisions()
local proximityThreshold = 0.25 -- Adjust this value based on testing
local function checkCollisionAndAdjust(direction: Vector3)
local distance = direction.Magnitude
direction = direction.Unit
local result = workspace:Raycast(scooterPhysicsBox.Position, direction * distance, raycastParams)
if result ~= nil and result.Instance ~= nil and distance < proximityThreshold then
-- Calculate the component of velocity along the surface normal
local velocityComponent = linearVelocity.PlaneVelocity:Dot(Vector2.new(result.Normal.X, result.Normal.Z))
-- If the component is towards the obstacle, zero it out
if velocityComponent > 0 then
linearVelocity.PlaneVelocity = linearVelocity.PlaneVelocity - velocityComponent * Vector2.new(result.Normal.X, result.Normal.Z)
end
-- Adjust orientation based on the surface normal
local newUpVector = result.Normal:Cross(direction):Cross(result.Normal).Unit
scooterPhysicsBox.CFrame = CFrame.lookAt(scooterPhysicsBox.Position, result.Position, newUpVector)
end
end
-- Check collisions for each part of the scooter (modify as needed)
local edgeDistance = maxDimension / 2
checkCollisionAndAdjust(scooterPhysicsBox.CFrame.LookVector * (edgeDistance + proximityThreshold))
checkCollisionAndAdjust(-scooterPhysicsBox.CFrame.LookVector * (edgeDistance + proximityThreshold))
checkCollisionAndAdjust(scooterPhysicsBox.CFrame.RightVector * (edgeDistance + proximityThreshold))
checkCollisionAndAdjust(-scooterPhysicsBox.CFrame.RightVector * (edgeDistance + proximityThreshold))
end
RunService.Heartbeat:Connect(handleCollisions)
bumping bc I updated original prompt as maybe the first video was not clear enough; there were several clicks on the initial first video but close to none on the following ones
Thanks for the reply! I’m thinking of a modified version: shooting multiple raycasts top to bottom along the direction of the velocity so that it not only works when the physics box is moving forward but also backward and also in edge cases where the obstacle is only blocking the bottom and/or top of the physics box. And if so, i will have to determine what component of the velocity will result in closing the distance further between the physics box and the obstacle and nullify that component.
I’ll post my testing results once I am able. In the meantime, I would appreciate if anyone posted an alternative solution because this is quite the hairy workaround.
I think this Method is going into the right direction, this is how I would have done it:
local physicsBox = --[[ your physics box ]]
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.FilterDescendantsInstances = {physicsBox}
local function checkCollision()
local raycastResult = workspace:Raycast(physicsBox.Position, physicsBox.Velocity.Unit * 10, raycastParams)
if raycastResult then
local normal = raycastResult.Normal
local velocity = physicsBox.Velocity
local reflectedVelocity = velocity - 2 * (velocity:Dot(normal)) * normal
physicsBox.Velocity = reflectedVelocity
end
end
game:GetService("RunService").Heartbeat:Connect(checkCollision)
It should prevent the jittering and glitching since we reflect the velocity off the wall. This is only an idea though and I don’t know it will work for sure.
Thanks for the reply! I modified the code and it works beautifully when colliding straight into the wall, but issues arise again when colliding at certain angles:
A little bit of context: the linearVelocity I keep referencing is a LinearVelocity constraint whose constraint mode is Plane. Assume that the constraint is constantly being updated such that the X component of the linearVelocity is always collinear with the look vector of the physicsBox, while the Y component is always collinear with the right vector of the physicsBox. There is no Z component since the constraint mode is Plane.
Here’s the code:
local function handleCollisions()
local velocity = scooterPhysicsBox.CFrame.LookVector * linearVelocity.PlaneVelocity.X
local origin = scooterPhysicsBox.Position
-- multiply by 0.55 to make ray go a little past the scooter
local direction = velocity.Unit * maxDimension * 0.55
local result = workspace:Raycast(origin, direction, raycastParams)
if result ~= nil and result.Instance ~= nil then
local incidentVector = velocity.Unit
local normalVector = result.Normal
local speed = linearVelocity.PlaneVelocity.X
local dampingFactor = 0.5
local reflectedVector = incidentVector - 2 * incidentVector:Dot(normalVector) * normalVector
local reflectedVelocity = reflectedVector * math.abs(speed) * dampingFactor
linearVelocity.PlaneVelocity = Vector2.new(reflectedVelocity.X, reflectedVelocity.Z)
end
end
RunService.Heartbeat:Connect(handleCollisions)
Thank you for your response! Yes, I have limited the max velocity. The problem in this case, though is with how Roblox treats the collision between the obstacle (e.g., a wall) and the physics box (an invisible rectangular prism that represents the hitbox of the scooter). No matter what the speed is (even if it’s as small as 1), the same issue occurs with the physics box jittering and sliding up the wall. The linear velocity constraint keeps pushing the physics box in the direction the player is trying to move (even when touching the obstacle) and so I guess this causes issues, but I would think that the rectangular prism would simply not move if it’s already pressed against the wall