Calculating Theta... of What?

So in my game I am detecting the future position of a mob to shoot at it. I have all the maths worked out, but I have reached a problem. I need to find the theta and I am not sure which angle I should calculate… here’s the math I have so far:


		if (inGame) {
			if ((xBullet >= targetX - 4 && xBullet <= targetX + 4) && (yBullet <= targetY + 4 && yBullet >= targetY - 4)) {
				inGame = false;
				shotMob.freeze();
				xBullet = 900;
				yBullet = 900;
			}
			int rotation = 0;
			Graphics2D g2d = (Graphics2D) g.create();
			if (rotation >= 360) {
				rotation = 0;
			} else {
				rotation++;
			}

			targetX = shotMob.x + shotMob.width / 6;
			targetY = shotMob.y + shotMob.height / 6;

			int theta = ??????;

			distance = Math.sqrt(Math.pow(targetX - xBullet, 2) + Math.pow(targetY - yBullet, 2));
			time = distance / shotMob.MOVESPEED;
			newPos = new Point((int) (targetX + (shotMob.MOVESPEED * time) * Math.cos(theta)), (int) (yBullet + (shotMob.MOVESPEED * time * Math.sin(theta))));

			distanceX = (int) ((double) newPos.x - xBullet);
			distanceY = (int) ((double) newPos.y - yBullet);
			sep = Math.sqrt((distanceX * distanceX) + (distanceY * distanceY));
			scale = 3;
			xBullet += (distanceX / sep) * scale;
			yBullet += (distanceY / sep) * scale;

			Screen.room.block[y][x].drawImage = false;
			g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(rotation), xBullet + Screen.room.blockSize / 2, yBullet + Screen.room.blockSize / 2));
			g2d.drawImage(Screen.tileset_air[Value.missile], (int) xBullet, (int) yBullet, Screen.room.blockSize, Screen.room.blockSize, null);
			g2d.dispose();
        }


any help is appreciated -cMp

You realize that “theta” stands for “angle”, right?

You are supposed to calculate the angle between the bullet’s firing position and the target position.

TIP: No need to call the function Math.pow(num, 2), when you can do (num*num)

Theta should be a float as well.


float diffX = targetX - xBullet;
float diffY = targetY - yBullet;
//float diffX = xBullet - targetX;
//float diffY = yBullet - targetY;

float theta = Math.atan2(diffY, diffX);
//float theta = Math.atan2(-diffY, diffX);

distance = Math.sqrt(diffX * diffX + diffY * diffY);

You see that commented line with -diffY?
Depending on if you are calculating the angle from the bullet to the target, or the target to the bullet, you may get a negative number. You may also get a negative number depending on the target’s position relative to the bullet, and vice versa. So, if the bullet goes the wrong way, try the -diffY one.

works perfect, the angle is correct but now the bullet is shot, moves towards where the mob will be and then arcs up to the mob… I think this has something to do with my adding to x and y maybe incorrectly

It’s probably because you’re calculating new velocities each step. So, as the bullet gets closer to the target, the distance changes, and since your velocity is based on the distance, it changes as well. You should set the velocity once, and only add it the position on every other update.

so do something like set scale to 1.5 and set sep only once?

Precisely.

Ah yes, a good example of this is in the code for my game PixelZombies. You can check out an example here https://github.com/Sammidysam/Zombies/blob/master/src/weapons/Bullet.java but the code is really bad and I apologize. But the bullet takes the arguments x (mouse click x), y (mouse click y), manx (where the person firing the bullet is located x), and many (where the person firing the bullet is located y) and then calculates the angle. Then, when moving, the code


x += Math.cos(angle) * SPEED;
y += Math.sin(angle) * SPEED;

is called. Is this what you are looking for? You can find the game here if you want to see it in action to make sure it works.

Yeah, this is what I was getting at. However, I usually do something like this:


//set the velocity in a fire() function
dx = Math.cos(angle) * SPEED;
dy = Math.sin(angle) * SPEED;

//in the update() function
x += dx;
y += dy;

Ah, does that increase speed by doing less trigonometric functions? If so, I’ll be sure to do that when updating my code in the future (as I need to fix it as it currently sucks).

Yeah, no need to call sin() and cos() on every update. Just set it once and you’ll save yourself many nanoseconds that will add up in the long run, especially if you have dozens of bullets on the screen at once…

Thank you! I will add that code in the next time I work on the project (I added it to my to-do list).

ok, so this is what I have done:



			targetX = shotMob.x + shotMob.width / 6;
			targetY = shotMob.y + shotMob.height / 6;

			float diffX = (float) (targetX - xBullet);
			float diffY = (float) (targetY - yBullet);
			float theta = (float) Math.atan2(diffY, diffX);

			distance = Math.sqrt(Math.pow(targetX - xBullet, 2) + Math.pow(targetY - yBullet, 2));
			time = distance / shotMob.MOVESPEED;

			newPos = new Point((int) (targetX + (shotMob.MOVESPEED * time) * Math.cos(theta)), (int) (yBullet + (shotMob.MOVESPEED * time * Math.sin(theta))));

			distanceX = (int) ((double) newPos.x - xBullet);
			distanceY = (int) ((double) newPos.y - yBullet);
			if (!beenSet) {
				sep = Math.sqrt((distanceX * distanceX) + (distanceY * distanceY));
				beenSet = true;
				System.out.println(beenSet);
			}


