├── Arcade Explo A.wav ├── DST-AngryMod.mp3 ├── README.md ├── enemy.png ├── evilFighter.png ├── explosion.png ├── explosion_strip16.png ├── foo-0.png ├── foo-1.png ├── foo-2.png ├── game.py ├── ship.png └── x.png /Arcade Explo A.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/Arcade Explo A.wav -------------------------------------------------------------------------------- /DST-AngryMod.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/DST-AngryMod.mp3 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a simple vertical shooter game similar to old games like Xenon. 2 | 3 | It was used a demo for a public session on programming simple games 4 | conducted at [The 5 | Lycaeum](http://thelycaeum.in/blog/2014/09/01/public_sessions/) in 6 | Sep/2014. 7 | 8 | The whole game was built from scratch in about two hours describing 9 | the process. 10 | 11 | -------------------------------------------------------------------------------- /enemy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/enemy.png -------------------------------------------------------------------------------- /evilFighter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/evilFighter.png -------------------------------------------------------------------------------- /explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/explosion.png -------------------------------------------------------------------------------- /explosion_strip16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/explosion_strip16.png -------------------------------------------------------------------------------- /foo-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/foo-0.png -------------------------------------------------------------------------------- /foo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/foo-1.png -------------------------------------------------------------------------------- /foo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/foo-2.png -------------------------------------------------------------------------------- /game.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import random 5 | import sys 6 | 7 | import pygame 8 | from pygame.locals import Rect, DOUBLEBUF, QUIT, K_ESCAPE, KEYDOWN, K_DOWN, \ 9 | K_LEFT, K_UP, K_RIGHT, KEYUP, K_LCTRL, K_RETURN, FULLSCREEN 10 | 11 | X_MAX = 800 12 | Y_MAX = 600 13 | 14 | LEFT, RIGHT, UP, DOWN = 0, 1, 3, 4 15 | START, STOP = 0, 1 16 | 17 | everything = pygame.sprite.Group() 18 | 19 | 20 | class Explosion(pygame.sprite.Sprite): 21 | def __init__(self, x, y): 22 | super(Explosion, self).__init__() 23 | sheet = pygame.image.load("x.png") 24 | self.images = [] 25 | for i in range(0, 768, 48): 26 | rect = pygame.Rect((i, 0, 48, 48)) 27 | image = pygame.Surface(rect.size) 28 | image.blit(sheet, (0, 0), rect) 29 | self.images.append(image) 30 | 31 | self.image = self.images[0] 32 | self.index = 0 33 | self.rect = self.image.get_rect() 34 | self.rect.center = (x, y) 35 | self.add(everything) 36 | 37 | def update(self): 38 | self.image = self.images[self.index] 39 | self.index += 1 40 | if self.index >= len(self.images): 41 | self.kill() 42 | 43 | 44 | class Star(pygame.sprite.Sprite): 45 | def __init__(self, x, y): 46 | super(Star, self).__init__() 47 | self.image = pygame.Surface((2, 2)) 48 | pygame.draw.circle(self.image, 49 | (128, 128, 200), 50 | (0, 0), 51 | 2, 52 | 0) 53 | self.rect = self.image.get_rect() 54 | self.rect.center = (x, y) 55 | self.velocity = 1 56 | self.size = 1 57 | self.colour = 128 58 | 59 | def accelerate(self): 60 | self.image = pygame.Surface((1, self.size)) 61 | 62 | if self.size < 200: 63 | self.size += 4 64 | self.colour += 20 65 | if self.colour >= 200: 66 | self.colour = random.randint(180, 200) 67 | else: 68 | self.colour -= 30 69 | if self.colour <= 20: 70 | self.colour = random.randrange(20) 71 | 72 | pygame.draw.line(self.image, (self.colour, self.colour, self.colour), 73 | (0, 0), (0, self.size)) 74 | 75 | if self.velocity < Y_MAX / 3: 76 | self.velocity += 1 77 | 78 | # x, y = self.rect.center 79 | # self.rect.center = random.randrange(X_MAX), y 80 | 81 | def update(self): 82 | x, y = self.rect.center 83 | if self.rect.center[1] > Y_MAX: 84 | self.rect.center = (x, 0) 85 | else: 86 | self.rect.center = (x, y + self.velocity) 87 | 88 | 89 | class BulletSprite(pygame.sprite.Sprite): 90 | def __init__(self, x, y): 91 | super(BulletSprite, self).__init__() 92 | self.image = pygame.Surface((10, 10)) 93 | for i in range(5, 0, -1): 94 | color = 255.0 * float(i)/5 95 | pygame.draw.circle(self.image, 96 | (0, 0, color), 97 | (5, 5), 98 | i, 99 | 0) 100 | self.rect = self.image.get_rect() 101 | self.rect.center = (x, y-25) 102 | 103 | def update(self): 104 | x, y = self.rect.center 105 | y -= 20 106 | self.rect.center = x, y 107 | if y <= 0: 108 | self.kill() 109 | 110 | 111 | class EnemySprite(pygame.sprite.Sprite): 112 | def __init__(self, x_pos, groups): 113 | super(EnemySprite, self).__init__() 114 | self.image = pygame.image.load("enemy.png").convert_alpha() 115 | self.rect = self.image.get_rect() 116 | self.rect.center = (x_pos, 0) 117 | 118 | self.velocity = random.randint(3, 10) 119 | 120 | self.add(groups) 121 | self.explosion_sound = pygame.mixer.Sound("Arcade Explo A.wav") 122 | self.explosion_sound.set_volume(0.4) 123 | 124 | def update(self): 125 | x, y = self.rect.center 126 | 127 | if y > Y_MAX: 128 | x, y = random.randint(0, X_MAX), 0 129 | self.velocity = random.randint(3, 10) 130 | else: 131 | x, y = x, y + self.velocity 132 | 133 | self.rect.center = x, y 134 | 135 | def kill(self): 136 | x, y = self.rect.center 137 | if pygame.mixer.get_init(): 138 | self.explosion_sound.play(maxtime=1000) 139 | Explosion(x, y) 140 | super(EnemySprite, self).kill() 141 | 142 | 143 | class StatusSprite(pygame.sprite.Sprite): 144 | def __init__(self, ship, groups): 145 | super(StatusSprite, self).__init__() 146 | self.image = pygame.Surface((X_MAX, 30)) 147 | self.rect = self.image.get_rect() 148 | self.rect.bottomleft = 0, Y_MAX 149 | 150 | default_font = pygame.font.get_default_font() 151 | self.font = pygame.font.Font(default_font, 20) 152 | 153 | self.ship = ship 154 | self.add(groups) 155 | 156 | def update(self): 157 | score = self.font.render("Health : {} Score : {}".format( 158 | self.ship.health, self.ship.score), True, (150, 50, 50)) 159 | self.image.fill((0, 0, 0)) 160 | self.image.blit(score, (0, 0)) 161 | 162 | 163 | class ShipSprite(pygame.sprite.Sprite): 164 | def __init__(self, groups, weapon_groups): 165 | super(ShipSprite, self).__init__() 166 | self.image = pygame.image.load("ship.png").convert_alpha() 167 | self.rect = self.image.get_rect() 168 | self.rect.center = (X_MAX/2, Y_MAX - 40) 169 | self.dx = self.dy = 0 170 | self.firing = self.shot = False 171 | self.health = 100 172 | self.score = 0 173 | 174 | self.groups = [groups, weapon_groups] 175 | 176 | self.mega = 1 177 | 178 | self.autopilot = False 179 | self.in_position = False 180 | self.velocity = 2 181 | 182 | def update(self): 183 | x, y = self.rect.center 184 | 185 | if not self.autopilot: 186 | # Handle movement 187 | self.rect.center = x + self.dx, y + self.dy 188 | 189 | # Handle firing 190 | if self.firing: 191 | self.shot = BulletSprite(x, y) 192 | self.shot.add(self.groups) 193 | 194 | if self.health < 0: 195 | self.kill() 196 | else: 197 | if not self.in_position: 198 | if x != X_MAX/2: 199 | x += (abs(X_MAX/2 - x)/(X_MAX/2 - x)) * 2 200 | if y != Y_MAX - 100: 201 | y += (abs(Y_MAX - 100 - y)/(Y_MAX - 100 - y)) * 2 202 | 203 | if x == X_MAX/2 and y == Y_MAX - 100: 204 | self.in_position = True 205 | else: 206 | y -= self.velocity 207 | self.velocity *= 1.5 208 | if y <= 0: 209 | y = -30 210 | self.rect.center = x, y 211 | 212 | def steer(self, direction, operation): 213 | v = 10 214 | if operation == START: 215 | if direction in (UP, DOWN): 216 | self.dy = {UP: -v, 217 | DOWN: v}[direction] 218 | 219 | if direction in (LEFT, RIGHT): 220 | self.dx = {LEFT: -v, 221 | RIGHT: v}[direction] 222 | 223 | if operation == STOP: 224 | if direction in (UP, DOWN): 225 | self.dy = 0 226 | if direction in (LEFT, RIGHT): 227 | self.dx = 0 228 | 229 | def shoot(self, operation): 230 | if operation == START: 231 | self.firing = True 232 | if operation == STOP: 233 | self.firing = False 234 | 235 | 236 | def create_starfield(group): 237 | stars = [] 238 | for i in range(100): 239 | x, y = random.randrange(X_MAX), random.randrange(Y_MAX) 240 | s = Star(x, y) 241 | s.add(group) 242 | stars.append(s) 243 | return stars 244 | 245 | 246 | def main(): 247 | game_over = False 248 | 249 | pygame.font.init() 250 | pygame.mixer.init() 251 | screen = pygame.display.set_mode((X_MAX, Y_MAX), DOUBLEBUF) 252 | enemies = pygame.sprite.Group() 253 | weapon_fire = pygame.sprite.Group() 254 | 255 | empty = pygame.Surface((X_MAX, Y_MAX)) 256 | clock = pygame.time.Clock() 257 | 258 | stars = create_starfield(everything) 259 | 260 | ship = ShipSprite(everything, weapon_fire) 261 | ship.add(everything) 262 | 263 | status = StatusSprite(ship, everything) 264 | 265 | deadtimer = 30 266 | credits_timer = 250 267 | 268 | for i in range(10): 269 | pos = random.randint(0, X_MAX) 270 | EnemySprite(pos, [everything, enemies]) 271 | 272 | # Get some music 273 | if pygame.mixer.get_init(): 274 | pygame.mixer.music.load("DST-AngryMod.mp3") 275 | pygame.mixer.music.set_volume(0.8) 276 | pygame.mixer.music.play(-1) 277 | 278 | while True: 279 | clock.tick(30) 280 | # Check for input 281 | for event in pygame.event.get(): 282 | if event.type == QUIT or ( 283 | event.type == KEYDOWN and event.key == K_ESCAPE): 284 | sys.exit() 285 | if not game_over: 286 | if event.type == KEYDOWN: 287 | if event.key == K_DOWN: 288 | ship.steer(DOWN, START) 289 | if event.key == K_LEFT: 290 | ship.steer(LEFT, START) 291 | if event.key == K_RIGHT: 292 | ship.steer(RIGHT, START) 293 | if event.key == K_UP: 294 | ship.steer(UP, START) 295 | if event.key == K_LCTRL: 296 | ship.shoot(START) 297 | if event.key == K_RETURN: 298 | if ship.mega: 299 | ship.mega -= 1 300 | for i in enemies: 301 | i.kill() 302 | 303 | if event.type == KEYUP: 304 | if event.key == K_DOWN: 305 | ship.steer(DOWN, STOP) 306 | if event.key == K_LEFT: 307 | ship.steer(LEFT, STOP) 308 | if event.key == K_RIGHT: 309 | ship.steer(RIGHT, STOP) 310 | if event.key == K_UP: 311 | ship.steer(UP, STOP) 312 | if event.key == K_LCTRL: 313 | ship.shoot(STOP) 314 | 315 | # Check for impact 316 | hit_ships = pygame.sprite.spritecollide(ship, enemies, True) 317 | for i in hit_ships: 318 | ship.health -= 15 319 | 320 | if ship.health < 0: 321 | if deadtimer: 322 | deadtimer -= 1 323 | else: 324 | sys.exit() 325 | 326 | # Check for successful attacks 327 | hit_ships = pygame.sprite.groupcollide( 328 | enemies, weapon_fire, True, True) 329 | for k, v in hit_ships.iteritems(): 330 | k.kill() 331 | for i in v: 332 | i.kill() 333 | ship.score += 10 334 | 335 | if len(enemies) < 20 and not game_over: 336 | pos = random.randint(0, X_MAX) 337 | EnemySprite(pos, [everything, enemies]) 338 | 339 | # Check for game over 340 | if ship.score > 1000: 341 | game_over = True 342 | for i in enemies: 343 | i.kill() 344 | 345 | ship.autopilot = True 346 | ship.shoot(STOP) 347 | 348 | if game_over: 349 | pygame.mixer.music.fadeout(8000) 350 | for i in stars: 351 | i.accelerate() 352 | if credits_timer: 353 | credits_timer -= 1 354 | else: 355 | sys.exit() 356 | 357 | # Update sprites 358 | everything.clear(screen, empty) 359 | everything.update() 360 | everything.draw(screen) 361 | pygame.display.flip() 362 | 363 | 364 | if __name__ == '__main__': 365 | main() 366 | -------------------------------------------------------------------------------- /ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/ship.png -------------------------------------------------------------------------------- /x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLycaeum/pygame/42b9b9bd8f062dbcea88cec4ff2354594eed7783/x.png --------------------------------------------------------------------------------