创建自动定位项目符号

时间:2018-05-15 10:38:54

标签: python pygame

我一直在使用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

1 个答案:

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

如果您不熟悉矢量,也可以使用三角法。

如果你想要一个寻的导弹,你必须将当前目标位置传递给子弹并重新计算每帧的directionvelocity

要瞄准移动目标,您需要计算射弹将击中目标的未来位置。你可以用二次方程来做到这一点。我在这里使用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