but the same thing still happens except it flies fast to the point of where the mob will be and slowly floats up to the mob. Is there something I am missing?

Your variable names aren’t clear, and your code needs to be organized. Create a fire() and update() function, instead of having it all in your update(). Your code is more complicated than it needs to be.

Here, let me give you this:


public void fire()
		{
			targetX = shotMob.x + shotMob.width / 6;
	        targetY = shotMob.y + shotMob.height / 6;
	
	        float diffX = (float) (targetX - xBullet);
	        float diffY = (float) (targetY - yBullet);
	        float theta = (float) Math.atan2(diffY, diffX);
	
			//MOVESPEED should be the distance you want the bullet to move every second
	        velocityX = Math.cos(theta) * shotMob.MOVESPEED;
	        velocityY = Math.sin(theta) * shotMob.MOVESPEED;
	
         }
         
         public void update(float deltaTime) //deltaTime passed in milliseconds
         {
         	xBullet += velocityX * (deltaTime * 1000f); //convert deltaTime to seconds
         	yBullet += velocityY * (deltaTime * 1000f);
         }


I’m going to take a guess and say that you probably don’t have a deltaTime. If that’s the case, then change the update() function to this:


public void update()
{
     	xBullet += velocityX;
        yBullet += velocityY;
}

Then, MOVESPEED should be the distance you want the object to move every update.

thank you! perfectly working now and I’ll be able to figure most of this stuff out now that I understand the base of it. My friend del is lookin at this and going nuts about it!

pow(x,2) = x*x.

calling atan2 just to pass the result to sin and/or cos is running in circles (yuck, yuck).

What do you suggest otherwise?

I started making a tower defence game a while ago and wrote some code for firing slow bullets at moving targets. My method of predicting the future position was originally similar yours but I found that this method is not exact and if the target is moving too fast or is too far away the bullet would miss. My solution at the time was slightly modifying the method to make it iterative, giving more accuracy for more iterations. This works ok but I thought there should be some exact solution and this the method I worked out:


public void fire()
{
    // quadratic equation for the time taken for the bullet to hit the target: a*t^2 + b*t + c = 0, where:
    double a = targetVelX*targetVelX + targetVelY*targetVelY - BULLET_SPEED*BULLET_SPEED;

    double b = 2*targetVelX*(targetX-bulletX) + 2*targetVelY*(targetY-bulletY);

    double c = (targetX-bulletX)*(targetX-bulletX) + (targetY-bulletY)*(targetY-bulletY);

    // discriminant of the quadratic formula
    double disc = b*b - 4*a*c;
    if (disc>=0){ // the bullet can't hit the target if the discriminant is negative
        double q = (b + Math.signum(b)*Math.sqrt(disc))/-2.0;
        double t1 = q/a;
        double t2 = c/q;
        double t = 0; //will be set later
        if (t1>=0 && t2>=0)// both positive
            t = math.min(t1,t2);
        else if (t1>=0)// only t1 positive
            t = t1;
        else if (t2>=0)// only t2 positive
            t = t2;
        else // both negative
            return; // the target cannot be hit. might want to do something else here as well/instead.
        
        bulletVelX = targetVelX + (targetX - bulletX)/t;
        bulletVelY = targetVelY + (targetY - bulletY)/t;
    }
}

This method seemed to work perfectly when I tested it. Also this solution doesn’t use any sines, cosines, or arctangents so it could actually be faster as well as more accurate (you probably don’t have to worry about speed too much but if you had lots of towers rapid firing it could matter).

I could post the derivation if anyone wants it but it would take a little while to type it all out… You can probably find find a derivation on the internet somewhere anyway.

EDIT: I made a slight mistake and left a factor of 2 out in the calculation of b.

EDIT2: Changed the calculation of t1 and t2 slightly due to Roquen’s post below.

I missed this reply. OK, you have some direction (x,y) the end result of atan2, sin & cos just yields a normalized version of the same direction…so
s = (float)(1.0/Math.sqrt(x*x+y*y); x *= s; y *= s;

quadratic: Maybe my brain’s not working and I’m too lazy to check, but I think what you have can lead to catastrophic cancellation. The value of disc itself as well can be very inaccurate via just plain-jane cancellation (I want to think up to 50% of the bits can be junk).

I’m not really sure if cancellation would affect it much. If b^2 was similar to 4ac then sqrt(discriminant) would be small compared to b in the calculation of the time so the cancellation error shouldn’t matter much there. I’ve been doing too much physics where if something is small you often ignore it to simplify the otherwise unsolvable maths. This can lead to inaccuracies like you said.

Possibly if the targets velocity was very similar to the bullets velocity then “a” would be small and could have significant error, in most practical applications though the bullet should be going significantly (at least, say, 1%) faster and the cancellation error wouldn’t matter.

Anyway, due to the way the bullet’s velocity is calculated at the end the bullet will still hit the target even if there was large error in t; It will simply move at a different speed to BULLET_SPEED. I checked this by adding 50 to the time before the bullet velocity is calculated. For the configuration of enemy and tower I was using to test it, the time came out to be about 65 so this is a huge relative error but the bullet still hit! (it went at about half the speed)

EDIT: I read this and you’re right; I’ve edited it to do it that way but there can still be a loss of 50% of the significant figures. There doesn’t seem to be any way around that except by implementing the b^2 - 4ac calculation in quadruple precision, but 50% is still the precision of a float so it’s not too bad. Thanks for reminding me, I hadn’t even thought of those errors.