Calculate position and angle

Hello everybody, I am self-teaching myself game physics and currently I am reading a book called "Mathematics and Physics for game programmers" and I am at chapter 7 exercise 1 where one of the exercises ask:

Write a function javelin(throwAngle, throwSpeed, time) which calculates the position and angle of a javelin over time. The function should return a vector representing the position of the javelin at time time after firing and the angle it makes with the horizontal. During its flight, a javelin is more or less oriented along the tangent to the curve—that is to say, parallel to the velocity vector

After giving it a go, this is what I managed to come up with:

1
2
3
4
  float Javelin(float ThrowAngle, float Velocity, float Acceleration, float Time)
{
	return ThrowAngle + Velocity * Time + Acceleration * Time * Time / 2.0f;
}


The function above is meant to return the position of the javelin after the throw, but I am not sure whether it is correct or not because the book does not contain the solution to this exercise (hence why I am posting). Do you think this looks alright? And if so, how can I obtain the angle?

Thank you so much for your help.
Last edited on
The function should return a vector

You definitely don't do that.

I'm not sure whether the function should return "position and angle", where position is a vector, or "a vector" that has both position and angle.

A position is apparently a 2D position. (x, y). How far the javelin has advanced horizontally (x) and how high (y) it is.

You definitely have to compute three floats: (x, y, angle).

Write a function javelin(throwAngle, throwSpeed, time)
Javelin(float ThrowAngle, float Velocity, float Acceleration, float Time)

Again a mismatch: your version has an additional argument.

There is acceleration in the equations. Acceleration created by gravity. That is a known constant for small objects flying on the surface of Earth. Passing the acceleration is fine, if the throw can occur on different planets.

Gravity does not affect horizontal movement. Resistance of air does. You can probably omit the air; there is already enough for you to do.


You have to understand the theoretical background before you can implement a computation.
Go back to your trigonometry math books about angles and triangles.
Go back to your physics books about the trajectory of an object.
The position of any object with respect to time is given by the integral of its velocity with respect to time (where the constant of integration is the initial position).
The velocity of such an object is given by the integral of its acceleration with respect to time (where the constant of integration is the initial velocity).

For an object obeying simple projectile motion, the acceleration is a vector constant (pointing down) given by gravitational acceleration. On earth, that vector happens to be 10m*s^-2 straight down towards the ground. (Or maybe more intuitively, -10m*s^-2 upwards. Flipping the sign allows us treat positive values as above the ground.) The initial velocity is imparted by the thrower, but with no acceleration in the horizontal direction, the velocity in that direction is constant.

Take the integral:

acceleration = a(t) = < 0, -10m/s^2 >
Given a initial velocity v applied at an angle theta, we can decompose it into a vector quantity:
v_i = < v*cos(theta) m/s, v*sin(theta) m/s >
The initial position can be assumed to be the zero vector.

v(t) = integral of a(t) wrt. t = < v_i_x, -t * (10m*s^-2) + v_i_y >
p(t) = integral of v(t) wrt. t = < (v_i_x * t), -t^2*(5m*s^-2) + (t*v_i_y) >
That leaves the equation for position:
1
2
3
4
vec2 position(float theta, float initial_velocity, float time) {
  vec2 const vi{ initial_velocity * std::cos(theta), initial_velocity * std::sin(theta) }; 
  return { vi.x * time, -5.*time*time + vi.y*time };
}


The javelin faces the direction it is travelling, so the angle is given by the inverse tangent of the velocity vector. Note that C++'s std::atan2 takes the arguments in reverse order:
1
2
3
4
5
float angle(float theta, float initial_velocity, float time) {
  vec2 const vi{ initial_velocity * std::cos(theta), initial_velocity * std::sin(theta) }; 
  vec2 const velocity{ vi.x, -10.*time + vi.y };
  return std::atan2(velocity.y, velocity.x);
}


Hopefully I haven't made too many typos...
Last edited on
You are adding angle + distance + distance, so no it is not dimensionally correct.

You might be best finding a physics or maths textbook and looking up "projectile motion" or "2-d motion under gravity".

Get your angle from the direction of the velocity vector (vx,vy) with horizontal (x) and vertical (y) components. There is an acceleration g = 9.81 m/s2 downward due to gravity; put another way, the acceleration vector is (ax,ay)=(0,-g)

For velocity and angle:
Horizontally: vx = ux
Vertically: vy = uy - gt
Then angle is arctan(vy/vx) to the horizontal.

