简体   繁体   English

如何让敌人向玩家移动并预测其在 pygame 中的路径

[英]how can I make the enemy move towards the player and predict its path in pygame

I am making a pygame game and I want my enemies follow the player and predict its path.我正在制作 pygame 游戏,我希望我的敌人跟随玩家并预测其路径。 I don't just want to reduce the distance between the player and the enemy.我不只是想减少玩家和敌人之间的距离。 The number of enemies will be according to the level, every 3 levels will add a new enemy.敌人的数量会根据等级而定,每3个等级会增加一个新的敌人。 I'm attaching my whole code along with a screenshot showing that my enemies are currently just moving in a straight line.我附上了我的整个代码以及一个屏幕截图,显示我的敌人目前只是在直线移动。

import pygame
import random

pygame.font.init()

width = 900
height = 600

screen = pygame.display.set_mode([width, height])

walkRight = [pygame.image.load('R1.png'), pygame.image.load('R2.png'), pygame.image.load('R3.png'),
             pygame.image.load('R4.png'), pygame.image.load('R5.png'), pygame.image.load('R6.png'),
             pygame.image.load('R7.png'), pygame.image.load('R8.png'), pygame.image.load('R9.png')]

walkLeft = [pygame.image.load('L1.png'), pygame.image.load('L2.png'), pygame.image.load('L3.png'),
            pygame.image.load('L4.png'), pygame.image.load('L5.png'), pygame.image.load('L6.png'),
            pygame.image.load('L7.png'), pygame.image.load('L8.png'), pygame.image.load('L9.png')]

char = pygame.image.load('standing.png')
bomb_pic = pygame.transform.scale(pygame.image.load('bomb.png'), (20,20))
bomb_explosion = pygame.transform.scale(pygame.image.load('explosion1.png'), (40,40))
pics = [bomb_pic, bomb_explosion]

# char_rect = char.get_rect()


enemy_Left = [pygame.image.load('L1E.png'), pygame.image.load('L2E.png'), pygame.image.load('L3E.png'),
            pygame.image.load('L4E.png'), pygame.image.load('L5E.png'), pygame.image.load('L6E.png'),
            pygame.image.load('L7E.png'), pygame.image.load('L8E.png'), pygame.image.load('L9E.png')] 



x = 50
y = 50
width = 40
height = 60
vel = 5
isJump = False
jumpCount = 10
left = False
right = False
down = False
up = False
walkCount = 0

enemy_vel = 2
enemy_list = []

shop = pygame.transform.scale(pygame.image.load("shop.png"), (60, 60))

clock = pygame.time.Clock()
FPS = 60

font = pygame.font.Font('freesansbold.ttf', 32)
items_font = pygame.font.Font('freesansbold.ttf', 16)

bombs =[]
explosions = []

bag = {'bomb': 0}
print(bag["bomb"])


class Button():
    def __init__(self, color, x, y, width, height, text=''):
        self.color = color
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.text = text

    def draw(self, win, outline=None):

        # Call this method to draw the button on the screen
        if outline:
            pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)

        pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)

        if self.text != '':
            font = pygame.font.SysFont('comicsans', 20)
            text = font.render(self.text, 1, (0, 0, 0))
            win.blit(text, (
                self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))


def shop_run():
    shop_bomb = Button((0, 200, 0), 820, 150, 60, 20, text="Bomb_b")
    bright_green = (0, 255, 0)
    green = (0, 200, 0)
    shop_bomb.draw(screen)


