Place Items In a Circle (like Tokens In Bee Swarm)

This is just a nifty little function I whipped up using a bit of trig that I felt like sharing.
It takes a vector3 and returns a table of vector3s which represent points in a circle around it in a landscape orientation, Similar to the ways tokens spawn in bee swarm simulator.

image

Nothing too complicated

--arclength represents the distance between each point (as an arc) 
--I recommend it be the size of the items plus a bit of leeway.
local function ComputeCircleCoords(StartVector3, amountofitems,Arclength)--> table of vector3s
	local AngleBetweenInDegrees = 360/amountofitems
	local AngleBetweenInRad = math.rad(AngleBetweenInDegrees)
	local Radius = Arclength/AngleBetweenInRad +2
	local tab = {}
	local currentangle = 0 --radians
	for num = 1, amountofitems do
		currentangle +=  AngleBetweenInRad
		local z = math.cos(currentangle)*Radius
		local x = math.sin(currentangle)*Radius 
		local vector3 = StartVector3 + Vector3.new(x,0,z)
		table.insert(tab,vector3)
	end
	
	return tab
	
end
How it works

Placing items in a circle around something consists of taking it’s location, a vector3, and calculating a set number of points the same distance around it, without changing the y axis (so all the points are at the same height).

In Trigonometry, there’s something called “The unit circle”, which is a circle who’s center is at (0,0) on a x-y plane, and has a radius of 1.
image

This is useful because there are a few properties of the unit circle which we can use.
The unit circle is on a plane, so it has 2 axises, but that’s fine since we need 2 axises to put things in a circle around something.

One property of the unit circle that we’ll be using is that it can be used to find the coordinates of points that lay on it, based on an angle measure. That is to say, if I start an angle with a ray at the positive X axis, have the vertex at the point (0,0), and a ray going through any place on the circle, I can find the coordinates of the place it intersects the circle. This is true for any point on the circle.
visual

How we do that is as follows: If the circle has a radius of 1 and is placed at (0,0), the Cosine and Sine of any angle (given in radians), represents the X and Y coordinates of the point where that angle would intersect the circle if measured the way we do above.
X coordinate = Cos(anglemeasureinradians)
Y coordinate = Sin(anglemeasureinradians)

We all know that the total amount of degrees in a circle is 360. So if I divide 360 by the number of items we want, I can get how far away from each other in degrees they are from each other, and then find their individual angle measurements easily. Then, we can simply convert it to radians to use the property I stated above to find the exact position it would be at on a circle with a radius of one.

This gives the X and Y coordinates, but there’s 2 problems.

1: It starts at point 0,0, when In reality we’d most like be starting elsewhere, since we want it to be around the given vector3, and not around the origin.

2: We may want the circle to have a larger radius than 1, since that’s probably too small to fit the items we want to place.

Solving the first problem is easy. Just take the starting vector3 and add the x and y positions we obtain from our calculations as offsets to get them to the appropriate position away from the starting vector3 . Note: although they are X and Y on the graph of the unit circle, they actually represent the X and Z axises in roblox, since those axises lay flat on the ground.

The second problem is a bit more challenging, and requires another property of circles.

In a circle with a radius different than 1, the position of a point on it can be represented in the same way as the unit circle, cosine and sine of the angle it makes, except each element is multiplied by the radius of the circle.
You can think of the unit circle as being a circle with a scale (radius) of 1, and since we want the circle to be bigger/smaller than that, we have to scale up the coordinates we found according to the radius.

Rather than specifying the radius of the circle, it’s slightly more convenient to specify the arclength, since this is ultimately how far away each item is from each other, and then find a suitable radius that can support that.
You can find the length of an arc in any circle by taking its angle measure, in radians, and multiplying by the radius of the circle.

visual(1)

ArcLength = AngleMeasurementInRadians * Radius

We can reverse this property to find the radius of the circle given the arclength, and the angle measure (which we found earlier)

Radius = Arclength/AngleMeasurementInRadians

Now we have a suitable radius that puts each item at the given arclength away from each other. We can multiply the coordinates we found by this, and add THIS as an offset to the starting vector3 to find the final location of each part.

Hopefully this explains the concept behind the function. Sorry for the college essay lol.

23 Likes

Is it possible to add any comments explaining how it works? Otherwise, cool resource!

1 Like

Understanding it requires a bit of prior knowledge in trigonometry, (which I’m currently taking, yay, happy to apply what I learned :sunglasses:) but yes I will add a section explaining how it works.

1 Like

I’m only in Algebra I right now, cut me some slack… :sweat_smile:

But thank you, it’d be nice to know how this works a little bit!

Add a screenshot so that more people will know what they are making. Cool tutorial though!

Oh wow, I didn’t learn anything personally from my geometry class except on how to apply SOH, CAH, TOA. Good job.

Funny that you use 3 different variable naming conventions in one argument list.
May you had 4 arguments you could use camelCase too :slight_smile:

Anyway, I know some guys get lost when it comes to sin and cos, so nice to share this script, even if it is a quite simple one (e.g. the objects’ CFrame could be set to face into the center or left/right/away etc.)

I use sg similar in NAAP plugin (Aligner module - Arrange anything orbital…), see how to use it in this video :slight_smile:

1 Like

Hello, is there a possibility to increase the base circle, since it is kinda small for me.

Ty

Yes, just change the +2 to be a larger number

--arclength represents the distance between each point (as an arc) 
--I recommend it be the size of the items plus a bit of leeway.
local function ComputeCircleCoords(StartVector3, amountofitems,Arclength)--> table of vector3s
	local AngleBetweenInDegrees = 360/amountofitems
	local AngleBetweenInRad = math.rad(AngleBetweenInDegrees)
	local Radius = Arclength/AngleBetweenInRad +2-- THIS +2 RIGHT HERE DETERMINES MINIMUM CIRCLE RADIUS
	local tab = {}
	local currentangle = 0 --radians
	for num = 1, amountofitems do
		currentangle +=  AngleBetweenInRad
		local z = math.cos(currentangle)*Radius
		local x = math.sin(currentangle)*Radius 
		local vector3 = StartVector3 + Vector3.new(x,0,z)
		table.insert(tab,vector3)
	end
	
	return tab
	
end
1 Like

Why don’t you just calculate the angle in between as math.pi*2/amountofitems?

Also why are you adding 2 to the radius?

Edit: just saw comment about radius, but math constraint functions exist, you can set it to be math.max(2, Arclength/AngleBetweenInRad)

I have combined it with CFrame.lookAt and it is now just how I wanted it to be, thank you so much!

1 Like