pygame一个敌人向玩家的二维移动,如何计算x和y速度?

2024-09-27 21:31:02 发布

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

在我目前正在制作的游戏中,我需要向一名玩家(这里的敌人是一个blob)移动一个ennemy移动。为了实现这一点,我已经创建了一个函数:def calculate_enemy_movement(enemy):,在它里面我放置了函数需要做的事情:

calculate what x value needs to be added each frame(x velocity), and what y value needs to be added each frame(y velocity) to the enemy.x and enemy.y to walk towards the player.x and player.y and then return x velocity and return y velocity.
This function gets called here : add x velocity and y velocity to x and y pos of enemy each frame enemy_blob.x,enemy_blob.y += calculate_enemy_movement(enemy_blob)

现在我只需要有人帮我创建这个函数。我已经研究过它了,我知道它和向量数学有关,但我不确定它到底是如何工作的,所以如果有人能写这个函数并向我解释它是如何工作的,我会非常高兴

如果您需要代码的其余部分:

import pygame
import os
import sys
from math import *
import math
from typing import Tuple
from pygame.locals import *

running = False

class Game():
    def __init__(self):
        main()

pygame.init()

FPS = 60
ani = 4

def diagonal_x_vel(velocity):
    diagonal_x = int((math.pi / 4) * velocity)
    return diagonal_x

def diagonal_y_vel(velocity):
    diagonal_y = int(sin(math.pi / 4) * velocity)
    return diagonal_y

def move(rect, movement):
    global player_rect
    rect.x += movement[0]
    rect.y += movement[1]
    return rect

WIDTH, HEIGHT = 1920, 1080
player_action = "idle"
player_frame = 0

class Player():
    def __init__(self):
        self.main_char_height, self.main_char_width = 35 * 4, 35 * 4
        self.main_char_x = WIDTH / 2 - self.main_char_width / 2
        self.main_char_y = HEIGHT / 2 - self.main_char_height / 2
        self.current_health = 80
        self.maximum_health = 100
        self.health_bar_length = 500
        self.health_ratio = self.maximum_health / self.health_bar_length
        self.moving_right, self.moving_left, self.moving_down, self.moving_up, self.idle = False, False, False, False, True
        self.player_rect = pygame.Rect(self.main_char_x, self.main_char_y, self.main_char_width, self.main_char_height)

        self.player_flip = False
        self.VEL = 8

        def update():
            pass

        def take_dmg(self, amount):
            if self.current_health > 0:
                self.current_health -= amount
            if self.current_health <= 0:
                self.current_health = 0

        def get_health(self, amount):
            if self.current_health < self.maximum_health:
                self.current_health += amount
            if self.current_health >= self.maximum_health:
                self.current_health = self.maximum_health

class Enemy():
    def __init__(self, health, attack_damage, raw_speed, x, y, height, width, action,diagonal_vel_x,diangonal_vel_y):
        self.x = x
        self.y = y
        self.health = health
        self.attack_damage = attack_damage
        self.speed = raw_speed
        self.enemy_rect = pygame.Rect(x,y,width,height)
        self.action = action

WIN = pygame.display.set_mode([WIDTH, HEIGHT], pygame.FULLSCREEN)
pygame.display.set_caption("first game")

player = Player()
animation_frames = {}

def load_animation(path, image_name, frame_duration, scale_width, scale_height):
    global animation_frames

    animation_frame_data = []
    n = 0
    for frame in frame_duration:
        animation_frame_id = image_name + "_" + str(n)
        img_loc = path + "/" + animation_frame_id + ".png"
        animation_image = pygame.transform.scale(pygame.image.load(img_loc),
                                                 (scale_width, scale_height)).convert_alpha()
        animation_image.convert()

        animation_frames[animation_frame_id] = animation_image.copy()
        for i in range(frame):
            animation_frame_data.append(animation_frame_id)
        n += 1
    return animation_frame_data

def change_action(action_var, frame, new_value):
    if action_var != new_value:
        action_var = new_value
    return action_var, frame

animation_database = {}

animation_database["idle"] = load_animation("assets", "darkpurpleknight_idle", [8, 8, 8, 8], player.main_char_width,
                                            player.main_char_height)