def redrawGameWindow():
    global walkCount
    global font
    global bag
    global items_font
    global enemy_list
    global pics

    current_time = pygame.time.get_ticks()

    screen.fill([166, 166, 166])
    for five_enemies in range(6):
        random_enemy_location_y = random.randrange(100, 400)
        random_enemy_location_x = random.randrange(800, 840)
        enemy_list.append([random_enemy_location_x, random_enemy_location_y])

    for enemies in range(6):
        screen.blit(enemy_Left[enemies], enemy_list[enemies])
        enemy_list[enemies][0] -= 0.3



    pygame.draw.rect(screen, (0, 0, 0), (800, 0, 100, 600))
    if x + char.get_width() < 60 and y + char.get_height() < 60:
        shop_run()

    screen.blit(shop, (0, 0))
    screen.blit(font.render("Menu", True, (255,255,255)),(805, 10))
    screen.blit(items_font.render("Bombs: "+ str(bag["bomb"]), True, (255, 255, 255)), (805, 550))
    # screen.blit(bomb_explosion, (450, 300))
    if walkCount + 1 >= 27:
        walkCount = 0

    if left:
        screen.blit(walkLeft[walkCount // 3], (x, y))
        walkCount += 1

    elif right:
        screen.blit(walkRight[walkCount // 3], (x, y))
        walkCount += 1

    elif down:
        screen.blit(char, (x, y))
        walkcount = 0

    elif up:
        screen.blit(char, (x, y))
        walkcount = 0

    else:
        screen.blit(char, (x, y))
        walkCount = 0


    for i in reversed(range(len(bombs))):
        pos, end_time = bombs[i]
        if current_time > end_time:
            bombs.pop(i)
            # current_time_2 = pygame.time.get_ticks()
            # for j in reversed(range(len(explosions))):
            #     pos2, end_time_2 = explosions[j]
            #     if current_time_2 > end_time_2:
            #         explosions.pop(j)
            #     else:
            #         screen.blit(bomb_explosion, pos2)
        else:
            screen.blit(pics[0], pos)

    for j in reversed(range(len(explosions))):
        pos, end_time_2 = explosions[j]
        if current_time > end_time_2:
            explosions.pop(j)

        elif current_time > (end_time_2 - 2000):
            screen.blit(pics[1], pos)

        else:
            continue


    pygame.display.update()

def main():
    run = True
    # shopper()
    pygame.display.set_caption("bomb-mania")

    global x
    global y
    global width
    global height
    global vel

    global isJump
    global jumpCount

    global left
    global right
    global down
    global up

    global walkCount

    global bomb_pic

    global font
    global bombs
    global explosions

    while run:

        current_time = pygame.time.get_ticks()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False


            if x + char.get_width() < 60 and y + char.get_height() < 60:
                buy = pygame.key.get_pressed()
                if buy[pygame.K_b]:
                    bag["bomb"] += 1
                    print(bag["bomb"])

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE and bag["bomb"] >= 1:
                    current_time_2 = pygame.time.get_ticks()
                    pos = x + char.get_width()/2, y + char.get_height() - 20
                    pos2 = ((x + char.get_width()/2)-10), y + char.get_height() - 30
                    end_time = current_time + 3000 # 3000 milliseconds = 3 seconds
                    end_time_2 = current_time_2 + 5000
                    explosions.append((pos2, end_time_2))
                    bombs.append((pos, end_time))
                    bag["bomb"] -= 1

        redrawGameWindow()



        keys = pygame.key.get_pressed()

        if keys[pygame.K_LEFT] and x > vel - 15:
            x -= vel
            left = True
            right = False
            down = False
            up = False



        elif keys[pygame.K_RIGHT] and x < 800 - vel - width:
            x += vel
            left = False
            right = True
            down = False
            up = False

        elif keys[pygame.K_DOWN] and y < 600 - height:
            y += vel
            left = False
            right = False
            down = True
            up = False

        elif keys[pygame.K_UP] and y > vel - 15:
            y -= vel
            left = False
            right = False
            down = False
            up = True

        else:
            left = False
            right = False
            down = False
            up = False
            walkCount = 0

        clock.tick(FPS)
        pygame.display.flip()


main()

目前只是直线移动的敌人

You'll need some vector math for this, so I recommend to restructure your code and learn how to use Sprites ;你需要一些向量数学,所以我建议你重构你的代码并学习如何使用Sprites you can find an example here .你可以在这里找到一个例子。

To find an answer to your question ("predict the path"), you could google for intercept vector or pursuit vector .要找到您问题的答案(“预测路径”),您可以在 Google 上搜索intercept vectorpursuit vector That should yield some results, such as How to calculate the vector of an interception?这应该会产生一些结果,例如如何计算截距的向量? or Calculating Intercepting Vector .计算截取向量

For example, I translated the last answer of the second question and copy/pasted it into one of my answers , since a) I'm too lazy to write everything again and b) there's a single point of code I have to change to implement the intercept logic (the EnemyController class).例如,我翻译了第二个问题的最后一个答案并将其复制/粘贴到我的一个答案中,因为 a)我懒得再写所有东西,b)我必须更改单点代码才能实现拦截逻辑( EnemyController类)。

import pygame
import random
import math
from pygame import Vector2

SPRITE_SHEET = None

GREEN_SHIP  = pygame.Rect(0, 292, 32, 32)
RED_SHIP    = pygame.Rect(0, 324, 32, 32)
BLUE_SHIP   = pygame.Rect(0, 356, 32, 32)
YELLOW_SHIP = pygame.Rect(0, 388, 32, 32)


class EnemyController:

    def __init__(self, target):
        self.direction = Vector2(1, 0)
        self.target = target

    def update(self, sprite, events, dt):
        k = self.target.vel.magnitude() / sprite.speed;

        distance_to_target = (sprite.pos - self.target.pos).magnitude()

        b_hat = self.target.vel
        c_hat = sprite.pos - self.target.pos

        CAB = b_hat.angle_to(c_hat)
        ABC = math.asin(math.sin(CAB) * k)
        ACB = math.pi - (CAB + ABC)

        j = distance_to_target / math.sin(ACB)
        a = j * math.sin(CAB)
        b = j * math.sin(ABC)

        time_to_collision = b / self.target.vel.magnitude() if self.target.vel.magnitude() > 0 else 1
        collision_pos = self.target.pos + (self.target.vel * time_to_collision)

        v = sprite.pos - collision_pos
        if v.length() > 0:
            sprite.direction = -v.normalize()

        if v.length() <= 10:
            sprite.pos = pygame.Vector2(400, 100)

class PlayerController:

    movement = {
        pygame.K_UP:    Vector2( 0, -1),
        pygame.K_DOWN:  Vector2( 0,  1),
        pygame.K_LEFT:  Vector2(-1,  0),
        pygame.K_RIGHT: Vector2( 1,  0)
    }

    def update(self, sprite, events, dt):
        pressed = pygame.key.get_pressed()
        v = Vector2(0, 0)
        for key in PlayerController.movement:
            if pressed[key]:
                v += PlayerController.movement[key]

        sprite.direction = v

        for e in events:
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE:
                    sprite.groups()[0].add(Explosion(sprite.pos))

class Animation:
    def __init__(self, frames, speed, sprite):
        self.sprite = sprite
        self.speed = speed
        self.ticks = 0
        self.frames = frames
        self.running = 0
        self.start()

    def cycle_func(self, iterable):
        saved = []
        for element in iterable:
            yield element
            saved.append(element)
        if hasattr(self.sprite, 'on_animation_end'):
            self.sprite.on_animation_end()
        while saved:
            for element in saved:
                yield element
            if hasattr(self.sprite, 'on_animation_end'):
                self.sprite.on_animation_end()
    def stop(self):
        self.running = 0
        if self.idle_image:
            self.sprite.image = self.idle_image

    def start(self):
        if not self.running:
            self.running = 1
            self.cycle = self.cycle_func(self.frames)
            self.sprite.image = next(self.cycle)

    def update(self, dt):
        self.ticks += dt
        if self.ticks >= self.speed:
            self.ticks = self.ticks % self.speed
            if self.running:
                self.sprite.image = next(self.cycle)

class AnimatedSprite(pygame.sprite.Sprite):

    def __init__(self, pos, frames, speed):
        super().__init__()
        self.animation = Animation(frames, speed, self)
        self.rect = self.image.get_rect(center=pos)
        self.pos = Vector2(pos)
        self.animation.start()

    def update(self, events, dt):
        self.animation.update(dt)

class Explosion(AnimatedSprite):

    frames = None

    def __init__(self, pos):
        if not Explosion.frames:
            Explosion.frames = parse_sprite_sheet(SPRITE_SHEET, pygame.Rect(0, 890, 64, 64), 6, 4)

        super().__init__(pos, Explosion.frames, 50)

    def on_animation_end(self):
        self.kill()

class DirectionalImageSprite(pygame.sprite.Sprite):

    directions = [(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1),(1,1),(0,0)]

    def __init__(self, pos, directional_images_rect):
        super().__init__()
        images = parse_sprite_sheet(SPRITE_SHEET, directional_images_rect, 9, 1)
        self.images = { x: img for (x, img) in zip(DirectionalImageSprite.directions, images) }
        self.direction = Vector2(0, 0)
        self.image = self.images[(self.direction.x, self.direction.y)]
        self.rect = self.image.get_rect(center=pos)
        self.pos = pygame.Vector2(pos)

class SpaceShip(DirectionalImageSprite):

    def __init__(self, pos, controller, directional_images_rect):
        super().__init__(pos, directional_images_rect)
        self.controller = controller
        self.speed = 2
        self.vel = pygame.Vector2(0, 0)

    def update(self, events, dt):
        super().update(events, dt)

        if self.controller:
            self.controller.update(self, events, dt)

        self.vel = Vector2(0, 0)
        if (self.direction.x, self.direction.y) in self.images:
            self.image = self.images[(self.direction.x, self.direction.y)]
        if self.direction.length():
            self.vel = self.direction.normalize() * self.speed
            self.pos += self.vel

        self.rect.center = int(self.pos[0]), int(self.pos[1])

def parse_sprite_sheet(sheet, start_rect, frames_in_row, lines):
    frames = []
    rect = start_rect.copy()
    for _ in range(lines):
        for _ in range(frames_in_row):
            frame = sheet.subsurface(rect)
            frames.append(frame)
            rect.move_ip(rect.width, 0)
        rect.move_ip(0, rect.height)
        rect.x = start_rect.x
    return frames

def main():
    screen = pygame.display.set_mode((800, 600))
    global SPRITE_SHEET
    SPRITE_SHEET = pygame.image.load("ipLRR.png").convert_alpha()
    clock = pygame.time.Clock()
    dt = 0
    player = SpaceShip((400, 300), PlayerController(), YELLOW_SHIP)
    enemy = SpaceShip((400, 100), EnemyController(player), GREEN_SHIP)
    enemy.speed = 4
    all_sprites = pygame.sprite.Group(
        player,
        enemy
    )

    while True:
        events = pygame.event.get()

        for e in events:
            if e.type == pygame.QUIT:
                return

        all_sprites.update(events, dt)

        screen.fill((0, 0, 0))
        all_sprites.draw(screen)
        pygame.display.flip()
        dt = clock.tick(120)

main()

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM