What Am I trying to Achieve?
So basically I am making a procedurally animated mob and want the torso to rotate relative to the ground
How I do this
I have a custom rig, the torso is attached to the rig (HumanoidRootPart) via a Motor6D
I change orientation of the toro via the C0 in the Motor6D
I DO NOT change the Y orientation value of the Motor6D, because it will always move on the Y axis with HumanoidRootPart
What is the issue?
For some unknown reason the Y value of the Motor6D will auto-set itself to 180 once a certain rotation threshold has been reached. Despite code setting Y as 0 so idk…
Code
local wedge = script.Parent
local rayCastPart = script.Parent.Parent.Base
local randomAxis = Vector3.new(1,0,0) -- Read this in the EgoMoose article
local motor6D = game.Workspace.MockTrial.Base.Motor6D
local base = script.Parent.Parent.Base
local function getRotationBetween(u, v, axis)
local dot, uxv = u:Dot(v), u:Cross(v)
if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
end
while true do
local dt = wait()
local rayResult = workspace:Raycast(rayCastPart.Position,Vector3.new(0,-100,0))
if rayResult then
local rotateToFloorCFrame = getRotationBetween(rayCastPart.CFrame.UpVector,rayResult.Normal,randomAxis)
local goalCF = rotateToFloorCFrame*rayCastPart.CFrame
local x,y,z = goalCF:ToEulerAnglesXYZ()
motor6D.C0 = CFrame.new(0,0,0) * CFrame.Angles(x,0,z) --set angles - y
end
end
Hi there I haven’t developed on Roblox for a while and I while I have done procedural animations that was a long time ago. Forgive me if I am rusty .
Okay.
So first it looks in your getRotationBetween you get the difference in rotation between 2 vectors and you are using a third vector to return a different value if there is no rotation needed.
I am not sure on how the math works here.
If you can assume that the humanoid will never be on an angle greater than 90 degrees you can simple use cross product to get a forward vector that is oriented to the surface of the floor.
Notice here I got a screenshot. You can assume that the blue flat vector is the humanoids Right vector, the red could reference the surface normal and the second blue vector would be your result. (Both vectors should be of length 1)
If you need a CFrame oriented in that position (and you can also assume that the humanoid will always be oriented upwards) you can use the from matrix CFrame with the cross product and you would cross the forward vector of the humanoid and the surface normal instead. (Because IIRC fromMatrix takes the right vector and up vector as input)
Then you can apply whatever rotations you want relative to the CFrame by using CFrame:ToWorldSpace() then Bob’s your uncle
I attempted to do what you said in your post, but I’m not entirely familiar with everything you described and now I am having an issue where the motor6d C0 only sets the rotation as if it the model was not rotated. So, basically if the model is rotated on the y-axis after the game runs it will not orientate correctly to the ground.
Visualization
The rotation is correct in the beginning, but then once I turn the model later it is not
New Code
local rayCastPart = script.Parent.Parent.Base
local torso = script.Parent
local randomAxis = Vector3.new(1,0,0) -- Read this in the EgoMoose article
local motor6D = game.Workspace.MockTrial.Base.Motor6D
local function getRotationBetween(u, v, axis)
local dot, uxv = u:Dot(v), u:Cross(v)
if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
end
while true do
wait(.1)
local rayResult = workspace:Raycast(rayCastPart.CFrame.Position,Vector3.new(0,-100,0))
if rayResult then
local rotateToFloorCFrame = getRotationBetween(rayCastPart.CFrame.UpVector,rayResult.Normal,randomAxis)
local goalCF = rotateToFloorCFrame*rayCastPart.CFrame
local x,y,z = goalCF:ToEulerAnglesXYZ()
local yVector = goalCF.UpVector
local xVector = Vector3.zAxis:Cross(yVector).Unit
local zVector = Vector3.xAxis:Cross(yVector).Unit
motor6D.C0 = CFrame.fromMatrix(Vector3.zero, -xVector, yVector, zVector)
end
end
This get Rotation Between function is where the issue must lie.
It looks like you are not accounting for the fact that the normal must be transferred into local space. Although, again I wouldn’t use this rather I would do something like this
-- Value
local RaycastParameters = RaycastParams.new()
local RaycastOrigin = script.Parent.Position
local RaycastDirection = -Vector3.yAxis * 100
local PRIMARY = workspace.Primary
local SECONDARY = workspace.Secondary
local OFFSET_MOTOR = script.Parent.Motor6D
-- Loop and check
while task.wait() do
RaycastOrigin = PRIMARY.Position
local RaycastResult: RaycastResult = workspace:Raycast(RaycastOrigin,RaycastDirection,RaycastParameters)
if not RaycastResult then continue end
local Look = PRIMARY.CFrame.RightVector
local NormalLocalSpace = PRIMARY.CFrame:VectorToObjectSpace(RaycastResult.Normal) -- New line added for Motor6D!
local RightVector = Look:Cross(NormalLocalSpace)
--local RightVector = Look:Cross(RaycastResult.Normal)
local TargetCFrame = CFrame.fromMatrix(
Vector3.zero,
RightVector,
NormalLocalSpace
)
--[[
local TargetCFrame = CFrame.fromMatrix(
PRIMARY.Position,
RightVector,
RaycastResult.Normal
)
]]
OFFSET_MOTOR.Transform = TargetCFrame
OFFSET_MOTOR.C0 = CFrame.new()
OFFSET_MOTOR.C1 = CFrame.new()
--SECONDARY.CFrame = TargetCFrame
end
I had to fit 2 examples in this code. The commented out bits are if you are going to use the version without motor6D and the other is with. I hate motor 6Ds. They are unreliable and I only ever deal with then when it comes to animations. But here is the code and you can compare both methods with videos below.
I means they both do literally the same thing. however one is oriented using the motor6D instead (you can tell that when I move it drops.)
Anyways what I meant was to get rid of the function as it seems unncecessary for what you need.
Instead you can use the Cross to calculate the right vector and from matrix to make the CFrame.
Then for motor6D you need to convert the normal into local space to use it.
Small completely different issue I found.
When I made my own “wall stick” I encountered this problem
If you are at a location and raycast down (0)
you end up in the not the right position. If you set the position to be where the original raycast was fired from then you end up raycasting the completely the wrong way. (1)
If you still raycast down you will end up with drive and will drift down the slope (2)
Just keep that in mind if you need to go upside down and also if you are raycasting at a distance from the ground