How can i make a block follow another block in pygame [duplicate]

余生长醉 提交于 2021-02-13 17:32:59

问题


I have two blocks, one is controlled by the user. When i move my block, i want the other block to follow me. I tried doing something like this

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)

However, the block ends up moving in the complete opposite direction to me . Any help would be appreciated.


回答1:


To make the algorithm work, you have to operate with floating point numbers. If m and p are pygame.Rect objects, then the algorithm won't work, pygame.Rect operates with integral numbers and the fraction part gets lost.
Note math.sin(angle_radians) and math.cos(angle_radians) is <= 1.

That means you have to store the positions of the objects in separate variables. Let's assume you have the floating point coordinates (mx, my) and (py, py)

You have to find the Unit vector from (mx, my) to (px, py).
The unit vector can be found by dividing the vector from (mx, m.y) to (px, py) by its length.
The length of a vector can be computed by the Euclidean distance.
Finally multiply the vector by a scale (step) that is not greater than the distance between the points and add it to the position. e.g:

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

If a pygame.Rect object is of need, then the position of the rectangle can be set. e.g:

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

But not you have to store the positions in (mx, my) respectively (px, py). If you would do mx, my = m.topleft respectively px, py = p.topleft, then the algorithm will break down, because the fraction component gets lost.




回答2:


Code works for me if I remove abs() from atan2()


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


来源:https://stackoverflow.com/questions/59799575/how-can-i-make-a-block-follow-another-block-in-pygame

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!