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

[pygame] Collision Issues

I've been messing around with a sample from the L Line's book on game programming, and I've tried to further develop their space.py example that demonstrates a ship without friction.  I've tried my hardest to properly enter collision detection on a set of 40,40 rects that are placed throughout the 800,600 screen, but it is proving to be a bit harder than first imagined.  The issues that I am noticing is that sometimes the sprite will catch the collisions, sometimes they won't.  Another is the rotate() method.  If I rotated, the sprite's rect will expand and throw off the collision detection as well, sending the sprite all throughout the screen and eventually off into the great unknown.  May I ask for assistance in pointing me to what I am doing wrong with this?  I would greatly appreciate any help provided!  Thank you very very much!
""" space.py
    simulate a spacecraft with
    no traction at all
import pygame, math
class Ship(pygame.sprite.Sprite):
    # Sprite containing the main ship that the user will control in the game.
    #   Methods:
    #       update()
    #       checkKeys()
    #       rotate()
    #       calcVector()
    #       setPos()
    #       checkCollision()
    def __init__(self, screen, walls, map):
        self.walls  = walls #import the wall sprite group for collision detection
        self.imageThrust    = pygame.image.load("shipThrust.png") #image for the sprite while applying thrust
        self.imageThrust    = self.imageThrust.convert()
        self.imageCruise    = pygame.image.load("shipCruise.png") #image for the sprite when idle
        self.imageCruise    = self.imageCruise.convert()
        self.imageLeft      = pygame.image.load("shipLeft.png")   #image for the sprite when rotating left
        self.imageLeft      = self.imageLeft.convert()
        self.imageRight     = pygame.image.load("shipRight.png")  #image for the sprite when rotating right
        self.imageRight     = self.imageRight.convert()
        #designate the initial master image and get the rect for it
        self.imageMaster    = self.imageCruise
        self.image          = self.imageMaster
        self.rect           = self.image.get_rect()
        #some initial properties, including initial position, difference in x/y, direction, turn rate,
        #and thrust of the Ship sprite
        self.x          = 100
        self.y          = 100
        self.dx         = 0
        self.dy         = 0
        self.dir        = 0
        self.turnRate   = 5
        self.thrust     = 0
    def update(self):
        #The default update method that is called in the main loop.
        #all further methods are properly encapsulated in order
        #to keep stuff tidy!
        #after all the methods get their chance, we finish up this frame by
        #placing the sprite in the proper x and y position
        self.rect.center = (self.x, self.y)
    def checkKeys(self):
        #we store the pressed key in the variable "keys" so that we may flip through
        #all the posibilities.  Note that we are doing individual if statements
        #so that we may apply multiple keys at once, instead of one at a time with
        #the elif approach
        keys = pygame.key.get_pressed()
        self.imageMaster = self.imageCruise #start with the cruising sprite image
        if keys[pygame.K_RIGHT]:
            self.dir -= self.turnRate #rotate clockwise
            if self.dir < 0:
                self.dir = 360 - self.turnRate
            self.imageMaster = self.imageRight
        if keys[pygame.K_LEFT]:
            self.dir += self.turnRate #rotate counter-clockwise
            if self.dir > 360:
                self.dir = self.turnRate
            self.imageMaster = self.imageLeft
        if keys[pygame.K_UP]:
            self.thrust = .1 #apply thrust
            self.imageMaster = self.imageThrust
            self.thrust = 0 #nothing was pressed, speed will remain constant
    def rotate(self):
        oldCenter = self.rect.center #get the old center so that we may recenter after rotating
        self.image = pygame.transform.rotate(self.imageMaster, self.dir)
        self.rect = self.image.get_rect() #get the rect for the newly rotated sprite
        self.rect.center = oldCenter #and center it back on it's old x,y coord's
    def calcVector(self):
        radians = self.dir * math.pi / 180
        thrustDx = self.thrust * math.cos(radians)
        thrustDy = self.thrust * math.sin(radians)
        thrustDy *= -1
        self.dx += thrustDx
        self.dy += thrustDy
        self.speed = math.sqrt((self.dx * self.dx) + (self.dy * self.dy))
    def setPos(self):
        #The final piece before the x/y coords are placed on the sprite
        self.x += self.dx
        self.y += self.dy
    def checkCollision(self):
        # !!BROKEN!!
        # Currently I have this set to detect for each rect in the walls sprite group
        # If it collides with any of them, it will determine based off of the difference
        # in x/y to see what direction it collided from.  Once it determines the direction,
        # it will clear the dx/dy (based on how it hit) so that the ship sprite will not
        # move once it goes to the setPos() method, and it will also move it so that it
        # is 15 pixels from the center of the direction it hit the wall.  I chose 15px
        # in this hopes that it will move it 1px extra away from the wall.
        for wall in self.walls:
            if self.rect.colliderect(wall.rect):
                if self.dx > 0:
                    self.dx = 0
                    self.x = wall.rect.left-15
                if self.dx < 0:
                    self.dx = 0
                    self.x = wall.rect.right+15
                if self.dy > 0:
                    self.dy = 0
                    self.y = wall.rect.top-15
                if self.dy < 0:
                    self.dy = 0
                    self.y = wall.rect.bottom+15

class Map(object):
    # Sprite containing the boundaries and walls
    def __init__(self,walls,screen):
        self.walls = walls
        self.screen = screen
        self.grid = [
            "+       +          +",
            "+       +          +",
            "+       +          +",
            "+       +          +",
            "+       +          +",
            "+             ++++++",
            "+                  +",
            "++++++             +",
            "+          +       +",
            "+          +       +",
            "+          +       +",
            "+          +       +",
            "+          +       +",
        self.x = self.y = 0
        for row in self.grid:
            for col in row:
                if col == "+":
                    self.walls.add(Walls(self.x, self.y, self.walls, self.screen))
                self.x += 40
            self.y += 40
            self.x = 0
class Walls(pygame.sprite.Sprite):
    def __init__(self, x, y, walls, screen):
        self.image = pygame.Surface((40,40))
        self.rect = self.image.get_rect()
        self.rect.topleft = x,y

def main():
    screen = pygame.display.set_mode((800, 600))
    pygame.display.set_caption("Space craft")
    background = "">    background.fill((200, 200, 200))
    screen.blit(background, (0, 0))
    walls = pygame.sprite.Group()
    map = Map(walls, screen)
    ship = Ship(screen, walls, map)
    allSprites = pygame.sprite.Group(ship, walls)
    clock = pygame.time.Clock()
    keepGoing = True
    while keepGoing:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
        allSprites.clear(screen, background)
if __name__ == "__main__":

of note, if anyone would like to test it, the images can be downloaded at these links:
Again, thank you very much for the help!

Scotty Miller

Email: miller.scott.j@xxxxxxxxx
Website: http://scottymiller.net
Blackberry: 760-468-7899