【问题标题】:How to move a sprite according to an angle in Pygame如何在 Pygame 中根据角度移动精灵
【发布时间】:2018-03-23 16:14:28
【问题描述】:

我在移动精灵时遇到问题。我可以毫无问题地在 x 和 y 轴上移动它们。我想不通的是如何根据某个角度移动它们。我的意思是,我正在尝试创建一个函数,其中包括我要移动的对象、它的速度和它的方向(应该以度为单位测量)。比如:

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

它更像是一个“生成函数”而不仅仅是运动......例如,我想创建一个“子弹”类的对象并希望它遵循某个方向(不同于 x 和 y 轴, 当然) 实际上,我对如何做这样的事情并不清楚,我想要一些建议以实现这一目标。 感谢您阅读本文!

编辑: @Joran Beasley 我试着按照你说的去做……但我想我做错了……

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

对象移动但...不在指定方向...

【问题讨论】:

  • 将其更改为calculate_new_xy(self.rect.center,...),然后将 x 作为 [0] 和 y 作为 [1] 访问并将速度设置为 2 ...它应该可以工作

标签: python python-3.x pygame sprite


【解决方案1】:

你只需要一点基本的触发

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】:

    我建议使用向量。要获得速度,请将起始方向矢量 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()
    

    关于您添加的示例中的代码,最简单的解决方案是在__init__ 方法中计算speed_xspeed_y(“速度”会更合适),然后只需更新self.rect.x 和@ update 方法中的 987654330@ 属性。

    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.Rects 只能有整数作为 x 和 y 属性,所以移动不会 100% 正确。为了解决这个问题,您需要将精灵的坐标/位置存储在单独的变量中,将速度添加到它们,然后更新矩形:

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

    【讨论】:

    • 我真的很感谢你的回答......但我不敢说......这有点令人困惑......另外......我不知道这是否是我想要的...也许我应该说类似的话:我想定义一个函数,它创建一个向某个方向移动的“移动对象”......也许......如果你向我解释......更简单。 ..字...? (对不起,这对我来说有点难以理解......)
    • 我猜...我没有
    • 好吧,我不能也不应该在这里解释向量数学,所以我建议去学校。 :P 你可以在 khanacademy.org 上学习它,但最好先学习三角函数。
    • Pygame 的向量在底层使用三角函数进行旋转,因此您不必直接处理它。移动非常简单:假设您有单独的pos_xpos_yvelocity_xvelocity_y 属性,那么您只需通过添加pos_x += velocity_xpos_y += velocity_y 来移动精灵。使用向量,您可以通过添加两个向量在一行中完成此操作:pos += velocity
    • 其实……我高中的时候学过三角学……但我不知道具体怎么用在这里……顺便说一下,我理解了那个简短的解释……
    【解决方案3】:

    如果你使用 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 只能存储整数坐标。这是因为pygame.Rect 应该代表屏幕上的一个区域:

    Rect 对象的坐标都是整数。 [...]

    如果您希望子弹以不能被 45° 整除的角度向某个方向直线移动,则必须以浮点精度存储对象位置。您必须将对象的位置分别存储在单独的变量和属性中,并同步pygame.Rect 对象。 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()
    

    【讨论】:

      猜你喜欢
      • 2022-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多