Physics of a projectile

Hey everyone,
This is my first game I have ever attempted and am running into road blocks left and right. At this point in the program I have a rock that is thrown from the origin towards x,y coordinates of a left mouse click. I want to have this rock thrown in a fairly realistic parabola using the magnitude of the throw (given by distance of click from origin), and angle of throw from the origin.

At this point I have written and rewritten the move method many times and simply can’t get it to work. I understand the physics and the math of it, but not how to translate them into java. I’ve looked at a lot of examples but can’t figure out how to apply it. Most of them talk about using time, but I am not sure how to apply it. Really any help will make a huge difference for me.

	

	private double velocity = (Math.sqrt(Math.pow((launchX-newX), 2) + (Math.pow((launchY-newY),2))))/150; // c^2 = sqrt(a^2 + b^2)
	private double gravity = 1;  
	private int launchX; // mouse input
	private int launchY; // mouse input
	private double initialX; // origin
	private double initialY; // origin

	synchronized public void move(){
		//currentTime = System.nanoTime() - startTime;
				if(newY < upperMax){ 
					active = false;
					}
					theta = (float) Math.toDegrees(Math.atan2(launchX - initialX, launchX - initialY));
				    newX = (int)(newX + ((moveSpeed * velocity))); 
				    newY = (int)(newY + ((-velocity + (gravity * velocity)) * -Math.cos(theta))); 
					
			} // end move

I think part of the issue is that you need two bits of code. The first sets up the initial x and y velocities and is only called once when the player “fires”. The second piece of code needs to be run at every time step and updates the positions (and y velocity) as time passes.


// When launching...
// NOTE I am not sure if it's cos, sin, or tan....the important thing is the initial value ONLY depends on the velocity, not gravity.
// I added const.  You probably want some constant factor to multiply by because raw pixel difference may not give you good results.
xVelocity = const * velocity * Math.cos(theta);
yVelocity = const * velocity * Math.sin(theta);


// Each update step.
// delta is the amount of time since the last frame.
public void update(float delta) {

   yVelocity -= gravity*delta;  // Update yvelocity due to gravity

   newY += yVelocity * delta;  // update y position.

   newX += xVelocity * delta;  // assuming no wind resistance, xVelocity is constant throughout.

   if (newY <=0) {
      // We have hit the ground.
   }
}

Also, a small thing, but if you are just squaring a number, it is usually better not to use Math.power(). It’s slower (doesn’t really matter in many cases) and (at least to my eyes) harder to read.


velocity = Math.sqrt( (launchX - newX) * (launchX - newX) + (launchY - newY) * (launchY - newY));

Thank you. Unfortunately now the projectile goes straight out of the guy then shoots upwards off the screen. Only difference is speed of the projectile when clicking elsewhere. I flipped the y for -+ and this lets it fall downwards instead of upwards, but does not start the projectile in an upwards motion. I suppose I don’t understand how to change my values to more appropriate numbers.


		xVelocity = (int)(2 * velocity * Math.cos(theta));
		yVelocity = (int)(2 * velocity * Math.sin(theta));
		

xVelocity is giving me readouts in the teens, while yVelocity is giving me numbers in the hundreds.

This is what I changed my move to. I have no idea how to get the delta you referred to; currTimeMillis - start time gave me awful results.


	synchronized public void move(){
		System.out.println("yvel " + yVelocity);
		System.out.println("xvel " + xVelocity);
				if(newY < upperMax){ 
					active = false;
					}
					theta = (float) Math.toDegrees(Math.atan2(launchX - initialX, launchX - initialY));

					   yVelocity += gravity*2;  // Update yvelocity due to gravity

					   newY -= yVelocity;  // update y position.

					   newX += xVelocity;  // assuming no wind resistance, xVelocity is constant throughout.

			} // end move

Some thoughts:

If the newX and newY are direct positions on the screen, then the problem is probably that the X,Y origin is at the top left corner of the screen. If that it is the case, then you’ll want to negate the yVelocity when you set it.

In terms of time, the best thing to do is to search for “Game loop” here. There are a number of approaches.

You’ll want to keep your xVelocity, yVelocity, newX, and newY as floats and then only convert to ints when you draw them. The reason for this is that depending on how short the frames are, you may only move a fraction of a pixel, which will round down to 0 and it will appear not to move.

Well to make things a bit easier, you could use a Vector class such as this for the velocity and such. More of a mathematical approach you could say:


public class Vector2d {

	public double x;
	public double y;

	public Vector2d(double num1, double num2){
		this.x = num1;
		this.y = num2;
	}

	public void add(double num){
		x += num;
		y += num;
	}

	public void sub(double num){
		x -= num;
		y -= num;
	}

	public void div(double num){
		x /= num;
		y /= num;
	}

	public void mult(double num){
		x *= num;
		y *= num;
	}

	public void add(Vector2d other){
		x += other.x;
		y += other.y;
	}

	public void sub(Vector2d other){
		x -= other.x;
		y -= other.y;
	}

	public void div(Vector2d other){
		x /= other.x;
		y /= other.y;
	}

	public void mult(Vector2d other){
		x *= other.x;
		y *= other.y;
	}
}