Bullets not getting shot out of the gun
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(106, 96)) * Point2D.distance(0, 0, 106, 96));
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..
Any help appreciated
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:
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());
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:
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:
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):
(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.