问题
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