Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
470 views
in Technique[技术] by (71.8m points)

python - Shooting a bullet in pygame in the direction of mouse

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.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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()

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...