spaceship shooter demo


/ Published in: Python
Save to your folder(s)

Code is GPL

Left/Right to rotate ship, Up for thrust, z to fire.
a to toggle anti-aliasing

debug - f for FPS

by Avinash Vora


Copy this code and paste it in your HTML
  1. # spaceship shooter test
  2. #
  3. # Copyright (c) 2007, Avinash Vora
  4. # http://www.avinashv.net
  5. #
  6. # Source code is protected by the GNU GPL
  7. #
  8. # May 26
  9. # - added randomly generated enemies
  10. # - added engine class
  11. # May 25
  12. # - added maximum ship speed
  13. # - added anti-aliasing toggle
  14. # - changed friction from 0.0075 to 0.01
  15. # - implemented bullets and physics
  16. # - fully object-oriented code
  17. # May 24
  18. # - added FPS read-out
  19. # - implemented ship and physics
  20.  
  21. import pygame, math, sys, random
  22. from pygame.locals import *
  23.  
  24. black = 0, 0, 0
  25. white = 255, 255, 255
  26. red = 255, 0, 0
  27. green = 0, 255, 0
  28.  
  29. d2p = lambda x1, y1, x2, y2: math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
  30.  
  31. class engine:
  32. def __init__(self, width, height):
  33. self.size = self.width, self.height = width, height
  34. self.running = True
  35. self.show_fps = False
  36. self.aaline = False
  37.  
  38. self.enemies = []
  39. self.ships = []
  40.  
  41. # state of keypresses
  42. self.right_pressed, self.left_pressed, self.up_pressed = False, False, False
  43. self.firing = False
  44.  
  45. pygame.init()
  46.  
  47. self.window = pygame.display.set_mode(self.size)
  48. self.window.fill(black)
  49.  
  50. def global_draw(self):
  51. for ship in self.ships:
  52. ship.draw()
  53. for bullet in ship.bullets:
  54. bullet.draw()
  55.  
  56. for enemy in self.enemies:
  57. enemy.draw()
  58.  
  59. def global_physics(self):
  60. # apply physics to every ship and bullet
  61. for ship in self.ships:
  62. ship.physics()
  63.  
  64. for bullet in ship.bullets:
  65. bullet.physics()
  66.  
  67. # check boundaries and destroy bullet if outside
  68. if (bullet.x > self.width) or (bullet.x < 0) or (bullet.y > self.height) or (bullet.y < 0):
  69. ship.bullets.remove(bullet)
  70. continue
  71.  
  72. # destroy bullets if they are older than 1000 ms
  73. if bullet.death <= pygame.time.get_ticks():
  74. ship.bullets.remove(bullet)
  75. continue
  76.  
  77. # check for collision
  78. for enemy in self.enemies:
  79. if d2p(bullet.x, bullet.y, enemy.x, enemy.y) < (bullet.radius + enemy.radius):
  80. enemy.health = enemy.health - bullet.strength
  81. ship.bullets.remove(bullet)
  82. if enemy.health <= 0:
  83. self.enemies.remove(enemy)
  84.  
  85. class bullet_class:
  86. def __init__(self, engine, parent):
  87. self.engine = engine
  88. self.parent = parent
  89. self.x_comp, self.y_comp = 0, 0
  90. self.x, self.y = self.parent.x, self.parent.y
  91. self.heading = self.parent.heading
  92. self.facing = self.parent.facing
  93. self.speed = 4.5
  94. self.radius = 4
  95. self.decay = 1000
  96. self.death = pygame.time.get_ticks() + self.decay
  97. self.strength = 3
  98.  
  99. # calculate resultant x and y components
  100. self.x_comp = self.parent.speed * math.sin(self.heading) + self.speed * math.sin(self.facing)
  101. self.y_comp = self.parent.speed * math.cos(self.heading) + self.speed * math.cos(self.facing)
  102.  
  103. # determine resultant speed
  104. self.speed= math.sqrt(self.x_comp ** 2 + self.y_comp ** 2)
  105.  
  106. # calculate resultant heading
  107. if self.y_comp >= 0:
  108. self.heading = math.atan(self.x_comp / self.y_comp)
  109. elif self.y_comp < 0:
  110. self.heading = math.atan(self.x_comp / self.y_comp) + math.pi
  111.  
  112. def draw(self):
  113. if self.engine.aaline:
  114. pygame.draw.circle(self.engine.window, green, (int(self.x), int(self.y)), self.radius, 1)
  115. else:
  116. pygame.draw.circle(self.engine.window, green, (int(self.x), int(self.y)), self.radius, 2)
  117.  
  118. def physics(self):
  119. # adjust (x, y) according to speed
  120. self.x = self.x + self.speed * math.sin(self.heading)
  121. self.y = self.y - self.speed * math.cos(self.heading)
  122.  
  123. class enemy_class:
  124. def __init__(self, engine, health):
  125. self.engine = engine
  126. self.x, self.y = random.randint(1, self.engine.width), random.randint(1, self.engine.height)
  127. self.health = health
  128. self.radius = self.health
  129.  
  130. def draw(self):
  131. if self.engine.aaline:
  132. pygame.draw.circle(self.engine.window, white, (int(self.x), int(self.y)), self.radius, 1)
  133. else:
  134. pygame.draw.circle(self.engine.window, white, (int(self.x), int(self.y)), self.radius, 2)
  135.  
  136. class spaceship_class:
  137. def __init__(self, engine):
  138. self.engine = engine
  139. self.accel = 0.06
  140. self.friction = 0.01
  141. self.max_speed = 4
  142. self.rotation_rate = 5
  143. self.x, self.y = self.engine.width / 2, self.engine.height / 2
  144. self.x_comp, self.y_comp = 0, 0
  145. self.facing = 0
  146. self.heading = 0
  147. self.speed = 0
  148. self.radius = 10
  149. self.bullet_delay = 200
  150. self.last_bullet_time = 0
  151.  
  152. # holds all the bullets created by this instance
  153. self.bullets = []
  154.  
  155. def draw(self):
  156. # calculate vertices of the ship
  157. vertices = [
  158. (self.x + self.radius * math.sin(self.facing), self.y - self.radius * math.cos(self.facing)),
  159. (self.x + self.radius * math.sin(self.facing + 2 * math.pi / 3), self.y - self.radius * math.cos(self.facing + 2 * math.pi / 3)),
  160. (self.x + self.radius * math.sin(self.facing + 4 * math.pi / 3), self.y - self.radius * math.cos(self.facing + 4 * math.pi / 3))
  161. ]
  162.  
  163. # draw lines with the back of the ship as red (anti-aliased if needed)
  164. if self.engine.aaline:
  165. pygame.draw.aaline(self.engine.window, white, vertices[0], vertices[1])
  166. pygame.draw.aaline(self.engine.window, white, vertices[2], vertices[0])
  167. pygame.draw.aaline(self.engine.window, red, vertices[1], vertices[2])
  168. else:
  169. pygame.draw.line(self.engine.window, white, vertices[0], vertices[1], 2)
  170. pygame.draw.line(self.engine.window, white, vertices[2], vertices[0], 2)
  171. pygame.draw.line(self.engine.window, red, vertices[1], vertices[2], 2)
  172.  
  173. def physics(self):
  174. if self.engine.right_pressed:
  175. # determine new direction
  176. self.facing = self.facing + (self.rotation_rate * math.pi) / 180
  177.  
  178. if self.engine.left_pressed:
  179. # determine new direction
  180. self.facing = self.facing - (self.rotation_rate * math.pi) / 180
  181.  
  182. if self.engine.up_pressed:
  183. # determine resultant x and y components
  184. self.x_comp = self.speed * math.sin(self.heading) + self.accel * math.sin(self.facing)
  185. self.y_comp = self.speed * math.cos(self.heading) + self.accel * math.cos(self.facing)
  186.  
  187. # determine resultant speed
  188. if self.speed < self.max_speed:
  189. self.speed = math.sqrt(self.x_comp ** 2 + self.y_comp ** 2)
  190. else:
  191. self.speed = self.max_speed
  192.  
  193. # calculate resultant heading
  194. if self.y_comp >= 0:
  195. self.heading = math.atan(self.x_comp / self.y_comp)
  196. elif self.y_comp < 0:
  197. self.heading = math.atan(self.x_comp / self.y_comp) + math.pi
  198.  
  199. # create new bullet
  200. if self.engine.firing:
  201. if pygame.time.get_ticks() >= (self.last_bullet_time + self.bullet_delay):
  202. self.bullets.append(bullet_class(self.engine, self))
  203. self.last_bullet_time = pygame.time.get_ticks()
  204.  
  205. # adjust speed for friction
  206. if self.speed > 0: self.speed = self.speed - self.friction
  207.  
  208. # adjust (x, y) according to speed
  209. self.x = self.x + self.speed * math.sin(self.heading)
  210. self.y = self.y - self.speed * math.cos(self.heading)
  211.  
  212. # check boundaries
  213. if self.x > self.engine.width: self.x = 0
  214. if self.x < 0: self.x = self.engine.width
  215. if self.y > self.engine.height: self.y = 0
  216. if self.y < 0: self.y = self.engine.height
  217.  
  218. if __name__ == '__main__':
  219. if not pygame.font: print 'Fonts disabled!'
  220. if not pygame.mixer: print 'Sound disabled!'
  221.  
  222. game = engine(640, 480)
  223. clock = pygame.time.Clock()
  224.  
  225. pygame.display.set_caption('shooter test')
  226.  
  227. game.ships.append(spaceship_class(game))
  228.  
  229. for item in range(1, random.randint(5, 15)): game.enemies.append(enemy_class(game, random.randint(5, 15)))
  230.  
  231. while game.running:
  232. # limit fps to 60
  233. clock.tick(60)
  234.  
  235. # clear screen
  236. game.window.fill(black)
  237.  
  238. # show fps
  239. if pygame.font and game.show_fps:
  240. font = pygame.font.Font(None, 16)
  241. text = font.render('fps ' + str(int(clock.get_fps())), 1, (150, 150, 150))
  242. text_position = text.get_rect(centerx = game.width / 2)
  243. game.window.blit(text, text_position)
  244.  
  245. # handle physics and drawing
  246. game.global_physics()
  247. game.global_draw()
  248.  
  249. # flip the screen
  250. pygame.display.update()
  251.  
  252. # handle input
  253. for event in pygame.event.get():
  254. if event.type == QUIT:
  255. running = False
  256.  
  257. # key down
  258. elif event.type == KEYDOWN:
  259. if event.key == K_ESCAPE:
  260. game.running = False
  261. elif event.key == K_RIGHT:
  262. game.right_pressed = True
  263. elif event.key == K_LEFT:
  264. game.left_pressed = True
  265. elif event.key == K_UP:
  266. game.up_pressed = True
  267. elif event.key == K_z:
  268. game.firing = True
  269. elif event.key == K_f:
  270. game.show_fps = not game.show_fps
  271. elif event.key == K_a:
  272. game.aaline = not game.aaline
  273.  
  274. # key up
  275. elif event.type == KEYUP:
  276. if event.key == K_RIGHT:
  277. game.right_pressed = False
  278. elif event.key == K_LEFT:
  279. game.left_pressed = False
  280. elif event.key == K_UP:
  281. game.up_pressed = False
  282. elif event.key == K_z:
  283. game.firing = False

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.