如何在 pygame 中向鼠标光标发射子弹

How to shoot a bullet towards mouse cursor in pygame

我正在尝试在 pygame 中创建一个自上而下的射击游戏。我想出了如何将子弹射向光标,但我的实现遇到了问题。当鼠标光标在玩家身上或非常靠近玩家时,射出的子弹非常慢。我不知道为什么。我该如何解决这个问题?

我的代码:

mouse_x, mouse_y = pygame.mouse.get_pos()
from_player_x, from_player_y = mouse_x - self.rect.x, mouse_y - self.rect.y
x_speed, y_speed = round(from_player_x * 0.1), round(from_player_y * 0.1)
self.speed_x = x_speed
self.speed_y = y_speed
...
self.rect.x += self.speed_x
self.rect.y += self.speed_y

要正确计算速度你不能直接使用from_player_xfrom_player_y而是用它来计算angle然后使用sin(),cos()计算 speed_x, speed_y

    player = pygame.Rect(screen_rect.centerx, screen_rect.bottom, 0, 0)
    SPEED = 5
    
    #---

    mouse_x, mouse_y = pygame.mouse.get_pos()
    
    distance_x = mouse_x - player.x
    distance_y = mouse_y - player.y
    
    angle = math.atan2(distance_y, distance_x)
    
    speed_x = SPEED * math.cos(angle)
    speed_y = SPEED * math.sin(angle)

最小工作示例

import pygame
import math

# === CONSTANS === (UPPER_CASE names)

BLACK = (  0,   0,   0)
WHITE = (255, 255, 255)

RED   = (255,   0,   0)
GREEN = (  0, 255,   0)
BLUE  = (  0,   0, 255)

SCREEN_WIDTH  = 600
SCREEN_HEIGHT = 400

# === MAIN === (lower_case names)

# --- init ---

pygame.init()

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()

# --- objects ---

player = pygame.Rect(screen_rect.centerx, screen_rect.bottom, 0, 0)
start = pygame.math.Vector2(player.center)
end = start
length = 50

SPEED = 5

all_bullets = []

# --- mainloop ---

clock = pygame.time.Clock()
is_running = True


while is_running:

    # --- events ---

    for event in pygame.event.get():

        # --- global events ---

        if event.type == pygame.QUIT:
            is_running = False

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                is_running = False
        
        elif event.type == pygame.MOUSEMOTION:
            mouse = pygame.mouse.get_pos()
            end = start + (mouse - start).normalize() * length
        
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            
            distance_x = mouse_x - player.x
            distance_y = mouse_y - player.y
            
            angle = math.atan2(distance_y, distance_x)
            
            # speed_x, speed_y can be `float` but I don't convert to `int` to get better position
            speed_x = SPEED * math.cos(angle)
            speed_y = SPEED * math.sin(angle)
            
            # I copy `player.x, player.y` because I will change these values directly on list
            all_bullets.append([player.x, player.y, speed_x, speed_y])
            
        # --- objects events ---

            # empty

    # --- updates ---

    # move using speed - I use indexes to change directly on list
    for item in all_bullets:
        # speed_x, speed_y can be `float` but I don't convert to `int` to get better position
        item[0] += item[2]  # pos_x += speed_x
        item[1] += item[3]  # pos_y -= speed_y

    # --- draws ---

    screen.fill(BLACK)

    pygame.draw.line(screen, RED, start, end)

    for pos_x, pos_y, speed_x, speed_y in all_bullets:
        # need to convert `float` to `int` because `screen` use only `int` values
        pos_x = int(pos_x)
        pos_y = int(pos_y)
        pygame.draw.line(screen, (0,255,0), (pos_x, pos_y), (pos_x, pos_y))
        
    pygame.display.update()

    # --- FPS ---

    clock.tick(25)

# --- the end ---

pygame.quit()

PyGame有模块pygame.math and object Vector2可以让计算更简单

    player = pygame.Rect(screen_rect.centerx, screen_rect.bottom, 0, 0)
    start = pygame.math.Vector2(player.center)

    SPEED = 5

    # ---

    mouse = pygame.mouse.get_pos()

    distance = mouse - start

    position = pygame.math.Vector2(start) # duplicate # start position in start of canon
    #position = pygame.math.Vector2(end)   # duplicate # start position in end of canon

    speed = distance.normalize() * SPEED

以后

    position += speed

最小工作示例

import pygame

# === CONSTANS === (UPPER_CASE names)

BLACK = (  0,   0,   0)
WHITE = (255, 255, 255)

RED   = (255,   0,   0)
GREEN = (  0, 255,   0)
BLUE  = (  0,   0, 255)

SCREEN_WIDTH  = 600
SCREEN_HEIGHT = 400

# === MAIN === (lower_case names)

# --- init ---

pygame.init()

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()

# --- objects ---

player = pygame.Rect(screen_rect.centerx, screen_rect.bottom, 0, 0)
start = pygame.math.Vector2(player.center)
end = start
length = 50

SPEED = 5

all_bullets = []

# --- mainloop ---

clock = pygame.time.Clock()
is_running = True


while is_running:

    # --- events ---

    for event in pygame.event.get():

        # --- global events ---

        if event.type == pygame.QUIT:
            is_running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                is_running = False
        elif event.type == pygame.MOUSEMOTION:
            mouse = pygame.mouse.get_pos()
            end = start + (mouse - start).normalize() * length
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse = pygame.mouse.get_pos()

            distance = mouse - start

            position = pygame.math.Vector2(start) # duplicate # start position in start of canon
            #position = pygame.math.Vector2(end)   # duplicate # start position in end of canon
            speed = distance.normalize() * SPEED
            
            all_bullets.append([position, speed])
            
        # --- objects events ---

            # empty

    # --- updates ---

    for position, speed in all_bullets:
        position += speed

    # --- draws ---

    screen.fill(BLACK)

    pygame.draw.line(screen, RED, start, end)

    for position, speed in all_bullets:
        # need to convert `float` to `int` because `screen` use only `int` values
        pos_x = int(position.x)
        pos_y = int(position.y)
        pygame.draw.line(screen, (0,255,0), (pos_x, pos_y), (pos_x, pos_y))
        
    pygame.display.update()

    # --- FPS ---

    clock.tick(25)

# --- the end ---

pygame.quit()