This is basically what I’m looking for. 3 parts rotating 3 studs away from the center part. Their orientation shouldn’t change, only their position to rotate along.

All the other solutions I’ve found propose using BodyVelocities, using a PrimaryPart on the Model (which wouldn’t quite work since I’m not looking to change their orientation as well by using the PrimaryPart as a hinge), or use the game’s heartbeat.

I ideally need it to be a TweenService variable so I can easily play, stop, cancel it… etc.

Hi! I suppose you can group them as a model, then rotate the model if you want some sort of circular rotation. If you want to rotate each individual piece while rotating the entire, model, you can tag each part then rotate using Collection Service. I hope this helps!

This may be overcomplicating it, but the way I would do it would be to use runService and update their cframes every frame using a script like this:

local rS = game:GetService("RunService")
local CircleCenter = Vector3.new(-18, 0, 22)
local Radius = 3
local function rotate(dT)
for _, part in game.Workspace.Folder:GetChildren() do
part.Value.Value += dT * 60
local x = CircleCenter.X + Radius * math.cos(math.rad(part.Value.Value))
local z = CircleCenter.Z + Radius * math.sin(math.rad(part.Value.Value))
part.Position = Vector3.new(x, part.Position.Y, z)
end
end
rS.Heartbeat:Connect(function(dt) rotate(dt) end)

All i’ve done here is make a folder that has all the parts in it and a intvalue with their starting rotation on the edge of a circle.

the example image shows what the folder would look like.

Isn’t there a way to transplant that to a Tween? I really don’t want to be overcomplicating the script even more by making it handle when I want it to stop or change.

You could, but it would be hard to get the orientations to stay the same while tweening. If it was all in a model you could weld all the parts to a primary one and rotate that, but that would also change their orientations. I am not aware of a weld like feature that doesn’t affect orientation

Made you a script that will allow you to orbit parts around a center point, while also keeping them equidistant from one another.

local part1 = script.Parent:WaitForChild("Part1")
local part2 = script.Parent:WaitForChild("Part2")
local part3 = script.Parent:WaitForChild("Part3")
local center = script.Parent:WaitForChild("Center")
local radius = 6 -- Radius of the orbit
local numParts = 3 -- Number of parts to orbit
local angleOffset = math.pi * 2 / numParts -- Angle offset for each part
local rotationSpeed = 2 -- Increase this value to make the rotation faster
local function updateOrbit()
local time = tick() * rotationSpeed -- Multiply time by rotationSpeed to make it rotate faster
local angle = time % (math.pi * 2) -- Ensures the angle loops
local pos1 = center.Position + Vector3.new(math.cos(angle), 0, math.sin(angle)) * radius
local pos2 = center.Position + Vector3.new(math.cos(angle + angleOffset), 0, math.sin(angle + angleOffset)) * radius
local pos3 = center.Position + Vector3.new(math.cos(angle + angleOffset * 2), 0, math.sin(angle + angleOffset * 2)) * radius
part1.Position = pos1
part2.Position = pos2
part3.Position = pos3
end
game:GetService("RunService").Heartbeat:Connect(updateOrbit)

Note: In this example I am using a script with RunContext set to client. Depending on your needs this exact set up may work for you.

You can move it around & orbiting parts follow while ignoring the rotation of the center part

Also if you would like for the parts to “face” the center point you can do this:

local part1 = script.Parent:WaitForChild("Part1")
local part2 = script.Parent:WaitForChild("Part2")
local part3 = script.Parent:WaitForChild("Part3")
local center = script.Parent:WaitForChild("Center")
local radius = 6 -- Radius of the orbit
local numParts = 3 -- Number of parts to orbit
local angleOffset = math.pi * 2 / numParts -- Angle offset for each part
local rotationSpeed = 2 -- Increase this value to make the rotation faster
local function updateOrbit()
local time = tick() * rotationSpeed -- Multiply time by rotationSpeed to make it rotate faster
local angle = time % (math.pi * 2) -- Ensures the angle loops
local pos1 = center.Position + Vector3.new(math.cos(angle), 0, math.sin(angle)) * radius
local pos2 = center.Position + Vector3.new(math.cos(angle + angleOffset), 0, math.sin(angle + angleOffset)) * radius
local pos3 = center.Position + Vector3.new(math.cos(angle + angleOffset * 2), 0, math.sin(angle + angleOffset * 2)) * radius
part1.Position = pos1
part2.Position = pos2
part3.Position = pos3
-- Calculate rotation for each part
local rotation1 = CFrame.new(pos1, center.Position)
local rotation2 = CFrame.new(pos2, center.Position)
local rotation3 = CFrame.new(pos3, center.Position)
-- Apply rotation to each part
part1.CFrame = rotation1 * CFrame.Angles(0, math.pi / 2, 0) -- Adjust the rotation angle as needed
part2.CFrame = rotation2 * CFrame.Angles(0, math.pi / 2, 0)
part3.CFrame = rotation3 * CFrame.Angles(0, math.pi / 2, 0)
end
updateOrbit() -- Run once to set initial positions and rotations
game:GetService("RunService").Heartbeat:Connect(updateOrbit) -- Use Heartbeat instead of Stepped for smoother animation

Furthermore to start/stop you could assign “game:GetService(“RunService”).Heartbeat:Connect(updateOrbit)” to a variable which you connect/disconnect at will.

Also here’s an example that uses a hinge, and requires no code
*I did use code to place the parts around the center equidistant from one-another, but could be done by hand
*You could also delete the secondary cylinder (the one that parts are not welded to), then apply a rotation tween to the model with the cylinder as the primary part.

Thank you so much for going so in-depth and making different models for different use cases. I appreciate you a lot!

Furthermore to start/stop you could assign “game:GetService(“RunService”).Heartbeat:Connect(updateOrbit)” to a variable which you connect/disconnect at will.

Also THIS. I didn’t think you could do that with RunService. Again, thank you!