Sign in to follow this  

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

This topic is 3504 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
In order to post python code, you should place it within [ code ] 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.

Share this post


Link to post
Share on other sites
camera.pan(0,-64)



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 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?

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites

This topic is 3504 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this