Pygame : trying to understand the Sprite class

跟風遠走 提交于 2021-02-16 14:39:09

问题


I found on the Pygame documentation a class named Sprite. I read the explanation but I didn't understood what this class is used for.

I understood that we were using it to inherit other classes with the constructor, but that's all.

If someone has a clear explanation of the usefulness of the class, and how to use it well, I am interested.


回答1:


Right, so move your mouse cursor around. That's a Sprite.

It's a square bitmap, with some transparency. But only the visible parts mean anything - you don't care if the transparent part has moved over some screen element, for example.

Sprites in PyGame are similar.

In the olden days ;) most arcade games used sprite-type animation. Think of Pac-Man (circa 1980); the creature that is a big yellow chomping mouth. It's implemented as a Sprite. So let's make one:

Now this thing has to be moved around the screen, the pixels that are transparent (the light & dark grey) need to show the background. Importantly, not so much for PacMan, but certainly for more jagged images, anything transparent should not be part of collision detection. This is another important aspects of Sprites.

Say the enemy ghosts in PacMan fired bullets (wow that's a scary thought!), you'd be quite upset if the bullet passed through a transparent corner of PacMan's sprite, yet this was still considered a "hit". To implement this properly a Mask is used. This is basically another logical-bitmap that says which pixels are to be considered part of the Sprite for collision, and which are not.

There is also the code needed to paint that bitmap to the screen. Now-days the CPU usage for this is only a minor consideration. A modest PC with PyGame can easily paint 1000 sprites to the screen at 60FPS. But in former times, this required significant processing time, and was hopefully done with some sort of hardware assistance. Even in the 1990's some video cards were advertised with "Hardware Mouse Cursor" support [citation needed]. PyGame's Sprite Class has very efficient code for painting the bitmaps to the screen.

So to summarise, why use sprites:

  • Easy to implement
  • Transparency and Masks
  • Fast collision detection
  • Your 1980's era hardware can't even dream of hardware 3D

Why use PyGame's Sprites:

  • They're already written
  • They're fast, efficient and bug free
  • They can use masks
  • They already have efficient collision detection code
  • The supporting Sprite Group class is super-useful

So ... How do I make a sprite?

Here's a reference class. I'll comment the code.

class Pacman( pygame.sprite.Sprite ):               # parent class is Sprite

    def __init__( self, x, y, pacman_bitmap ):      # New PacMan at (x,y)
        pygame.sprite.Sprite.__init__( self )       # Init the parent obj too
        self.image = pacman_bitmap                  # it MUST be self.image for sprites
        self.rect  = self.image.get_rect()          # it MUST be self.rect for sprites
        self.rect.center = ( x, y )                 # move the sprite
        self.move_dir_x  = 0                        
        self.move_dir_y  = 0                        # This pacman never stops

    # Semi-optional Part 
    def update( self ):                             # called to somehow effect the sprite.  
        # Move pacman                               # MUST be named "update"
        x, y = self.rect.center
        x += self.move_dir_x
        y += self.move_dir_y
        self.rect.center = ( x, y )                 # move the sprite

    # Optional Part
    def setDirection( self, dir ):                  # Called when user sends
        if ( dir == 'up' ):                         # input to change direction
            self.move_dir_x = 0
            self.move_dir_y = -1
        elif ( dir == 'right' ):
            self.move_dir_x = 1
            self.move_dir_y = 0
        #elif ... TODO - rest of direction changes

And that's it. The base Sprite object expects the subclass to "overwrite" the image and rect member variables. The Sprite Group uses the update() function to somehow modify a sprite. This one just moves in whatever direction has been set (which would change depending on user input).

So we might use this like:

pacman_image   = pygame.image.load( 'pacman.png' ).convert()
pacman_sprite  = Pacman( 100, 100, pacman_image )
player1_sprite = pygame.sprite.GroupSingle()           # Just 1 player for now
player_sprites.add( pacman_sprite )

# Imagine a similar sprite class for ghosts
ghost_image   = pygame.image.load( 'blue_ghost.png' ).convert()
...
ghost_sprites = pygame.sprite.Group()
for i in range( 4 ):
    ghost_sprites.add( Ghost( 10, 50, ghost_image ) )

And then in the Main Loop:

game_over = False
while not game_over:

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            game_over = True

    # Movement keys too (not event driven)
    keys = pygame.key.get_pressed()
    if ( keys[pygame.K_UP] ):
        player_sprite.setDirection( 'up' )
    elif ( keys[pygame.K_RIGHT] ):
        player_sprite.setDirection( 'right' )
    #elif  TODO - rest of keys

    # re-position all the players and ghosts
    player_sprites.update()
    ghost_sprites.update()

    # Paint the background
    paintPacmanEnvironment( window )

    # Paint all the sprites
    player_sprites.draw( window )
    ghost_sprites.draw( window )

    # Did the player hit a ghost? 
    # spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
    if ( pygame.sprite.spritecollide( player_sprite, ghost_sprites, False ) ):
        # TODO: handle hitting a ghost

So yes, it's a bit involved to setup the sprites initially. But look at that main loop! With just a few function calls you get painting and collisions almost for nothing, with a little more setup (about 1 line) you also get bitmap-masks. That's why you should use PyGame Sprites.

EDIT: A quick note on the Sprite.update():

The update() function is a good place to handle not just movement, but also animation. Obviously we need multiple frames of animation to make Pacman's mouth move. To implement this, the Pacman.update() would look at the current clock-time, and use it to advance to the next frame of animation. So instead of creating a Pacman with only a single bitmap, we could create it with a list of images, and then Pacman.image would be set to the correct image inside the .udpate(). It works really well.



来源:https://stackoverflow.com/questions/61088785/pygame-trying-to-understand-the-sprite-class

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!