Pygame 三角学:沿着斜边?
Pygame trigonometry: Following the hypotenuse?
我的 Enemy
class 中有一个名为 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
敌人随机放置在一个二维自上而下的平原周围,他们随机在这个平原上漫游。我计算了到玩家最短距离的斜边 = Enemy.dist2p
-- 当 dist2p
值 < 200 时,敌人将分别向玩家移动 p.x
和 p.y
.
我上面的解决方案很粗糙,所以我的问题是敌人在 x 或 y 轴上平均移动 1 个位置,导致每个轴的对角线移动,然后沿着轴滑动直到它到达玩家。 (玩家在中心屏幕附近的固定位置。)
你能帮我修复 huntPlayer
method/algorithm 使敌人沿着斜边路径到达玩家,而不是到 x/y 轴的最快路径吗?
编辑:如果您需要我可能遗漏的任何进一步信息,请告诉我。
在斜边上移动很可能需要您的对象在 y 或 x-axis 中每帧移动少于一个像素,并且由于 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()
结果
因为我们想沿着斜边移动,所以我们可以使用毕达哥拉斯定理。这是一个简短的片段,应该能让您大致了解。
我将 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
不为零,否则您将得到除以零的错误,但这只会在敌人到达玩家时发生,所以我假设你无论如何都想做一些特别的事情。 :)
我应该提一下,如果位置是浮点数,而不是整数像素坐标,这种技术效果最好。
仅根据斜边计算距离是不够的。您必须将敌人的坐标传递到函数中并计算斜率,或者将斜率也按值传递到函数中。然后你应该移动到你当前位置周围 8 个像素之一,你移动到的那个最能代表敌人的方向路径。基本上,如果角度的 tan 小于 2 或大于 1/2,则沿对角线移动,否则沿垂直或水平方向移动。你需要绘制一组 3x3 的像素来查看实际情况,如果你无法想象的话。
我的 Enemy
class 中有一个名为 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
敌人随机放置在一个二维自上而下的平原周围,他们随机在这个平原上漫游。我计算了到玩家最短距离的斜边 = Enemy.dist2p
-- 当 dist2p
值 < 200 时,敌人将分别向玩家移动 p.x
和 p.y
.
我上面的解决方案很粗糙,所以我的问题是敌人在 x 或 y 轴上平均移动 1 个位置,导致每个轴的对角线移动,然后沿着轴滑动直到它到达玩家。 (玩家在中心屏幕附近的固定位置。)
你能帮我修复 huntPlayer
method/algorithm 使敌人沿着斜边路径到达玩家,而不是到 x/y 轴的最快路径吗?
编辑:如果您需要我可能遗漏的任何进一步信息,请告诉我。
在斜边上移动很可能需要您的对象在 y 或 x-axis 中每帧移动少于一个像素,并且由于 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()
结果
因为我们想沿着斜边移动,所以我们可以使用毕达哥拉斯定理。这是一个简短的片段,应该能让您大致了解。
我将 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
不为零,否则您将得到除以零的错误,但这只会在敌人到达玩家时发生,所以我假设你无论如何都想做一些特别的事情。 :)
我应该提一下,如果位置是浮点数,而不是整数像素坐标,这种技术效果最好。
仅根据斜边计算距离是不够的。您必须将敌人的坐标传递到函数中并计算斜率,或者将斜率也按值传递到函数中。然后你应该移动到你当前位置周围 8 个像素之一,你移动到的那个最能代表敌人的方向路径。基本上,如果角度的 tan 小于 2 或大于 1/2,则沿对角线移动,否则沿垂直或水平方向移动。你需要绘制一组 3x3 的像素来查看实际情况,如果你无法想象的话。