Skip to content

Commit

Permalink
up to page 293
Browse files Browse the repository at this point in the history
  • Loading branch information
mistwire committed Apr 29, 2024
1 parent 7b3794b commit 67087c5
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 26 deletions.
100 changes: 79 additions & 21 deletions alien_invasion.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from alien import Alien
from time import sleep
from game_stats import GameStats
from button import Button
from scoreboard import Scoreboard


class AlienInvasion:
Expand All @@ -23,7 +25,9 @@ def __init__(self) -> None:
pygame.display.set_caption("Alien Invasion")

# Create an instance to store game stats
# and create a scoreboard
self.stats = GameStats(self)
self.sb = Scoreboard(self)

self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
Expand All @@ -34,13 +38,21 @@ def __init__(self) -> None:
# Set the background color.
self.bg_color = (230, 230, 230)

# Start game in an inactive state.
self.game_active = False

# Make the Play button
self.play_button = Button(self, "Play")

def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_aliens()

if self.game_active:
self.ship.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
self.clock.tick(60)

Expand All @@ -54,6 +66,32 @@ def _check_events(self):
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)

def _check_play_button(self, mouse_pos):
"""Start when player clicks on Play button"""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.game_active:
# Reset game speed settings
self.settings.initialize_dynamic_settings()

# Reset game statistics.
self.stats.reset_stats()
self.sb.prep_score()
self.game_active = True

# Empty screen of aliens & bullets.
self.bullets.empty()
self.aliens.empty()

# Create a new fleet & recenter ship.
self._create_fleet()
self.ship.center_ship()

# Hide the mouse cursor
pygame.mouse.set_visible(False)

def _check_keydown_events(self, event):
"""Respond to keypresses"""
Expand Down Expand Up @@ -83,24 +121,30 @@ def _fire_bullet(self):
def _update_bullets(self):
"""Update position of bullets & delete old bullets."""
# Update bullet positions.
self.bullets.update()
self.bullets.update()

# Delete bullets at top of screen
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)

self._check_bullet_alien_collisions()
self._check_bullet_alien_collisions()

def _check_bullet_alien_collisions(self):
"""Respond to bullet-alien collisions"""
# Remove any bullets & aliens that have collided
collisions = pygame.sprite.groupcollide(
self.bullets, self.aliens, True, True)
collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)

if collisions:
for aliens in collisions.values():
self.stats.score += self.settings.alien_points * len(aliens)
self.sb.prep_score()

if not self.aliens:
#destroy existing bullets & make new fleet
# destroy existing bullets & make new fleet
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()

def _update_aliens(self):
"""Check if the fleet is at an edge, then update positions"""
Expand All @@ -111,23 +155,26 @@ def _update_aliens(self):
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 _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.
alien = Alien(self)
alien_width, alien_height = alien.rect.size
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

# Finish a row: reset x value, 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 row"""
new_alien = Alien(self)
Expand All @@ -151,17 +198,21 @@ def _change_fleet_direction(self):

def _ship_hit(self):
"""Actions when alien hits ship"""
# Decrement ships left
self.stats.ships_left -= 1
if self.stats.ships_left > 0:
# Decrement ships left
self.stats.ships_left -= 1

# Reset the game board
self.bullets.empty()
self.aliens.empty()
self._create_fleet()
self.ship.center_ship()
# Reset the game board
self.bullets.empty()
self.aliens.empty()
self._create_fleet()
self.ship.center_ship()

# Pause
sleep(0.5)
# Pause
sleep(0.5)
else:
self.game_active = False
pygame.mouse.set_visible(True)

def _check_aliens_bottom(self):
"""Check if any aliens have reached the bottom of the screen"""
Expand All @@ -171,7 +222,6 @@ def _check_aliens_bottom(self):
self._ship_hit()
break


def _update_screen(self):
"""Update images on the screen, and flip to the new screen."""
self.screen.fill(self.settings.bg_color)
Expand All @@ -180,8 +230,16 @@ def _update_screen(self):
self.ship.blitme()
self.aliens.draw(self.screen)

# Draw the score info.
self.sb.show_score()

# Draw the play button if the game is inactive.
if not self.game_active:
self.play_button.draw_button()

pygame.display.flip()


if __name__ == "__main__":
# Make a game instance & run the game.
ai = AlienInvasion()
Expand Down
33 changes: 33 additions & 0 deletions button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pygame.font

class Button:
"""A class to build buttons for a game"""
def __init__(self, ai_game, msg) -> None:
"""Init button attribs"""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()

# Set dimensions & properties of button
self.width, self.height = 200, 50
self.button_color = (0, 135, 0)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)

# Build the button's rect object & place it.
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center

# Button msg needs to be prepped once.
self._prep_msg(msg)

def _prep_msg(self, msg):
"""Turn msg into a rendered image & center it on the button"""
self.msg_image = self.font.render(msg, True, self.text_color, self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center

def draw_button(self):
"""Draw blank button and then draw message"""
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)

2 changes: 2 additions & 0 deletions game_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ def __init__(self, ai_game) -> None:
def reset_stats(self):
"""Init stats that can change during the game"""
self.ships_left = self.settings.ship_limit
self.score = 0

1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pygame==2.5.2
34 changes: 34 additions & 0 deletions scoreboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import pygame.font

class Scoreboard:
"""A class to report scoring info"""

def __init__(self, ai_game,):
"""Init scorekeeping attribs"""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
self.settings = ai_game.settings
self.stats = ai_game.stats

# Font settings for scoring
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)

# Prep the score image
self.prep_score()

def prep_score(self):
"""Turn the score into an image"""
rounded_score = round(self.stats.score, -1)
score_str = f"{rounded_score:,}"
score_str = str(self.stats.score)
self.score_image = self.font.render(score_str, True, self.text_color, self.settings.bg_color)

# Display score at top right of screen.
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 20
self.score_rect.top = 20

def show_score(self):
"""Draw score to the screen"""
self.screen.blit(self.score_image, self.score_rect)
33 changes: 28 additions & 5 deletions settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,48 @@ class Settings:
"""A class to store all settings for Alien Invasion"""

def __init__(self) -> None:
"""Initialize the game's settings"""
"""Initialize the game's static 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 = 5

# Bullet settings
self.bullet_speed = 4.0
self.bullet_width = 3

self.bullet_width = 300
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
self.bullets_allowed = 5

#Alien settings
self.alien_speed = 1.0
self.fleet_drop_speed = 10


# How quickly the game speeds up.
self.speedup_scale = 1.1
# Increase alien point value for new levels
self.score_scale = 1.5

self.initialize_dynamic_settings()

def initialize_dynamic_settings(self):
"""Init settings that change throughout the game"""
self.ship_speed = 1.5
self.bullet_speed = 4.0
self.alien_speed = 1.0
# fleet direction 1 = right, -1 = left
self.fleet_direction = 1

# Scoring settings
self.alien_points = 50

def increase_speed(self):
"""Increase speed settings and alien point values"""
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_speed *= self.speedup_scale

self.alien_points = int(self.alien_points * self.score_scale)

0 comments on commit 67087c5

Please sign in to comment.