animation_database["run_top"] = load_animation("assets", "darkpurpleknight_run_up", [8, 8, 8, 8, 8, 8],
                                               player.main_char_width,
                                               player.main_char_height)
animation_database["run_bot"] = load_animation("assets", "darkpurpleknight_run_down", [8, 8, 8, 8, 8, 8],
                                               player.main_char_width, player.main_char_height)
animation_database["run"] = load_animation("assets", "darkpurpleknight_run", [8, 8, 8, 8, 8, 8], player.main_char_width,
                                           player.main_char_height)
scroll = [0, 0]

blob_height = 17
blob_width = 25
blob_frame = 0

enemy_blob = Enemy(50, 10, 5, 1000 , 1000, 17, 25, "blob_idle",0,0)

animation_database["blob_idle"] = load_animation("assets", "blob_idle", [30, 30], blob_width * 4, blob_height * 4)

clock = pygame.time.Clock()

def collision(rectA, rectB):
    if rectB.right < rectA.left:
        # rectB est à gauche
        return False
    if rectB.bottom < rectA.top:
        # rectB est au-dessus
        return False
    if rectB.left > rectA.right:
        # rectB est à droite
        return False
    if rectB.top > rectA.bottom:
        # rectB est en-dessous
        return False

    elif rectB.right > rectA.left:
        # rectB est en collision avec la gauche
        return True
    elif rectB.bottom < rectA.top:
        # rectB est en collision avec le haut
        return True
    elif rectB.left > rectA.right:
        # rectB est en collision avec la droite
        return True
    elif rectB.top > rectA.bottom:
        # rectB est en collision avec le bas
        return True

bg_img = pygame.transform.scale(pygame.image.load("assets/DUNGEON_PISKEL_REMAKE_WITH_WALL_PROPS-1.png"),
                                (WIDTH * 2, HEIGHT * 2)).convert()

def animate_sprite(frame_counter, action):
    global animation_database
    frame_counter += 1
    if frame_counter >= len(animation_database[action]):
        frame_counter = 0
    img_id = animation_database[action][frame_counter]
    animated_image = animation_frames[img_id]
    return animated_image, frame_counter

#def calculate_enemy_movement(enemy):
    #calculate what x value needs to be added each frame(x velocity), and what y value needs to be added each frame(y velocity) to the enemy.x and enemy.y to walk
    #towards the player.x and player.y.
    #return x velocity and return y velocity

def draw_window(window):
    global enemy_blob
    window = window
    global main_char_x, main_char_y
    global player_frame, bg_img, blob_frame
    global scroll
    global slide

    #add x velocity and y velocity to x and y pos of enemy each frame
    #enemy_blob.x,enemy_blob.y += calculate_enemy_movement(enemy_blob)

    window.blit(bg_img, (-WIDTH / 2 - scroll[0], -HEIGHT / 2 - scroll[1]))

    blob_img, blob_frame = animate_sprite(blob_frame, enemy_blob.action)
    window.blit(blob_img, (enemy_blob.x - scroll[0], enemy_blob.y - scroll[1]))

    player_img, player_frame = animate_sprite(player_frame, player_action)

    window.blit(pygame.transform.flip(player_img, player.player_flip, False), (player.player_rect.x, player.player_rect.y))

    pygame.display.update()

borders = []
coliding = False

def set_borders(x, y, width, height):
    border = pygame.Rect((x, y), (width, height))
    return border

def check_colision(target, list):
    is_coliding = []
    for i in list:
        is_coliding.append(collision(i, target))
    return is_coliding

