Wednesday, April 3, 2013

Pygame Sprite Animation

Okay, animation. Not just moving rectangles either, that stuff is covered by a number of other tutorials freely available on the interwebz. I posted an earlier version of this class a while back, but I noticed some weirdness with the way I was changing images. This is a revised version. Let's just jump right into the code and I'll explain as best I can after.

class Player(pygame.sprite.Sprite):
  # initialize the player object
  # requires x and y coordinates
  def __init__(self,x,y):
    # initialize the sprite (parent class)
    pygame.sprite.Sprite.__init__(self)
    # Set initial variables
    #change_x and y are used to move the object around the screen
    self.change_x=0
    self.change_y=0
    #facing keeps track of the direction
    #this is important so we know which set of images to use
    self.facing = 0 
    #here are the images we're going to loop over depending on the direction
    #start by setting the image to the image to start with
    self.image = pygame.image.load('hero.png')
    self.down = [pygame.image.load('hero.png').convert_alpha(),
                 pygame.image.load('hero1.png').convert_alpha(),
                 pygame.image.load('hero2.png').convert_alpha(),
                 pygame.image.load('hero3.png').convert_alpha(),
                 pygame.image.load('hero2.png').convert_alpha(),
                 pygame.image.load('hero1.png').convert_alpha()]
    self.left = [pygame.image.load('herol.png').convert_alpha(),
                 pygame.image.load('herol1.png').convert_alpha(),
                 pygame.image.load('herol2.png').convert_alpha(),
                 pygame.image.load('herol3.png').convert_alpha(),
                 pygame.image.load('herol2.png').convert_alpha(),
                 pygame.image.load('herol1.png').convert_alpha()]
    self.right = [pygame.image.load('heror.png').convert_alpha(),
                  pygame.image.load('heror1.png').convert_alpha(),
                  pygame.image.load('heror2.png').convert_alpha(),
                  pygame.image.load('heror3.png').convert_alpha(),
                  pygame.image.load('heror2.png').convert_alpha(),
                  pygame.image.load('heror1.png').convert_alpha()]
    self.up = [pygame.image.load('herou.png').convert_alpha(),
               pygame.image.load('herou1.png').convert_alpha(),
               pygame.image.load('herou2.png').convert_alpha(),
               pygame.image.load('herou3.png').convert_alpha(),
               pygame.image.load('herou2.png').convert_alpha(),
               pygame.image.load('herou1.png').convert_alpha()]


    # give the object a rectangle using image size
    self.rect = self.image.get_rect()
    # set the topleft location to the x and y coordinates
    self.rect.topleft = [x,y]
 
  # Used to move the object
  def changespeed(self,x,y):
    self.change_x+=x
    self.change_y+=y
     
  # This is where we update the images and location of the player object
  def update(self):

    # Update position according to our speed (vector)
    new_x=old_x+self.change_x
    new_y=old_y+self.change_y
     
    # Put the player in the new spot
    self.rect.topleft = (new_x,new_y)

    # This next bit is where we change images
    # First we see if the object is stationary. If both change coordinates are 0,
    # we're not moving and we can set the image to the 0 element in the array
    if self.change_x == 0 and self.change_y == 0:

      if self.facing == 3:
        self.image = self.left[0]
      if self.facing == 2:
        self.image = self.down[0]
      if self.facing == 4:
        self.image = self.right[0]
      if self.facing == 1:
        self.image = self.up[0]

    # The rest is just repeated code changing only the direction the player
    # object is moving. In this case < 0 means we're moving left   
    if self.change_x < 0:

      # set facing to 3 or left
      self.facing = 3
      # if imgvar is 0, we just started moving
      if self.imgvar == 0:
        # set image to the first element
        self.image = self.left[0]
        self.imgvar +=1
      # if imgvar is anything else, we've been moving
      else:
        # make sure imgvar isn't greater than the length of the image array
        if self.imgvar > len(self.left)-1:
          self.imgvar = 0
        # update imgvar and change the image
        self.imgvar += 1
        self.image = self.left[self.imgvar]  
  

Okay, that's a lot to take in (that's what she said), if you're not accustomed to pygame. First you'll notice that my Player class implements a pygame parent class. This can be seen in the first line:

class Player(pygame.sprite.Sprite):

I utilize the sprite class for a number of reasons. Collision detection is one thing that sprites do very well. There is a complete list of sprite functions here:  http://www.pygame.org/docs/ref/sprite.html

In any case, the important bits are the class attributes and functions. The class attributes to take note of are the change_x and y, as well as the facing and image array attributes.

change_x and change_y are used to move the object around the screen. They do this by utilizing the changespeed function. This function changes these values, which are later used in the update function to move the player. The first thing that update does is it moves the player rectangle to the new coordinates by adding the change_x and  change_y values to the current x/y values. Again, I won't go into too much detail because there are plenty of better tutorials out there on moving objects.

Next we have the facing attribute. This is just an integer that keeps track of the directions your character is facing. With the setup shown above we only have animation images for 4 directions, but you could easily add 4 more for diagonal movements. You'll notice this also gets changed in the update function based on the change_x and change_y values. If change_x is a positive number we'd say facing is right, or whatever number we're using to designate moving right.

The image arrays (up,down,left, and right) are where we store the images we have to loop through to get our animation. The current image is stored under the (you guessed it) image variable. All we do to animate our character is move along to the next image in our array, setting it as the current image.

Within the update function, the comments kind of explain what's happening. We move our rectangle, then check the change_x and y variables to determine which way we're facing and which image from which array we need to be on. Just keep in mind that I only put in the left direction. You have to repeat that chunk of code to include the other directions. 

Now you're saying, "This is all well and good, but how do we actually do anything with this class?" and "Why didn't you go into the other features of pygame, like the load.image function you used?" and "God this guy is retarded"

The short answer is 1)that you create an object within your game loop, use pygame events to utilize the changespeed function, and call the object's update function each loop. 2)I think there are much better explanations in the pygame documentation. 3)You're right, but there's nothing I can do about that.

I'll get more into how to use the sprite next week. We can plug it into the surface demo I did last week and we'll see how it works out.

-Newt

No comments:

Post a Comment