[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Optimising a tile map renderer



Best way to speed up this kind of game is only updating the bits of the screen which change (e.g. dirty sprite list). You might also see speed improvements from using sprites rather than blitting to a surface, e.g. one sprite per tile. Makes it a lot easier to implement a dirty sprite list too (since this is built into some of pygame's sprite group classes).

I wrote the basics of an iso game engine which does basically the same thing as this (grid of tiles), you might want to have a look for inspiration :)

http://www.pygame.org/project-pyTile-871-.html

It doesn't have scrolling optimisation (but doing that for a top-down grid game is easier than for iso, using scrolled offsets to calculate the number of new sprites to add to the spritegroup). As an example, my game above gets around 300-400fps, which drops to 20-40fps when you scroll. This is because when you scroll it's redrawing all tiles on the screen. Never got around to optimising that.


On 05/03/2010 10:20, Leith wrote:
Hi,

I am trying to make a pygame version of SimCity using the open source
Micropolis engine.

The engine spits out a 2d array of tile indexes that form the world
map.

I am having problems getting it to run fast at decent resolutions...

Here is the code:
class Map:
    def __init__(self, engine):
        self.engine = engine

        tilesFileName = 'images/micropolisEngine/tiles.png'

        self.tileSize = micropolisengine.EDITOR_TILE_SIZE
        tileSize = self.tileSize
        self.tiles = pygame.image.load(tilesFileName).convert()
        self.tilesPerRow = self.tiles.get_width() / self.tileSize

        self.worldWidth = micropolisengine.WORLD_W
        self.worldHeight = micropolisengine.WORLD_H
        self.width = self.worldWidth * tileSize
        self.height = self.worldHeight * tileSize

    def draw(self, surface, view):
        engine = self.engine
        tileSize = self.tileSize

        for y in range(view.top / tileSize, view.bottom / tileSize +
1):
            for x in range(view.left / tileSize, view.right / tileSize
+ 1):
                tile = engine.getTile(x, y)

                if tile == micropolisengine.TILE_INVALID:
                    break

                # FIXME - why is blinking not working?
                if engine.blinkFlag and (tile&
micropolisengine.ZONEBIT) and not (tile&  micropolisengine.PWRBIT):
                    index = micropolisengine.LIGHTNINGBOLT
                else:
                    index = tile&
micropolisengine.LOMASK

                # FIXME - why do we get these from the sim engine?
                if index>  960:
                    print "Tile (%d, %d) is out of bounds = %d" % (x,
y, tile)
                    break

                if index>= micropolisengine.TILE_COUNT:
                    break

                surface.blit(
                    self.tiles,
                    pygame.Rect(
                        x * tileSize - view.left,
                        y * tileSize - view.top,
                        tileSize,
                        tileSize),
                    pygame.Rect(
                        (index % self.tilesPerRow) * tileSize,
                        (index / self.tilesPerRow) * tileSize,
                        tileSize,
                        tileSize))

cProfile shows that the surface.blit and engine.getTiles calls are the
slow ones.

At this stage I can't do too much about getTiles, but I would like to
speed up the blitting.
Also I hope at one stage I can a list of 'dirty' tiles to reduce the
number of tiles to render, but that might take a while.

If it is not possible to do it fast with pygame are there any other
fast 2d python libraries?

I get about 37-40 FPS on my gaming rig at fullscreen 1280*1024:
Windows 7 64bit
ATI Radeon HD 5850
AMD Phenom II X4 @ 3.21GHz
4GB 1600 MHz DDR3