代码之家  ›  专栏  ›  技术社区  ›  DarkBlood202

如何在Pygame中根据角度移动精灵

  •  4
  • DarkBlood202  · 技术社区  · 7 年前

    我在移动精灵方面遇到了麻烦。我可以毫无问题地在x轴和y轴上移动它们。我不明白的是,我怎么能把它们按一定的角度移动。我的意思是,我试图创建一个函数,其中包括我试图移动的物体,它的速度和方向(应该用度来衡量)。类似于:

    MovingObject(obj,speed,direction) #this is the function I'm trying to define
    

    这更像是一个“产卵功能”,而不仅仅是运动。。。举个例子,我想创建一个“Bullet”类的对象,并希望它遵循特定的方向(当然,不同于x轴和y轴) 事实上,我不清楚如何做这样的事情,我想一些建议,以实现这一点。

    @乔兰·比斯利我试着照你说的做。。。但我想我做错了。。。

    import pygame, math, time
    screen=pygame.display.set_mode((320,240))
    clock=pygame.time.Clock()
    pygame.init()
    def calculate_new_xy(old_xy,speed,angle_in_radians):
        new_x = old_xy.x + (speed*math.cos(angle_in_radians))
        new_y = old_xy.y + (speed*math.sin(angle_in_radians))
        return new_x, new_y
    class Bullet(pygame.sprite.Sprite):
        def __init__(self,x,y,direction,speed):        
                pygame.sprite.Sprite.__init__(self)
                self.image=pygame.Surface((16, 16))
                self.image.fill((255,0,0))
                self.rect=self.image.get_rect()
                self.rect.center=(x,y)
                self.direction=math.radians(direction)
                self.speed=speed
        def update(self):
                self.rect.center=calculate_new_xy(self.rect,self.speed,self.direction)
    spr=pygame.sprite.Group()
    bullet=Bullet(160,120,45,1); spr.add(bullet)
    play=True
    while play:
        clock.tick(60)
        for ev in pygame.event.get():
            if ev.type == pygame.QUIT:
                play=False
        screen.fill((0,0,0))
        spr.update()
        spr.draw(screen)
        pygame.display.flip()
    pygame.quit()
    

    3 回复  |  直到 7 年前
        1
  •  4
  •   Joran Beasley    7 年前

    你只需要一点基本的三角

    def calculat_new_xy(old_xy,speed,angle_in_radians):
        new_x = old_xy.X + (speed*math.cos(angle_in_radians))
        new_y = old_xy.Y + (speed*math.sin(angle_in_radians))
        return new_x, new_y
    

    这是您从上面编辑的代码

    import pygame, math, time
    screen=pygame.display.set_mode((320,240))
    clock=pygame.time.Clock()
    pygame.init()
    def calculate_new_xy(old_xy,speed,angle_in_radians):
        new_x = old_xy[0] + (speed*math.cos(angle_in_radians))
        new_y = old_xy[1] + (speed*math.sin(angle_in_radians))
        return new_x, new_y
    class Bullet(pygame.sprite.Sprite):
        def __init__(self,x,y,direction,speed):
                pygame.sprite.Sprite.__init__(self)
                self.image=pygame.Surface((16, 16))
                self.image.fill((255,0,0))
                self.rect=self.image.get_rect()
                self.rect.center=(x,y)
                self.direction=math.radians(direction)
                self.speed=speed
        def update(self):
                self.rect.center=calculate_new_xy(self.rect.center,self.speed,self.direction)
    spr=pygame.sprite.Group()
    bullet=Bullet(160,120,45,2); spr.add(bullet)
    play=True
    while play:
        clock.tick(60)
        for ev in pygame.event.get():
            if ev.type == pygame.QUIT:
                play=False
        screen.fill((0,0,0))
        spr.update()
        spr.draw(screen)
        pygame.display.flip()
    pygame.quit()
    
        2
  •  1
  •   skrx    7 年前

    我建议使用向量。要获得速度,请旋转开始方向矢量 Vector2(1, 0) update 方法,并更新 rect 移动精灵的位置。(按“a”或“d”旋转,按鼠标1或空格进行拍摄。)

    import pygame as pg
    from pygame.math import Vector2
    
    
    pg.init()
    screen = pg.display.set_mode((640, 480))
    screen_rect = screen.get_rect()
    
    FONT = pg.font.Font(None, 24)
    BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
    pg.draw.polygon(BULLET_IMAGE, pg.Color('aquamarine1'),
                    [(0, 0), (20, 5), (0, 11)])
    
    
    class Bullet(pg.sprite.Sprite):
    
        def __init__(self, pos, angle):
            super().__init__()
            self.image = pg.transform.rotate(BULLET_IMAGE, -angle)
            self.rect = self.image.get_rect(center=pos)
            # To apply an offset to the start position,
            # create another vector and rotate it as well.
            offset = Vector2(40, 0).rotate(angle)
            # Then add the offset vector to the position vector.
            self.pos = Vector2(pos) + offset  # Center of the sprite.
            # Rotate the direction vector (1, 0) by the angle.
            # Multiply by desired speed.
            self.velocity = Vector2(1, 0).rotate(angle) * 9
    
        def update(self):
            self.pos += self.velocity  # Add velocity to pos to move the sprite.
            self.rect.center = self.pos  # Update rect coords.
    
            if not screen_rect.contains(self.rect):
                self.kill()
    
    
    def main():
        clock = pg.time.Clock()
        cannon_img = pg.Surface((40, 20), pg.SRCALPHA)
        cannon_img.fill(pg.Color('aquamarine3'))
        cannon = cannon_img.get_rect(center=(320, 240))
        angle = 0
        bullet_group = pg.sprite.Group()  # Add bullets to this group.
    
        while True:
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    return
                elif event.type == pg.MOUSEBUTTONDOWN:
                    # Left button fires a bullet from center with
                    # current angle. Add the bullet to the bullet_group.
                    if event.button == 1:
                        bullet_group.add(Bullet(cannon.center, angle))
    
            keys = pg.key.get_pressed()
            if keys[pg.K_a]:
                angle -= 3
            elif keys[pg.K_d]:
                angle += 3
            if keys[pg.K_SPACE]:
                bullet_group.add(Bullet(cannon.center, angle))
    
            # Rotate the cannon image.
            rotated_cannon_img = pg.transform.rotate(cannon_img, -angle)
            cannon = rotated_cannon_img.get_rect(center=cannon.center)
    
            bullet_group.update()
    
            # Draw
            screen.fill((30, 40, 50))
            screen.blit(rotated_cannon_img, cannon)
            bullet_group.draw(screen)
            txt = FONT.render('angle {:.1f}'.format(angle), True, (150, 150, 170))
            screen.blit(txt, (10, 10))
            pg.display.update()
    
            clock.tick(30)
    
    if __name__ == '__main__':
        main()
        pg.quit()
    

    speed_x speed_y (“速度”更合适)在 __init__ self.rect.x y 中的属性 方法

    import math
    import pygame
    
    
    pygame.init()
    
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    
    BULLET_IMAGE = pygame.Surface((20, 11), pygame.SRCALPHA)
    pygame.draw.polygon(BULLET_IMAGE, pygame.Color('aquamarine1'),
                    [(0, 0), (20, 5), (0, 11)])
    
    
    class Bullet(pygame.sprite.Sprite):
    
        def __init__(self, x, y, angle, speed):
            pygame.sprite.Sprite.__init__(self)
            # Rotate the bullet image (negative angle because y-axis is flipped).
            self.image = pygame.transform.rotate(BULLET_IMAGE, -angle)
            self.rect = self.image.get_rect(center=(x, y))
            angle = math.radians(angle)
            self.speed_x = speed * math.cos(angle)
            self.speed_y = speed * math.sin(angle)
    
        def update(self):
            self.rect.x += self.speed_x
            self.rect.y += self.speed_y
    
    spr = pygame.sprite.Group()
    bullet = Bullet(10, 10, 60, 3)
    bullet2 = Bullet(10, 10, 30, 3)
    spr.add(bullet, bullet2)
    
    play = True
    while play:
        clock.tick(60)
        for ev in pygame.event.get():
            if ev.type == pygame.QUIT:
                play = False
        screen.fill((30,30,40))
        spr.update()
        spr.draw(screen)
        pygame.display.flip()
    
    pygame.quit()
    

    有个问题,因为 pygame.Rect

        # In `__init__`.
        self.pos_x = x
        self.pos_y = y
    
    def update(self):
        self.pos_x += self.speed_x
        self.pos_y += self.speed_y
        self.rect.center = (self.pos_x, self.pos_y)
    
        3
  •  1
  •   Rabbid76    4 年前

    如果您正在使用Pygame,我建议使用 pygame.math.Vector2 用于此任务。根据其设置方向向量 Polar coordinates ( speed , angle_in_degrees )与 from_polar() sin cos :

    def calculate_new_xy(old_xy, speed, angle_in_degrees):
        move_vec = pygame.math.Vector2()
        move_vec.from_polar((speed, angle_in_degrees))
        return old_xy + move_vec
    

    pygame.Rect 只能存储整数坐标。这是因为 应该表示屏幕上的一个区域:

    如果希望项目符号以不可被45°整除的角度沿某个方向笔直移动,则必须以浮点精度存储对象位置。您必须将对象的位置分别存储在单独的变量属性中,并同步 pygame。矩形 round 坐标并将其分配给位置(例如。 .topleft

    class Bullet(pygame.sprite.Sprite):
        def __init__(self, x, y, direction, speed):
                # [...]
    
                self.rect = self.image.get_rect(center = (x, y))
                self.pos = (x, y)
    
        def update(self, screen):
                self.pos = calculate_new_xy(self.pos, self.speed, -self.direction)
                self.rect.center = round(self.pos[0]), round(self.pos[1])
    

    另请参见 Move towards target


    最小示例

    import pygame
    
    def calculate_new_xy(old_xy, speed, angle_in_degrees):
        move_vec = pygame.math.Vector2()
        move_vec.from_polar((speed, angle_in_degrees))
        return old_xy + move_vec
    
    class Bullet(pygame.sprite.Sprite):
        def __init__(self, x, y, direction, speed):
                pygame.sprite.Sprite.__init__(self)
                self.image = pygame.Surface((16, 8), pygame.SRCALPHA)
                self.image.fill((255, 0, 0))
                self.image = pygame.transform.rotate(self.image, direction)
                self.rect = self.image.get_rect(center = (x, y))
                self.pos = (x, y)
                self.direction = direction
                self.speed = speed
        def update(self, screen):
                self.pos = calculate_new_xy(self.pos, self.speed, -self.direction)
                self.rect.center = round(self.pos[0]), round(self.pos[1])
                if not screen.get_rect().colliderect(self.rect):
                    self.kill()
    
    pygame.init()
    screen = pygame.display.set_mode((320,240))
    clock = pygame.time.Clock()
    spr = pygame.sprite.Group()
    play = True
    frame_count = 0
    while play:
        clock.tick(60)
        for ev in pygame.event.get():
            if ev.type == pygame.QUIT:
                play = False
        
        spr.update(screen)
        if (frame_count % 10) == 0:
            spr.add(Bullet(*screen.get_rect().center, frame_count, 2))
        frame_count += 1
    
        screen.fill((0,0,0))
        spr.draw(screen)
        pygame.draw.circle(screen, (64, 128, 255), screen.get_rect().center, 10)
        pygame.display.flip()
    
    pygame.quit()
    exit()