所以我正在用pygame编写一款游戏,其中玩家从僵尸块中逃跑并射击杀死它们。我让子弹射向我的鼠标位置,这是我想要的。然而,子弹似乎只能从大约10个不同的轴上射击,而不是在360度的任何地方射击。我如何解决这个问题,使它使子弹总是直接到鼠标光标,而不是接近它?
-这是我的整个程序,所以你可以复制/粘贴它来尝试游戏。注意子弹并没有完全跟随鼠标的位置。如何解决这个问题?子弹的代码将张贴在整个游戏代码的下面。方向键移动,鼠标瞄准射击,按'P'退出:
import pygame
import math
import random
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
ORANGE = (255, 119, 0)
ZOMBIE_GREEN = (122, 172, 34)
cursor_x = 100
cursor_y = 100
class Player(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
# pygame.Surface will create a rectangle with the width and height given
# and the command below it tells it to fill it in with that color
self.image = pygame.Surface([15, 15])
self.image.fill(color)
self.rect = self.image.get_rect()
# This defines the starting position (x, y)
# of whatever sprite is passed through
self.rect.x = 600
self.rect.y = 300
# This is the current speed it will move when drawn
self.change_x = 0
self.change_y = 0
self.walls = None
# Defines how the player will move
def movement(self, x, y):
self.change_x += x
self.change_y += y
# Updates the information so the screen shows the player moving
def update(self):
self.rect.x += self.change_x
# Did this update cause us to hit a wall?
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
# If we are moving right, set our right side to the left side of
# the item we hit
if self.change_x > 0:
self.rect.right = block.rect.left
else:
# Otherwise if we are moving left, do the opposite.
self.rect.left = block.rect.right
self.rect.y += self.change_y
# Check and see if we hit anything
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
# Reset our position based on the top/bottom of the object.
if self.change_y > 0:
self.rect.bottom = block.rect.top
else:
self.rect.top = block.rect.bottom
class Enemy(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
self.image = pygame.Surface([20, 20])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(35, screen_width - 35)
self.rect.y = random.randrange(35, screen_height - 135)
self.change_x = 0
self.change_y = 0
self.walls = None
def movement(self, x, y):
self.change_x += x
self.change_y += y
class Wall(pygame.sprite.Sprite):
def __init__(self, color, x, y, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Cursor(pygame.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(RED)
self.rect = self.image.get_rect()
self.walls = None
# This updates the cursor to move along with your
# mouse position (defined in control logic)
def update(self):
self.rect.x = cursor_x
self.rect.y = cursor_y
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([8, 8])
self.image.fill(ORANGE)
self.rect = self.image.get_rect()
self.rect.x = player.rect.x + 4
self.rect.y = player.rect.y + 4
self.walls = None
self.change_x = 0
self.change_y = 0
def bullet_movement(self, cursor_pos_x, cursor_pos_y, player_pos_x, player_pos_y):
bullet_vec_x = cursor.rect.x - player.rect.x
bullet_vec_y = cursor.rect.y - player.rect.y
vec_length = math.sqrt(bullet_vec_x ** 2 + bullet_vec_y ** 2) # Normalizing the Vector
bullet_vec_x = (bullet_vec_x / vec_length) * 5 # These numbers determine how
bullet_vec_y = (bullet_vec_y / vec_length) * 5 # fast the bullets travel
self.change_x += bullet_vec_x
self.change_y += bullet_vec_y
def update(self):
self.rect.x += self.change_x
self.rect.y += self.change_y
pygame.init()
screen_size = pygame.display.Info()
#size = (900, 700)
#screen = pygame.display.set_mode(size)
size = (screen_size.current_w, screen_size.current_h)
screen = pygame.display.set_mode(
((screen_size.current_w, screen_size.current_h)),pygame.FULLSCREEN
)
screen_width = screen_size.current_w
screen_height = screen_size.current_h
pygame.display.set_caption("Zombie Shooter")
wall_list = pygame.sprite.Group()
sprites_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
all_sprites_list = pygame.sprite.Group()
# Walls are made here = (x_coord for where it starts,
# y_coord for where it starts, width of wall, height of wall)
# These walls are made with fullscreen dimentions, not any set dimentions
# Left
wall = Wall(BLUE, 0, 0, 10, screen_height)
wall_list.add(wall)
all_sprites_list.add(wall)
# Top
wall = Wall(BLUE, 0, 0, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# Bottom
wall = Wall(BLUE, 0, screen_height - 10, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# Right
wall = Wall(BLUE, screen_width - 10, 0, 10, screen_width)
wall_list.add(wall)
all_sprites_list.add(wall)
# HUD Border
wall = Wall(BLUE, 0, screen_height - 100, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# This creates the actual player with the parameters set in ( ).
# However, we must add the player to the all_sprites_list
# so that it will actually be drawn to the screen with the draw command
# placed right after the screen.fill(BLACK) command.
player = Player(WHITE)
player.walls = wall_list
all_sprites_list.add(player)
cursor = Cursor(7, 7)
cursor.walls = wall_list
all_sprites_list.add(cursor)
zombie = Enemy(ZOMBIE_GREEN)
for i in range(10):
zombie = Enemy(ZOMBIE_GREEN)
all_sprites_list.add(zombie)
sprites_list.add(zombie)
bullet = Bullet()
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
# -------- Main Program Loop -----------
while not done:
# --- Main event loop ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
done = True
# Keyboard controls. The numbers inside change the speed of the player
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.movement(-4, 0)
elif event.key == pygame.K_d:
player.movement(4, 0)
elif event.key == pygame.K_w:
player.movement(0, -4)
elif event.key == pygame.K_s:
player.movement(0, 4)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movement(4, 0)
elif event.key == pygame.K_d:
player.movement(-4, 0)
elif event.key == pygame.K_w:
player.movement(0, 4)
elif event.key == pygame.K_s:
player.movement(0, -4)
pos = pygame.mouse.get_pos()
cursor_x = pos[0]
cursor_y = pos[1]
if cursor_x <= 10:
cursor_x = 10
if cursor_x >= (screen_width - 17):
cursor_x = (screen_width - 17)
if cursor_y <= 10:
cursor_y = 10
if cursor_y >= (screen_height - 107):
cursor_y = (screen_height - 107)
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet = Bullet()
all_sprites_list.add(bullet)
bullet_list.add(bullet)
bullet.bullet_movement(cursor.rect.x, cursor.rect.y, player.rect.x, player.rect.y)
all_sprites_list.update()
pygame.mouse.set_visible(0)
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, sprites_list, True)
for i in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, wall_list, False)
for i in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
# .update() will 'update' or change the screen with what
# we've told it to everytime we run throught the loop. Without
# this our player would not appear to move on the screen because
# we wouldn't be telling the screen to change the coordinates of the player.
cursor.update()
bullet_list.update()
screen.fill(BLACK)
all_sprites_list.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
以下是子弹矢量的计算方法。是否可以对其进行编辑以使子弹更准确地指向红色光标位置?class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([8, 8])
self.image.fill(ORANGE)
self.rect = self.image.get_rect()
self.rect.x = player.rect.x + 4
self.rect.y = player.rect.y + 4
self.walls = None
self.change_x = 0
self.change_y = 0
def bullet_movement(self, cursor_pos_x, cursor_pos_y, player_pos_x, player_pos_y):
bullet_vec_x = cursor.rect.x - player.rect.x
bullet_vec_y = cursor.rect.y - player.rect.y
vec_length = math.sqrt(bullet_vec_x ** 2 + bullet_vec_y ** 2) # Normalizing the Vector
bullet_vec_x = (bullet_vec_x / vec_length) * 5 # These numbers determine how
bullet_vec_y = (bullet_vec_y / vec_length) * 5 # fast the bullets travel
self.change_x += bullet_vec_x
self.change_y += bullet_vec_y
def update(self):
self.rect.x += self.change_x
self.rect.y += self.change_y
我怀疑这是一个整数数学问题。在某些角度,你为子弹计算的速度需要在给定方向上移动一小部分像素,但pygame.rect
只支持整数坐标。
我不确定Pygame的类型是如何实现的,但我怀疑这意味着更新位置的小数部分在每一帧上被丢弃。因为许多速度矢量以同样的方式四舍五入,你最终得到的角度比你想要的更有限。
一个解决方案是保持你自己的(浮点)位置值。您的更新代码将速度分量添加到浮点位置值,然后将更新的值复制到pygame.rect
坐标。这样舍入误差就不会累积,并且您可以看到不同向量之间的差异。
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([8, 8])
self.image.fill(ORANGE)
self.rect = self.image.get_rect()
self.pos_x = player.rect.x + 4 # Set up pos_x and pos_y here
self.pos_y = player.rect.y + 4 # rather than rect.x and rect.y
self.walls = None
self.change_x = 0
self.change_y = 0
def bullet_movement(self, cursor_pos_x, cursor_pos_y, player_pos_x, player_pos_y):
bullet_vec_x = cursor.rect.x - player.rect.x
bullet_vec_y = cursor.rect.y - player.rect.y
vec_length = math.sqrt(bullet_vec_x ** 2 + bullet_vec_y ** 2)
bullet_vec_x = (bullet_vec_x / vec_length) * 5
bullet_vec_y = (bullet_vec_y / vec_length) * 5
self.change_x += bullet_vec_x
self.change_y += bullet_vec_y
def update(self):
self.pos_x += self.change_x # Update pos_x and pos_y. They will become floats
self.pos_y += self.change_y # which will let them maintain sub-pixel accuracy.
self.rect.x = self.pos_x # Copy the pos values into the rect, where they will be
self.rect.y = self.pos_y # rounded off. That's OK since we never read them back.
这段代码有一些不一致的地方,比如根本不使用bullet_movement
函数的参数,而只是查找全局值player
和cursor
(您还可以在__init__
方法中查找player
)。我没有修改这些,但你可能想,一旦你得到四舍五入的问题得到控制。我还会考虑将bullet_movement
的代码合并到__init__
中,因为没有任何情况下你想要创建一个子弹而不设置它的移动。