我一直在使用Python和pygame sprites创建一种典型的太空入侵者游戏。我已经完成了大部分工作并且工作正常。但实际上在我的游戏中,子弹总是直射,但我希望它能够瞄准敌人并且总是射击敌人所在的地方。
在我的播放器更新中,我只是在空间被击中时这样做,它会发射子弹。
if (keys[pygame.K_SPACE]):
self.fire()
以下是我的fire
方法,它只是调用手枪(这是我的子弹类):
def fire(self):
now = pygame.time.get_ticks()
self.shoot_delay = 600
self.shot_position = handguns.rect.x - enemy.rect.x
print (self.shot_position)
if (now - self.last_shot > self.shoot_delay):
self.last_shot = now
shot = HandGun(self.rect.centerx, self.rect.top)
Every_Sprite.add(shot)
handgun.add(shot)
以下是我的敌人类,其职位是随机的:
class Enemy_Agent(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join(img_folder, "ship2.png")).convert()
self.image.set_colorkey(WHITE)
self.rect =self.image.get_rect()
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = random.randrange(1,8)
self.speedx = random.randrange(-3,3)
def update(self):
if (self.rect.right > width):
self.rect.right = width
if (self.rect.left < 0):
self.rect.left = 0
self.rect.y += self.speed
#print("pos: ", self.rect.y)
self.rect.x += self.speedx
if(self.rect.top > Height - 10 or self.rect.left < -30 or self.rect.right > width + 20):
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = random.randrange(1, 8)
并且,这是我的HandGun
课程。如果有人能帮助我并建议我让子弹瞄准敌人,那将是一个很大的帮助。
class HandGun(pygame.sprite.Sprite):
def __init__(self, cx, cy):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join(img_folder, "bullet.png")).convert()
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
self.rect.bottom = cy
self.rect.centerx =cx
self.speedy = -1
self.speedx = None
def update(self):
self.rect.y += self.speedy + enemy.rect.centerx
if(self.rect.bottom < 0):
self.kill()
pass
答案 0 :(得分:3)
这是一个最小的例子,我只是从鼠标位置向目标射击子弹。
首先我们需要一个指向目标的vector(此处称为direction
),因此我们从目标位置减去鼠标位置。
我们使用direction
向量的角度(您可以使用as_polar
方法(polar coordinates))来旋转子弹。
要获得速度矢量,我们可以对direction
进行标准化并将其乘以标量,以将其缩放到所需的长度(即速度)。
import pygame as pg
from pygame.math import Vector2
BACKGROUND_COLOR = pg.Color(30, 30, 50)
BLUE = pg.Color('dodgerblue1')
LIME = pg.Color(192, 255, 0)
class Bullet(pg.sprite.Sprite):
""" This class represents the bullet. """
def __init__(self, pos, target, screen_rect):
"""Take the pos, direction and angle of the player."""
super().__init__()
self.image = pg.Surface((16, 10), pg.SRCALPHA)
pg.draw.polygon(self.image, LIME, ((0, 0), (16, 5), (0, 10)))
# The `pos` parameter is the center of the bullet.rect.
self.rect = self.image.get_rect(center=pos)
self.position = Vector2(pos) # The position of the bullet.
# This vector points from the mouse pos to the target.
direction = target - pos
# The polar coordinates of the direction vector.
radius, angle = direction.as_polar()
# Rotate the image by the negative angle (because the y-axis is flipped).
self.image = pg.transform.rotozoom(self.image, -angle, 1)
# The velocity is the normalized direction vector scaled to the desired length.
self.velocity = direction.normalize() * 11
self.screen_rect = screen_rect
def update(self):
"""Move the bullet."""
self.position += self.velocity # Update the position vector.
self.rect.center = self.position # And the rect.
# Remove the bullet when it leaves the screen.
if not self.screen_rect.contains(self.rect):
self.kill()
def main():
pg.init()
screen = pg.display.set_mode((800, 600))
screen_rect = screen.get_rect()
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
bullet_group = pg.sprite.Group()
target = Vector2(400, 300)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
# Shoot a bullet. Pass the start position (in this
# case the mouse position) and the direction vector.
bullet = Bullet(event.pos, target, screen_rect)
all_sprites.add(bullet)
bullet_group.add(bullet)
all_sprites.update()
screen.fill(BACKGROUND_COLOR)
all_sprites.draw(screen)
pg.draw.rect(screen, BLUE, (target, (3, 3)), 1)
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
pg.quit()
如果您不熟悉矢量,也可以使用三角法。
如果你想要一个寻的导弹,你必须将当前目标位置传递给子弹并重新计算每帧的direction
和velocity
。
要瞄准移动目标,您需要计算射弹将击中目标的未来位置。你可以用二次方程来做到这一点。我在这里使用Jeffrey Hantin的解决方案from this answer。你必须将子弹的起始位置,速度和目标位置和速度传递给intercept
函数,然后求解二次方程。它将返回子弹和目标相遇的位置向量。然后只是在此点而不是当前目标点进行拍摄(您仍然可以在Bullet
类中使用相同的代码。)
import math
import pygame as pg
from pygame.math import Vector2
BACKGROUND_COLOR = pg.Color(30, 30, 50)
BLUE = pg.Color('dodgerblue1')
LIME = pg.Color(192, 255, 0)
class Bullet(pg.sprite.Sprite):
""" This class represents the bullet. """
def __init__(self, pos, target, screen_rect):
"""Take the pos, direction and angle of the player."""
super().__init__()
self.image = pg.Surface((16, 10), pg.SRCALPHA)
pg.draw.polygon(self.image, LIME, ((0, 0), (16, 5), (0, 10)))
# The `pos` parameter is the center of the bullet.rect.
self.rect = self.image.get_rect(center=pos)
self.position = Vector2(pos) # The position of the bullet.
# This vector points from the mouse pos to the target.
direction = target - pos
# The polar coordinates of the direction vector.
radius, angle = direction.as_polar()
# Rotate the image by the negative angle (because the y-axis is flipped).
self.image = pg.transform.rotozoom(self.image, -angle, 1)
# The velocity is the normalized direction vector scaled to the desired length.
self.velocity = direction.normalize() * 11
self.screen_rect = screen_rect
def update(self):
"""Move the bullet."""
self.position += self.velocity # Update the position vector.
self.rect.center = self.position # And the rect.
# Remove the bullet when it leaves the screen.
if not self.screen_rect.contains(self.rect):
self.kill()
def intercept(position, bullet_speed, target, target_velocity):
a = target_velocity.x**2 + target_velocity.y**2 - bullet_speed**2
b = 2 * (target_velocity.x * (target.x - position.x) + target_velocity.y * (target.y - position.y))
c = (target.x - position.x)**2 + (target.y - position.y)**2
discriminant = b*b - 4*a*c
if discriminant < 0:
print("Target can't be reached.")
return None
else:
t1 = (-b + math.sqrt(discriminant)) / (2*a)
t2 = (-b - math.sqrt(discriminant)) / (2*a)
t = max(t1, t2)
x = target_velocity.x * t + target.x
y = target_velocity.y * t + target.y
return Vector2(x, y)
def main():
pg.init()
screen = pg.display.set_mode((800, 600))
screen_rect = screen.get_rect()
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
bullet_group = pg.sprite.Group()
target = Vector2(50, 300)
target_velocity = Vector2(4, 3)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
target_vector = intercept(Vector2(event.pos), 11, target, target_velocity)
# Shoot a bullet. Pass the start position (in this
# case the mouse position) and the target position vector.
if target_vector is not None: # Shoots only if the target can be reached.
bullet = Bullet(event.pos, target_vector, screen_rect)
all_sprites.add(bullet)
bullet_group.add(bullet)
target += target_velocity
if target.x >= screen_rect.right or target.x < 0:
target_velocity.x *= -1
if target.y >= screen_rect.bottom or target.y < 0:
target_velocity.y *= -1
all_sprites.update()
screen.fill(BACKGROUND_COLOR)
all_sprites.draw(screen)
pg.draw.rect(screen, BLUE, (target, (5, 5)))
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
pg.quit()
实际上,最好使用broofa's solution,因为它需要考虑一些特殊情况。
def intercept(position, bullet_speed, target, target_velocity):
tx, ty = target - position
tvx, tvy = target_velocity
v = bullet_speed
dstx, dsty = target
a = tvx*tvx + tvy*tvy - v*v
b = 2 * (tvx*tx + tvy*ty)
c = tx*tx + ty*ty
ts = quad(a, b, c)
sol = None
if ts:
t0 = ts[0]
t1 = ts[1]
t = min(t0, t1)
if t < 0:
t = max(t0, t1)
if t > 0:
sol = Vector2(dstx + tvx * t,
dsty + tvy * t)
return sol
def quad(a, b, c):
sol = None
if abs(a) < 1e-6:
if abs(b) < 1e-6:
sol = [0, 0] if abs(c) < 1e-6 else None
else:
sol = [-c/b, -c/b]
else:
disc = b*b - 4*a*c
if disc >= 0:
disc = math.sqrt(disc)
a = 2*a
sol = [(-b-disc)/a, (-b+disc)/a]
return sol