Sprite不能正确地向播放器旋转如何解决此问题?

时间:2020-06-23 19:10:30

标签: python pygame sprite

我正在尝试使精灵向播放器旋转,但是旋转位置不合适。这是video of the behaviour

我不确定如何解决此问题,

Sprite类

  #-------------------------------- enemy shoots left and right

    shotsright = pygame.image.load("canss.png")
    class enemyshoot:
        def __init__(self,x,y,height,width,color):
            self.x = x
            self.y =y
            self.height = height
            self.width = width
            self.color = color
            self.rect = pygame.Rect(x,y,height,width)
            self.health = 10
            self.hitbox = (self.x + -20, self.y + 30, 31, 57)
           #-------------------------------------------------------
            # Make a Reference Copy of the bitmap for later rotation
            self.shootsright = pygame.image.load("canss.png")
            self.shootsright = pygame.transform.scale(self.shootsright,(self.shootsright.get_width()-150,self.shootsright.get_height()-150))            
            self.image    = self.shootsright
            self.rect     = self.image.get_rect()
            self.position = pygame.math.Vector2( (200, 180) )
            self.isLookingAtPlayer = False
        def draw(self):
            self.rect.topleft = (self.x,self.y)
            window.blit(self.image, self.rect)
            self.hits = (self.x + 20, self.y, 28,60)
            pygame.draw.rect(window, (255,0,0), (self.hitbox[0], self.hitbox[1] - 60, 100, 10)) # NEW
            pygame.draw.rect(window, (0,255,0), (self.hitbox[0], self.hitbox[1] - 60, 100 - (5 * (10 - self.health)), 10))
            self.hitbox = (self.x + 200, self.y + 200, 51, 65)
        def lookAt( self, coordinate ):
            # Rotate image to point in the new direction
            delta_vector  = coordinate - self.position
            radius, angle = delta_vector.as_polar()
            self.image    = pygame.transform.rotate(self.shootsright, -angle)
            # Re-set the bounding rectangle and position since 
            # the dimensions and centroid will have (probably) changed.
            current_pos      = self.rect.center
            self.rect        = self.image.get_rect()
            self.rect.center = current_pos
            


            
    black = (0,0,0)
    enemyshooting = []
    platformGroup = pygame.sprite.Group
    platformList = []
    level = ["                                                                                                                     p               p           p                         p                        p        ",
             "                                       ",
             "                             ",
             "                                      ",
             "                                  ",
             "                           ",
             "                                      ",
             "                                      ",
             "                                    ",
             "                                   ",
             "                    ",]
    for iy, row in enumerate(level):
        for ix, col in enumerate(row):
            if col == "p":
                new_platforms = enemyshoot(ix*10, iy*50, 10,10,(255,255,255))
                enemyshooting.append(new_platforms)

这是旋转部分


class enemyshoot:
     def __init__(self,x,y,height,width,color):
       # [................]
           #-------------------------------------------------------
            # Make a Reference Copy of the bitmap for later rotation
            self.shootsright = pygame.image.load("canss.png")
            self.shootsright = pygame.transform.scale(self.shootsright,(self.shootsright.get_width()-150,self.shootsright.get_height()-150))            
            self.image    = self.shootsright
            self.rect     = self.image.get_rect()
            self.position = pygame.math.Vector2( (200, 180) )      


        def lookAt( self, coordinate ):
            # Rotate image to point in the new direction
            delta_vector  = coordinate - pygame.math.Vector2(self.rect.center)
            radius, angle = delta_vector.as_polar()
            self.image    = pygame.transform.rotate(self.shootsright, -angle)
            # Re-set the bounding rectangle and position since 
            # the dimensions and centroid will have (probably) changed.
            current_pos      = self.rect.center
            self.rect        = self.image.get_rect()
            self.rect.center = current_pos



在这里我调用LookAt函数面对播放器:

      # so instead of this 
            for enemyshoot in enemyshooting:
                if not enemyshoot.isLookingAtPlayer:
                    enemyshoot.lookAt((playerman.x, playerman.y)) 

旋转位置不合适,我不知道如何解决它。我正在尝试使大炮的嘴朝玩家旋转,因为那是子弹的发源地。

2 个答案:

答案 0 :(得分:1)

self.position已在构造函数中设置,但未更新。使用self.rect.center计算方向向量:

delta_vector = coordinate - pygame.math.Vector2(self.rect.center)

