Make a sprite move to the mouse click position ste

2020-02-07 07:19发布

I'm writing a little pirate game in Pygame. If you played sea battles in Empires Total War, you have an idea of what I would like to achieve:

The ship's sprite is at position (x1|y1). The player now clicks at position (x2|y2) on the screen. The sprite is now supposed to take (x2|y2) as its new position - by going there step by step, not by beaming there instantly.

I figured out that it has something to do with the diagonal of the rectangle (x1|y1),(x1|y2),(x2|y2),(x2|y1) but I just can't figure it out, especially not with keeping the speed the same no matter what angle that diagonal has and considering that the x and y values of either (ship or click) might be bigger or smaller than the respective other.

This little snippet is my last try to write a working function:

def update(self, new_x, new_y, speed, screen, clicked):

    if clicked:
        self.xshift = (self.x - new_x)
        self.yshift = ((self.y - new_y) / (self.x - new_x))

    if self.x > (new_x + 10):
        self.x -= 1
        self.y -= self.yshift
    elif self.x > new_x and self.x < (new_x + 10):
        self.x -= 1
        self.y -= self.yshift
    elif self.x < (new_x - 10):
        self.x += 1
        self.y += self.yshift
    elif self.x < new_x and self.x < (new_x - 10):
        self.x += 1
        self.y += self.yshift
    else:
        self.x += 0
        self.y += 0

    screen.set_at((self.x, self.y), (255, 0, 255))

The "ship" is just a pink pixel here. The reaction it shows upon my clicks onto the screen is to move roughly towards my click but to stop at a seemingly random distance of the point I clicked.

The variables are:

new_x, new_y = position of mouseclick
speed = constant speed depending on ship types
clicked = set true by the MOUSEBUTTONDOWN event, to ensure that the xshift and yshift of self are only defined when the player clicked and not each frame again.

How can I make the ship move smoothly from its current position to the point the player clicked?

标签: python pygame
1条回答
仙女界的扛把子
2楼-- · 2020-02-07 07:55

Say the current position is pos, and the point the player clicked is target_pos, then take the vector between pos and target_pos.

Now you know how to get from pos to target_pos, but to move in constant speed (and not the entire distance at once), you have to normalize the vector, and apply a speed constant by scalar multiplication.

That's it.


Complete example: (the relevant code is in the Ship.update method)

import pygame

class Ship(pygame.sprite.Sprite):

    def __init__(self, speed, color):
        super().__init__()
        self.image = pygame.Surface((10, 10))
        self.image.set_colorkey((12,34,56))
        self.image.fill((12,34,56))
        pygame.draw.circle(self.image, color, (5, 5), 3)
        self.rect = self.image.get_rect()

        self.pos = pygame.Vector2(0, 0)
        self.set_target((0, 0))
        self.speed = speed

    def set_target(self, pos):
        self.target = pygame.Vector2(pos)

    def update(self):
        move = self.target - self.pos
        move_length = move.length()

        if move_length < self.speed:
            self.pos = self.target
        elif move_length != 0:
            move.normalize_ip()
            move = move * self.speed
            self.pos += move

        self.rect.topleft = list(int(v) for v in self.pos)

def main():
    pygame.init()
    quit = False
    screen = pygame.display.set_mode((300, 300))
    clock = pygame.time.Clock()

    group = pygame.sprite.Group(
        Ship(1.5, pygame.Color('white')),
        Ship(3.0, pygame.Color('orange')),
        Ship(4.5, pygame.Color('dodgerblue')))

    while not quit:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return 
            if event.type == pygame.MOUSEBUTTONDOWN:
                for ship in group.sprites():
                    ship.set_target(pygame.mouse.get_pos())

        group.update()
        screen.fill((20, 20, 20))
        group.draw(screen)
        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()
查看更多
登录 后发表回答