Making a 3 axis robot arm look at something

I want to make a 3 axis robot arm with a cool laser cannon which you can get to point the laser cannon directly at a vector3. this would be easy if the cannon was just on a stick and looked at stuff, but i decided it’d be cool to make a 3 axis robot arm instead.

I do not know how to do the math or calculations, I have made code that set the angles of the axis to whatever I want in degrees but thats about it.

I have tried to ask dev friends for help, chatgpt, nothing worked.

heres some details on the robot arm :

heres how each joint moves for clarity

heres my module script :

local module = {}

function module:CalculateJointCFrame(Joint:Instance, Angle:number)
	local Position = Joint:GetAttribute("JointDefaultPosition") or Vector3.zero
	local JointCFrame = CFrame.new(Position)
	
	local RotationCFrame = CFrame.new()
	
	local DefaultRotation = Joint:GetAttribute("JointDefaultRotation")
	if DefaultRotation and typeof(DefaultRotation) == "Vector3" then
		RotationCFrame*=CFrame.Angles(
			math.rad(DefaultRotation.X),
			math.rad(DefaultRotation.Y),
			math.rad(DefaultRotation.Z)
		)
	end
	
	local AxisUsed = Joint:GetAttribute("AxisUsed") or "X"
	
	local RotationTable = {0,0,0}
	local AxisDictionary = {x = 1, y = 2, z = 3}
	
	local IndexToChange = AxisDictionary[string.lower(AxisUsed)] or 1
	RotationTable[IndexToChange] = math.rad(Angle)
	
	RotationCFrame*=CFrame.Angles(table.unpack(RotationTable))
	
	JointCFrame*=RotationCFrame
	
	return JointCFrame
end

function module:PointCannonAt(LookPos:Vector3)
	local Cannon = script.Parent
	if not Cannon:IsA("Model") then return end
	
	local Arm = Cannon:FindFirstChild("Arm")
	
	if not Arm or not Arm:IsA("Model") then return end
	
	local Joints = {
		Joint1 = Arm:FindFirstChild("Joint1"),
		Joint2 = Arm:FindFirstChild("Joint2"),
		Joint3 = Arm:FindFirstChild("Joint3")
	}
	for _, joint in Joints do if not joint or typeof(joint) ~= "Instance" or not joint:IsA("BasePart") then return end end
	
	local Cannon = Arm:FindFirstChild("Cannon")
	if not Cannon or not Cannon:IsA("Model") then return end
	
	local MainCannonPart = Cannon:FindFirstChild("CannonShell") or Cannon:FindFirstChild("CannonFrame") or Cannon.PrimaryPart
	if not MainCannonPart or not MainCannonPart:IsA("BasePart") then return end
	
	local ImportantPositions = {
		Joint1Pos = Joints.Joint1.Position,
		Joint2Pos = Joints.Joint2.Position,
		Joint3Pos = Joints.Joint3.Position,
		CannonPos = MainCannonPart.Position
	}
	
	-- how do i calculatey the angles for each motor?
	
	local XRotation = 0
	local YRotation = 0
	local ZRotation = 0
	
	
	local Motors = Arm.Joints
	
	local Motor1 = Motors.Joint1
	local Motor2 = Motors.Joint2
	local Motor3 = Motors.Joint3
	
	Motor1.C1 = module:CalculateJointCFrame(Motor1, XRotation)
	Motor2.C1 = module:CalculateJointCFrame(Motor2, ZRotation)
	Motor3.C1 = module:CalculateJointCFrame(Motor3, YRotation)
end

return module

heres a simplified version of the cannon model with the scripts to help even more
SimplifiedCannon.rbxm (49.8 KB)

it’d be great if you could figure out the math

1 Like

why are you creating a method if you never use self :skull: :red_question_mark::red_exclamation_mark:
image

1 Like

from what i know you dont need to use self and i thought it was another way of making a function in a module

the thing you wrote basically creates absolutelly unneeded argument.
Its equalent to be:

function module.CalculateJointCFrame(self:typeof(module),Joint:Instance, Angle:number)

end

that argument does not exist when i use it

it does exist.
What are you talking about?

when i use the function, it does not exist

Lie.

local module = {}
function module:CalculateJointCFrame()
print(self)
end
local module = {}
function module.CalculateJointCFrame(self)
print(self)
end

look. nowhere is “self”

your probably talking about if you call the function a different way, ive encountered self when doing that but not like this

self doesn’t take up an actual argument in the list of parameters you have specified for the function when you use : to define your method instead of ., it’s pretty much a variable injected into the function that references the table it is apart of if you define the function using :.

he just poorly explained how it actually worked.

well its not really a problem is it?

It’s only really a problem when it comes to code readability. It’s a lot easier to tell at a glance whether or not the function call is altering state specifically tied to the object it is being called on as opposed to a more static function like that.

Also the fact that it comes back to bite you later if you forget you defined it with : and try to call it with . (calling these functions with a dot index will expect you to pass along whatever should be self as the first argument to the method call before providing any additional arguments the function requires)

1 Like

im used to using : so its not really a problem for me

1 Like

Then there’s no issue there. Getting back onto topic of what your original post was asking about though, it sounds like your issue is exactly the kind of thing Inverse Kinematics should be used for, so maybe go ahead and try to experiment around with that.

that cause you are automaticlaly passing it through using :
Also do you understand that you are legit arguing against a fact (bytecode)?

Well too bad you picked programming instead of art.
There was never a choice
Programming is not democratic.
There is only efficient and not efficient code.

you made me think it was a variable with your explanation

it is a variable in a way - function argument

this is definitely a micro-optimization that would only save like a megabyte if i converted all my module scripts to use . instead of :