Making barrage arms go in an arc motion?

So this might be a bit confusing. I’m working on a Jojo game and I want to replicate this effect from yba, where the arms in the barrage go in an arc motion. Does anyone have an idea of how I could do this?

https://gyazo.com/f4b4a38d7b1923403275b14d3b2f57c0

I’m thinking that it’d be something to do with moving the frame using some kind of math expression.

7 Likes

That’s pretty easy to do, review about bezier curves then use for loop then some trails then you’re pretty much done.

4 Likes

Yes, exactly what @EnigmaticDevil said.

I was wondering how to make an effect similar, but not entirely what you were trying to achieve awhile ago, so I did so. I can share the formula I used to make your life a little bit easier. :slight_smile:

Now, usually, Bézier curves take in any amount of points, but for the sake of ease, I just let this quadratic Bézier take in 3 points. A start, curve (how far it should bend, essentially), and an end.

Best of luck in the future! :grinning_face_with_smiling_eyes:

function Bezier_Module:QuadraticBezier(Time,Point0,Point1,Point2)
	return (1-Time)^2*Point0+2*(1-Time)*Time*Point1+t^2*Point2; --This'll give you that curve you were looking for :)
end;

Here’s how it looks real-time being tweened to its goal, following this quadratic Bézier path:

2 Likes

Btw how is this any different from the original?

Btw how is this any different from the original?

What do you mean?

I never said it was any different. I’m just saying, it takes in 3 points only, despite Bezier curves being known for taking more than 3 points.

I listed this specifically just in case he wanted to know, these are what bezier curves are known for.

yea i didn’t see lmao mb

Thanks a lot for your response! I’m sorry if I’m being a bit stupid right now but how could I tween my object along the path?

You would notice that there’s a Time argument in the function.
Just put the function in a loop-until-duration code ( I believe the function returns a Vector3)

local duration = 1
local timeElapsed = 0
while timeElapsed < duration do
    local pointAtTimeElapsed = Bezier_Module:QuadraticBezier(timeElapsed, startPoint, curvePoint, endPoint)
    timeElapsed += runService.Heartbeat:Wait()
end

If you want a smoother movement, put this in Client and create a tween for each pointAtTimeElapsed.

Here’s how I would go about tweening this personally:

for t = 0,1,Speed.Value do
    local TargetPosition = QuadraticBezier(t , Position_Start, (CFrame.new(Midpoint)*Random_Offset).p, Position_End);
    local Goal = {
        Position = TargetPosition;
    };
    local Tween = TweenService:Create(Part_To_Tween, TweeningInfo, Goal);
    Tween:Play();
end

I hope this helped, if you have any more questions, feel free to ask away. :slight_smile:
Though, the solution above is probably less memory-consuming, and probably more effcient, honestly. :sweat_smile:
This was my implementation (as practice), though not the best obviously, for the time I was learning about Bezier curves, however.

1 Like

Thank you! I managed to make it work however now I am faced with a different problem. It’s not that major but it kind of ruins the effect.

The arms seem to be turning around again when reaching the end.

Here’s my code for reference. The code is in a local script that activates by a remote from the server. (keep in mind I just used whatever worked so it might be inefficient.)

spawn(function()
		local duration = 1-(arm.Size.Z/10)
		local timeElapsed = 0
		
		local rnd = Random.new()
		local rndCF = CFrame.new(rnd:NextInteger(-1,1),rnd:NextNumber(-1.5,1.5),rnd:NextNumber(-1,-5.5))
			
		local speed = 0.05
			
		while timeElapsed < duration do
			local pointAtTimeElapsed = Bezier_Module:QuadraticBezier(timeElapsed,arm.Position,(arm.CFrame * rndCF).p, pivotCFrame.p)
			local lookAt = Bezier_Module:QuadraticBezier(timeElapsed+speed,arm.Position,(arm.CFrame * rndCF).p, pivotCFrame.p)
			
			if timeElapsed == 0 then
				arm.CFrame = CFrame.new(pointAtTimeElapsed,Bezier_Module:QuadraticBezier(timeElapsed+.01,arm.Position,(arm.CFrame * rndCF).p, pivotCFrame.p)) -- Makes the arm face the right way
			elseif timeElapsed == speed then
				local tween = TweenService:Create(arm,TweenInfo.new(.4),{Transparency = 0}):Play()
			end
			
			timeElapsed += speed
			local tween = TweenService:Create(arm,TweenInfo.new(speed),{CFrame = CFrame.new(pointAtTimeElapsed,lookAt)}):Play()
			wait(speed)
		end
			
		arm:Destroy()
end)

Ok so, first of all,
In the example I made there’s a mistake ( I think) :

the time value for quadratic curve is supposed to be 0 <= t <= 1
To convert timeElapsed to time for curve, you do

local t = timeElapsed/duration
local pointAtTimeElapsed = bezier(t, start, mid, endPos)

Your speed variable isn’t exactly speed; it’s the delay between tweens
Here’s how you can implement speed

local speed = script.Parent:GetAttribute("speed") -- insert your value (I use between  1-2)
local duration = (armStartCf.p - pivotCFrame.Position).Magnitude / speed

You might notice there’s a armStartCf variable; that’s the solution to your problem.
Here lies the problem:

