EDIT: QuadTrees (http://www.pygame.org/wiki/QuadTree?parent=CookBook) may be my answer here, going to try this out and will report back. Please don't hesitate to give me some advice anyway!
I have a series of obstacles in pygame that I am converting to Rect objects, looping over the list before every tick of the frame to see if my Hero Rect object has collided.
I believe this is causing me performance issues. I also know that I could try switching to "Dirty rect animation" instead of updating the whole screen every tick, but before I go down that road I wanted to see if I could do this more efficiently.
The particular code in question occurs near the very bottom (if pillars.checkCollision(hero.rect):), but I provided everything for context:
import sys
import pygame
from pygame.locals import *
import pyganim
from decimal import *
import math
## start pymgame
pygame.init()
## window and lines
windowWidth = 387
windowHeight = 340
lineThickness = 3
animationSpeed = .15
## fps and speed
increaseSpeed = 1
fps = 60
fpsClock = pygame.time.Clock()
## window
##windowSurface = pygame.display.set_mode((windowWidth, windowHeight), pygame.FULLSCREEN)
windowSurface = pygame.display.set_mode((windowWidth, windowHeight))
class Pillars():
def __init__(self):
self.image = pyganim.PygAnimation([("grave.png", animationSpeed)])
self.xSize = self.image.getMaxSize()[0]
self.ySize = self.image.getMaxSize()[1]
self.numberOfColumns = int(math.ceil(Decimal((windowWidth - (self.xSize * 4))) / Decimal((self.xSize * 3))))
self.numberOfRows = int(math.ceil(Decimal((windowHeight - (self.ySize * 4))) / Decimal((self.ySize * 3))))
self.rects = []
self.image.play()
def draw(self):
xPos = (self.xSize * 2)
yPos = (self.ySize * 2)
columnCounter = 1
for num in range(0, (self.numberOfColumns * self.numberOfRows)):
self.rects.append(Rect(xPos, yPos, self.xSize, self.ySize))
self.image.blit(windowSurface, (xPos, yPos))
xPos += (self.xSize * 3)
if columnCounter >= self.numberOfColumns:
columnCounter = 0
xPos = (self.xSize * 2)
yPos += (self.ySize * 3)
columnCounter += 1
def checkCollision(self, Rect):
for pillarRect in self.rects:
if pillarRect.colliderect(hero.rect):
return True
class Hero():
def __init__(self):
self.standingLeft = pyganim.PygAnimation([("left_standing_link.png", animationSpeed)])
self.standingRight = pyganim.PygAnimation([("right_standing_link.png", animationSpeed)])
self.standingUp = pyganim.PygAnimation([("up_standing_link.png", animationSpeed)])
self.standingDown = pyganim.PygAnimation([("down_standing_link.png", animationSpeed)])
self.walkingUp = pyganim.PygAnimation([("up_standing_link.png", animationSpeed), ("up_walking_link.png", animationSpeed)])
self.walkingDown = pyganim.PygAnimation([("down_standing_link.png", animationSpeed), ("down_walking_link.png", animationSpeed)])
self.walkingLeft = pyganim.PygAnimation([("left_standing_link.png", animationSpeed), ("left_walking_link.png", animationSpeed)])
self.walkingRight = pyganim.PygAnimation([("right_standing_link.png", animationSpeed), ("right_walking_link.png", animationSpeed)])
self.walkingRight.play()
self.walkingLeft.play()
self.standingLeft.play()
self.standingRight.play()
self.standingUp.play()
self.standingDown.play()
self.walkingUp.play()
self.walkingDown.play()
self.x = 100
self.y = 100
self.dirX = 0 ## -1 = left 1 = right
self.dirY = 0 ## -1 = up 1 = down
self.orientation = 0 ## 0 = right, 1 = down, 2 = left, 3 = up
self.rect = None
def move(self, animationObj):
self.animationObject = animationObj
animationObj.x = self.x + (self.dirX * increaseSpeed)
animationObj.y = self.y + (self.dirY * increaseSpeed)
self.x = animationObj.x
self.y = animationObj.y
if self.dirX == 1:
self.orientation = 0
elif self.dirX == -1:
self.orientation = 2
elif self.dirY == -1:
self.orientation = 3
elif self.dirY == 1:
self.orientation = 1
tempRect = animationObj.getRect()
self.rect = Rect(animationObj.x, animationObj.y, tempRect[2], tempRect[3])
def draw(self, animationObj):
if self.y > (windowHeight - (lineThickness * 5.5)):
self.y = windowHeight - (lineThickness * 5.5)
elif self.y < lineThickness:
self.y = lineThickness
elif self.x > (windowWidth - (lineThickness * 6)):
self.x = windowWidth - (lineThickness * 6)
elif self.x < lineThickness:
self.x = lineThickness
animationObj.blit(windowSurface, (self.x, self.y))
def moveAndDrawWalkingRight(self):
self.dirX = 1
self.dirY = 0
self.move(self.walkingRight)
self.draw(self.walkingRight)
def moveAndDrawWalkingLeft(self):
self.dirX = -1
self.dirY = 0
self.move(self.walkingLeft)
self.draw(self.walkingLeft)
def moveAndDrawWalkingUp(self):
self.dirX = 0
self.dirY = -1
self.move(self.walkingUp)
self.draw(self.walkingUp)
def moveAndDrawWalkingDown(self):
self.dirX = 0
self.dirY = 1
self.move(self.walkingDown)
self.draw(self.walkingDown)
def drawStanding(self):
if self.orientation == 0:
self.dirX = 0
self.dirY = 0
self.move(self.standingRight)
self.draw(self.standingRight)
elif self.orientation == 2:
self.dirX = 0
self.dirY = 0
self.move(self.standingLeft)
self.draw(self.standingLeft)
elif self.orientation == 1:
self.dirX = 0
self.dirY = 0
self.move(self.standingDown)
self.draw(self.standingDown)
elif self.orientation == 3:
self.dirX = 0
self.dirY = 0
self.move(self.standingUp)
self.draw(self.standingUp)
def drawArena():
windowSurface.fill((0,0,0))
hero = Hero()
pillars = Pillars()
while True: # main loop
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
## if event.key == K_RIGHT:
## hero.moveAndDrawWalkingRight()
## elif event.key == K_LEFT:
## hero.moveAndDrawWalkingLeft()
## elif event.key == K_UP:
## hero.moveAndDrawWalkingUp()
## elif event.key == K_DOWN:
## hero.moveAndDrawWalkingDown()
## else:
## hero.drawStanding()
drawArena()
pillars.draw()
priorHeroX = hero.x
priorHeroY = hero.y
keys = pygame.key.get_pressed() #checking pressed keys
if keys[pygame.K_RIGHT]:
hero.moveAndDrawWalkingRight()
elif keys[pygame.K_LEFT]:
hero.moveAndDrawWalkingLeft()
elif keys[pygame.K_UP]:
hero.moveAndDrawWalkingUp()
elif keys[pygame.K_DOWN]:
hero.moveAndDrawWalkingDown()
else:
hero.drawStanding()
if pillars.checkCollision(hero.rect):
hero.x = priorHeroX
hero.y = priorHeroY
pygame.display.update()
fpsClock.tick(fps)