I've written a class that allows me to easily create sprites on the screen, and then do things like: setting an image or an animation; move the sprite as a platformer or as a top down game; and test for many things like collision and orientation. So far I've found this to be very useful, and it has made it really easy to make games. Now I've finished getting everything to work, and writing a text file of the documentation I thought that I'd post it here for review (I'll post the documentation if requested).The idea is that it would be imported into a file using import classes.sprite.Sprite. With classes being the folder that it's in, sprite.py the name of the file, and Sprite the name of the class.The folder structure would be:Projects/Pygame/Game/main.pyProjects/Pygame/Game/classes/sprite.pyProjects/Pygame/Game/data/The data folder is used to store any images or text files that the game will need.import os, pygamefrom pygame.locals import *from math import *pygame.init()WIDTH  = NoneHEIGHT = NoneSCREEN = NoneFPS    = Nonepath = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..', 'data'))def load_rotated_image(sprite):    '''Loads an image for the rotate functions in Sprite class'''    sprite.surface = pygame.transform.rotate(sprite.image, sprite.angle)    sprite.rect    = sprite.surface.get_rect(center = (sprite.rect.x + sprite.rect.width / 2, sprite.rect.y + sprite.rect.height / 2))def init(surface, fps):    '''Initialises the sprite class'''    global WIDTH, HEIGHT, SCREEN, FPS #I know globals are bad but I don't know how to avoid them here    SCREEN = surface    WIDTH, HEIGHT = SCREEN.get_size()    FPS    = fpsclass Sprite(object):    '''Creates a sprite object'''    def __init__ (self, x = 0, y = 0, width = 50, height = 50, colour = (255, 255, 255), boundry = None, Clamp = False, flip = False):        '''Initialises the sprite object'''                    self.x       = x        self.y       = y        self.width   = width        self.height  = height        if boundry == None: self.boundry = (0, 0, WIDTH, HEIGHT)        else:               self.boundry = boundry        self.clamp   = Clamp        self.colour  = colour        self.do_flip = flip        self.angle   = 0        self.animation   = False        self.appear  = True        self.flip    = False        self.moving  = False        self.surface = pygame.Surface((self.width, self.height))        self.image   = self.surface        self.rect    = self.surface.get_rect()        self.rect.x  = x        self.rect.y  = y                self.surface.fill(colour)        self.xvel = 0        self.yvel = 0    def set_image(self, image, scale = 1, size = (0, 0), colourkey = (255, 0, 255)):        '''Initilizes a sprite to display an image'''        self.surface = pygame.image.load(os.path.join(path, image)).convert()        #self.surface = pygame.image.load(image).convert()        if not colourkey == None: self.surface.set_colorkey(colourkey)        if size != (0, 0):            self.width   = round(size[0])            self.height  = round(size[1])        else:            self.width   = round(scale * self.surface.get_width())            self.height  = round(scale * self.surface.get_height())        self.image   = pygame.transform.scale(self.surface, (self.width, self.height))        self.surface = self.image        self.rect    = self.surface.get_rect()        self.rect.x  = self.x        self.rect.y  = self.y    def set_animation(self, frames = [], scale = 1, fps = 1, animate_on_move = False, idle_frame = None, colourkey = (255, 0, 255)):        '''Initilizes a sprite to display an animation'''        self.animation  = True        self.frames = []        self.animate_on_move = animate_on_move        self.idle_frame = idle_frame        self.change = max(round(FPS / abs(fps)), 1)        self.tick   = 0        for image in frames:            self.frame  = pygame.image.load(os.path.join(path, image)).convert()            if not colourkey == None: self.frame.set_colorkey(colourkey)            self.width  = round(scale * self.frame.get_width())            self.height = round(scale * self.frame.get_height())            self.frames.append(pygame.transform.scale(self.frame, (self.width, self.height)))        self.rect = self.frames[0].get_rect()        self.rect.x = self.x        self.rect.y = self.y        self.frame = 0    def show(self):        '''Show this sprite'''        self.appear = True    def hide(self):        '''Hide this sprite'''        self.appear = False    def move(self, speed = 5, colliders = [], platformer = False, jump_height = 1):        '''Allows the arrow keys to control the sprite'''        keys = pygame.key.get_pressed()        if keys[K_a] or keys[K_LEFT]:            self.rect.x -= int(abs(speed))            self.flip    = False            self.moving  = True            if self.rect_collide(colliders):                self.rect.x += int(abs(speed))        if keys[K_d] or keys[K_RIGHT]:            self.rect.x += int(abs(speed))            self.flip = True            self.moving = True            if self.rect_collide(colliders):                self.rect.x -= int(abs(speed))        if (keys[K_a] or keys[K_LEFT]) and (keys[K_d] or keys[K_RIGHT]) : self.moving = False        if platformer == True:            if not (keys[K_a] or keys[K_LEFT] or keys[K_d] or keys[K_RIGHT]): self.moving = False            self.rect.y += 10            if (keys[K_w] or keys[K_UP] or keys[K_SPACE]) and self.rect_collide(colliders) and self.yvel < 1:                self.yvel = int(abs(speed)) * (2 + ((jump_height - 1) / 2))                self.rect.y -= 15                if self.rect_collide(colliders): self.yvel = 0                self.rect.y += 15            self.rect.y -= 10            self.rect.y -= self.yvel            if self.rect_collide(colliders):                if self.yvel > 0: self.rect.y += self.yvel + 1                else:             self.rect.y += self.yvel                self.yvel = 0        else:            if not (keys[K_a] or keys[K_LEFT] or keys[K_d] or keys[K_RIGHT] or keys[K_w] or keys[K_UP] or keys[K_s] or keys[K_DOWN]): self.moving = False            if (keys[K_w] or keys[K_UP]) and (keys[K_s] or keys[K_DOWN]) : self.moving = False            if keys[K_w] or keys[K_UP]:                self.rect.y -= int(abs(speed))                self.moving = True                if self.rect_collide(colliders):                    self.rect.y += int(abs(speed))            if keys[K_s] or keys[K_DOWN]:                self.rect.y += int(abs(speed))                self.moving = True                if self.rect_collide(colliders):                    self.rect.y -= int(abs(speed))    def render(self, colour = None, frame = None):        '''Renders the sprite'''        if not colour == None:            self.colour = colour            self.surface.fill(self.colour)        if self.clamp:            self.rect.x = min(max(self.rect.x, self.boundry[0]), self.boundry[0] + self.boundry[2] - self.rect.width )            self.rect.y = min(max(self.rect.y, self.boundry[1]), self.boundry[1] + self.boundry[3] - self.rect.height)        if self.appear:            if self.animation:                self.tick += 1                if self.tick == self.change:                    if self.frame + 1 == len(self.frames):                                self.frame = 0                    else:                        self.frame += 1                    self.tick = 0                if not frame == None: self.frame = frame % len(self.frames)                if self.animate_on_move:                    if not self.moving: self.frame = self.idle_frame                self.rect.width  = self.frames[self.frame].get_width()                self.rect.height = self.frames[self.frame].get_height()                if self.do_flip:                    if self.flip: SCREEN.blit(pygame.transform.flip(self.frames[self.frame], True, False), (self.rect.x, self.rect.y))                    else:         SCREEN.blit(self.frames[self.frame], (self.rect.x, self.rect.y))                else: SCREEN.blit(self.frames[self.frame], (self.rect.x, self.rect.y))            else:                if self.do_flip:                    if self.flip: SCREEN.blit(pygame.transform.flip(self.surface, True, False), (self.rect.x, self.rect.y))                    else:         SCREEN.blit(self.surface, (self.rect.x, self.rect.y))                else: SCREEN.blit(self.surface, (self.rect.x, self.rect.y))    def wrap(self):        '''Wraps the sprite around the edge of the screen'''        self.wrap_around = False        if self.rect.x < 0:            self.rect.x += WIDTH            self.wrap_around = True        if self.rect.x + self.width > WIDTH:            self.rect.x -= WIDTH            self.wrap_around = True        if self.rect.y < 0:            self.rect.y += HEIGHT            self.wrap_around = True        if self.rect.y + self.height > HEIGHT:            self.rect.y -= HEIGHT            self.wrap_around = True        if self.wrap_around:            if self.animation:                if self.do_flip:                    if self.flip: SCREEN.blit(pygame.transform.flip(self.frames[self.frame], True, False), (self.rect.x, self.rect.y))                    else:         SCREEN.blit(self.frames[self.frame], (self.rect.x, self.rect.y))                else: SCREEN.blit(self.frames[self.frame], (self.rect.x, self.rect.y))            else:                if self.do_flip:                    if self.flip: SCREEN.blit(pygame.transform.flip(self.surface, True, False), (self.rect.x, self.rect.y))                    else:         SCREEN.blit(self.surface, (self.rect.x, self.rect.y))                else: SCREEN.blit(self.surface, (self.rect.x, self.rect.y))        self.rect.x %= WIDTH        self.rect.y %= HEIGHT    def rect_collide(self, sprites):        '''Returns True if two sprites are colliding'''        self.rect = pygame.Rect(self.rect.x, self.rect.y, self.rect.width, self.rect.height)        if self.appear:            for sprite in sprites:                if self.rect.colliderect(sprite) and sprite.appear:                    return True        return False    def clamp(self, boundry = None, Clamp = None):        '''Clamps a sprite to a boundry'''        if not Clamp == None: self.clamp = Clamp        else:            if self.clamp == True:  self.clamp = False            if self.clamp == False: self.clamp = True        if not boundry == None:            self.boundry = boundry    def Collect(self, player):        '''Makes an object collectable'''        if player.rect_collide([self]):            self.hide()            return True    def mouse_hover(self):        '''Returns True if the mouse is over the sprite'''        mouse_pos = pygame.mouse.get_pos()        if self.rect.collidepoint(mouse_pos): return True        else: return False    def mouse_click(self, button = 1):        '''Returns True if the sprite is clicked'''        mouse_pos     = pygame.mouse.get_pos()        mouse_pressed = pygame.mouse.get_pressed()        if self.rect.collidepoint(mouse_pos) and mouse_pressed[button - 1]: return True        else: return False    def move_in_direction(self, magnitude, direction = None):        '''Moves the sprite a certain distance in the direction it's facing'''        if direction != None:            self.rect.x  += magnitude * cos(radians(direction))            self.rect.y  -= magnitude * sin(radians(direction))        else:            self.rect.x  += magnitude * cos(radians(self.angle))            self.rect.y  -= magnitude * sin(radians(self.angle))    def point_in_direction(self, direction):        '''Points a sprite in a specific direction'''        self.angle   = direction        load_rotated_image(self)    def point_towards(self, pos = (0,0), sprite = None, anchor = 'center'):        '''Points sprite towards another sprite'''        if sprite == None: self.angle = 360 - atan2(pos[1] - self.rect.centery, pos[0] - self.rect.centerx) * 180 / pi         else:              exec('self.angle = 360 - atan2(sprite.rect.' + anchor + '[1] - self.rect.centery, sprite.rect.' + anchor + '[0] - self.rect.centerx) * 180 / pi')        load_rotated_image(self)    def distance_to(self, pos = (0, 0), sprite = None, anchor = 'center'):        '''Returns the distance to a sprite or point'''        exec('self.pos = (self.rect.' + anchor + '[0], self.rect.' + anchor + '[1])')                  if sprite == None: return sqrt(abs(self.pos[0] - pos[0])**2 + abs(self.pos[1] - pos[1])**2)        else:              return sqrt(abs(self.pos[0] - sprite.rect.x)**2 + abs(self.pos[1] - sprite.rect.y)**2)    def turn(self, angle):        '''Turns the sprite a certain amout of degrees'''        self.angle  += angle        load_rotated_image(self)
Multifunction sprite class for pygame
python;object oriented;python 3.x;pygame