I have been looking at CFrame animation scripts for making the player face the mouse position, and I have seen it done similarly in this way every time:
What is confusing me is that the argument in the Y component of CFrame.Angles( ) does not SEEM to be a measurement of angular displacement. From my understanding of this component:
(HeadPosition - MousePosition).Unit is the Unit Vector MH pointing from Mouse.Hit.Position to the players Head, and TorsoLookVector is merely the Unit Vector the torso is pointing, as seen in the image below:
The Y directional component of this unit vector is then used to rotate the Neck/Waist Motor6D C0 component in a CFrame.Angles() multiplication. This is where I am confused and would greatly appreciate an explanation as to why they are not using an angle for this component.
No need for understanding or maths. Itâs just cross product property and the fact it will generate a vector that is perpendicular to the two vectors inputted.
What clever source I believe has done is think oh I know cross product has a direction component so it can tell the difference between looking right and left vs dot product which doesnât.
So you use the two inputs which I believe you already know torso look vector, and mousehit to head position. Here is the results:
When the mouse is right the cross product will be pointing downwards, hence the Y component is - which indicates clockwise movement looking at the character from a birds eye view from the Y axis.
When the mouse is left the cross product will be pointing upwards, hence the Y component is - which indicates anti clockwise movement looking at the character from a birds eye view from the Y axis.
And thatâs pretty much it then itâs been multiplied by 0.5 in order to make sure the torso doesnât rotate too far, and yeah.
For this part
It actually kinda is when you consider that the magnitude of the cross product of two unit vectors
The formula to convert two unit vectors cross product into an angle is
local unit1 = (HeadPosition - Point).Unit
local unit2 = TorsoLookVector
local vector = ((unit1 ):Cross(unit2 ))
print(math.asin(vector.Magnitude)*180/math.pi) --get the angle in degrees
--Assuming X,Z component is zero or close to it like the above two images then
--vector.Magnitude â vector.Y -- close enough lol
However this gets rid of the directionality component of the unit vector
Here is the visualized version of the code feel free to play around with it:
Vector visualization
local RunService = game:GetService("RunService")
local Player = game.Players.LocalPlayer
local PlayerMouse = Player:GetMouse()
local Camera = workspace.CurrentCamera
local Character = Player.Character or Player.CharacterAdded:Wait()
local Head = Character:WaitForChild("Head")
local Neck = Head:WaitForChild("Neck")
local Torso = Character:WaitForChild("UpperTorso")
local Waist = Torso:WaitForChild("Waist")
local Humanoid = Character:WaitForChild("Humanoid")
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
for i,v in pairs(Character:GetChildren()) do
if v:IsA("BasePart") then
v.Transparency = 0.8
end
end
local NeckOriginC0 = Neck.C0
local WaistOriginC0 = Waist.C0
--Neck.MaxVelocity = 1/3
local function Visualize(v3, origin, color, thickness, transparency)
local origin = origin or Vector3.new()
local part = Instance.new("Part")
part.CanCollide = false
part.Name = "Vector"
part.BrickColor = color or BrickColor.Random()
part.Transparency = transparency or 0
part.Anchored = true
part.Size = Vector3.new(thickness or 0.2, thickness or 0.2, v3.magnitude)
part.CFrame = CFrame.new((origin+v3/2), origin+v3)
part.Locked = true
part.Parent = workspace
return part
end
RunService.RenderStepped:Connect(function()
local CameraCFrame = Camera.CoordinateFrame
if Character:FindFirstChild("UpperTorso") and Character:FindFirstChild("Head") then
local TorsoLookVector = Torso.CFrame.lookVector
local HeadPosition = Head.CFrame.p
if Neck and Waist then
if Camera.CameraSubject:IsDescendantOf(Character) or Camera.CameraSubject:IsDescendantOf(Player) then
local Point = PlayerMouse.Hit.p
local Distance = (Head.CFrame.p - Point).magnitude
local Difference = Head.CFrame.Y - Point.Y
local vector = (((HeadPosition - Point).Unit):Cross(TorsoLookVector))
local part = Visualize(vector*3,HeadPosition,BrickColor.Red())
local mouse = Visualize(HeadPosition - Point,Point, BrickColor.Green())
local lookVector = Visualize(TorsoLookVector*10,HeadPosition, BrickColor.Blue())
game.Debris:AddItem(part,0.1)
game.Debris:AddItem(mouse,0.1)
game.Debris:AddItem(lookVector,0.1)
--print(math.asin(vector.Magnitude)*180/math.pi) -- to convert it into a angle
local yAngle = (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 0.5
Neck.C0 = Neck.C0:lerp(NeckOriginC0 * CFrame.Angles(-(math.atan(Difference / Distance) * 0.5), (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 1, 0), 0.5 / 2)
Waist.C0 = Waist.C0:lerp(WaistOriginC0 * CFrame.Angles(-(math.atan(Difference / Distance) * 0.5), yAngle, 0), 0.5 / 2)
end
end
end
end)
And there is one more thing, you said: It actually kinda is when you consider that the magnitude of the cross product of two unit vectors . Well, can you explain more, i did search on google about unit vector on unit circle and as far as i can tell, it still doesnât connect .
Rearranging it and applying the magnitude operator on both sides of the equation we can find the angle:
In this case with a = torsolookvector and b = anotherUnitVector you can simplify it to this as @ThanksRoBama pointed out in your post.
However the creator thought as an approximation that:
|a Ă b| â (a Ă b).Y --magnitude is approximately close enough to the y component
And as we can see from the images I sent above itâs basically close enuff as X and Z are close to zero when the mouse is in those positions.
If you want another method which measures the angle between the vectors more accurately I suggest using the angleBetweenSigned function made by @sleitnick
ok so the answer to my question is that it just works. Ok, that is an interesting answer but what about: |a Ă b| â (a Ă b).Y --magnitude is approximately close enough to the y component
as well as we can see from the images I sent above itâs basically close enuff as X and Z are close to zero when the mouse is in those positions.. I think i would need more explanations than just that. Iâm not sure if i understanded what you mean
Do you know how to calculate magnitude of |a Ă b|?
you take the xyz components and do this
|a Ă b| = (x,y,z)
magnitude of a x b = sqrt (x^2 + y^2 + z ^2)
--now ignore x, z, it'll make the actual value lower but its an approximation
--Who cares if the angle is lower then it should be, we don't want the torso to look directly at the mouse anyways
--x = 0, z = 0 like the red cross product vector in the image above
magnitude of a x b= sqrt(0^2 +y^2 +0^2)
magnitude of a x b = sqrt(y^2)
|a x b| â y --notice the squiggly line, that means approximately as it's not exactly the answer
yeah, but i canât seem to understand why would X and Z = 0 because when i visualize it on the paper, it seems that if X = 0 then Z would equal to some random number. Anyway, it might be because it is 12 am in my country and i get a little bit tired, i mean headache.
You can test it out yourself in studio with his script by printing the values. Within the first 60 degree arc infront of the player (30 degree rotation to each side) arcsin(Crossproduct.Y) is very very similar to Crossproduct.Y.
However, as the player starts turning a larger angle, such as 45 degrees, we can see that the approximation is worse. However, this approximation is merely an UNDER-estimated value of 45 degrees, eg:
Where 44.1 degree rotation is actually approximated to only 39.9 degrees.
One can assume that @CleverSource intended this for a more realistic animation.
Nevertheless, you can add in math.asin for your TRUE mathematically accurate result.