3

I just cant figure out why my bullet is not working. I made a bullet class and here it is:

class Bullet:
    def __init__(self):
        self.x = player.x
        self.y = player.y
        self.height = 7
        self.width = 2
        self.bullet = pygame.Surface((self.width, self.height))
        self.bullet.fill((255, 255, 255))

Now I added several functions in my game class and here is the new code:

class Game:
    def __init__(self):
        self.bullets = []
    
    def shoot_bullet(self):
         if self.bullets:
            for bullet in self.bullets:
                rise = mouse.y - player.y
                run = mouse.x - player.x
                angle = math.atan2(rise, run)

                bullet.x += math.cos(angle)
                bullet.y += math.sin(angle)

                pygame.transform.rotate(bullet.bullet, -math.degrees(angle))
                D.blit(bullet.bullet, (bullet.x, bullet.y))


    def generate_bullet(self):
        if  mouse.is_pressed():
            self.bullets.append(Bullet())

What I was expecting the code to do was a Bullet() would get added to game.bullets every time I pressed the mouse button, then game.shoot_bullet would calculate the angle between the player and the mouse and shoot the bullet accordingly in the direction of the mouse. However, the result is a complete mess and the bullets actually don't rotate and don't move. They get generated and move weirdly to the left of the screen. I am not sure if I have messed up something or the method I have used is completely wrong.

Qiu YU
  • 505
  • 3
  • 19
  • Could this be the trig' functions producing negative results when in different quadrants? - https://www.teachoo.com/7240/1406/Signs-of-sin--cos--tan-in-different-quadrants/category/Finding-Value-of-trignometric-functions--given-other-functions/ Does it work when the mouse is upper-right? – Kingsley Jan 29 '20 at 23:30
  • I printed out sin and cos values and that seems to be the case (looked like sin was negative in second quadrant). How do i fix this?Thanks –  Jan 29 '20 at 23:40
  • Upper right sin is positive and cos is negative –  Jan 29 '20 at 23:41
  • 1
    What about using polar co-ordinates? Ref: https://stackoverflow.com/questions/6775897/pygame-making-a-sprite-face-the-mouse – Kingsley Jan 30 '20 at 01:00

1 Answers1

9

First of all pygame.transform.rotate does not transform the object itself, but creates a new rotated surface and returns it.

If you want to fire a bullet in a certain direction, the direction is defined the moment the bullet is fired, but it does not change continuously. When the bullet is fired, set the starting position of the bullet and calculate the direction vector to the mouse position:

self.pos = (x, y)
mx, my = pygame.mouse.get_pos()
self.dir = (mx - x, my - y)

The direction vector should not depend on the distance to the mouse, but it has to be a Unit vector. Normalize the vector by dividing by the Euclidean distance

length = math.hypot(*self.dir)
if length == 0.0:
    self.dir = (0, -1)
else:
    self.dir = (self.dir[0]/length, self.dir[1]/length)

Compute the angle of the vector and rotate the bullet. In general, the angle of a vector can be computed by atan2(y, x). The y-axis needs to be reversed (atan2(-y, x)) as the y-axis generally points up, but in the PyGame coordinate system the y-axis points down (see How to know the angle between two points?):

angle = math.degrees(math.atan2(-self.dir[1], self.dir[0]))

self.bullet = pygame.Surface((7, 2)).convert_alpha()
self.bullet.fill((255, 255, 255))
self.bullet = pygame.transform.rotate(self.bullet, angle)

To update the position of the bullet, it is sufficient to scale the direction (by a velocity) and add it to the position of the bullet:

self.pos = (self.pos[0]+self.dir[0]*self.speed, 
            self.pos[1]+self.dir[1]*self.speed)

To draw the rotated bullet in the correct position, take the bounding rectangle of the rotated bullet and set the center point with self.pos (see How do I rotate an image around its center using PyGame?):

bullet_rect = self.bullet.get_rect(center = self.pos)
surf.blit(self.bullet, bullet_rect)  

See also Shoot bullets towards target or mouse


Minimal example: repl.it/@Rabbid76/PyGame-FireBulletInDirectionOfMouse

import pygame
import math

pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()

class Bullet:
    def __init__(self, x, y):
        self.pos = (x, y)
        mx, my = pygame.mouse.get_pos()
        self.dir = (mx - x, my - y)
        length = math.hypot(*self.dir)
        if length == 0.0:
            self.dir = (0, -1)
        else:
            self.dir = (self.dir[0]/length, self.dir[1]/length)
        angle = math.degrees(math.atan2(-self.dir[1], self.dir[0]))

        self.bullet = pygame.Surface((7, 2)).convert_alpha()
        self.bullet.fill((255, 255, 255))
        self.bullet = pygame.transform.rotate(self.bullet, angle)
        self.speed = 2

    def update(self):  
        self.pos = (self.pos[0]+self.dir[0]*self.speed, 
                    self.pos[1]+self.dir[1]*self.speed)

    def draw(self, surf):
        bullet_rect = self.bullet.get_rect(center = self.pos)
        surf.blit(self.bullet, bullet_rect)  

bullets = []
pos = (250, 250)
run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            bullets.append(Bullet(*pos))

    for bullet in bullets[:]:
        bullet.update()
        if not window.get_rect().collidepoint(bullet.pos):
            bullets.remove(bullet)

    window.fill(0)
    pygame.draw.circle(window, (0, 255, 0), pos, 10)
    for bullet in bullets:
        bullet.draw(window)
    pygame.display.flip()
Rabbid76
  • 142,694
  • 23
  • 71
  • 112