Experienced pygame users please check my first-run of tile engine

Started by
4 comments, last by Oluseyi 15 years, 11 months ago
ALCON: I have been playing with game programming for some time, but recently decided to try the pygame 1.8 library for python. I have hacked together a basic 2D tile engine that implements mouse and key based scrolling. I have a long TODO list on this project, but I thought I should show what i've got so far and let anyone who's better at this than I am make suggestions and/or comments:

"""
pygame7.py: First implementation of a pygame 2D Tile Engine
Author: Josh Madden
License: Public
Version: 14.05.08.0846
"""


# imports
import sys
import pygame
import exceptions
from pygame.locals import *
from string import Template

# constants
SCREEN_SIZE = (800, 600)
WHITE = (255, 255, 255)
TILE_W = 64
TILE_H = 64
MAP_TILE_W = 20
MAP_TILE_H = 20
SCREEN_TILE_W = 12
SCREEN_TILE_H = 9
SCREEN_CLIP = pygame.Rect(0, 0, SCREEN_TILE_W*TILE_W, SCREEN_TILE_H*TILE_H)
DISPLAY_TILE_POS = False
DEF_IMAGE = "grass64.png"

# exception classes

# interface functions

# classes

class Tile(pygame.sprite.Sprite):
    def __init__(self, image, left, top):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(image).convert()
        self.abs_pos = [left*TILE_W, top*TILE_H]
        self.rect = pygame.Rect(self.abs_pos[0], self.abs_pos[1], TILE_W,                                TILE_H)

    def reset(self):
        self.rect.top = 0;
        self.rect.left = 0;

    def showPos(self,font_object,surface):
        self.txt_pos = self.rect.center
        self.print_pos = self.abs_pos[0]/TILE_W, self.abs_pos[1]/TILE_H
        self.msg_txt = Template('($x,$y)')
        self.msg_txt = self.msg_txt.substitute(x = self.print_pos[0],                                               y = self.print_pos[1])
        self.pos_msg = font_object.render(self.msg_txt, False, (255,0,0),                                          (0,0,0))
        surface.blit(self.pos_msg, self.txt_pos)

        
class SpriteGroup(pygame.sprite.Group):

    def __init__(self):
        pygame.sprite.Group.__init__(self)


class ViewSpriteGroup(pygame.sprite.Group):
    
    def __init__(self, camera, sprite_group):
        pygame.sprite.Group.__init__(self)
        self.dX = 0
        self.dY = 0
        for sprite in sprite_group:
            if sprite.rect.top >= camera.top:
                if sprite.rect.left >= camera.left:
                    if sprite.rect.right <= camera.right:
                        if sprite.rect.bottom <= camera.bottom:
                            self.add(sprite)

    def clearViewable(self, camera, sprite_group):
        for sprite in self:
            sprite.rect.topleft = sprite.abs_pos
            if sprite.rect.top <= camera.top:
                self.remove(sprite)
            if sprite.rect.bottom >= camera.bottom:
                self.remove(sprite)
            if sprite.rect.left <= camera.left:
                self.remove(sprite)
            if sprite.rect.right >= camera.right:
                self.remove(sprite)

    def updateViewable(self, camera, sprite_group):         
        for sprite in sprite_group:
            if sprite.rect.top >= camera.top:
                if sprite.rect.left >= camera.left:
                    if sprite.rect.right <= camera.right:
                        if sprite.rect.bottom <= camera.bottom:
                            self.add(sprite)
        for sprite in self:
            sprite.rect.top -= camera.top
            sprite.rect.left -= camera.left            
                        
                            
class Camera(pygame.Rect):

    def __init__(self):
        pygame.Rect.__init__(self, 0, 0, SCREEN_TILE_W*TILE_W,                             SCREEN_TILE_H*TILE_H)

    def pan(self, dX, dY):
        self.move_ip(dX, dY)
        if self.left < -64:
            self.left = -64
        if self.right > (MAP_TILE_W * TILE_W)+64:
            self.right = (MAP_TILE_W * TILE_W)+64
        if self.top < -64:
            self.top = -64
        if self.bottom > (MAP_TILE_H * TILE_H)+64:
            self.bottom = (MAP_TILE_H * TILE_H)+64


# internal functions & classes
def printCam(camera):
    print "camera top: ", camera.top, "\n",      "camera left: ", camera.left, "\n",      "camera right: ", camera.right, "\n",      "camera bottom: ", camera.bottom, "\n"

def printTilePos(sprite_group, font_object, surface):
    for sprite in sprite_group:
        sprite.showPos(font_object, surface)

