# How to create an 8-way burst of projectiles?

1. What do you want to achieve? Keep it simple and clear!
I want to make a part clone and shoot 8 projectiles in different directions, if that makes sense? Example below.

2. What is the issue? Include screenshots / videos if possible!
I simply don’t know how to do it efficiently, I suppose I could make the projectile with a script for moving forward and place it in replicated storage, clone it inside the part, and make them face different directions manually, although I’m not sure if that would lag the game or if there’s a simpler way. Forgive me, I’m VERY new to scripting.

3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
I looked for solutions multiple times, but I couldn’t find any, likely because I’m unsure how to word it. Any ideas would be appreciated.

4 Likes

Is something like this what you’re looking to create?

3 Likes

if they’re bursting away from the center one, then yes

2 Likes

With a small bit of Trigononmetry, its possible, all you are really doing here is using `math.sin()` and `math.cos()` to create a circle around the object, and dividing the circle into 8 parts, and placing the points into each corresponding part.

From there you can apply an “offset” direction to have them move into the said random direction.

3 Likes

After you have the projectile made, you can store it in ServerStorage. Then use a function to clone it 8 times and add 45 degrees to the Y value of the orientation each time. Add an attachment each time as well for a LinearVelocity instance. To make it move, use the LinearVelocity with a high MaxForce to keep it in the air and change the Z value of the VectorVelocity to control the speed (Make sure RelativeTo is set to Attachment 0 and Attachment 0 is set to the attachment of the object).

4 Likes

Is the diagram you made a top-down view, or do you want something like a shotgun effect?

3 Likes

I guess you can try using Vector3 or BodyVelocity on each part and make them work via a RemoteEvent I guess

4 Likes

the diagram i made is in fact a top-down view, yes

3 Likes

this seems useful, although i am unsure how i would do that, any examples?

2 Likes

A way that I like to get vectors without using trig is to do cframe operations. You can get evenly rotated lookvectors and then use the lookvectors when creating your new projectiles.

``````local PROJECTILE_COUNT = 8

for i=1, PROJECTILE_COUNT do
local lookVec = CFrame.Angles(0,math.pi*2*(i/PROJECTILE_COUNT),0).LookVector
fireProjectile(firePos, lookVec)
end
``````
3 Likes

I’ll try this out, but could you clear up what you mean by the “fireProjectile(firePos, lookVec)” line? I assume that’s meant to be a function that I create to spawn the projectiles?

2 Likes

The Angles in `CFrames` are measured in Radians, which is a part of Trigonometry, so you are still using Trig to calulate a Rotation here.

3 Likes

Here’s a more or less complete walkthrough.

Firstly, cloning 8 parts and positioning them would cause absolutely no performance issues. If you ever want to create large amounts of parts, use the module below:

Anyways, the hardest part of this question is positioning the projectiles because we’ll need to do some math.

We firstly need the CFrame of the center of the part, because all of the projectiles will be spawned relative to that position.

``````local center = part.CFrame
``````

Since you want an octagonal pattern, we want to clone 8 projectiles; this is best done in a for loop

``````for i = 1, 8 do
local clone = proj:Clone()

clone.CFrame = center -- Set their CFrame to the center first
clone.Parent = workspace -- Make sure we can see the clones
end
``````

Next, we need to rotate the parts. I’m assuming the drawing you provided is a bird’s eye view - if so, we’ll have to rotate them along the y-axis.

``````for i = 1, 8 do
local clone = proj:Clone()

clone.CFrame = center * CFrame.Angles(0, math.pi * 2 / 8 * i, 0)
clone.Parent = workspace
end
``````

`CFrame.Angles(0, math.pi * 2 / 8 * i, 0)` describes the rotation of the clone. It takes radians so we’re using pi; the degree equivalent would be `360 / 8 * i`, or `45 * i` (pi radians = 180 degrees). We want the projectiles to be evenly rotated about the center, so you can think about this as slicing a cake into 8 slices; each cut would be 360 degrees / 8 = 45 degrees apart. This is essentially what the loop would create:

Finally, to get the projectiles to move, you’d want to use some sort of physics mover like `LinearVelocity` (use whatever you want if you don’t like it). `LinearVelocity` requires an Attachment, so make sure your projectile has one. We need to set the `VectorVelocity` property, which describes the direction and force of the physics mover.

The direction we want is the direction the clones are facing, which is described by their `LookVector` property. We’d then need to multiply this by some number of your choosing to get them moving at your preferred speed.

Your code should look something like this finally:

``````for i = 1, 8 do
local clone = proj:Clone()

clone.CFrame = center * CFrame.Angles(0, math.pi * 2 / 8 * i, 0)
clone.Parent = workspace

-- You would want to put this in a separate function
local linearVelocity = Instance.new("LinearVelocity")
linearVelocity.Attachment0 = clone.Attachment -- Whatever your attachment is called
linearVelocity.Parent = clone
end
``````
3 Likes

There’s different ways to do this, I personally use CFrame because they’re what I’m more familiar with.
I created a sample script you can look over and experiment with.
I will try to break down what my script is doing the best I can (This more or less isn’t optimized and more of a demonstration)

