我如何让敌人向玩家移动并在游戏中预测其路径

2024-05-04 01:58:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在做一个pygame游戏,我希望我的敌人跟随玩家并预测它的路径。我不只是想缩短玩家和敌人之间的距离。敌人的数量将根据等级而定,每3级将增加一个新的敌人。我附上了我的全部代码和一个截图,显示我的敌人目前只是在直线移动

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

enemies currently just moving in a straight line


Tags: imageselffalsegetiftimepngload
1条回答
网友
1楼 · 发布于 2024-05-04 01:58:59

为此,您需要一些向量数学,因此我建议重新构造代码并学习如何使用^{};你可以找到一个例子here

要找到你问题的答案(“预测路径”),你可以在谷歌上搜索intercept vectorpursuit vector。这将产生一些结果,例如How to calculate the vector of an interception?Calculating Intercepting Vector

例如,我翻译了第二个问题的最后一个答案,并将其复制/粘贴到我的answers中,因为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()

enter image description here

相关问题 更多 >