15

I'm making a little game just for fun and I got stuck making the bullets come out of the gun. In the code below, the direction of the player is a degree angle called rot.

float gunOffsetX = 106, gunOffsetY = 96;
double angle = Math.toRadians(rot); // convert direction of player from degrees to radians for sin and cos
x = getX(); // player X
y = getY(); // player Y

float bulletX = (float) (x + (gunOffsetX * Math.cos(angle) - gunOffsetY * Math.sin(angle)));
float bulletY = (float) (y + (gunOffsetX * Math.sin(angle) + gunOffsetY * Math.cos(angle)));
            
Instances.fire.add(new Fire(bulletX, bulletY, rot, weapon));

Also tried:

bulletX = (float) (x + Math.cos(angle + Math.atan2(gunOffsetX, gunOffsetY)) * Point2D.distance(0, 0, gunOffsetX, gunOffsetY));

But same results

Supposedly the bullets should spawn at the end of the gun but this isn't the case as you can see in the following gif..

enter image description here

Any help appreciated

David Gomes
  • 650
  • 2
  • 10
  • 34
  • 3
    Can you make the `106` and `96` constants? – wake-0 Jun 20 '16 at 16:58
  • 1
    What happens when you don't move the player – wake-0 Jun 20 '16 at 16:59
  • 2
    I assume you are aware that the coord system in java has 0 degrees facing eastwards, 180 facing west, and 270 facing north? – Ashwin Gupta Jun 20 '16 at 17:00
  • I suggest some debugging to see what the angle and the results are and were the discrepancy happens. Split out the calculation into several statements. – hotzst Jun 20 '16 at 17:00
  • Is this done in Java2D/Swing? – Ashwin Gupta Jun 20 '16 at 17:01
  • Or calculate angle and distance from player center to gun tip upfront - i.e. Math.atan2(106, 96) and Point2D.distance (0, 0, 106, 96). Then when you shoot, bulletX becomes `x + Math.cos(angle + precalculated_angle) * precalculated_distance;` – Coderino Javarino Jun 20 '16 at 17:04
  • What tools are you using for this? – Mr. DROP TABLE Jun 20 '16 at 17:06
  • @KevinWallis when I don't move the player it's the same thing – David Gomes Jun 20 '16 at 17:08
  • @AshwinGupta yes for both questions – David Gomes Jun 20 '16 at 17:08
  • @Mr.DROPTABLE Java2D and nothing more, just sprites. – David Gomes Jun 20 '16 at 17:10
  • @CoderinoJavarino that didn't work and it looks like the bullets are getting shooted just as before – David Gomes Jun 20 '16 at 17:14
  • 1
    @DavidGomes 2 things. 1) How do you calculate the gunOfset variables? (like: `0.5 * w`?) 2) Did you mean to add the gunOfsetX to the Math.sin() here `(y + (gunOffsetX * Math.sin(angle)`? – Ashwin Gupta Jun 20 '16 at 17:17
  • @AshwinGupta Well 1) I got the offsets from looking at the picture, 0, 0 was the origin of the picture then the offset is the end of gun; 2) hmm tbh I don't know because I copy pasted the code, I'm really that good at math. – David Gomes Jun 20 '16 at 17:19
  • 1
    @DavidGomes ok hold on, I'm going to try to work through your math. I just want to be very clear here though: Where is 0,0 in your coordinate system? In the center of the screen? Top left? Bottom right? Also, does y increase as you go south, or decrease as you go south? – Ashwin Gupta Jun 20 '16 at 17:21
  • @AshwinGupta Thanks for the help! 0, 0 is the top left, the y increases going south and x increases going east – David Gomes Jun 20 '16 at 17:27
  • @DavidGomes I need to understand the update cycle and stuff. Can you put you full code on git or pastebin and link it? – Ashwin Gupta Jun 20 '16 at 17:30
  • @AshwinGupta I have a lot of classes.. what do you need? The draw and mouse aim code? – David Gomes Jun 20 '16 at 17:36
  • nice question btw. Could you 1) show the guy in the default image, and hilite where gunoffsetx and y are in that image? 2) where is getX() and getY() mapped to on the man? in the center, show that too? one hopes that getX/Y is the center, and offsetx/y are offsets from the center – MeBigFatGuy Jun 20 '16 at 17:37
  • @DavidGomes preferably all of them. If not, the one that renders, the one with the game loop, and the one with this logic in it are the important ones. – Ashwin Gupta Jun 20 '16 at 17:42
  • @MeBigFatGuy I don't think I need to post a photo, I'll say and if you don't understand I post another image. 1) gunoffsetx and y are the coordinates of the end of the gun 2) the getX/Y is the top left corner of the player img – David Gomes Jun 20 '16 at 17:44
  • i'd think your problems would go away if 1) getX/getY where the center of the image, and 2) offsetX/offsetY where offsets from the center of the image – MeBigFatGuy Jun 20 '16 at 17:48
  • @AshwinGupta Ok I'll put my project in git – David Gomes Jun 20 '16 at 17:50
  • @AshwinGupta sorry for the late, I didn't know how to import eclipse project into git but the link is in the question. – David Gomes Jun 20 '16 at 18:25

