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
pygame.init()
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):
pygame.sprite.Sprite.__init__(self)
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!
self.checkKeys()
self.rotate()
self.calcVector()
self.checkCollision()
self.setPos()
#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
self.checkCollision()
if keys[pygame.K_LEFT]:
self.dir += self.turnRate #rotate counter-clockwise
if self.dir > 360:
self.dir = self.turnRate
self.imageMaster = self.imageLeft
self.checkCollision()
if keys[pygame.K_UP]:
self.thrust = .1 #apply thrust
self.imageMaster = self.imageThrust
else:
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):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((40,40))
self.image.fill((0,0,0))
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 = pygame.Surface(screen.get_size())
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:
clock.tick(50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
allSprites.clear(screen, background)
allSprites.update()
allSprites.draw(screen)
pygame.display.flip()
if __name__ == "__main__":
main()
of note, if anyone would like to test it, the images can be downloaded at
these links:
http://www.cs.iupui.edu/~aharris/pygame/ch09/shipCruise.png
http://www.cs.iupui.edu/~aharris/pygame/ch09/shipLeft.png
http://www.cs.iupui.edu/~aharris/pygame/ch09/shipRight.png
http://www.cs.iupui.edu/~aharris/pygame/ch09/shipThrust.png
Again, thank you very much for the help!
Sincerely,
Scotty Miller
Email: [email protected]
Website: http://scottymiller.net
Blackberry: 760-468-7899