My goal is to appropriately align an object (specifically a scooter union operation) to be perpendicular to the surface upon which it lies at all times, with the player’s character to always be aligned correctly on the scooter. Essentially, the player should simply move and rotate with the object (excluding animations). Rotating the scooter is for some reason adding complications, though.
Whenever the object is on an incline, it seems to vibrate. This is because of a weld that keeps the player’s character attached to the object. The weld seems to try to “fight” against the orientation changes. When my reorientScooter
function changes the orientation of the scooter, the weld resets the orientation, causing a back-and-forth since the function is called whenever the scooter’s orientation is not perpendicular to the surface it is on. The resetting that the weld does causes it to not be perpendicular to the surface and so it looks like the object is vibrating, as shown in the following clip:
I tried disabling the weld just before changing the orientation of the object and re-enabling it afterward. Although this resolves the vibration issue, this results in the player’s character not being aligned with the scooter, as shown in the following clip:
In case it helps, here is the code responsible for welding the player to the object:
...
local function pinPlayerToScooter(character: Model, scooter: UnionOperation): RBXScriptConnection
...
local primaryPart: Part = -- player primary part
local scooterBaseRegion: Part = -- scooter base
...
local weld = Instance.new("WeldConstraint")
weld.Name = "ScooterPlayerPin"
weld.Part0 = primaryPart
weld.Part1 = scooterBaseRegion
weld.Parent = scooterBaseRegion
...
end
...
Here is the code responsible for reorienting the object, given the surface normal of the surface upon which it lies (Note that reorientScooter
is the main function here):
...
local scooter: UnionOperation = -- scooter union operation
local scooterPlayerPin: WeldConstraint = -- weld responsible for pinning player to scooter
...
local function calculateReorientationAngle(surfaceNormal: Vector3): number
return math.acos(scooter.CFrame.UpVector:Dot(surfaceNormal))
end
local function determineReorientationAxis(angle, surfaceNormal: Vector3): Vector3
local axis: Vector3 = nil
if angle < 0.00001 or angle > math.pi + 0.00001 then
axis = Vector3.xAxis
else
axis = scooter.CFrame.UpVector:Cross(surfaceNormal)
end
return axis
end
local function reorientScooter(surfaceNormal: Vector3): ()
local angle = calculateReorientationAngle(surfaceNormal)
local axis = determineReorientationAxis(angle, surfaceNormal)
local newCFrame = CFrame.fromAxisAngle(axis, angle) * scooter.CFrame.Rotation + scooter.CFrame.Position
-- Does not change orientation if there is an insignificant difference between the current orientation and the new orientation
if isInsignificantVector3Difference(radianToDegreeVector3(Vector3.new(newCFrame.Rotation:ToOrientation())), scooter.Orientation) then return end
scooterPlayerPin.Enabled = false -- disable weld whilst rotating scooter
scooter.CFrame = newCFrame
scooterPlayerPin.Enabled = true -- re-enable weld after rotating scooter
end
...