问题
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