1 Answers1

13

One big issue (at least in my opinion) is how this game handles anchor points of shapes, like the player.

We can highlight the anchor point by drawing a little red rectangle on its place:

g.setColor(Color.RED);
g.drawRect((int)player.getX() -5, (int)player.getY() -5, 10, 10);

This comes into the Draw#renderGame(Graphics2D) method, so it looks like:

private void renderGame(Graphics2D g) {
    g.rotate(Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
    g.drawImage(player.getCurrentFrame(), (int)player.getX(), (int)player.getY(), player.getWidth(), player.getHeight(), null);
    g.setColor(Color.RED);
    g.drawRect((int)player.getX() -5, (int)player.getY() -5, 10, 10);
    g.rotate(-Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
    //...

then we'll see that the anchor point is not in the center of the image:

image with anchor point

As you can see, the anchor point (the original (0,0) point before the rotation) isn't in the center of the image and the crosshair is related to it, instead of the view of the player.

This happens due to the shifting operation while the player rotation:

g.rotate(Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
//...
g.rotate(-Math.toRadians(player.rot), player.getX()+64, player.getY()+64);

You're shifting the postion with +64. I suggest to remove that and add the shifting to the g.drawImage call instead, so the anchor point is correctly in the center (mind that I avoided the fixed value 64):

g.rotate(Math.toRadians(player.rot), player.getX(), player.getY());
g.drawImage(player.getCurrentFrame(), (int)player.getX() - (player.getWidth() / 2), (int)player.getY() - (player.getHeight() / 2), player.getWidth(), player.getHeight(), null);
g.rotate(-Math.toRadians(player.rot), player.getX(), player.getY());

image with centerd anchor

Then you now fire the gun you'll see that the bullet always "starts" from a certain position from the player. The problem here is the incorrect offset you used. The proper values are:

float gunOffsetX = 35, gunOffsetY = 29;

(I got them by trial and error, so you may adjust them a bit more, if you like)

Now it looks like this:

shot fired, still misplaced

As you can see, the shot is still a bit misplaced, but this happens due to the incorrect rotation of the bullet (like you did it for the player shape):

g.rotate(Math.toRadians(f.rot), f.getX()+f.getWidth()/2, f.getY()+f.getHeight()/2);
g.drawImage(f.img, (int)f.getX(), (int)f.getY(), f.getWidth(), f.getHeight(), null);
g.rotate(-Math.toRadians(f.rot), f.getX()+f.getWidth()/2, f.getY()+f.getHeight()/2);

It should look like this (without any X or Y adjustments):

g.rotate(Math.toRadians(f.rot), f.getX(), f.getY());
g.drawImage(f.img, (int)f.getX(), (int)f.getY(), f.getWidth(), f.getHeight(), null);
g.rotate(-Math.toRadians(f.rot), f.getX(), f.getY());

The end result is:

shot fired, correctly placed

The player now correctly looks at the crosshair and the shots are placed in front of the gun.


If you like to fire directly through the center of the crosshair, you'll only need to adjust the player position and the bullet offset a bit.

Player (in Draw#renderGame(Graphics2D)):

g.drawImage(player.getCurrentFrame(), (int)player.getX() - (player.getWidth() / 2), (int)player.getY() - (player.getHeight() / 2) - 30, player.getWidth(), player.getHeight(), null);

(mind the -30 in (int)player.getY() - (player.getHeight() / 2) - 30)

Bullet:

float gunOffsetX = 35, gunOffsetY = 0;

Now the bullet travels right through the crosshair (mind that the red rectangle is right on the weapon):

shot fired, bullet through crosshair

(I'm a bit too stupid to create proper GIF files, so I can only provide pictures)


Now you have the necessary offset values to get the result you want, but you should definitely try to understand why the values are like they are right now. You need to replace them later with dynamic values, since different weapons need different offsets for the bullet, because the player image differs. It should be helpful to have some kind of class with instances for each weapon type, which contains the images and the coordinates where the weapon barrel is located in the image. Then you can use these coordinates to correctly set the offsets for the bullet image.

Tom
  • 16,842
  • 17
  • 45
  • 54
  • 1
    @DavidGomes You're welcome. Don't forget to read the last paragraph. These constant offset values will cause you trouble if you implement different weapons (which are planned in the `Weapon` enum), so try to find a way to use dynamic offsets instead (they are in relation to the image size and barrel position in the image). – Tom Jun 20 '16 at 21:08
  • Oh! You're so right! Thanks dude, btw can't I just make the offsets inside the enum and type the offsets in each weapon constructor then when using the offset just make "weapon.offsetX" for instance? – David Gomes Jun 21 '16 at 00:23
  • 1
    @DavidKenz Yes, that's also possible. Here is an example for user defined variables in an enum: https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html – Tom Jun 21 '16 at 09:56
  • 1
    Yea I know that, I did it too for fire interval :p but thanks for the huge help! – David Gomes Jun 21 '16 at 15:45