使用math.atan2计算旋转角度:
(请参见How do I make my player rotate towards mouse position?

angle = (180 / math.pi) * math.atan2(-delta_vector.x, -delta_vector.y)

draw中设置矩形位置需要旋转lookAt中的子画面。请注意旋转的矩形的大小被放大。参见How do I rotate an image around its center using Pygame?

我建议在lookAt中设置查找位置,并在draw中计算旋转后的图像:

class enemyshoot:
    def __init__(self,x,y,height,width,color):
       # [...]

       self.look_at_pos = (x, y)

    def draw(self):
   
        self.rect = self.shootsright.get_rect(topleft = (self.x, self.y))

        dx = self.look_at_pos[0] - self.rect.centerx
        dy = self.look_at_pos[1] - self.rect.centery 
        angle = (180 / math.pi) * math.atan2(-dx, -dy)

        self.image = pygame.transform.rotate(self.shootsright, angle)
        self.rect  = self.image.get_rect(center = self.rect.center)

        window.blit(self.image, self.rect)
        self.hits = (self.x + 20, self.y, 28,60)
        pygame.draw.rect(window, (255,0,0), (self.hitbox[0], self.hitbox[1] - 60, 100, 10)) # NEW
        pygame.draw.rect(window, (0,255,0), (self.hitbox[0], self.hitbox[1] - 60, 100 - (5 * (10 - self.health)), 10))
        self.hitbox = (self.x + 200, self.y + 200, 51, 65)

    def lookAt(self, coordinate):
        self.look_at_pos = coordinate

答案 1 :(得分:1)

我同意@ Rabbid76在上述答案中所说的一切。

我怀疑您的问题的一部分可能是位图的“人类可读”部分未围绕位图的centroid居中。因此,当旋转时,它通过弧形“扫掠”,而不是“围绕自身”旋转。 (保持中心坐标是保持对象的质心平稳旋转的重要步骤。

考虑两个位图(右图在左上角有一个大的3/4透明部分):

centred image off-centred image

两者均绕其质心旋转,但由于第二张图像上的可见部分未居中,因此旋转异常。

example video

因此,请确保您的实际位图位于其内部居中。

参考代码:

import pygame
import random

# Window size
WINDOW_WIDTH    = 800
WINDOW_HEIGHT   = 400
WINDOW_SURFACE  = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE

DARK_BLUE = (   3,   5,  54 )

class RotationSprite( pygame.sprite.Sprite ):
    def __init__( self, image, x, y, ):
        pygame.sprite.Sprite.__init__(self)
        self.original = image
        self.image    = image
        self.rect     = self.image.get_rect()
        self.rect.center = ( x, y )
        # for maintaining trajectory
        self.position    = pygame.math.Vector2( ( x, y ) )
        self.velocity    = pygame.math.Vector2( ( 0, 0 ) )

    def lookAt( self, co_ordinate ):
        # Rotate image to point in the new direction
        delta_vector  = co_ordinate - self.position
        radius, angle = delta_vector.as_polar()
        self.image    = pygame.transform.rotozoom( self.original, -angle, 1 )
        # Re-set the bounding rectagle
        current_pos      = self.rect.center
        self.rect        = self.image.get_rect()
        self.rect.center = current_pos



### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption( "Rotation Example" )

### Missiles!
rotation_sprites = pygame.sprite.Group()
rotation_image1 = pygame.image.load( 'rot_offcentre_1.png' )
rotation_image2 = pygame.image.load( 'rot_offcentre_2.png' )
rotation_sprites.add( RotationSprite( rotation_image1, WINDOW_WIDTH//3, WINDOW_HEIGHT//2 ) )
rotation_sprites.add( RotationSprite( rotation_image2, 2*( WINDOW_WIDTH//3 ), WINDOW_HEIGHT//2 ) )


### Main Loop
clock = pygame.time.Clock()
done = False
while not done:

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True
        elif ( event.type == pygame.MOUSEBUTTONUP ):
            # On mouse-click
            pass

    # Record mouse movements for positioning the paddle
    mouse_pos = pygame.mouse.get_pos()
    for m in rotation_sprites:
        m.lookAt( mouse_pos )
    rotation_sprites.update()

    # Update the window, but not more than 60 FPS
    window.fill( DARK_BLUE )
    rotation_sprites.draw( window )
    pygame.display.flip()

    # Clamp FPS
    clock.tick_busy_loop(60)

pygame.quit()