Programming: Sprites Pt. 1

In a utilitarian sense, a game could work with just red and blue boxes moving around a grey background. But visuals are quite important to games; we'd otherwise lose out on the immersion and aesthetics that complete the experience.

The functions use the object's x, y, width, and height values to determine screen position and collision between other objects. The "pygame.draw.rect" function is used to create the object's visuals, which has been a placeholder during testing. 

The Pygame module has the function "pygame.image.load" used for importing image files into the game. I can use old-school 2D sprites to give my game a bit more personality. I'll use the sprite sheet below from "Pokemon Mystery Dungeon" to now represent our "Red_Guy" player character (he'll now be a green guy) 

Sprite Import

A sprite sheet works by having all frames laid out in a consistent alignment. The function below will take the load the entire image, and save each frame into a list. The function works by taking the pixel coordinates (x, y, width, height) of the sprite sheet, and drawing it onto a new surface. By iterating through a "For" loop, each frame will get loaded into a list with an assignable index value. There's also the input for setting the color transparency as well for each loaded frame.

def Load_Sprites(spritesheet, rectangle, colorkey = None):
    rect = pygame.Rect(rectangle) #Create rectangle 
    frame = pygame.Surface(rect.size) #Create surface from rectangle size
    frame.blit(spritesheet, (0,0), rect) #Draws frame onto surface
    if colorkey is not None:
        frame.set_colorkey(colorkey, pygame.RLEACCEL) #Set transparency
    return frame

I revised the sprite sheet to make each frame evenly distributed and centered. Each frame is within a 30 x 30 box. It's very important that each sprite is centered precisely within the frame; this makes for smooth animation in motion. I included the index numbers as well to help me know which sprites should be used for which condition (standing, walking, hit, etc). I'll hide the index numbers and gridlines when I export the image. Lastly, I note which RGB color I'm using for the background (0, 128, 192). 


Implementation 

In order to load each frame to the object, I'll create additional variables for importing and storing the frames. I'll use a "For" loop to iterate between each frame dimension (frame 0 at (0,0), frame 1 at (25, 0), etc.). Each frame loaded is then appended to the list:

class Characters(object):
def __init__(self, x, y, width, height, color):
                ... 
#Load Sprites
self.spritesheet = pygame.image.load("Bulbasaur.png").convert() 
self.sprites = []
for y in range(2):
for x in range(15):
self.sprites.append(Load_Sprites(self.spritesheet,                                    (x*30,y*30,30,30), colorkey=(0,128,192)))

This function needs to be inserted into the "Characters" class. Once I define the character, all of the sprites should be loaded and easily accessible. If I inserted into the "While" loop, the function would attempt to reload all frames to the list every cycle, which would be a massive and unnecessary performance loss. 

In the "While" loop I'll then draw the index "0" at the "Red_Guy" coordinate:

win.blit(Red_Guy.sprites[0], (Red_Guy.x, Red_Guy.y))

And voila, the "Red_Guy" object is being represented by both the red rectangle and the Bulbasaur sprite (I probably should update that name now). There's also an offset between the rectangle and the sprite, since they're both drawn from the same coordinate. 



Notice there's an offset between the rectangle and the sprite, since they're both drawn from the same coordinate. By playing around with the offset, I can get the sprite to overlap better with the hitbox.

win.blit(Red_Guy.sprites[0], (Red_Guy.x-11, Red_Guy.y-9))


This is quite important since the player will only ever see the sprite, but not the hitbox. Yet, the hitbox is what's used for performing the collision detection. Thus, they should represent each other accurately to give a fair player experience. 

Follow-up

The last issue is that I've explicitly called out index "0" in the blit function, so no other sprite is going to be drawn. Additional values and conditions need to be set in the "Character" class to accurately draw the correct sprite for the player.


Comments