Pygame三角函数:跟随斜边?

4

我在我的Enemy类中有一个方法叫做huntPlayer,它会接收一个玩家对象p。这是它的代码:

def huntPlayer(self, p):   
  if self.dist2p < 200:
    self.hunting = True
    if p.x > self.rect.x:
      self.rect.x += self.speed #this is a constant at value 1
    elif p.x < self.rect.x:
      self.rect.x -= self.speed
    else:
      self.rect.x += 0
    if p.y > self.rect.y:
      self.rect.y += self.speed
    elif p.y < self.rect.y:
      self.rect.y -= self.speed 
    else:
      self.rect.y += 0
  else:
    self.rect.x += 0
    self.rect.y += 0

敌人会随机出现在一个2D平面上,他们会随机漫游这个平面。我计算了到玩家最短距离的斜边长度 = Enemy.dist2p -- 当 dist2p 值 < 200 时,敌人会向玩家移动,分别使用 p.xp.y
我的解决方案很简单,所以我的问题是敌人在x轴或y轴上等距移动,导致对每个轴都有对角线运动,然后沿着轴滑动直到到达玩家(玩家位于屏幕中心附近的固定位置)。
你能帮我修复huntPlayer方法/算法,使敌人沿着斜边路径追踪玩家,而不是最快的x/y轴路径吗?
编辑:如果您需要我遗漏的任何进一步信息,请告诉我。

提示:与其单独考虑每个坐标,不如将敌人的速度存储为向量可能更有帮助。 - user554546
嗨,杰克,我是一位热情的游戏开发新手,你能详细解释一下如何使用速度吗?我是不是完全错了,关于敌人移动到玩家的实现方式? - zzoop
速度是一个向量量。这意味着它具有大小和方向。https://zh.wikipedia.org/wiki/%E9%80%9F%E5%BA%A6 - user554546
3个回答

7
在斜边上移动很可能需要您的对象在y轴或x轴每帧移动不到一个像素,由于rects只能容纳整数,因此您需要一个新属性position,其中包含精灵的浮点精度位置。您可以使用pygame.math.Vector2创建具有实用方法的向量,例如normalize()和向其他向量添加、减去、乘以等。

假设您已经创建了一个属性self.position = pygame.math.Vector2(0, 0)(或任何您想要它开始的位置),您可以像这样操作:

def hunt_player(self, player):
    player_position = pygame.math.Vector2(player.rect.topleft)
    direction = player_position - self.position
    velocity = direction.normalize() * self.speed

    self.position += velocity
    self.rect.topleft = self.position

通过将玩家的位置减去敌人的位置,您将获得一个指向玩家的向量。如果我们将方向向量添加到我们的位置,我们将立即传送到玩家。相反,我们规范化向量(使其长度为1像素),并乘以我们的速度属性。新创建的向量将是一个指向玩家的向量,其长度为我们的速度。

完整示例

import pygame
pygame.init()


SIZE = WIDTH, HEIGHT = 720, 480
FPS = 60
BACKGROUND_COLOR = pygame.Color('white')

screen = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()


class Hunter(pygame.sprite.Sprite):

    def __init__(self, position):
        super(Hunter, self).__init__()

        self.image = pygame.Surface((32, 32))
        self.image.fill(pygame.Color('red'))
        self.rect = self.image.get_rect(topleft=position)
        self.position = pygame.math.Vector2(position)
        self.speed = 2

    def hunt_player(self, player):
        player_position = player.rect.topleft
        direction = player_position - self.position
        velocity = direction.normalize() * self.speed

        self.position += velocity
        self.rect.topleft = self.position

    def update(self, player):
        self.hunt_player(player)


class Player(pygame.sprite.Sprite):

    def __init__(self, position):
        super(Player, self).__init__()

        self.image = pygame.Surface((32, 32))
        self.image.fill(pygame.Color('blue'))
        self.rect = self.image.get_rect(topleft=position)

        self.position = pygame.math.Vector2(position)
        self.velocity = pygame.math.Vector2(0, 0)
        self.speed = 3

    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.velocity.x = -self.speed
        elif keys[pygame.K_RIGHT]:
            self.velocity.x = self.speed
        else:
            self.velocity.x = 0

        if keys[pygame.K_UP]:
            self.velocity.y = -self.speed
        elif keys[pygame.K_DOWN]:
            self.velocity.y = self.speed
        else:
            self.velocity.y = 0

        self.position += self.velocity
        self.rect.topleft = self.position

player = Player(position=(350, 220))
monster = Hunter(position=(680, 400))
running = True
while running:

    clock.tick(FPS)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    player.update()
    monster.update(player)

    screen.fill(BACKGROUND_COLOR)
    screen.blit(player.image, player.rect)
    screen.blit(monster.image, monster.rect)

    pygame.display.update()

结果

这是一张图片,请点击链接查看


这是一张图片,请点击链接查看。

好答案。我以为我也发布了几乎相同的答案,但那一定是在另一个网站上。个人而言,我总是写self.velocity.x而不是self.velocity[0],但这没有问题。祝贺你获得2000分。 :) - skrx
@skrx 谢谢!是的,那是个好观点!我必须编辑答案,因为我注意到了几个拼写错误 :P - Ted Klein Bergman

1

既然我们想沿着斜边移动,我们可以使用勾股定理。以下是一个简短的代码片段,应该能够给你一个大致的想法。

我将使用p.x,p.y表示玩家的位置,e.x,e.y表示敌人的位置。

# Find the horizontal & vertical distances between player & enemy
dx = p.x - e.x
dy = p.y - e.y

#Get the hypotenuse
d = sqrt(dx*dx + dy*dy)

#Calculate the change to the enemy position
cx = speed * dx / d
cy = speed * dy / d
# Note that sqrt(cx*cx + cy*cy) == speed

# Update enemy position
e.x += cx
e.y += cy

你需要添加一些额外的代码来确保d不等于零,否则你将会得到一个除以零错误,但只有当敌人接近玩家时才会发生这种情况,所以我假设当这种情况发生时你想要做一些特殊的事情。 :)
我应该提到,如果位置是浮点数而不是整数像素坐标,则此技术的效果最佳。

谢谢 - 非常有帮助。 - zzoop
math 模块有一个 hypot 函数,用于计算 sqrt(x*x + y*y),这可能比在纯 Python 中执行更快。 - martineau

0

仅仅基于斜边计算距离是不够的。你必须将敌人的坐标传递到函数中,并计算斜率,或者通过值将斜率也传递到函数中。然后,你应该移动到当前位置周围8个像素中的一个,其中你移动到的像素最能代表通往敌人方向的路径。如果角度的正切小于2或大于1/2,则基本上你会对角线移动,否则你会沿着垂直或水平方向移动。如果你无法想象,请绘制一个3x3像素集以查看实际情况。

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接