I wrote a game that is meant to shoot a blue square up the screen when the spacebar is pressed. However, all I get is the blue square right above my shooter. Here is my code:
import pygame, sys, random
from gameobjects import *
def main():
pygame.init()
screen = pygame.display.set_mode((800, 800))
numberOfColumns = 5
columnWidth = screen.get_width() / numberOfColumns
numberOfRows = 5
rowWidth = screen.get_width() / numberOfRows
magazineImage = pygame.image.load("images/Magazine.bmp")
magazineIssueOneImage = pygame.image.load("images/Magazine #1.bmp")
monsterState1 = pygame.image.load("images/Zombie Pos 1.bmp")
monsterState2 = pygame.image.load("images/Zombie Pos 2.bmp")
monsterState3 = pygame.image.load("images/Zombie Pos 3.bmp")
monsters = []
forryState1 = pygame.image.load("images/Forry Pos 1.bmp")
forryState2 = pygame.image.load("images/Forry Pos 2.bmp")
forryDelayTime = 8
gameObjects = []
gameObjectBlittingCounter = 0
monsterSpawnSpotList = [(rowWidth * -5), (rowWidth * -4), (rowWidth * -3), (rowWidth * -2), (rowWidth * -1)]
for monster in range(0, 5):
monster = Monster(monsterState1, monsterState2, monsterState3, ((columnWidth / 2 - monsterState1.get_width() / 2) + gameObjectBlittingCounter * columnWidth), random.choice(monsterSpawnSpotList))
monsters.append(monster)
gameObjects.append(monster)
gameObjectBlittingCounter += 1
forry = Forry(forryState1, forryState2, columnWidth, rowWidth, screen, forryDelayTime, magazineImage, magazineIssueOneImage)
gameObjects.append(forry)
while True:
screen.fill((0,255,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
#Update Projectiles
if forry.projectileCall == True:
magazine = Magazine(magazineImage, forry)
gameObjects.append(magazine)
#Update Game Objects
for gameObject in gameObjects:
gameObject.update()
#Render
gameObjectBlittingCounter = 0
for gameObject in gameObjects:
screen.blit(gameObjects[gameObjectBlittingCounter].image, ((gameObjects[gameObjectBlittingCounter].rect.x), (gameObjects[gameObjectBlittingCounter].rect.y)))
gameObjectBlittingCounter += 1
pygame.display.flip()
if __name__ == "__main__":
main()
The file gameobjects.py looks like this:
import pygame
class Monster(pygame.sprite.Sprite):
def __init__(self, monsterState1, monsterState2, monsterState3, xCoord, yCoord):
self.monsterState1 = monsterState1
self.monsterState2 = monsterState2
self.monsterState3 = monsterState3
self.image = self.monsterState1
self.rect = self.image.get_rect()
self.rect.y = yCoord
self.rect.x = xCoord
def update(self):
self.moveDown()
def moveDown(self):
self.rect.y += 1
def onSpawn(self):
return
def onDeath(self):
return
def reset(self):
return
class Forry(pygame.sprite.Sprite):
def __init__(self, forryState1, forryState2, columnWidth, rowWidth, screen, delayTime, magazineImage, magazineIssueOneImage):
self.forryState1 = forryState1
self.forryState2 = forryState2
self.image = self.forryState1
self.columnWidth = columnWidth
self.rowWidth = rowWidth
self.rect = self.image.get_rect()
self.rect.y = self.columnWidth / 2 - self.image.get_width() / 2
self.rect.y = screen.get_height() - (self.rowWidth / 2 - self.image.get_height() / 2)
self.delayTime = delayTime
self.delay = 0
self.magazineImage = magazineImage
self.magazineIssueOneImage = magazineIssueOneImage
self.projectileCall = False
self.rightBound = screen.get_width() - (self.columnWidth / 2 + self.image.get_width() / 2)
self.leftBound = (self.columnWidth / 2 - self.image.get_width() / 2)
def update(self):
#Check Player Input
playerInput = self.checkPlayerInput()
#Update Position
if self.delay > 0:
self.delay -= 1
elif self.delay < 0:
self.delay = 0
else:
self.updatePosition(playerInput)
#Keep on the Screen
self.keepOnScreen()
def checkPlayerInput(self):
left = pygame.key.get_pressed() [pygame.K_LEFT]
right = pygame.key.get_pressed() [pygame.K_RIGHT]
shoot = pygame.key.get_pressed() [pygame.K_SPACE]
return (left, right, shoot)
def updatePosition(self, playerInput):
left = playerInput[0]
right = playerInput[1]
shoot = playerInput[2]
if self.projectileCall == True:
self.projectileCall = False
self.image = self.forryState1
if shoot:
self.image = self.forryState2
self.projectileCall = True
self.delay = self.delayTime
if left:
self.rect.x -= self.columnWidth
self.delay = self.delayTime
if right:
self.rect.x += self.columnWidth
self.delay = self.delayTime
def keepOnScreen(self):
if self.rect.x >= self.rightBound:
self.rect.x = self.rightBound
elif self.rect.x <= self.leftBound:
self.rect.x = self.leftBound
def onSpawn(self):
return
def onDeath(self):
return
def reset(self):
return
class Magazine(pygame.sprite.Sprite):
def __init__(self, image, shooter):
self.image = image
self.rect = image.get_rect()
self.rect.x = shooter.rect.x
self.rect.y = shooter.rect.y - shooter.image.get_height()
def update(self):
self.rect.y -= 5
if self.rect.y <= 0 - self.image.get_height():
self.kill()
My magazine is a blue square
So why doesn't my projectile go up the screen?
++++++++
With all the fixes in the answer below, now I have another error:
When the blue box goes off screen, it is supposed to be killed because of self.kill() in the update function of the Magazine class.
However, instead of just being killed, the minute one of them gets off screen, it gives me these errors:
Traceback (most recent call last):
File "main.py", line 106, in <module>
main()
File "main.py", line 78, in main
projectileObject.update()
File "/Users/number1son100/Desktop/Famous Monsters Game/gameobjects.py", line 117, in update
self.kill()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pygame/sprite.py", line 174, in kill
for c in self.__g.keys():
AttributeError: 'Magazine' object has no attribute '_Sprite__g'
Stumped me for a while, a classic case of "right under our noses":
Look at the indentation of your "update" function under Magazine, it is indented one too far to the right, which means it is a function defined -inside- __init__() instead of as a method of Magazine as you wanted. If this didnt make much sense to you, just "dedent" that block so it looks like this
class Magazine(pygame.sprite.Sprite):
def __init__(self, image, shooter):
self.image = image
self.rect = image.get_rect()
self.rect.x = shooter.rect.x
self.rect.y = shooter.rect.y - shooter.image.get_height()
def update(self):
self.rect.y -= 5
if self.rect.y <= 0 - self.image.get_height():
self.kill()
some explaination why nothing happened but no error: As your update function was not a method of Magazine, it did not overwrite the one that was inherited from pygame.sprite.Sprite. As a result when it was called from your main loop, the placehold/basic method was called, not your custom one. This meant you didnt get an error/exception but it didnt work!
Moving on, there are a few major things the issue here too that could do with correction:
Not neccessarily an issue, but you can use the python "for x in y" syntax like you do here:
for gameObject in gameObjects:
gameObject.update()
later on in the render section, i.e.:
#Render
gameObjectBlittingCounter = 0
for gameObject in gameObjects:
screen.blit(gameObjects[gameObjectBlittingCounter].image, ((gameObjects[gameObjectBlittingCounter].rect.x), (gameObjects[gameObjectBlittingCounter].rect.y)))
gameObjectBlittingCounter += 1
becomes:
for gameObject in gameObjects:
screen.blit(gameObject.image, gameObject.rect)
note also you can just pass the whole rect instead of splitting to rect.x and rect.y
You will notice this once you fix the update function, your current code spawns multiple shots when you press the spacebar, this is because your spawn mechanic looks if spacebar is held and spawns a projectile every loop, try removing this spawning from the loop and just do it once when space is pressed or released
Cap the framerate
In my testing the framerate could spike very high and make the game run oddly. Cap the framerate using a pygame.Clock object and calling clock.tick(framerate) in your loop
In preparation for further complexity, consider splitting your "gameObjects" lists up into several different types (enemyObjects, projectileObjects, etc), because these may need different treatment at some point, or saves you looping the entire list for something that only effects enemies if that occurs.
Finally a small plug of my website, I have source and docs for some pygame libraries that you can use and may be help with you in future.