Modeling a projectile's motion

Love the tutorial, especially showing the importance of math to doing this stuff!

One question though, if I don’t have a sphere following the path but a rod, what would be the best way to rotate it so that it is always pointing in the direction of the arc? At first I was thinking about doing a BodyAngularVelocity from 0 to pi, but that won’t work in a local script. Would I have to use CFrames then?

EDIT: Ok, here’s what I’ve got so far:
Right before the while (nt < t*2) loop, I put this in to set the starting angle of the projectile and point it at the target.

local angle = math.pi / 4
p.CFrame = CFrame.new(p.Position, hrp.Position)

Then, inside of the while loop I have:

p.CFrame = p.CFrame * CFrame.Angles(0, angle, 0)

And after the RenderStepped update:

angle = (math.pi / 4) - ((nt / (t * 2)) * (math.pi / 2))

What this should do is start the projectile pointing at the target, then up at a 45 degree angle. Then, on each render step, it subtracts from that angle a proportion of the full rotation of the projectile. The end result is moving from a +45 angle to a -45 angle by the end of the animation.

However, when I run it, I don’t see the projectile actually rotating. I printed out all my angles as it calculates them, and they look correct, running from +.76 to -.80.

Can anyone see what I’m doing wrong?

EDIT 2: I figured out some of the mistakes I made in that code. Turns out that I can’t do the CFrame.Angles with the argument of the angle I want it pointed to, but I have to use an argument for how much I want it rotated by.

So instead of calculating the necessary angle for each step, I just have to calculate the change in angle between each frame:

local angleDelta = ((math.pi / 4) / t) / 60

Because I couldn’t see a good way to calculate the number of times the animation loop will run, I took the amount of the rotation (45 degrees) divided by the time to take that rotation (t) divided by the number of frames per second (I assumed 60 because that is the cap and I don’t know a good way to get the actual value).

Then, in the animation loop, I have:

projectile.CFrame = projectile.CFrame * CFrame.Angles(-(angleDelta), 0, 0)

Now when I have a static projectile that is not moving, it rotates the correct amount in the correct duration. But when I try to combine it with the CFrame change from projectile.CFrame = CFrame.new((.5 * g * nt * nt) + (v0 * nt) + source.Position), it doesn’t rotate. I also tried combining the two together with a multiplication, but that doesn’t work either.

Any ideas why I can’t get the two working together?

6 Likes

Sorry I for whatever reason didn’t see this question when you originally posted it.

For this I would use the velocity equation to get the direction you’re object is going to be facing and then from there you can use CFrame.lookAt or CFrame.fromMatrix.

local g, v0, x0 -- define these

local function x(t)
	return 0.5*g*t*t + v0*t + x0
end

local function v(t)
	return g*t + v0
end

local t = 0
game:GetService("RunService").Heartbeat:Connect(function(dt)
	t = t + dt
	local position = x(t)
	local velocity = v(t)
	
	local cframe = CFrame.lookAt(position, position + velocity)	
end)

Now the one issue you may find with this is when the parabola reaches its peak then velocity will have a magnitude of zero (as shown when deriving the max jump height in the OP).

In that case we need to handle the situation a little differently:

local t = 0
game:GetService("RunService").Heartbeat:Connect(function(dt)
	t = t + dt
	local position = x(t)
	local velocity = v(t)
	
	if velocity:Dot(velocity) == 0 then -- magnitude^2 == 0
		velocity = v(t - dt) * Vector3.new(1, 0, 1)
	end
	

	local cframe = CFrame.lookAt(position, position + velocity)	
end)

Hope that helps!

35 Likes

This is exactly what I was looking for! Thank you so much :smiley:

1 Like

Thank you so much for the resource!

It turns out that the trajectory will have inaccuracies for reasons other than collision-based velocity damping. One of them is the integration method Roblox is currently using (the formula that updates the object position based on its velocity, and its velocity based on its acceleration). Users @GFink, @GloriedRage and I have recently figured how to suppress this offset, if you’d like to take a look!

1 Like

I saw this thread and i wanted to ask you something. How can i make a projectile (or any part etc.) move into a special trajectory (not roblox world physics) like in Original Snido Life. I mean projectile goes to mouse point but not up and down but from left to right. Also, self growing trees from Shindo Life. image

6 Likes

Is it possible to somehow overcome the small delay before the projectile starts moving when velocity and not heartbeat is used. When i replicate the code here in a server script there is a small but very noticeable delay before the projectile starts moving and it really is quite infuriating. I am hoping there is some simple way to resolve this issue.

here for reference

Thanks for your time

1 Like

I have this exact same issue. I would go about maybe having a premade projectile (and then set the network ownership)? But, I would prefer not to do that in my game due to the fact that player’s can customize the tools they throw and I would just rather clone the handle. You could try implementing buffer module but I am unsure of how to do that.

i ended up implenting the module but i am not too pleased with it. Using pre made projectiles also didn’t help, nor did network ownership help. Honestly i am just disappointed with Roblox for this being an issue.

