SideScrollingShootEmUp
Attribution
Create your own side-scrolling shoot-'em-up, pages 58-63, by Jordi Sontanja.
Licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported.
Original Python code
import pgzrun
from enum import Enum
import math
from random import randint
from pgzero.builtins import Actor
from shapely.geometry import Point, Polygon, LineString
from shapely.affinity import translate
isFastComputer = True
if isFastComputer:
def drawBackground(screen, frame):
screen.blit("background_1",(800-frame%(800*8)/4,0))
screen.blit("background_0",(800-(frame+800*4)%(800*8)/4,0))
foregroundNames = ['centreforeground', 'leftforeground', 'leftvertforeground',
'rightforeground', 'rightvertforeground',
'centreforeground_top', 'leftforeground_top', 'leftvertforeground_top',
'rightforeground_top', 'rightvertforeground_top',
'centreforeground_narrow', 'centreforeground_narrow_top',
'leftforeground_narrow', 'leftforeground_narrow_top',
'leftvertforeground_narrow', 'leftvertforeground_narrow_top',
'rightforeground_narrow', 'rightforeground_narrow_top',
'rightvertforeground_narrow', 'rightvertforeground_narrow_top',
'centre']
foregroundMap = [(12,1634,571),(10,1831,571),(10,2072,571),(10,2314,571),(14,2420,506),(0,2557,569),(0,2800,569),(0,3041,569),
(0,3284,569),(0,3525,569),(0,3767,569),(13,3840,31),(0,4009,569),(11,4038,31),(0,4250,569),(11,4280,31),
(0,4492,569),(11,4522,31),(0,4734,569),(11,4765,31),(0,4976,569),(11,5008,31),(12,5081,249),(13,5081,314),
(0,5219,569),(11,5251,31),(10,5278,249),(11,5279,314),(0,5462,569),(11,5493,31),(10,5521,249),(11,5522,314),
(0,5704,569),(11,5736,31),(10,5763,249),(11,5763,314),(0,5946,569),(11,5978,31),(10,6006,249),(11,6006,314),
(0,6189,569),(11,6221,31),(10,6249,249),(11,6249,314),(0,6432,569),(11,6463,31),(16,6446,249),(17,6447,314),
(0,6675,569),(11,6706,31),(0,6918,569),(11,6949,31),(0,7161,569),(11,7191,31),(0,7403,569),(11,7433,31),(0,7645,569),
(11,7675,31),(0,7887,569),(17,7873,31),(0,8128,569),(0,8370,569),(0,8613,569),(0,8856,569),(1,9112,377),(20,9099,549),
(20,9342,549),(0,9438,377),(20,9585,548),(0,9680,377),(20,9827,546),(0,9923,377),(20,10069,546),(0,10166,377),
(20,10310,546),(0,10409,377),(20,10551,546),(0,10652,377),(20,10652,543),(4,10788,548),(4,10788,377)]
foregroundPolygons = [[(-122, -96), (120, -96), (120, 96), (-122, 96)], [(-204, 96), (186, -96), (204, -96), (204, 95)],
[(-14, 95), (-14, -95), (14, -95), (14, 95)], [(-206, -95), (-187, -95), (200, 95), (-206, 95)],
[(-15, -95), (12, -95), (12, 95), (-15, 95)], [(120, -95), (120, 95), (-122, 95), (-122, -95)],
[(204, -95), (204, 95), (186, 95), (-202, -96)], [(14, 95), (-14, 95), (-14, -95), (13, -95)],
[(-207, -96), (201, -96), (-187, 95), (-206, 95)], [(-15, -96), (12, -96), (12, 95), (-15, 95)],
[(-122, -33), (120, -33), (120, 31), (-122, 31)], [(-122, -33), (120, -33), (120, 31), (-122, 31)],
[(-73, 31), (56, -32), (75, -32), (75, 31)], [(75, 31), (56, 31), (-74, -33), (75, -33)],
[(14, 31), (-14, 31), (-14, -32), (13, -32)], [(14, 32), (-14, 32), (-14, -33), (13, -33)],
[(-77, -33), (-58, -33), (70, 31), (-77, 31)], [(-77, -33), (72, -33), (-58, 31), (-77, 31)],
[(-15, -33), (12, -33), (12, 31), (-15, 31)], [(-15, -33), (13, -33), (13, 31), (-15, 31)],
[]]
foregroundBiggestObjectHalfXSize = 206
class Foreground:
def __init__(self):
self.elements = []
self.indexes = []
self.mapElement = 0
self.polygons = []
def draw(self):
for element in self.elements:
element.draw()
def update(self, globalTime):
for i in reversed(range(len(self.elements))):
self.elements[i].x -= 1
if self.elements[i].x < -foregroundBiggestObjectHalfXSize:
del self.elements[i]
del self.indexes[i]
if self.mapElement < len(foregroundMap):
while foregroundMap[self.mapElement][1] < globalTime + 800 + foregroundBiggestObjectHalfXSize:
self.indexes.append(foregroundMap[self.mapElement][0])
self.elements.append(Actor(foregroundNames[foregroundMap[self.mapElement][0]], (foregroundMap[self.mapElement][1] - globalTime, foregroundMap[self.mapElement][2])))
self.mapElement += 1
if self.mapElement >= len(foregroundMap):
break
self.polygons = []
for i in range(len(self.indexes)):
polygon = translate(Polygon(foregroundPolygons[self.indexes[i]]), self.elements[i].x, self.elements[i].y)
self.polygons.append(polygon)
class Bullet:
def __init__(self, pos, speed):
self.actor = Actor('bullet', pos)
self.speed = speed
def update(self):
self.actor.x += self.speed[0]
self.actor.y += self.speed[1]
def draw(self):
self.actor.draw()
class AllBullets:
def __init__(self):
self.bullets = []
def update(self):
for i in reversed(range(len(self.bullets))):
self.bullets[i].update()
if self.bullets[i].actor.x < -4 or self.bullets[i].actor.y < -4 or self.bullets[i].actor.x > 804 or self.bullets[i].actor.y > 604:
del self.bullets[i]
def draw(self):
for bullet in self.bullets:
bullet.draw()
def append(self, posX, posY, velX, velY):
self.bullets.append(Bullet((posX, posY), (velX, velY)))
def collide(self, polygon):
collided = False
for i in reversed(range(len(self.bullets))):
if Point(self.bullets[i].actor.x, self.bullets[i].actor.y).within(polygon):
collided = True
del self.bullets[i]
return collided
enemyNames = ['ball', 'cannon', 'cannon_top', 'flying_saucer',
'rocket', 'ship_alien', 'ship_cannon', 'ship_fighter', 'ship_pistol', 'big_ship' ]
enemyPolyCoords = [[(-6, -30), (-21, -27), (-30, -19), (-33, -3), (-27, 11), (-19, 20), (-5, 30), (11, 31), (26, 17), (29, -12), (10, -23)],
[(-17, 29), (-13, -4), (-23, -11), (-21, -29), (9, -24), (23, -13), (18, 30)],
[(-17, -29), (-12, 3), (-22, 6), (-21, 27), (6, 24), (23, 12), (18, -30)],
[(-24, 7), (-56, 23), (53, 23), (17, 6), (17, -10), (6, -23), (-14, -23), (-22, -9)],
[(73, -20), (-27, -20), (-64, -14), (-85, -5), (-75, 5), (-33, 15), (11, 19), (73, 19), (83, 8), (83, -13)],
[(-36, -19), (-48, -10), (-50, 5), (-44, 16), (-31, 23), (-11, 23), (24, 16), (41, -20), (10, -22), (-26, -21)],
[(28, -31), (-74, -31), (-74, 8), (-37, 8), (-20, 22), (9, 31), (37, 30), (62, 19), (72, 13), (72, -12), (48, -24), (31, -28)],
[(-51, -15), (-60, -11), (-63, -1), (-54, 8), (-21, 8), (18, 22), (41, 24), (58, 18), (61, -26), (23, -24), (-19, -16)],
[(-50, -22), (-23, 15), (-9, 27), (4, 32), (26, 24), (42, 7), (49, -15), (41, -28), (16, -33), (7, -33), (7, -26)],
[(-144, -32), (-160, -35), (-173, -8), (-164, 11), (-151, 19), (-119, 24), (-142, 30), (-139, 41), (-93, 37), (-82, 31), (-72, 51), (-35, 38), (-30, 53), (5, 65), (100, 69), (108, 61), (109, 44), (152, 28), (169, 15), (169, -2), (154, -12), (127, -24), (154, -41), (70, -69), (39, -43), (21, -56), (-23, -61), (-69, -48), (-74, -42), (-96, -47), (-124, -47), (-109, -40), (-138, -35)]]
enemyNumberOfHits = [1, 4, 4, 10, 1, 1, 12, 6, 12, 1000]
enemyPaths = [[(800+32, 0), (790, 1), (780, 5), (770, 11), (760, 19), (750, 29), (740, 40), (730, 53), (720, 68), (710, 83), (700, 98), (690, 114), (680, 129), (670, 143), (660, 157), (650, 169), (640, 179), (630, 188), (620, 194), (610, 198), (600, 200), (590, 199), (580, 196), (570, 191), (560, 184), (550, 174), (540, 163), (530, 150), (520, 136), (510, 121), (500, 106), (490, 90), (480, 75), (470, 60), (460, 47), (450, 34), (440, 23), (430, 14), (420, 7), (410, 3), (400, 0), (390, 0), (380, 3), (370, 7), (360, 14), (350, 23), (340, 34), (330, 47), (320, 60), (310, 75), (300, 90), (290, 106), (280, 121), (270, 136), (260, 150), (250, 163), (240, 174), (230, 184), (220, 191), (210, 196), (200, 199), (190, 200), (180, 198), (170, 194), (160, 188), (150, 179), (140, 169), (130, 157), (120, 143), (110, 129), (100, 114), (90, 98), (80, 83), (70, 68), (60, 53), (50, 40), (40, 29), (30, 19), (20, 11), (10, 5), (0, 1), (-10, 0), (-160, 0)],
[(800+24, 0), (-160, 0)],
[(800+24, 0), (-160, 0)],
[(800+57, 0), (790, -1), (780, -3), (770, -6), (760, -11), (750, -16), (740, -23), (730, -31), (720, -40), (710, -50), (700, -60), (690, -71), (680, -83), (670, -94), (660, -106), (650, -117), (640, -129), (630, -140), (620, -150), (610, -160), (600, -169), (590, -177), (580, -184), (570, -189), (560, -194), (550, -197), (540, -199), (530, -200), (520, -199), (510, -197), (500, -194), (490, -189), (480, -184), (470, -177), (460, -169), (450, -160), (440, -150), (430, -140), (420, -129), (410, -117), (400, -106), (390, -94), (380, -83), (370, -71), (360, -60), (350, -50), (340, -40), (330, -31), (320, -23), (310, -16), (300, -11), (290, -6), (280, -3), (270, -1), (260, 0), (250, -1), (240, -3), (230, -6), (220, -11), (210, -16), (200, -23), (190, -31), (180, -40), (170, -50), (160, -60), (150, -71), (140, -83), (130, -94), (120, -106), (110, -117), (100, -129), (90, -140), (80, -150), (70, -160), (60, -169), (50, -177), (40, -184), (30, -189), (20, -194), (10, -197), (0, -199), (-10, -200), (-160, -200)],
[(800+85, 0), (-160, 0)],
[(800+50, 0), (762, 0), (711, -5), (680, -13), (663, -21), (634, -34), (608, -42), (578, -52), (522, -62), (459, -66), (-160, -66)],
[(800+75, 0), (790, 0), (780, 0), (770, 1), (760, 1), (750, 2), (740, 3), (730, 4), (720, 5), (710, 6), (700, 7), (690, 9), (680, 11), (670, 12), (660, 14), (650, 16), (640, 19), (630, 21), (620, 23), (610, 26), (600, 29), (590, 31), (580, 34), (570, 37), (560, 40), (550, 43), (540, 47), (530, 50), (520, 53), (510, 57), (500, 60), (490, 64), (480, 68), (470, 71), (460, 75), (450, 79), (440, 83), (430, 86), (420, 90), (410, 94), (400, 98), (390, 102), (380, 106), (370, 110), (360, 114), (350, 117), (340, 121), (330, 125), (320, 129), (310, 132), (300, 136), (290, 140), (280, 143), (270, 147), (260, 150), (250, 153), (240, 157), (230, 160), (220, 163), (210, 166), (200, 169), (190, 171), (180, 174), (170, 177), (160, 179), (150, 181), (140, 184), (130, 186), (120, 188), (110, 189), (100, 191), (90, 193), (80, 194), (70, 195), (60, 196), (50, 197), (40, 198), (30, 199), (20, 199), (10, 200), (0, 200), (-160, 200)],
[(800+63, 0), (790, 0), (780, 1), (770, 3), (760, 5), (750, 7), (740, 11), (730, 14), (720, 19), (710, 23), (700, 29), (690, 34), (680, 40), (670, 47), (660, 53), (650, 60), (640, 68), (630, 75), (620, 83), (610, 90), (600, 98), (590, 106), (580, 114), (570, 121), (560, 129), (550, 136), (540, 143), (530, 150), (520, 157), (510, 163), (500, 169), (490, 174), (480, 179), (470, 184), (460, 188), (450, 191), (440, 194), (430, 196), (420, 198), (410, 199), (400, 200), (390, 200), (380, 199), (370, 198), (360, 196), (350, 194), (340, 191), (330, 188), (320, 184), (310, 179), (300, 174), (290, 169), (280, 163), (270, 157), (260, 150), (250, 143), (240, 136), (230, 129), (220, 121), (210, 114), (200, 106), (190, 98), (180, 90), (170, 83), (160, 75), (150, 68), (140, 60), (130, 53), (120, 47), (110, 40), (100, 34), (90, 29), (80, 23), (70, 19), (60, 14), (50, 11), (40, 7), (30, 5), (20, 3), (10, 1), (0, 0), (-160, 0)],
[(800+51, 0), (751, 4), (704, 15), (660, 33), (620, 57), (585, 87), (556, 121), (535, 158), (520, 196), (513, 235), (513, 272), (521, 307), (535, 337), (554, 363), (577, 382), (604, 395), (632, 400), (661, 398), (688, 388), (713, 371), (734, 348), (750, 319), (760, 286), (764, 250), (760, 212), (748, 173), (729, 135), (703, 100), (671, 68), (633, 42), (590, 21), (544, 7), (495, 1), (447, 1), (399, 10), (353, 25), (311, 47), (274, 74), (243, 107), (218, 143), (201, 181), (191, 219), (188, 257), (193, 293), (204, 326), (221, 353), (243, 375), (269, 390), (297, 399), (325, 399), (354, 393), (380, 379), (403, 358), (421, 332), (433, 300), (439, 265), (438, 227), (430, 188), (414, 150), (390, 114), (360, 81), (324, 52), (283, 29), (238, 12), (191, 2), (142, 0), (-160, 0)],
[(800+180, 0), (800-176, 0)]]
enemySpeeds = [4, 1, 1, 3, 6, 4, 2, 4, 3, 0.5 ]
def PositionFromLength(path, length):
totalLength = 0.0
for i in range(1, len(path)):
dx = path[i][0] - path[i-1][0]
dy = path[i][1] - path[i-1][1]
nextLength = math.sqrt(dx*dx + dy*dy)
if length <= totalLength + nextLength:
localParameter = (length - totalLength) / nextLength
return True, (path[i-1][0] + localParameter * dx, path[i-1][1] + localParameter * dy)
totalLength += nextLength
return False, path[-1]
def Accelerate(path, length):
length = length * length / 800
return PositionFromLength(path, length)
def BigBossMovement(path, length):
inPath, position = PositionFromLength(path, length)
if inPath:
return True, position
totalLength = path[0][0] - path[1][0]
return True, (position[0], 200*math.sin((length-totalLength)/40))
enemyMovement = [PositionFromLength, PositionFromLength, PositionFromLength, PositionFromLength,
Accelerate, PositionFromLength, PositionFromLength, PositionFromLength,
PositionFromLength, BigBossMovement ]
def NoBullets(bullets, spaceShip, posX, posY, length, speed):
pass
def BulletToSpaceship(bullets, spaceShip, posX, posY, speed):
dx = spaceShip.actor.x - posX
dy = spaceShip.actor.y - posY
distance = math.sqrt(dx*dx + dy*dy)
if distance > 20:
bullets.append(posX, posY, dx / distance * (speed+3.0), dy / distance * (speed+3.0))
def SomeBulletsToSpaceship(bullets, spaceShip, posX, posY, length, speed):
if 0 == randint(0,300):
BulletToSpaceship(bullets, spaceShip, posX, posY, speed)
def MoreBulletsToSpaceship(bullets, spaceShip, posX, posY, length, speed):
if 0 == randint(0,100):
BulletToSpaceship(bullets, spaceShip, posX, posY, speed)
def BulletsBigBoss(bullets, spaceShip, posX, posY, length, speed):
if length < 360:
MoreBulletsToSpaceship(bullets, spaceShip, posX, posY, length, speed)
MoreBulletsToSpaceship(bullets, spaceShip, posX, posY, length, speed)
else:
if 0 == round(length*2) % 50:
for i in range(0, 360, 20):
bullets.append(posX, posY, 3.0 * math.sin(i / 180.0 * math.pi), 3.0 * math.cos(i / 180.0 * math.pi))
elif 0 == round(length*2) % 71:
for i in range(0, 360, 10):
bullets.append(posX, posY, 3.0 * math.sin(i / 180.0 * math.pi), 3.0 * math.cos(i / 180.0 * math.pi))
bulletStrategy = [NoBullets, MoreBulletsToSpaceship, MoreBulletsToSpaceship, SomeBulletsToSpaceship,
NoBullets, NoBullets, MoreBulletsToSpaceship, MoreBulletsToSpaceship,
MoreBulletsToSpaceship, BulletsBigBoss ]
def NoPowerup(powerups, posX, posY):
pass
def AddPowerup(powerups, posX, posY):
powerups.append(posX, posY)
powerUpStrategy = [AddPowerup, NoPowerup, NoPowerup, NoPowerup,
NoPowerup, NoPowerup, NoPowerup, NoPowerup,
NoPowerup, NoPowerup ]
class Enemy:
def __init__(self, index, startY):
self.index = index
self.startY = startY
self.actor = Actor(enemyNames[index], (800+200,startY))
self.actorWhite = Actor(enemyNames[index]+"_white", (800+200,startY))
self.positionInPath = 0.0
self.polygon = Polygon(enemyPolyCoords[index])
self.numberOfHits = enemyNumberOfHits[index]
self.lastHit = 0
def draw(self):
if self.lastHit == 0:
self.actor.draw()
else:
self.actorWhite.draw()
self.lastHit -= 1
def translate(self, posX, posY):
self.actor.x = self.actorWhite.x = posX
self.actor.y = self.actorWhite.y = posY
def update(self, bullets, spaceShip):
self.positionInPath += enemySpeeds[self.index]
active, position = enemyMovement[self.index](enemyPaths[self.index], self.positionInPath)
if active:
self.translate(position[0], position[1] + self.startY)
bulletStrategy[self.index](bullets, spaceShip, position[0], position[1] + self.startY, self.positionInPath, enemySpeeds[self.index])
return active
def hit(self, powerups):
self.numberOfHits -= 1
if self.numberOfHits <= 0:
powerUpStrategy[self.index](powerups, self.actor.x, self.actor.y)
return True
self.lastHit = 5
return False
enemyStart = [[5, 275, 245], [5, 261, 315], [5, 245, 381], [5, 230, 454], [5, 210, 529],
[5, 513, 650], [5, 496, 738], [5, 483, 805], [5, 465, 877], [5, 445, 944],
[0, 106, 1000], [7, 188, 1190], [7, 208, 1264], [7, 221, 1346], [7, 237, 1436],
[7, 253, 1509], [0, 106, 1833], [1, 444, 2000], [1, 444, 2080], [1, 444, 2160],
[4, 122, 2602], [4, 173, 2659], [4, 232, 2728], [4, 297, 2798], [4, 353, 2879],
[4, 298, 2968], [4, 223, 3046], [4, 160, 3120], [1, 444, 3620], [1, 444, 3700],
[1, 444, 3780], [2, 94, 3960], [2, 94, 4040], [2, 94, 4120], [5, 441, 4489],
[5, 441, 4573], [5, 180, 4760], [5, 180, 4807], [5, 180, 4860], [5, 441, 4971],
[5, 441, 5016], [5, 441, 5080], [4, 140, 5416], [4, 408, 5540], [4, 133, 5618],
[4, 419, 5694], [5, 233, 5908], [7, 157, 6457], [7, 171, 6558], [7, 198, 6617],
[7, 234, 6673], [3, 385, 7027], [3, 378, 7154], [3, 352, 7238], [5, 392, 7564],
[5, 339, 7597], [5, 281, 7631], [5, 237, 7678], [5, 194, 7780], [6, 94, 7987],
[6, 114, 8175], [6, 161, 8300], [6, 209, 8484], [1, 252, 9000], [1, 252, 9080],
[1, 252, 9160], [1, 252, 9240], [1, 252, 9320], [5, 192, 9688], [5, 174, 9731],
[5, 150, 9782], [5, 130, 9835], [5, 111, 9899], [0, 164, 10644], [3, 480, 10903],
[3, 431, 10943], [3, 394, 10979], [3, 350, 11017], [3, 292, 11071], [8, 117, 11392],
[8, 125, 11507], [8, 151, 11611], [8, 179, 11756], [8, 131, 11988], [8, 100, 12098],
[8, 68, 12249], [8, 97, 12413], [0, 189, 12842], [5, 190, 13524], [5, 210, 13561],
[5, 231, 13598], [5, 247, 13637], [5, 488, 13686], [5, 454, 13712], [5, 432, 13745],
[5, 414, 13769], [5, 150, 13811], [5, 438, 13845], [5, 147, 13882], [5, 433, 13919],
[5, 143, 13958], [5, 428, 13992], [5, 153, 14033], [5, 416, 14075], [8, 138, 14288],
[8, 151, 14327], [8, 138, 14381], [8, 121, 14415], [8, 93, 14461], [0, 181, 14915],
[9, 300, 15369]]
class EnemyList:
def __init__(self):
self.listEnemies = []
self.nextEnemyIndex = 0
self.allPolygons = []
def draw(self):
for enemy in self.listEnemies:
enemy.draw()
def update(self, globalTime, bullets, spaceShip):
for i in reversed(range(len(self.listEnemies))):
active = self.listEnemies[i].update(bullets, spaceShip)
if not active:
del self.listEnemies[i]
if self.nextEnemyIndex < len(enemyStart):
if globalTime >= enemyStart[self.nextEnemyIndex][2]:
self.listEnemies.append(Enemy(enemyStart[self.nextEnemyIndex][0], enemyStart[self.nextEnemyIndex][1]))
self.nextEnemyIndex += 1
self.allPolygons = []
for enemy in self.listEnemies:
polygon = translate(enemy.polygon, enemy.actor.x, enemy.actor.y)
self.allPolygons.append(polygon)
def destroyEnemy(self, index, explosions):
explosions.append(1, self.listEnemies[index].actor.x, self.listEnemies[index].actor.y)
del self.listEnemies[index]
del self.allPolygons[index]
def laserHit(self, index, powerups, explosions):
destroyed = self.listEnemies[index].hit(powerups)
if destroyed:
self.destroyEnemy(index, explosions)
shipName = 'spaceship'
shipPolyCoords = [(-48, -5), (4, -9), (10, -3), (40, 1), (19, 5), (-1, 5), (-1, 18), (-37, 18)]
class SpaceShip:
def __init__(self):
self.actor = Actor(shipName, (100,300))
self.centredPolygon = Polygon(shipPolyCoords)
self.polygon = translate(self.centredPolygon, self.actor.x, self.actor.y)
self.numberOfLasers = 1
self.vulnerable = True
self.lastHit = 0
self.numberOfLives = 3
def draw(self):
if self.vulnerable:
self.actor.draw()
elif self.lastHit < 120 and 0 == (self.lastHit // 3) % 2:
self.actor.draw()
def update(self):
if not self.vulnerable:
self.lastHit -= 1
if self.lastHit == 0:
self.vulnerable = True
def translate(self, dispX, dispY):
self.actor.x += dispX
self.actor.y += dispY
if self.actor.x < 45:
self.actor.x = 45
elif self.actor.x > 800-48:
self.actor.x = 800-48
if self.actor.y < 9:
self.actor.y = 9
elif self.actor.y > 600-18:
self.actor.y = 600-18
self.polygon = translate(self.centredPolygon, self.actor.x, self.actor.y)
def shootLaser(self, lasers):
if self.numberOfLives != 0 and self.lastHit < 120:
startY = (self.numberOfLasers - 1) / 2 * 10
for i in range(self.numberOfLasers):
lasers.addLaser((self.actor.x + 20, self.actor.y+2 - startY + i * 10))
def hit(self, explosions, showScore):
if self.vulnerable:
self.numberOfLives -= 1
showScore.update(self.numberOfLives)
self.lastHit = 180
self.numberOfLasers = 1
explosions.append(1, self.actor.x, self.actor.y)
self.actor.x = 100
self.actor.y = 300
self.polygon = translate(self.centredPolygon, self.actor.x, self.actor.y)
self.vulnerable = False
class Lasers:
def __init__(self):
self.allLasers = []
def draw(self):
for laser in self.allLasers:
laser.draw()
def update(self):
for i in reversed(range(len(self.allLasers))):
self.allLasers[i].x += 20
if self.allLasers[i].x > 800+10:
del self.allLasers[i]
def addLaser(self, position):
self.allLasers.append(Actor('laser', position))
class PowerUps():
def __init__(self):
self.powerups = []
def draw(self):
for powerup in self.powerups:
powerup.draw()
def update(self):
for i in reversed(range(len(self.powerups))):
self.powerups[i].x -= 1
if self.powerups[i].x < -16:
del self.powerups[i]
def append(self, posX, posY):
self.powerups.append(Actor("plus", (posX, posY)))
class ShowScore():
def __init__(self, number):
self.ship = Actor("lives", (37, 20))
self.number1 = Actor("text_0", (80, 20))
self.number2 = Actor("text_"+str(number), (108, 20))
def draw(self):
self.ship.draw()
self.number1.draw()
self.number2.draw()
def update(self, number):
self.number2.image = "text_"+str(number)
explosionNames = ["impact_", "explosion_"]
explosionFrames = [8, 10]
class Explosion():
def __init__(self, index, posX, posY):
self.index = index
self.actor = Actor(explosionNames[index]+"0", (posX, posY))
self.imageIndex = 0
self.frame = 0
def draw(self):
self.actor.draw()
def update(self):
self.frame += 1
if 0 == self.frame % 6:
self.imageIndex += 1
if self.imageIndex < explosionFrames[self.index]:
self.actor.image = explosionNames[self.index]+str(self.imageIndex)
else:
return False
return True
class ExplosionList():
def __init__(self):
self.explosions = []
def draw(self):
for explosion in self.explosions:
explosion.draw()
def update(self):
for i in reversed(range(len(self.explosions))):
active = self.explosions[i].update()
if not active:
del self.explosions[i]
def append(self, index, posX, posY):
self.explosions.append(Explosion(index, posX, posY))
def collisionShipBullets(bullets, spaceShip, explosions, showScore):
if bullets.collide(spaceShip.polygon):
spaceShip.hit(explosions, showScore)
def collisionBulletsForeground(bullets, foreground, explosions):
for i in reversed(range(len(bullets.bullets))):
for poly in foreground.polygons:
point = Point(bullets.bullets[i].actor.x, bullets.bullets[i].actor.y)
if point.within(poly):
explosions.append(0, point.x, point.y)
del bullets.bullets[i]
break
def collisionLaserForeground(lasers, foreground, explosions):
for j in reversed(range(len(lasers.allLasers))):
laserLine = LineString([(lasers.allLasers[j].x - 26, lasers.allLasers[j].y),
(lasers.allLasers[j].x + 26, lasers.allLasers[j].y)])
for i in reversed(range(len(foreground.polygons))):
if not laserLine.disjoint(foreground.polygons[i]):
explosions.append(0, lasers.allLasers[j].x + 26, lasers.allLasers[j].y)
del lasers.allLasers[j]
break
def collisionLaserEnemies(lasers, enemyList, explosions, powerups):
for j in reversed(range(len(lasers.allLasers))):
laserLine = LineString([(lasers.allLasers[j].x - 26, lasers.allLasers[j].y),
(lasers.allLasers[j].x + 26, lasers.allLasers[j].y)])
for i in reversed(range(len(enemyList.allPolygons))):
if not laserLine.disjoint(enemyList.allPolygons[i]):
explosions.append(0, lasers.allLasers[j].x + 26, lasers.allLasers[j].y)
enemyList.laserHit(i, powerups, explosions)
del lasers.allLasers[j]
break
def collisionShipPolygons(spaceShip, polygons):
for i in reversed(range(len(polygons))):
if not spaceShip.polygon.disjoint(polygons[i]):
return True, i
return False, 0
def collisionShipForeground(spaceShip, foreground, explosions, showScore):
hit, index = collisionShipPolygons(spaceShip, foreground.polygons)
if hit:
spaceShip.hit(explosions, showScore)
def collisionShipEnemies(spaceShip, enemyList, explosions, showScore, powerups):
hit, index = collisionShipPolygons(spaceShip, enemyList.allPolygons)
if hit:
spaceShip.hit(explosions, showScore)
enemyList.laserHit(index, powerups, explosions)
def collisionShipPowerUps(spaceShip, powerups):
for i in reversed(range(len(powerups.powerups))):
polygon = Polygon([(powerups.powerups[i].x-16, powerups.powerups[i].y-16),
(powerups.powerups[i].x-16, powerups.powerups[i].y+16),
(powerups.powerups[i].x+16, powerups.powerups[i].y+16),
(powerups.powerups[i].x+16, powerups.powerups[i].y-16)])
if not spaceShip.polygon.disjoint(polygon):
spaceShip.numberOfLasers += 1
del powerups.powerups[i]
foreground = Foreground()
bullets = AllBullets()
enemyList = EnemyList()
spaceShip = SpaceShip()
lasers = Lasers()
powerups = PowerUps()
showScore = ShowScore(spaceShip.numberOfLives)
explosions = ExplosionList()
Status = Enum('Status', 'PLAYING GAME_OVER LEVEL_CLEAR')
gameStatus = Status.PLAYING
globalTime = 0
def draw():
if isFastComputer:
drawBackground(screen, globalTime)
else:
screen.clear()
foreground.draw()
enemyList.draw()
if gameStatus != Status.GAME_OVER:
spaceShip.draw()
powerups.draw()
lasers.draw()
bullets.draw()
explosions.draw()
showScore.draw()
if gameStatus == Status.GAME_OVER:
screen.draw.text("GAME OVER", center=(400, 260), fontsize=55, color=(255,0,0))
elif gameStatus == Status.LEVEL_CLEAR:
screen.draw.text("LEVEL CLEAR", center=(400, 260), fontsize=55, color=(0,0,255))
def update():
global globalTime, gameStatus
globalTime += 1
bullets.update()
lasers.update()
powerups.update()
foreground.update(globalTime)
enemyList.update(globalTime, bullets, spaceShip)
if gameStatus != Status.GAME_OVER:
if keyboard.left: spaceShip.translate(-4, 0)
if keyboard.right: spaceShip.translate( 4, 0)
if keyboard.up: spaceShip.translate( 0, -4)
if keyboard.down: spaceShip.translate( 0, 4)
spaceShip.update()
explosions.update()
collisionBulletsForeground(bullets, foreground, explosions)
collisionLaserForeground(lasers, foreground, explosions)
collisionLaserEnemies(lasers, enemyList, explosions, powerups)
if gameStatus == Status.PLAYING:
collisionShipBullets(bullets, spaceShip, explosions, showScore)
collisionShipForeground(spaceShip, foreground, explosions, showScore)
collisionShipEnemies(spaceShip, enemyList, explosions, showScore, powerups)
collisionShipPowerUps(spaceShip, powerups)
if spaceShip.numberOfLives == 0:
gameStatus = Status.GAME_OVER
elif globalTime > enemyStart[-1][2] + 10 and len(enemyList.listEnemies) == 0:
gameStatus = Status.LEVEL_CLEAR
def on_key_down(key):
if key == keys.SPACE:
spaceShip.shootLaser(lasers)
pgzrun.go()