How to make a part orbit another part

Hello! So I’m making a game. In the game I need some alarms to go off when a certain event happens. So I’ve built the alarms using beams for the lights. Now though I need to make the attachments orbit around the alarm and I’m not sure how. Any help? Thanks!

This can be done with some basic trigonometry. In order to get a point on the outside edge of a circle (which will be our orbit), we need just two variables: the radius of the circle/orbit (distance from the center of the circle), and the angle (which we will call “theta”). With those two variables, we can construct the point on the circle using sine and cosine trig functions.

So, let’s define our variables:

local radius = 10 -- Whatever the radius should be
local theta = 0 -- We will manipulate this in order to rotate the object in orbit

As the comments above show, radius will remain a constant and not change. However, in order to make our object rotate around the orbit, we will change theta. Again, theta, is the essentially the rotation angle on our orbit. Think of it this way, if the angle goes from 0 degrees to 180 degrees, it’s the same as the moon going from one side of the planet to the other.

I am running off of the assumption that you want it to orbit around the Y axis.

Alright, so now let’s see how we can get our point on the circle. Again, we’re going to use sine and cosine to do this. This gets into the basics of unit circles, which you can read about separately. Here’s the code:

local x = math.cos(theta) * radius
local z = math.sin(theta) * radius

Now we have our X and Z values. However, this assumes that our origin point (the point we are orbiting around) is at the center of the world (0, 0). In your case, we want to offset this by the attachment of your light. So we could change it to the following:

local x = (math.cos(theta) * radius) + lightAttachment.WorldPosition.X
local z = (math.sin(theta) * radius) + lightAttachment.WorldPosition.Z

Now we can move our other attachment to that position in orbit:

anotherAttachment.WorldPosition = Vector3.new(x, lightAttachment.WorldPosition.Y, z)

With all of that, we now have code that will take the radius and theta variables and position an attachment at a certain point in orbit around the light attachment. However, we’re not done. Now we need to animate it. Orbits are boring if they don’t move! To do this, we will use RunService.Heartbeat to continuously update theta at some sort of constant rate:

-- How many degrees it should rotate in orbit every second. Make this bigger to go faster:
local degreesPerSecond = math.rad(60)

And now our Heartbeat loop:

local function UpdateOrbit(deltaTime) -- 'deltaTime' is the time since the last UpdateOrbit

	-- Calculate the new theta:
	local deltaTheta = degreesPerSecond * deltaTime
	theta = (theta + deltaTheta) % (math.pi * 2)

	-- We paste in our code from before:
	local x = (math.cos(theta) * radius) + lightAttachment.WorldPosition.X
	local z = (math.sin(theta) * radius) + lightAttachment.WorldPosition.Z
	anotherAttachment.WorldPosition = Vector3.new(x, lightAttachment.WorldPosition.Y, z)

end

game:GetService("RunService").Heartbeat:Connect(UpdateOrbit)

The newest part of the code there is the couple lines where we calculate the new theta. That might look a bit weird. What we’re doing on the first line to calculate deltaTheta is simply transforming our degreesPerSecond variable to fit in line with the deltaTime. So, for instance, if the deltaTime is 0.5, then our degreesPerSecond will rightfully be half.

Then we add deltaTheta to our original theta. However, then we do that weird thing with % (math.pi * 2). What’s that all about?? Well, the % is the modulus operator. What it does is it returns the remainder of dividing the right hand operand (math.pi * 2 in this case). This is a fun trick to wrap values. In other words, if we go over math.pi * 2, then it will wrap back to 0. We are essentially clamping our rotation between [0, 2π]. For clarification, is the amount of radians around a circle (equivalent to 360 degrees).


Will all of that, we can combine everything into the following code:

local radius = 10 -- Whatever the radius should be
local theta = 0 -- We will manipulate this in order to rotate the object in orbit
local degreesPerSecond = math.rad(60) -- Rotation speed

local lightAttachment = SOME_ATTACHMENT_HERE
local orbitAttachment = ANOTHER_ATTACHMENT_HERE

local function UpdateOrbit(deltaTime) -- 'deltaTime' is the time since the last UpdateOrbit

	-- Calculate the new theta:
	local deltaTheta = degreesPerSecond * deltaTime
	theta = (theta + deltaTheta) % (math.pi * 2)

	-- We paste in our code from before:
	local x = (math.cos(theta) * radius) + lightAttachment.WorldPosition.X
	local z = (math.sin(theta) * radius) + lightAttachment.WorldPosition.Z
	orbitAttachment.WorldPosition = Vector3.new(x, lightAttachment.WorldPosition.Y, z)

end

game:GetService("RunService").Heartbeat:Connect(UpdateOrbit)

Hope this helps!

18 Likes

Wow! That was the longest answer I’ve ever got! thanks a lot!

2 Likes

First time seeing Mr sleitnick’s reply? YW

You could try my module. It is a simple and easy to use free module that contains circular orbits, eccentric orbits and elliptical orbits currently!

Get it here now : Tomroblox54321’s Advanced Planetary Orbit Module!