More Effective Orientation Tracking?

I have a local script that allows the right arm to follow the player’s mouse movements. I’d also like to keep track of the orientation of the arm relative to the character. I need to track this because I’d like to add some restrictions or features when the arm is in a certain orientation. This is my script that fires every heartbeat:

local NewCFrame = CFrame.new(Character.UpperTorso.Position, Mouse.Hit.Position) * CFrame.Angles(math.pi/2, 0, 0)
	local NewCFrame = RightArmOffset * Character.UpperTorso.CFrame:toObjectSpace(NewCFrame)
	RightArmWeld.C0 = NewCFrame

This is my current solution for tracking the arm:

Local Script:

local ArmDirection = DataHandler.FindDirection({NewCFrame:ToEulerAnglesXYZ()})

Module Script:

local Directions = {
	--X,Y,Z
	["Front"]		 = {{90,0,0}},
	["Up Front"]	 = {{180,0,0}},
	["Down Front"]	 = {{0,0,0}},
	
	["Back"]		 = {{90,0,-180},{90,0,180}},
	["Up Back"]	 	 = {{0,0,-180},{0,0,180}},
	["Down Back"]	 = {{180,0,180},{180,0,-180}},
	
	["Left"]		 = {{90,0,-90}},
	["Down Left"]	 = {{90,90,-90}},
	["Up Left"]		 = {{90,-90,-90},{90,-90,90}},
	
	["Right"]		 = {{90,0,90}},
	["Down Right"]	 = {{90,-90,90}},
	["Up Right"]	 = {{90,90,90}},
	
}


function DataFunctions.FindDirection(Data)

	local function DirectionFormat(Num)
		return RoundToNearestMultiple(FormatDegree(Num),90)
	end
	
	local x = DirectionFormat(Data[1])
	local y = DirectionFormat(Data[2])
	local z = DirectionFormat(Data[3])
	
	for Direction,Possibility in pairs(Directions)do
		for i, Angle in ipairs(Possibility) do
		local X = Angle[1]
		local Y = Angle[2]
		local Z = Angle[3]
		
		if x == X and y == Y and z == Z then
			return Direction
		end
		end
	end
	return "Direciton Not Found"
end

I had to list out every possibility for each direction, this may not be the most effective method. My knowledge of trigonometry is also not too concrete.

Here is a video of it:

If anyone has a more effective way of scripting this, please let me know. I appreciate you reading this c:

2 Likes

NewCFrame in your first block is a CFrame whose lookVector will, for example, be (0, 0, 1) if the arm is pointing exactly forward, regardless of how the character is oriented in the world.
I would look at the LookVector to know where the arm is pointing.

You may find the angle between the LookVector and (0, 0, 1).

The cosine of the angle between two vectors is equal to the dot product of this vectors divided by the product of vector magnitude.

local look = NewCFrame.lookVector
local forward = Vector3.new(0, 0, 1)
local angle = math.acos(look:Dot(forward) / (look.magnitude * forward.magnitude)) -- angle is in radians, use math.deg to convert to degrees
if angle < math.deg(45) then
	print("Facing forward")
end

-- optimization
local threshold = math.cos(math.rad(45)) -- only do trig once
if look:Dot(forward) < threshold then -- look and forward both have magnitude of 1, this avoids the (foo / (1 * 1))
	print("Facing forward")
end

You seem more concerned with dividing up the entire space that the arm can point to. And the above code wouldn’t count pointing near the feet as “forward”.
For that, examine the LookVector and where it is.
Ignore the Y component for now.
Where does your vector lie on this image?

If the absolute value of the Z component is greater than the absolute value of the X component, then it must be in Front or Back. Otherwise, it must be in Left or Right.
You can begin to make assertions like this to find out which direction the vector is.
Try a graphing calculator like GeoGebra or Evaldraw to more visually see whether your guesses hit the mark.



(just flexing)

You may also find the angle between the arm in 2D and forward (0, 1). If the angle is small, it is facing forward; if the angle is much greater than 90, then the arm is way back; the X component will disambiguate left forward from right forward. This is simpler and easier to customize than the above approach.

Now look at the Y component to see how upward or downward the arm is pointing.
If the Y component is in the high positives or negatives (it can’t get greater than 1 though), then you know it’s way down or up.
The angle between the horizon and the arm is just math.acos(LookVector.Y), too.

3 Likes

Wow! This is extremely helpful to me. I’ll have to spend some time digesting all of this. Breaking it down helps me immensely. Thank you very very much!