/ Published in: Python
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
Expand |
Embed | Plain Text
# spaceship shooter test # # Copyright (c) 2007, Avinash Vora # http://www.avinashv.net # # Source code is protected by the GNU GPL # # May 26 # - added randomly generated enemies # - added engine class # May 25 # - added maximum ship speed # - added anti-aliasing toggle # - changed friction from 0.0075 to 0.01 # - implemented bullets and physics # - fully object-oriented code # May 24 # - added FPS read-out # - implemented ship and physics import pygame, math, sys, random from pygame.locals import * black = 0, 0, 0 white = 255, 255, 255 red = 255, 0, 0 green = 0, 255, 0 d2p = lambda x1, y1, x2, y2: math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) class engine: def __init__(self, width, height): self.size = self.width, self.height = width, height self.running = True self.show_fps = False self.aaline = False self.enemies = [] self.ships = [] # state of keypresses self.right_pressed, self.left_pressed, self.up_pressed = False, False, False self.firing = False pygame.init() self.window = pygame.display.set_mode(self.size) self.window.fill(black) def global_draw(self): for ship in self.ships: ship.draw() for bullet in ship.bullets: bullet.draw() for enemy in self.enemies: enemy.draw() def global_physics(self): # apply physics to every ship and bullet for ship in self.ships: ship.physics() for bullet in ship.bullets: bullet.physics() # check boundaries and destroy bullet if outside if (bullet.x > self.width) or (bullet.x < 0) or (bullet.y > self.height) or (bullet.y < 0): ship.bullets.remove(bullet) continue # destroy bullets if they are older than 1000 ms if bullet.death <= pygame.time.get_ticks(): ship.bullets.remove(bullet) continue # check for collision for enemy in self.enemies: if d2p(bullet.x, bullet.y, enemy.x, enemy.y) < (bullet.radius + enemy.radius): enemy.health = enemy.health - bullet.strength ship.bullets.remove(bullet) if enemy.health <= 0: self.enemies.remove(enemy) class bullet_class: def __init__(self, engine, parent): self.engine = engine self.parent = parent self.x_comp, self.y_comp = 0, 0 self.x, self.y = self.parent.x, self.parent.y self.heading = self.parent.heading self.facing = self.parent.facing self.speed = 4.5 self.radius = 4 self.decay = 1000 self.death = pygame.time.get_ticks() + self.decay self.strength = 3 # calculate resultant x and y components self.x_comp = self.parent.speed * math.sin(self.heading) + self.speed * math.sin(self.facing) self.y_comp = self.parent.speed * math.cos(self.heading) + self.speed * math.cos(self.facing) # determine resultant speed self.speed= math.sqrt(self.x_comp ** 2 + self.y_comp ** 2) # calculate resultant heading if self.y_comp >= 0: self.heading = math.atan(self.x_comp / self.y_comp) elif self.y_comp < 0: self.heading = math.atan(self.x_comp / self.y_comp) + math.pi def draw(self): if self.engine.aaline: pygame.draw.circle(self.engine.window, green, (int(self.x), int(self.y)), self.radius, 1) else: pygame.draw.circle(self.engine.window, green, (int(self.x), int(self.y)), self.radius, 2) def physics(self): # adjust (x, y) according to speed self.x = self.x + self.speed * math.sin(self.heading) self.y = self.y - self.speed * math.cos(self.heading) class enemy_class: def __init__(self, engine, health): self.engine = engine self.x, self.y = random.randint(1, self.engine.width), random.randint(1, self.engine.height) self.health = health self.radius = self.health def draw(self): if self.engine.aaline: pygame.draw.circle(self.engine.window, white, (int(self.x), int(self.y)), self.radius, 1) else: pygame.draw.circle(self.engine.window, white, (int(self.x), int(self.y)), self.radius, 2) class spaceship_class: def __init__(self, engine): self.engine = engine self.accel = 0.06 self.friction = 0.01 self.max_speed = 4 self.rotation_rate = 5 self.x, self.y = self.engine.width / 2, self.engine.height / 2 self.x_comp, self.y_comp = 0, 0 self.facing = 0 self.heading = 0 self.speed = 0 self.radius = 10 self.bullet_delay = 200 self.last_bullet_time = 0 # holds all the bullets created by this instance self.bullets = [] def draw(self): # calculate vertices of the ship vertices = [ (self.x + self.radius * math.sin(self.facing), self.y - self.radius * math.cos(self.facing)), (self.x + self.radius * math.sin(self.facing + 2 * math.pi / 3), self.y - self.radius * math.cos(self.facing + 2 * math.pi / 3)), (self.x + self.radius * math.sin(self.facing + 4 * math.pi / 3), self.y - self.radius * math.cos(self.facing + 4 * math.pi / 3)) ] # draw lines with the back of the ship as red (anti-aliased if needed) if self.engine.aaline: pygame.draw.aaline(self.engine.window, white, vertices[0], vertices[1]) pygame.draw.aaline(self.engine.window, white, vertices[2], vertices[0]) pygame.draw.aaline(self.engine.window, red, vertices[1], vertices[2]) else: pygame.draw.line(self.engine.window, white, vertices[0], vertices[1], 2) pygame.draw.line(self.engine.window, white, vertices[2], vertices[0], 2) pygame.draw.line(self.engine.window, red, vertices[1], vertices[2], 2) def physics(self): if self.engine.right_pressed: # determine new direction self.facing = self.facing + (self.rotation_rate * math.pi) / 180 if self.engine.left_pressed: # determine new direction self.facing = self.facing - (self.rotation_rate * math.pi) / 180 if self.engine.up_pressed: # determine resultant x and y components self.x_comp = self.speed * math.sin(self.heading) + self.accel * math.sin(self.facing) self.y_comp = self.speed * math.cos(self.heading) + self.accel * math.cos(self.facing) # determine resultant speed if self.speed < self.max_speed: self.speed = math.sqrt(self.x_comp ** 2 + self.y_comp ** 2) else: self.speed = self.max_speed # calculate resultant heading if self.y_comp >= 0: self.heading = math.atan(self.x_comp / self.y_comp) elif self.y_comp < 0: self.heading = math.atan(self.x_comp / self.y_comp) + math.pi # create new bullet if self.engine.firing: if pygame.time.get_ticks() >= (self.last_bullet_time + self.bullet_delay): self.bullets.append(bullet_class(self.engine, self)) self.last_bullet_time = pygame.time.get_ticks() # adjust speed for friction if self.speed > 0: self.speed = self.speed - self.friction # adjust (x, y) according to speed self.x = self.x + self.speed * math.sin(self.heading) self.y = self.y - self.speed * math.cos(self.heading) # check boundaries if self.x > self.engine.width: self.x = 0 if self.x < 0: self.x = self.engine.width if self.y > self.engine.height: self.y = 0 if self.y < 0: self.y = self.engine.height if __name__ == '__main__': if not pygame.font: print 'Fonts disabled!' if not pygame.mixer: print 'Sound disabled!' game = engine(640, 480) clock = pygame.time.Clock() pygame.display.set_caption('shooter test') game.ships.append(spaceship_class(game)) for item in range(1, random.randint(5, 15)): game.enemies.append(enemy_class(game, random.randint(5, 15))) while game.running: # limit fps to 60 clock.tick(60) # clear screen game.window.fill(black) # show fps if pygame.font and game.show_fps: font = pygame.font.Font(None, 16) text = font.render('fps ' + str(int(clock.get_fps())), 1, (150, 150, 150)) text_position = text.get_rect(centerx = game.width / 2) game.window.blit(text, text_position) # handle physics and drawing game.global_physics() game.global_draw() # flip the screen pygame.display.update() # handle input for event in pygame.event.get(): if event.type == QUIT: running = False # key down elif event.type == KEYDOWN: if event.key == K_ESCAPE: game.running = False elif event.key == K_RIGHT: game.right_pressed = True elif event.key == K_LEFT: game.left_pressed = True elif event.key == K_UP: game.up_pressed = True elif event.key == K_z: game.firing = True elif event.key == K_f: game.show_fps = not game.show_fps elif event.key == K_a: game.aaline = not game.aaline # key up elif event.type == KEYUP: if event.key == K_RIGHT: game.right_pressed = False elif event.key == K_LEFT: game.left_pressed = False elif event.key == K_UP: game.up_pressed = False elif event.key == K_z: game.firing = False
Comments
Subscribe to comments
You need to login to post a comment.

30 fps here, this is extremely bad. Your mistake is that you update the entire screen when there is no need to do that. Note that blit returns a rect that describes the area updated, and display.update accepts a list of just these rectangles.
The ship is only three pixels that randomly flicker on and off for me. I can't help with that, though, haven't worked with aaline yet.
Updating the whole screen is not what i would do normally. It was just for this demo. However, i was getting 50+ fps regardless, and was hoping it was the norm so that maybe i don't have to bother working with the rectangles.
aaline works fine for me, but your problem is worrying. the documentation is kind of weak in terms of what support aaline has.
I'll try and find you on IRC.
Updated to use line instead of aaline, and a thickness of 2px for the ship.