def main():
    global scroll
    global player_frame
    global player_action
    global player_flip
    global player_rect, rect1, coliding
    global VEL
    global top_wall
    global borders

    global main_char_x, main_char_y

    global moving_left, moving_right, moving_up, moving_down, idle
    game_running = True

    while game_running:
        clock.tick(FPS)

        for event in pygame.event.get():

            if event.type == pygame.KEYDOWN:
                if event.key == K_ESCAPE:
                    game_running = False
                    from start_menu import Open_launcher
                    Open_launcher()
                    sys.exit()

        top_map_border = set_borders(-WIDTH / 2 - scroll[0], -HEIGHT / 2 + 250 - scroll[1], 5000, 1)
        bot_map_border = set_borders(-WIDTH / 2 - scroll[0], HEIGHT + 355 - scroll[1], 5000, 1)
        left_map_border = set_borders(-WIDTH / 2 + 160 - scroll[0], -HEIGHT / 2 - scroll[1], 1, 5000)
        right_map_border = set_borders(WIDTH + 785 - scroll[0], -HEIGHT / 2 + 150 - scroll[1], 1, 5000)

        borders = [top_map_border, bot_map_border, left_map_border, right_map_border]
        enemies = []

        is_coliding_with_borders = check_colision(player.player_rect, borders)
        is_coliding_with_enemy = check_colision(player.player_rect, enemies)

        top_coliding = is_coliding_with_borders[0]
        bot_coliding = is_coliding_with_borders[1]

        left_coliding = is_coliding_with_borders[2]
        right_coliding = is_coliding_with_borders[3]

        keys = pygame.key.get_pressed()

        # EVERY MOVEMENT THAT USES "A"

        if keys[pygame.K_a]:

            # DIAGONAL TOP LEFT
            if keys[pygame.K_w] and not keys[pygame.K_d]:

                if not left_coliding and not top_coliding:
                    player.player_flip = True
                    player_action, player_frame = change_action(player_action, player_frame, "run")
                    scroll[0] -= diagonal_x_vel(player.VEL)
                    scroll[1] -= diagonal_y_vel(player.VEL)

                elif left_coliding and not top_coliding:
                    player.player_flip = True
                    scroll[1] -= player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run_top")

                elif top_coliding and not left_coliding:
                    player.player_flip = True
                    scroll[0] -= player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run")

                elif left_coliding and top_coliding:
                    player.player_flip = False
                    player_action, player_frame = change_action(player_action, player_frame, "idle")


            # DIAGONAL BOT LEFT
            elif keys[pygame.K_s] and not keys[pygame.K_d]:

                if not left_coliding and not bot_coliding:
                    player.player_flip = True
                    player_action, player_frame = change_action(player_action, player_frame, "run")
                    scroll[0] -= diagonal_x_vel(player.VEL)
                    scroll[1] += diagonal_y_vel(player.VEL)

                elif left_coliding and not bot_coliding:
                    player.player_flip = True
                    scroll[1] += player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run_bot")

                elif not left_coliding and bot_coliding:
                    player.player_flip = True
                    scroll[0] -= player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run")

                elif left_coliding and bot_coliding:
                    player.player_flip = False
                    player_action, player_frame = change_action(player_action, player_frame, "idle")

            # LEFT MOVEMENT
            elif not keys[pygame.K_s] and not keys[pygame.K_d] and not keys[pygame.K_w]:

                if not left_coliding:
                    player.player_flip = True
                    scroll[0] -= player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run")

                else:
                    player.player_flip = True
                    player_action, player_frame = change_action(player_action, player_frame, "run")

            else:
                player_action, player_frame = change_action(player_action, player_frame, "idle")

        # EVERY MOVEMENT THAT USES "D"

        if keys[pygame.K_d]:
            # DIAGONAL TOP RIGHT
            if keys[pygame.K_w] and not keys[pygame.K_a] and not keys[pygame.K_s]:

                if not right_coliding and not top_coliding:
                    player.player_flip = False
                    player_action, player_frame = change_action(player_action, player_frame, "run")
                    scroll[0] += diagonal_x_vel(player.VEL)
                    scroll[1] -= diagonal_y_vel(player.VEL)

                elif right_coliding and not top_coliding:
                    player.player_flip = False
                    scroll[1] -= player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run_top")

                elif top_coliding and not right_coliding:
                    player.player_flip = False
                    scroll[0] += player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run")

                elif right_coliding and top_coliding:
                    player.player_flip = False
                    player_action, player_frame = change_action(player_action, player_frame, "idle")


            # DIAGONAL BOT RIGHT
            elif keys[pygame.K_s] and not keys[pygame.K_a] and not keys[pygame.K_w]:

                if not right_coliding and not bot_coliding:
                    player.player_flip = False
                    player_action, player_frame = change_action(player_action, player_frame, "run")
                    scroll[0] += diagonal_x_vel(player.VEL)
                    scroll[1] += diagonal_y_vel(player.VEL)

                elif right_coliding and not bot_coliding:
                    player.player_flip = False
                    scroll[1] += player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run_bot")

                elif not right_coliding and bot_coliding:
                    player.player_flip = False
                    scroll[0] += player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run")

                elif right_coliding and bot_coliding:
                    player.player_flip = False
                    player_action, player_frame = change_action(player_action, player_frame, "idle")

            # RIGHT MOVEMENT
            elif not keys[pygame.K_s] and not keys[pygame.K_a] and not keys[pygame.K_w]:

                if not right_coliding:
                    player.player_flip = False
                    scroll[0] += player.VEL
                    player_action, player_frame = change_action(player_action, player_frame, "run")

                else:
                    player.player_flip = False
                    player_action, player_frame = change_action(player_action, player_frame, "run")

            else:
                player_action, player_frame = change_action(player_action, player_frame, "idle")

        # EVERY MOVEMENT THAT USES "W"
        if keys[pygame.K_w]:

            # UP MOVEMENT
            if not keys[pygame.K_d] and not keys[pygame.K_a]:
                if not top_coliding:
                    player_action, player_frame = change_action(player_action, player_frame, "run_top")
                    scroll[1] -= player.VEL
                else:
                    player_action, player_frame = change_action(player_action, player_frame, "run_top")

        # EVERY MOVEMENT THAT USES "S"
        if keys[pygame.K_s]:

            # DOWN MOVEMENT
            if not keys[pygame.K_d] and not keys[pygame.K_a]:
                if not bot_coliding:
                    player_action, player_frame = change_action(player_action, player_frame, "run_bot")
                    scroll[1] += player.VEL
                else:
                    player_action, player_frame = change_action(player_action, player_frame, "run_bot")

        if not keys[pygame.K_a] and not keys[pygame.K_s] and not keys[pygame.K_d] and not keys[pygame.K_w]:
            player_action, player_frame = change_action(player_action, player_frame, "idle")

        draw_window(WIN)

