【问题标题】:How can i make a block follow another block in pygame [duplicate]我怎样才能让一个块跟随pygame中的另一个块[重复]
【发布时间】:2020-01-18 10:21:30
【问题描述】:

我有两个块,一个由用户控制。当我移动我的街区时,我希望另一个街区跟随我。我试着做这样的事情

def follow():
    distance = math.hypot(abs(m.x - p.x), abs(m.y - p.y))
    angle_radians = math.atan2(abs(m.y - p.y), abs(m.x - p.x))
    if distance !=  0:
        p.y += math.sin(angle_radians)
        p.x += math.cos(angle_radians)

但是,块最终向与我完全相反的方向移动。任何帮助将不胜感激。

【问题讨论】:

  • 如果它向相反方向移动,那么可能将+=更改为-=
  • 试过了,但也没有用
  • 然后使用 print() 查看变量中的值 - 这有助于查看问题。
  • 如果我从 atan2() 中删除 abs(),代码对我有用

标签: python math pygame


【解决方案1】:

要使算法有效,您必须使用浮点数进行操作。如果mppygame.Rect 对象,那么算法将不起作用,pygame.Rect 使用整数运算,小数部分会丢失。
注意 math.sin(angle_radians)math.cos(angle_radians)

这意味着您必须将对象的位置存储在单独的变量中。假设您有浮点坐标 (mx, my) 和 (py, py)

你必须找到从(mxmy)到(pxpy)的Unit vector
单位向量可以通过将向量从 (mx, m.y) 到 (px, py) 除以它的长度来找到。
向量的长度可以通过Euclidean distance 来计算。
最后将向量乘以不大于点之间距离的比例(step)并将其添加到位置。例如:

stepDist = 1

# vector from (`mx`,  `my`) to (`px`, `py`)
dx, dy = p.y - mx, py - px

# [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance)
len = math.sqrt(dx*dx + dy*dy)

if len > 0:
    # [Unit vector](https://en.wikipedia.org/wiki/Unit_vector)
    ndx, ndy = dx/len, dy/len

    # minimum of step size and distance to target
    step = min(len, stepDist)

    # step forward
    px += ndx * step
    py += ndy * step

如果需要pygame.Rect对象,则可以设置矩形的位置。例如:

m.topleft = round(mx), round(my)
p.topleft = round(px), round(py)

但您不必分别将位置存储在 (mx, my) (px, py)。如果你分别做mx, my = m.topleftpx, py = p.topleft,那么算法就会失败,因为分数部分会丢失。

【讨论】:

    【解决方案2】:

    如果我从 atan2() 中删除 abs(),代码对我有用


    import pygame
    import random
    import math
    
    # --- constants --- (UPPER_CASE_NAMES)
    
    SCREEN_WIDTH = 800
    SCREEN_HEIGHT = 600
    
    FPS = 25 # for more than 220 it has no time to update screen
    
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    
    # --- classes --- (CamelCaseNames)
    
    class Player(pygame.sprite.Sprite):
    
        def __init__(self, x=SCREEN_WIDTH//2, y=SCREEN_HEIGHT//2):
            super().__init__()
            self.image = pygame.image.load("image.png").convert()
            #self.rect = self.image.get_rect(x=x, y=y)
            self.rect = self.image.get_rect(centerx=x, centery=y)
    
        def update(self):
            #self.rect.centerx = random.randint(0, SCREEN_WIDTH)
            #self.rect.centery = random.randint(0, SCREEN_HEIGHT)
            move_x = random.randint(-15, 15)
            move_y = random.randint(-15, 15)
            self.rect.move_ip(move_x,move_y)
    
        def draw(self, surface):
            surface.blit(self.image, self.rect)
    
    class Follower(Player):
    
        def update(self, player):
            distance = math.hypot(abs(player.rect.x - self.rect.x), abs(player.rect.y - self.rect.y))
            angle_radians = math.atan2((player.rect.y - self.rect.y), (player.rect.x - self.rect.x))
    
            if distance !=  0:
                self.rect.y += 5*math.sin(angle_radians)
                self.rect.x += 5*math.cos(angle_radians)        
    
    # --- functions --- (lower_case_names)
    
    # --- main ---
    
    pygame.init()
    
    screen = pygame.display.set_mode( (SCREEN_WIDTH, SCREEN_HEIGHT) )
    
    player = Player()
    follower = Follower(0, 0)
    
    # --- mainloop ---
    
    clock = pygame.time.Clock()
    
    running = True
    while running:
    
        # --- events ---
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
    
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_ESCAPE:
                    running = False
    
    
        # --- changes/moves/updates ---
    
        if not pygame.key.get_pressed()[pygame.K_SPACE]:
            player.update()
            follower.update(player)
        # --- draws ---
    
        screen.fill(BLACK)
    
        player.draw(screen)
        follower.draw(screen)
    
        pygame.display.flip()
    
        # --- FPS ---
    
        ms = clock.tick(FPS)
        #pygame.display.set_caption('{}ms'.format(ms)) # 40ms for 25FPS, 16ms for 60FPS
        fps = clock.get_fps()
        pygame.display.set_caption('FPS: {}'.format(fps))
    
    # --- end ---
    
    pygame.quit()
    

    【讨论】:

    • 这有点奇怪,因为我正在阅读 atan2 的工作原理,它只能接受绝对值
    • 这不适用于步距为 1 且不准确,因为 pygame.Rect 使用整数运算。
    • @Rabbid76 什么距离?我不使用任何距离。我设置5*math.sin5*math.cos 移动得更快,我对小值没有问题
    • @furas 你的步距是 5。(self.rect.y += 5*math.sin(angle_radians))。在问题中,距离为 1。
    • @Rabbid76 是的,对于1*math.sin,它不起作用,但对于这个值,它移动得太慢以至于没用。所以使用5 我自动解决了这两个问题。
    猜你喜欢
    • 2021-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 2014-08-11
    相关资源
    最近更新 更多