For position:
x = uxt
y = uyt - gt2/2

ux and uy are the initial velocity components:
ux = throwspeed * cos(throwangle)
uy = throwspeed * sin(throwangle)

At the moment, your function seems to be closer to finding a vertical position than anything else.

From a c++ perspective, if you are returning multiple things (two components of position and an angle) then you will either have to return them by reference or put them in a single composite object.

Your question does say return them as a vector, so you could return x,y,angle as the 0,1,2 components of a vector, say; e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <vector>
 ....

vector<double> javelin( double throwAngle, double throwSpeed, double time )
{
   const double GRAVITY = 9.81;         // varies from 9.78 to 9.83 m/s2 from equator to poles
   const double PI = 3.14159265359;    // you will need this for any degrees-to-radians conversion

   vector<double> result(3);
   double x, y, ux, uy, vx, vy, angle;

   ux = ...     // formulae as given
   uy = ...     
   x = ...      
   y = ...      
   vx = ...     
   vy = ...     
   angle = .... 

   result[0] = x;
   result[1] = y;
   result[2] = angle;

   return result;
}
Last edited on
you can optimize position, it is last position + a delta each time slice, you don't need to recompute from the inital conditions again. Angle as well, I think. These sorts of loops are often run 80+ times a second and need to be FAST to render the object in graphics at high framerates of today's games.

FYI this is XY and P of the standard 3-d physics (XYZ and H(eading) P(itch) and R(oll) which are terms from aerospace & flight sims. Later you need to track all 6 all the time, and there are some trig singularities so you usually track these with quaternions to avoid those. Just some cool stuff to think about as you learn...

It's a javelin @jonnin! A dumb missile! No lifting or control surfaces and "roll" won't be very meaningful!
closed account (48T7M4Gy)
Yep, except for extracting the constant terms (v,sinθ, cosθ and g) and calculating them only once, the flight path here is determined by the initial conditions.

x(t) = v.cosθ.t
y(t) = v.sinθ.t - 1/2gt^2

r(t),θ(t) the polar coordinate (vector from origin) position comes from x and y.
or that. I knew it could be boiled down to a tiny computation.

Yes, its just a simple problem for now. No reason not to read/think ahead. Knowing where something is going prevents a lot of re-work and re-design.



Thank you for your assistance.

I found this website that helped me understand these things far better (http://www.physicsclassroom.com/)

Here is my final attempt at it using lastchance example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define GRAVITY 9.81
#define PI 3.14159

std::vector<double> Javelin(double throwAngle, double throwSpeed, double time)
{
	std::vector<double> vResult(3);
	double x, y, ux, uy, vx, vy, angle;

	ux = throwSpeed * cos(throwAngle);
	uy = throwSpeed * sin(throwAngle);
	x = ux * time;
	y = uy * time - GRAVITY * time * time / 2;
	vx = ux;
	vy = uy - GRAVITY * time;
	angle = atan(vy / vx);

	vResult[0] = x;
	vResult[1] = y;
	vResult[2] = angle;

	return vResult;
}


Looks okay now?

Thank you all again for helping me out.
It is beautiful.

remember that c++ angles are in radians. If you ever need degrees, it is usually done by additional constants that you multiply to convert.

Note that there is an atan2 function to handle the issues with atan, if you need it (you do not, here but its something you might not notice if new to the language).

vx=ux is only meaningful to the human reader. You can just use ux everywhere vx is below and save a step.

you don't use x again after computation, so vResult[0] = ux * time; skip another 3 steps (allocate 3 variables, moving result to x,y,angle, moving x,y,angle to result).

that cuts the code down to a slightly less readable but slightly tighter version:

std::vector<double> Javelin(double throwAngle, double throwSpeed, double time)
{
std::vector<double> vResult(3);
double ux, uy, vx, vy;

ux = throwSpeed * cos(throwAngle);
uy = throwSpeed * sin(throwAngle);
vResult[0] = ux * time;
vy = GRAVITY * time; //only multiply this one time this way.
vResult[1] = = uy * time - vy * time / 2;
vy = uy - vy;
vResult[2] = atan(vy / ux);

return vResult;
}

This isn't something that is easy to do while writing it. Typically you write what you did, and then after that look over it searching for redundant copies and such.
Last edited on
Thank you Jonnin :)
Topic archived. No new replies allowed.