local pointAtTimeElapsed = Bezier_Module:QuadraticBezier(timeElapsed,arm.Position,(arm.CFrame * rndCF).p, pivotCFrame.p)
local lookAt = Bezier_Module:QuadraticBezier(timeElapsed+speed,arm.Position,(arm.CFrame * rndCF).p, pivotCFrame.p)
arm.CFrame = CFrame.new(pointAtTimeElapsed,Bezier_Module:QuadraticBezier(timeElapsed+.01,arm.Position,(arm.CFrame * rndCF).p, pivotCFrame.p))

Your start and middle position is not constant, the curve tries to get to the turning point, but it never reaches it since it is always behind resulting in a spiral.

Just change
arm.Position
into armStartCf.p

and
arm.CFrame * rndCf
into armStartCf * rndCf

where armStartCf is the starting CFrame of the arm // spawn cframe of the arm

Here’s the code that I used

local TweenService = game:GetService("TweenService")
local arm = nil
local pivotCFrame = CFrame.new(script.Parent.Attachment.WorldPosition)
local Bezier_Module = {}
script.Parent:SetAttribute("speed", 2)
function Bezier_Module:QuadraticBezier(Time,Point0,Point1,Point2)
	return (1-Time)^2*Point0+2*(1-Time)*Time*Point1+Time^2*Point2; --This'll give you that curve you were looking for :)
end;

local function TweenArm(arm)
	spawn(function()
		local armStartCf = arm.CFrame
		local timeElapsed = 0

		local rnd = Random.new()
		local rndCF = CFrame.new(rnd:NextInteger(-1,1),rnd:NextNumber(-2.5,2.5),rnd:NextNumber(-5,-5.5))

		local speed = script.Parent:GetAttribute("speed")
		local duration = (armStartCf.p - pivotCFrame.Position).Magnitude / speed
		local tweenDelay = 0.1
		local new  = Instance.new("Part")
		new.CFrame = arm.CFrame * rndCF
		new.Size = Vector3.new(1,1,1)
		new.Anchored = true
		new.CanCollide = false
		new.Material = Enum.Material.Neon
		new.Parent = script.Parent
		game.Debris:AddItem(new, 1)
		
		print(duration)
		--while timeElapsed < duration do
		--	local pointAtTimeElapsed = Bezier_Module:QuadraticBezier(timeElapsed,armStartCf,(arm.CFrame * rndCF).p, pivotCFrame.p)
		--	local new  = Instance.new("Part")
		--	new.Position = pointAtTimeElapsed
		--	new.Size = Vector3.new(.2,.2,.2)
		--	new.Anchored = true
		--	new.CanCollide = false
		--	new.Material = Enum.Material.Neon
		--	new.Parent = script.Parent
		--	game.Debris:AddItem(new, 1)
		----	arm.CFrame = CFrame.new(pointAtTimeElapsed,Bezier_Module:QuadraticBezier(timeElapsed+.001,arm.Position,(arm.CFrame * rndCF).p, pivotCFrame.p)) -- Makes the arm face the right way
		--	timeElapsed += 0.01
		--end
		timeElapsed = 0
		while timeElapsed < duration do
			local progress = timeElapsed / duration
			local pointAtTimeElapsed = Bezier_Module:QuadraticBezier(progress,armStartCf.p,(armStartCf * rndCF).p, pivotCFrame.p)
			local lookAt = Bezier_Module:QuadraticBezier(progress+0.01,armStartCf.p,(armStartCf * rndCF).p, pivotCFrame.p)
			
			
			if timeElapsed == 0 then
				arm.CFrame = CFrame.new(pointAtTimeElapsed,Bezier_Module:QuadraticBezier(progress+.01,armStartCf.p,(armStartCf * rndCF).p, pivotCFrame.p)) -- Makes the arm face the right way
			elseif timeElapsed == speed then
				local tween = TweenService:Create(arm,TweenInfo.new(.4),{Transparency = 0}):Play()
			end

			timeElapsed += speed
			local tween = TweenService:Create(arm,TweenInfo.new(tweenDelay),{CFrame = CFrame.new(pointAtTimeElapsed,lookAt)})
			tween:Play()
			wait(tweenDelay)
		end

		arm:Destroy()
	end)
end

while true do
	local arm = script.Parent.Arm:Clone()
	arm.Position = script.Parent.Position
	arm.Parent = script.Parent
	pivotCFrame = CFrame.new(script.Parent.Attachment.WorldPosition)
	TweenArm(arm)
	wait(0.5)
end

Also, you may want to increase the rndCf’s range to get a wider curve. (Like about half the maximum height of the curve you want)
(Think of the curve tool in MS Paint to get an idea how your curve would look like; set 2 points and drag the line to curve it)
image

9 Likes

I was able to make it work! Thank you so much.

2 Likes

good tut, appreciate your help

Sorry for the late reply, I stumbled upon your post while trying to make my own barrage, and I can’t help but notice the bezier_module, do you mind sharing it with me so i can use it as well?

1 Like

Bezier_module is

function Bezier_Module:QuadraticBezier(Time,Point0,Point1,Point2)
	return (1-Time)^2*Point0+2*(1-Time)*Time*Point1+Time^2*Point2
end;```

Whenever I Plug it in the red underlines just appear dispite it being the exact same idk

do
local Bezier_Module = {}
– the bezier function below here

1 Like

ty I ended up getting it after I sent the message lol

1 Like