There will always be a slight delay when a client makes a request to the server. You should look into lag compensation so that you can minimize the visual impact.

(also, never have the server itself render your projectiles, do it locally)

Here are some resources to look at:

https://www.ra.is/unlagged/network.html

3 Likes

i can’t find it now but a roblox staff member posted here that if the position of your projectiles is imporant(that is that all players see them being in exactly the same place) then you shouldn’t do it locally as there can be huge differences depending on the circumstances. As the position of my bombs is super important for the players i have to do it on the server. There will be a max of 12 players each can throw 1 bomb every few seconds. If this can cause issues then roblox should just shut down tbh.

this isn’t eve lag related. It’s not like you click then you have to wait for the part to appear. No you click and the part instantly spawns BUT then it takes a very small but very visible time interval before the physics start being simulated that is the part starts flying according to the set velocity.

It’s hard to see in the video so here is the link so you can see it for yourself if you like.

the tool called Bomb has the issue in question.

3 Likes

I was testing out somethings and I’m trying to make a shooting system where the ball always goes into the hoop but it just doesn’t work the ball just flies really high up into the air and it doesn’t go into the hoop. I basically anchored an invisible part in the hoop and made it so that instead of going to the mouse position it goes there .
image

local function Shoot(Player,Data,Ball)    print(ShootingPlayers[ Player.Name ].Goal.Name)
	--local Percent = CalculatePercent(Player,Data)  
	if Player.Character:FindFirstChild("Shoot") then Player.Character:FindFirstChild("Shoot"):Destroy() end  
	local Time = 1
	local Gravity = Vector3.new(0, -workspace.Gravity, 0)
	local Position  = Player.Character.HumanoidRootPart.CFrame * Vector3.new(0,2,-2) --x0 
	local Mag = ShootingPlayers[ Player.Name ].Mag
	local Velocity  =  (workspace.Goals.Goal1.Position - Position - 0.5 * Gravity * Time * Time)/Time 
	local BB = Ball:Clone() ; BB.Parent = game.Workspace
	BB.CFrame = CFrame.new(Position) 
	BB.Velocity = Velocity 
	
	
	




end

https://gyazo.com/59f44a91f6472182def7a3f28872d119

2 Likes

I fixed this it was broken because I had a bodyforce in the ball but can someone please help me to implement a Body Force into this so that it looks more smooth

1 Like

I need this aswell. The ball flies up so high in my basketball game! It doesn’t hit any ceilings or anything but I really need a way to make the ball not flight up as high, and more or less make it a not so huge arc. Just enough to make it above the basket and go in.

1 Like

This is not user friendly whatsoever please use more readable words when defining variables like instead of ‘t’ use ‘time’, dont put whole names inside the actual formulas tho since ‘t’ still means ‘time’ in this case but you might not know this until you read everything which is too long & too boring for most people.

Here are some variables used & what they mean;

t = the Time it takes to reach the target
nt = the Elapsed time (DeltaTime)
g = Gravity (-game.workspace.Gravity)
v0 = Initial velocity
x0 = Initial position

4 Likes

(or maybe just use light mode)

lol

1 Like

If you’re not going to read the whole thing then why are you here?

The purpose of this post is not to just have copy paste code, it’s to actually explain the math behind it so you can work it into your own stuff. I fully expect people to read the whole thing. If they can’t be bothered, then I’m quite happy w/ them not understanding.

14 Likes

You are going to have to mess with the time variable or the t variable the lower the number the lower the arc.

Very helpful tutorial! I was able to make a nice bow system. 10/10 :D.

Really love this tutorial, helped a lot.

But I would like to add onto the section for the Mapping the projectile’s path with a beam section with the mapping method for a beam which works perfectly unless in a specific scenario where t1 is very large.

Turns out internally within the engine they do use the method of drawing the curve with intervals according to the documentation but with textures instead of parts.

This means if you set the projectile’s end path time t1 at a very large value the curve will droop a lot as shown below because of the lack of segments:

The red beam is the projectile path output when t1 is set to 0.5
The blue beam is the projectile path output when t1 is generally set more than > 1 with the default settings.

The fix is really simple the segments property of the beam to which I set to an arbitrary high value multiplied by the input t1.

beam.Segments = 10*math.round(t1*3)

And it’s all good the red beam aligns with the blue beam and the projectile follows accordingly :+1:

Also be wary of too many beams as this will cause trails and beams to not render due to lag reasons.

1 Like

Ego,
Thanks loads. Used it already for another’s game.

Bonus question, but no pressure: For my game on a globe; Earth’s core at 0,0,0; happen to know all the bell’s and whistles to turn off, to use physics, or you would use CFrames, anyway…? Also: LookVector becomes close to useless .(Programmers like me: put the CFrames on a flat Earth; Do Look; then put them back on the globe.
(It’s a wargame, so don’t need real physics; just effects)
Oh… the beam would be so nice to have too (cannons et al); but my entire post is an illegal question.

Jeni,
I beg to differ: For programmers like me; it sounds like the equations, in Dark Theme, would have been way easier on the eyes…