Tags: andrunselfifnotactionkeyschange
1条回答
网友
1楼 · 发布于 2024-09-27 21:31:02

计算从敌方位置到玩家位置的矢量:

dx = player_x - enemy_x
dy = player_y - enemy_y

计算向量的长度(Euclidean distance):

dist = math.sqrt(dx*dx + dy*dy)

dist = math.hypot(dx, dy)

规范化向量(Unit vector)。规格化向量的长度为1:

if dist > 0:
    dx /= dist
    dy /= dist

按矢量方向将敌人移动一定距离。确保移动距离不大于敌人与玩家的剩余距离:

move_dist = min(enemy_vel, dist)

enemy_x += move_dist * dx
enemy_y += move_dist * dy 

有关更复杂的解决方案,请参见How to make smooth movement in pygame

另见Follow target or mouse


最简单的例子:

import pygame, math

pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
player_x, player_y, player_vel = 100, 100, 5
enemy_x, enemy_y, enemy_vel = 300, 300, 3

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False          
        
    keys = pygame.key.get_pressed()
    player_x = max(10, min(390, player_x + player_vel * (keys[pygame.K_d] - keys[pygame.K_a])))
    player_y = max(10, min(390, player_y + player_vel * (keys[pygame.K_s] - keys[pygame.K_w])))

    dx = player_x - enemy_x
    dy = player_y - enemy_y
    dist = math.hypot(dx, dy)
    if dist > 0:
        enemy_x += min(enemy_vel, dist) * dx / dist
        enemy_y += min(enemy_vel, dist) * dy / dist

    window.fill(0)
    pygame.draw.circle(window, (0, 128, 255), (player_x, player_y), 10)
    pygame.draw.circle(window, (255, 32, 32), (enemy_x, enemy_y), 10)
    pygame.display.flip()

pygame.quit()
exit()

对于特定代码,calculate_enemy_movement函数可能如下所示:

def calculate_enemy_movement(enemy_blob):
    dx = player.player_rect.x - enemy_blob.x
    dy = player.player_rect.y - enemy_blob.y
    dist = math.hypot(dx, dy)
    if dist > 0:
        move_x = min(enemy_blob.speed, dist) * dx / dist
        move_y = min(enemy_blob.speed, dist) * dy / dist
        return move_x, move_y
    return 0, 0
move_x, move_y = calculate_enemy_movement(enemy_blob)
enemy_blob.x += move_x
enemy_blob.y += move_y

相关问题 更多 >

    热门问题