Reputation: 415
So hey, I'm new into making games and programming at all, I tried to make a small game thingy in Pygame that would include a dude shooting bullets and stuff. However, I can't really get the shooting mechanics right. I have code like this
class Projectile():
def __init__(self, surface, color, start_pos, end_pos, move_speed):
super().__init__()
# Shooter and target positions
self.start_pos = start_pos
self.end_pos = end_pos
# Trigonometry stuffs and like
self.dx = self.end_pos[0] - self.start_pos[0]
self.dy = self.end_pos[1] - self.start_pos[1]
self.rads = atan2(-self.dy,self.dx)
self.rads %= 2*pi
self.degs = degrees(self.rads)
# More stuff I dont understand but it works nearly well.
self.quarter = self.get_quarter(self.degs)
# Change the way rel_x and rel_y are calculated so stuff works fair enough
if self.quarter == 1:
self.rel_x = -self.end_pos[0] + self.start_pos[0]
self.rel_y = -self.end_pos[1] + self.start_pos[1]
elif self.quarter == 2:
self.rel_x = self.end_pos[0] - self.start_pos[0]
self.rel_y = -self.end_pos[1] + self.start_pos[1]
elif self.quarter == 3:
self.rel_x = -self.end_pos[0] + self.start_pos[0]
self.rel_y = self.end_pos[1] - self.start_pos[1]
elif self.quarter == 4:
self.rel_x = self.end_pos[0] - self.start_pos[0]
self.rel_y = self.end_pos[1] - self.start_pos[1]
self.d = (self.rel_x**2 + self.rel_y**2)**0.5
self.angle_x = ((self.rel_x * cos(self.rads)) / self.d) * move_speed
self.angle_y = ((self.rel_y * -sin(self.rads)) / self.d) * move_speed
def move(self):
self.leftover_x += self.angle_x
self.leftover_y += self.angle_y
self.rect.x += int(self.leftover_x)
self.rect.y += int(self.leftover_y)
self.leftover_x = (self.leftover_x % 1)
self.leftover_y = (self.leftover_y % 1)
Move is written that way because Pygame doesn't let you move something 1.337 of a pixel, so I move something by 1, then save .337, and when the saved endings of the number go over 1 they get added as well.
In result, I get bullets going something like that
With the numbers I marked screen quarters that get used in the if statement within init.
You can see, kinda doesn't work. I want the bullets to make a circle. What should I do.
I will admit, I don't know trigonometry. I didn't learn it in school yet, will have it in a year. Thus, I would like someone to explain it to me as simply as possible. And tell me of a better way to get this going instead of using this quarters thing.
Upvotes: 0
Views: 428
Reputation: 1237
Congratulations! You've discovered sin() and cos() for yourself.
Pull out a piece of paper and mark two points: one for the cursor position (=target), and one for the bullet's current (starting?) position.
Now draw a line between them. The length of that line is your code's .d (for distance, I presume).
Then draw a horizontal line from one of the points, far enough so it ends exactly over or under the other point. The length of that is your rel_x (in Calculus you'll call it "dx").
Then draw the vertical line to finish a triangle -- that length is your rel_y (or "dy").
I'm not sure why you need the variables you've called angle_x and angle_y. Once fired, the angle will always be the same, like a bullet (unless the target is moving and you're flying a pursuit course, like a homing missile; to do a "pure pursuit" you just re-calculate the course each turn, so it always points to where the target is at that time).
What you need next is how far to move. That's going to be some part of d. If your speed is greater than d, you cover all of d and arrive; otherwise you only cover a fraction of d. So you want something like:
if (speed > d): fraction_of_d = 1 else: fraction_of_d = speed/d
Now all you need to do is notice that if you're going 1/n of the way to the target, you'll also be covering 1/n of the x and of the y distances (stare at the paper longer if you don't see that right off).
But you already have the total x and y distances (rel_x and rel_y). So you just multiply each one by fraction_of_d, and that tells you how far to move along that direction. Then you update the bullet's location by those amounts.
You'll learn in trig that sin() is just the length of the "opposite" (the vertical side of the triangle), divided by the length of the hypotenuse (d). cos() is just the length of the "adjacent" (the horizontal side) divided by the length of the hypotenuse. So you've effectively calculated the sine and cosine already.
Hope that makes it clearer.
-steve
Upvotes: 2
Reputation: 415
Okay. I found the solution myself. Bullets still kinda go not exactly to the cursor in quarters 2 and 3 but it makes a circle.
As it turned out, the fact that I had the sin() and cos() functions there was the problem. I just removed them to make the code be
self.angle_x = (self.rel_x / self.d) * move_speed
self.angle_y = (self.rel_y / self.d) * move_speed
and it works now. I also could remove this quarter detection thing, it works now without it. Final code looks like this:
class Projectile():
def __init__(self, surface, color, start_pos, end_pos, move_speed):
super().__init__()
# Shooter and target positions
self.start_pos = start_pos
self.end_pos = end_pos
# Trigonometry stuffs and like
# NOW REDUNDANT
# self.dx = self.end_pos[0] - self.start_pos[0]
# self.dy = self.end_pos[1] - self.start_pos[1]
# self.rads = atan2(-self.dy,self.dx)
# self.rads %= 2*pi
# self.degs = degrees(self.rads)
# More stuff I dont understand but it works nearly well.
# self.quarter = self.get_quarter(self.degs)
self.rel_x = self.end_pos[0] - self.start_pos[0]
self.rel_y = self.end_pos[1] - self.start_pos[1]
self.d = (self.rel_x**2 + self.rel_y**2)**0.5
self.angle_x = (self.rel_x / self.d) * move_speed
self.angle_y = (self.rel_y) / self.d) * move_speed
and the result looks like this:
Still, someone should post a better answer that also resolves the issue of bullets not going exactly to the mouse click position.
Upvotes: 1