def main():
    pygame.init()
    pygame.display.set_mode(SCREEN_SIZE)
    pygame.font.init()
    test_font = pygame.font.Font(None, 15)

    sprite_group = SpriteGroup()
    camera = Camera()

    for x in range(MAP_TILE_W):
        for y in range(MAP_TILE_H):
            sprite_group.add(Tile(DEF_IMAGE, x, y))

    view_sprite_grp = ViewSpriteGroup(camera, sprite_group)

    screen = pygame.display.get_surface()
    screen.set_clip(SCREEN_CLIP)
    screen.fill(WHITE)
    pygame.display.flip()   

    printCam(camera)

    while 1:
        #handle events
        for event in pygame.event.get():
            #handle quit events
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            #handle arrow key scrolling
            if event.type == KEYDOWN and event.key == K_DOWN:
                camera.pan(0,64)
                printCam(camera)
            if event.type == KEYDOWN and event.key == K_UP:
                camera.pan(0,-64)
                printCam(camera)
            if event.type == KEYDOWN and event.key == K_LEFT:
                camera.pan(-64,0)
                printCam(camera)
            if event.type == KEYDOWN and event.key == K_RIGHT:
                camera.pan(64,0)
                printCam(camera)
            # Toggle displaying of tile position text
            if event.type == KEYDOWN and event.key == K_F2:
                if DISPLAY_TILE_POS == False:
                    DISPLAY_TILE_POS = True
                else:
                    DISPLAY_TILE_POS = False
            # Handle mouse scrolling
            if event.type == MOUSEMOTION and event.pos[0]               <= SCREEN_CLIP.left + 16:
                camera.pan(-8,0)
                printCam(camera)
            if event.type == MOUSEMOTION and event.pos[0]               >= SCREEN_CLIP.right - 16:
                camera.pan(8,0)
                printCam(camera)
            if event.type == MOUSEMOTION and event.pos[1]               <= SCREEN_CLIP.top + 16:
                camera.pan(0,-8)
                printCam(camera)
            if event.type == MOUSEMOTION and               event.pos[1] >= SCREEN_CLIP.bottom - 16:
                camera.pan(0,8)
                printCam(camera)

        #update all sprites:
        screen.fill(WHITE)
        view_sprite_grp.updateViewable(camera, sprite_group)
        #draw all sprites:
        view_sprite_grp.draw(screen)

        try:
            if DISPLAY_TILE_POS == True:
                printTilePos(view_sprite_grp, test_font, screen)
        except UnboundLocalError:
            DISPLAY_TILE_POS = False
        
        #update the display
        pygame.display.flip()
        view_sprite_grp.clearViewable(camera, sprite_group)


#program entry point    
if __name__ == '__main__':
    status = main()
    sys.exit(status)

Edit: GDNet's [source] tag has rudimentary support for Python. Use [source lang="python"] - Oluseyi
Advertisement
In order to post python code, you should place it within tags and post a link to a downloadable file. Python code uses whitespace as syntax. The whitespace has been stripped from your post, so what you've posted won't run.
class SpriteGroup(pygame.sprite.Group):    def __init__(self):        pygame.sprite.Group.__init__(self)

A very pointless class. If you want a shorter name, you can say
SpriteGroup=pygame.sprite.Group

Omae Wa Mou Shindeiru

camera.pan(0,-64)


if self.left < -64:    self.left = -64if self.right > (MAP_TILE_W * TILE_W)+64:    self.right = (MAP_TILE_W * TILE_W)+64


if event.type == MOUSEMOTION and event.pos[0] <= SCREEN_CLIP.left + 16:    camera.pan(-8,0)


test_font = pygame.font.Font(None, 15)


You should define constants (or variables) in place of scattered and repeated naked numbers like these; using TILE_W and + 64 in the same line is particularly sad.

What if you decide to scale your fonts less blindly? A real game is going to have much more complex text drawing.
What if mouse scrolling speed becomes an user option?
What if multiple tile sizes (e.g. magnified for large screens) need to be supported?

Omae Wa Mou Shindeiru

Those are excellent points, precisely why I wanted to post this code for community examination. I originally created the SpriteGroup class as an extension to pygame.sprite.group, thinking I might at some point in the future extend the class. It is the base class that I use to store all sprites from which I can pull to display on the screen. I appreciate the feedback.

grazie mille

This is a FNG question, but what do I need to include in the post exactly for the python code to show up with the proper indentations and so forth?
Quote:Original post by cyrex562
This is a FNG question, but what do I need to include in the post exactly for the python code to show up with the proper indentations and so forth?

[source lang="python"]<Python code here>[/source]

This topic is closed to new replies.

Advertisement