``````local RunService = game:GetService("RunService")
--This is a Part but can be any CFrame really, provided you fix up any references to it (since the script -will expect it to be a Part, it will attempt to index .CFrame of it (which isn't a valid member of CFrame.))
local Origin = script.Parent

local function shootProjectiles(numberOfParts, startingDistance, projectileLifeTime, speed)
--Table for storing all newly created projectiles
local parts = {}

--Creates the desired numbber of Parts and Positions them accordingly in a circular arrangment
for count = 1, numberOfParts do
local smile = Instance.new("Decal")
smile.Face = Enum.NormalId.Front
smile.Texture = "rbxasset://textures/face.png"

local Part = Instance.new("Part")
Part.Size = Vector3.new(2, 2, 2)
Part.Anchored = true
Part.BottomSurface = Enum.SurfaceType.Smooth
Part.TopSurface = Enum.SurfaceType.Smooth

--Position newly created Part around Origin using Trigonometry
local angle = (math.pi*2/numberOfParts)*count
Part.CFrame = Origin.CFrame * CFrame.new(math.sin(angle)*startingDistance, math.cos(angle)*startingDistance, 0)

smile.Parent = Part
Part.Parent = workspace

table.insert(parts, Part)
end

--Updates all Parts within the 'parts' table and moves them away from the Origin each frame.
--elapsedTime keeps track of how much time has bassed since starting projectile movement
--Lifetime determines how long parts will move before being destroyed
--originStore stores Position of the Origin on reference
local originStore = Origin.CFrame
local elapsedTime = 0
local Connection
Connection = RunService.Heartbeat:Connect(function(deltaTime)
elapsedTime += deltaTime
--Terminate Connection and Destroy all Parts when total elapsed time is equal to lifeTime
Connection:Disconnect()
for _, part in ipairs(parts) do
part:Destroy()
end
else
for _, part in ipairs(parts) do
--For each Part, calculate distance and direction from origin
local distance = speed*deltaTime
local displacementFromOrigin = part.CFrame.Position - originStore.Position
local direction = displacementFromOrigin.Unit

--Applies calculated translation to the Part's Position
part.CFrame += (distance*direction)
end
end
end)
end

shootProjectiles(16, 1, 10, 10)
``````

First, the Script will place newly created parts in a circular arrangement around an Origin using Trigonometry. It then will set up a `RunService.Heartbeat` Connection to update the position of every Part every frame relative to itself and the Origin. (`RunService.Heartbeat` will pass a value called `deltaTime` that describes the time between each frame, and I use this in distance calculation to account for time between frames.)

Others have given great answers and I just wanted to provide how I would go about doing this via CFrames in hopes that you can learn something from it. (I love CFrames, what can I say? )

3 Likes

I really appreciate you going in-depth and explaining all this to me, but I’m a little lost in the final result. You said I need to put the last 4 lines as a separate function? Should I make the function like:

``````function linVel()
local linearVelocity = Instance.new("LinearVelocity")
linearVelocity.Attachment0 = clone.Attachment -- Whatever your attachment is called
linearVelocity.Parent = clone
end
``````

…and then call to that function where you put “You would want to put this in a separate function”? Sorry if I’m a little dense.

1 Like

If you’re going to be creating many more projectiles, you should place the linear velocity creation code in a separate function so you won’t have to repeat yourself those extra times - it’s a good (and necessary) practice for writing clean, easily workable code.

The function should look like this. The `part` parameter is whatever part you want to apply the velocity to; in this case, you would pass `clone` or whatever variable you’re using to store the projectile clone as the argument, and `forceScalar` is a number describing how fast you want the part to travel.

``````function addLinearVelocity(part, forceScalar)
local linearVelocity = Instance.new("LinearVelocity")
linearVelocity.Attachment0 = part:FindFirstChildOfClass("Attachment")
linearVelocity.VectorVelocity = part.CFrame.LookVector * forceScalar
linearVelocity.Parent = part

return linearVelocity
end
``````
1 Like

I gave this one a go, and it’s very customizable, so I easily altered it to get what I am looking for, thanks much! I’d like to thank everyone for helping on this problem as well, of course!

1 Like

reposted cuz accidental deletion and devforum didnt let me undelete

``````local projectile = pathtoprojectile
local centre = pathtocentre

local studs = 20 --how far the projectile goes
local speed = 1 --how fast in seconds the projectile goes before disappearing
local damage = 10 --how much damage the projectile deals

function isDecimal(number)
return math.floor(number) ~= number
--isDecimal(0.5)
--0 ~= 0.5 == true
end

function shootProjectiles(amount)
local projectiles = amount or 8
if type(projectiles) ~= "number" then projectiles = 8 end --if we put "HELLO", it will be 8
if isDecimal(projectiles) then projectiles = 8 end
for i = projectiles, 360, (360 / projectiles) do
local clonedProjectile = projectile:Clone()
clonedProjectile.CFrame = CFrame.lookAt(centre.CFrame.Position, Vector3.new(i, 0, 0)
clonedProjectile.Parent = workspace
game:GetService("TweenService"):Create(clonedProjectile, TweenInfo.new(speed, Enum.EasingStyle.Exponential), {CFrame = CFrame + CFrame.LookVector * studs}):Play()
local debounce = false
clonedProjectile.Touched:Connect(function(hit)
if debounce then return end
if hit and hit.Parent:FindFirstChildWhichIsA("Humanoid") then
debounce = true
hit.Parent.Humanoid.Health -= damage
clonedProjectile:Destroy()
end
end)
end
end

while true do
shootProjectiles() --shoot 8 projectiles by default