we can learn more about Pygame and about managing a large project. And also learn to detect collisions between game objects,like bullets and aliens. Detecting collisions helps you define interactions between elements in your games.
1.Reviewing the Project
we'll do the following:
- Add a single alien to the top-left corner of the screen, with appropriate spacing around it.
- Fill the upper portion of the screen with as many aliens as we can fit horizontally.We'll then create additional rows of aliens until we have a full fleet.
- Make the fleet move sideways and down until the entire fleet is shot down, an alien hits the ship, or an alien reaches the ground.If the entire fleet is shot down, we'll create a new fleet. If an alien hits the ship or the ground, we'll destroy the ship and create a new fleet,
- Limit the number of ships the player can use, and end the game when the player has used up the allotted number of ships.
Creating the First Alien
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""A class to represent a single alien in the fleet."""
def __init__(self, ai_game):
"""Initialize the alien and set its starting position."""
super().__init__()
self.screen = ai_game.screen
# Load the alien image and set its rect attribute.
self.image = pygame.image.load('images/alien.bmp')
self.rect = self.image.get_rect()
# Start each new alien near the top left of the screen.
self.rect.x = self.rect.width
self.rect.y = self.rect.height
# Store the alien's exact horizontal position.
self.x = float(self.rect.x)
2.Creating an Instance of the Alien
# import modules
import sys
import pygame
from settings import Settings
from ship import Ship
from bullet import Bullet
from alien import Alien
class AlienInvasion:
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
# the pygame.init() function initializes the background settings that Pygame need to work properly.
pygame.init()
# define the clock in the __init() method.ensure the clock ticks once on each pass through the main loop.
self.clock = pygame.time.Clock()
# create a display window. the argument (1200, 800) is a tuple that defines the dimensions of the game window,which will be 1200 pixels wide by 800 pixels high.
#self.screen = pygame.display.set_mode((1200, 800))
self.settings = Settings()
#self.screen = pygame.display.set_mode((self.settings.screen_width,self.settings.screen_height))
# Running the Game in Fullscreen Mode
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption("Alien Invasion")
# the call to Ship() requires one argument:an instance of AlienInvasion.
self.ship = Ship(self)
# create the group that holds the bullets in __init__()
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_fleet()
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self.ship.update()
self.bullets.update()
self._update_bullets()
self._update_screen()
# the tick() method takes one argument: the frame rate for the game.
self.clock.tick(60)
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self,event):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
# Pressing Q to Quit
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_keyup_events(self,event):
"""Respond to releases."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
"""Create a new bullet and add it to the bullets group."""
# if len(self.bullets) < self.settings.bullet_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
"""Update position of bullets and get rid of old bullets."""
#Update bullet positions.
self.bullets.update()
# Ged rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
def _update_screen(self):
"""Update images on the screen, and flip to the new screen."""
# Redraw the screen during each pass through the loop.
self.screen.fill(self.settings.bg_color)
# draw the ship on the screen by calling ship.blitme(),so the ship appears on top of the background.
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.ship.blitme()
self.aliens.draw(self.screen)
# Make the most recently drawn screen visible
pygame.display.flip()
def _create_fleet(self):
"""Create the fleet of aliens."""
# Make an alien.
alien = Alien(self)
self.aliens.add(alien)
if __name__ == '__main__':
# Make a game instance, and run the game.
ai = AlienInvasion()
ai.run_game()
3.Building the Alien Fleet
3.1 alien_invasion.py
# import modules
import sys
from time import sleep
import pygame
from settings import Settings
from game_stats import GameStats
from ship import Ship
from bullet import Bullet
from alien import Alien
class AlienInvasion:
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
# the pygame.init() function initializes the background settings that Pygame need to work properly.
pygame.init()
# define the clock in the __init() method.ensure the clock ticks once on each pass through the main loop.
self.clock = pygame.time.Clock()
# create a display window. the argument (1200, 800) is a tuple that defines the dimensions of the game window,which will be 1200 pixels wide by 800 pixels high.
#self.screen = pygame.display.set_mode((1200, 800))
self.settings = Settings()
#self.screen = pygame.display.set_mode((self.settings.screen_width,self.settings.screen_height))
# Running the Game in Fullscreen Mode
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption("Alien Invasion")
# Create an instance to store game statistics.
self.stats = GameStats(self)
# the call to Ship() requires one argument:an instance of AlienInvasion.
self.ship = Ship(self)
# create the group that holds the bullets in __init__()
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_fleet()
# Start Alien Invasion in an active state.
self.game_active = True
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
if self.game_active:
self.ship.update()
# self.bullets.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
# the tick() method takes one argument: the frame rate for the game.
self.clock.tick(60)
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self,event):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
# Pressing Q to Quit
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_keyup_events(self,event):
"""Respond to releases."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
"""Create a new bullet and add it to the bullets group."""
# if len(self.bullets) < self.settings.bullet_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
"""Update position of bullets and get rid of old bullets."""
#Update bullet positions.
self.bullets.update()
# Ged rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
self._check_bullet_alien_collisions()
def _check_bullet_alien_collisions(self):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
if not self.aliens:
# Destroy exsiting bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
def _update_aliens(self):
"""Check if the fleet is at an edge, then update positions."""
self._check_fleet_edges()
self.aliens.update()
# Look for alien-ship collisions
if pygame.sprite.spritecollideany(self.ship, self.aliens):
self._ship_hit()
# Look for aliens hitting the bottom of the screen.
self._check_aliens_bottom()
def _update_screen(self):
"""Update images on the screen, and flip to the new screen."""
# Redraw the screen during each pass through the loop.
self.screen.fill(self.settings.bg_color)
# draw the ship on the screen by calling ship.blitme(),so the ship appears on top of the background.
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.ship.blitme()
self.aliens.draw(self.screen)
# Make the most recently drawn screen visible
pygame.display.flip()
def _create_fleet(self):
"""Create the fleet of aliens."""
# Create an alien and keep adding aliens until there's no room left.
#Spacing between aliens is one alien width and one alien height.
# Make an alien.
alien = Alien(self)
alien_width, alien_height = alien.rect.size
current_x, current_y = alien_width,alien_height
while current_y < (self.settings.screen_height - 3 * alien_height):
while current_x < (self.settings.screen_width - 2 * alien_width):
self._create_alien(current_x,current_y)
current_x += 2 * alien_width
# Finished a row; reset x value, and increment y value.
current_x = alien_width
current_y += 2 * alien_height
def _create_alien(self, x_position,y_position):
"""Create an alien and place it in the fleet."""
new_alien = Alien(self)
new_alien.x = x_position
new_alien.rect.x = x_position
new_alien.rect.y = y_position
self.aliens.add(new_alien)
def _check_fleet_edges(self):
"""Respond appropriately if any aliens have reached an edge."""
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break
def _change_fleet_direction(self):
"""Drop the entire fleet and change the fleet's direction."""
for alien in self.aliens.sprites():
alien.rect.y += self.settings.fleet_drop_speed
self.settings.fleet_direction *= -1
def _ship_hit(self):
"""Respond to the ship being hit by an alien."""
if self.stats.ship_left > 0:
# Decrement ships_left.
self.stats.ship_left -= 1
# Get rid of any remaining bullets and aliens.
self.bullets.empty()
self.aliens.empty()
# Create a new fleet and center the ship.
self._create_fleet()
self.ship.center_ship()
# Pause.
sleep(0.5)
else:
self.game_active = False
def _check_aliens_bottom(self):
"""Check if any aliens have reached the bottom of the screen."""
for alien in self.aliens.sprites():
if alien.rect.bottom >= self.settings.screen_height:
# Treat this the same as if the ship got hit.
self._ship_hit()
break
if __name__ == '__main__':
# Make a game instance, and run the game.
ai = AlienInvasion()
ai.run_game()
3.2 ship.py
import pygame
class Ship:
"""A class to manage the ship."""
def __init__(self, ai_game):
"""Initialize the ship and set its starting position."""
self.screen = ai_game.screen
self.settings = ai_game.settings
self.screen_rect = ai_game.screen.get_rect()
# Load the ship image and get its rect
self.image = pygame.image.load('/Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter12/alien_invasion/images/ship.bmp')
self.rect = self.image.get_rect()
# Start each new ship at the bottom center of the screen.
self.rect.midbottom = self.screen_rect.midbottom
# Store a float for the ship's exact horizontal position.
self.x = float(self.rect.x)
# Movement flag: start with a ship that's not moving.
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ship's position based on the movement flag."""
# Update the ship's x value, not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.x += self.settings.ship_speed
if self.moving_left and self.rect.left > 0:
self.x -= self.settings.ship_speed
# Update rect object from self.x.
self.rect.x = self.x
def blitme(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
def center_ship(self):
"""Center the ship on the screen."""
self.rect.midbottom = self.screen_rect.midbottom
self.x = float(self.rect.x)
3.3 game_stats.py
class GameStats:
"""Track statistics for Alien Invasion."""
def __init__(self, ai_game):
"""Initialize statistics."""
self.settings = ai_game.settings
self.reset_stats()
def reset_stats(self):
"""Initialize statistics that can change during the game."""
self.ship_left = self.settings.ship_limit
3.4 settings.py
class Settings:
"""A class to store all settings for Alien Invasion"""
def __init__(self):
"""Initialize the game's settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
# Ship settings
self.ship_speed = 1.5
self.ship_limit = 3
# Bullet settings
self.bullet_speed = 2.5
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
# limiting the number of Bullets.
# self.bullet_allowed = 3
# Alien settings
self.alien_speed = 1.0
# creating settings for fleet direction.
self.fleet_drop_speed = 10
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
3.5 bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from the ship."""
def __init__(self, ai_game):
"""Create a bullet object at the ship's current position."""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
self.color = self.settings.bullet_color
# Create a bullet rect at (0, 0) and then set correct position.
self.rect = pygame.Rect(0, 0, self.settings.bullet_width,self.settings.bullet_height)
self.rect.midtop = ai_game.ship.rect.midtop
# Store the bullet's position as a float.
self.y = float(self.rect.y)
def update(self):
"""Move the bullet up the screen"""
# Update the exact position of the bullet.
self.y -= self.settings.bullet_speed
# Update the rect position.
self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet to the screen."""
pygame.draw.rect(self.screen, self.color, self.rect)
4.Summary
we learned how to add a large number of identical elements to a game by creating a fleet of aliens. we used nested loops to create a grid of elements, and we made a large set of game elements move by calling each element's update() method. we learned to control the direction of objects on the screen and to respond to specific situations.such as when the fleet reaches the edge of the screen. We detected and responded to collisions when bullets hit aliens and aliens hit the ship.We also learned how to track statistics in a game and use a game_active flag to determine when the game is over.
370

被折叠的 条评论
为什么被折叠?



