From db5b9b9a0fc54cb8ab06bb73e84efb012691d5b0 Mon Sep 17 00:00:00 2001 From: JasonHomeWorkstationUbuntu Date: Thu, 10 Sep 2020 21:02:06 +1000 Subject: [PATCH] Finished Chapter 12 --- chap12_a_ship_that_fires_bullets.md | 589 ++++++++++++++++++ .../alien_invasion.py | 92 +++ src/project_i_alien_invasion/bullet.py | 34 + src/project_i_alien_invasion/settings.py | 20 + src/project_i_alien_invasion/ship.bmp | Bin 0 -> 8694 bytes src/project_i_alien_invasion/ship.py | 36 ++ 6 files changed, 771 insertions(+) create mode 100644 chap12_a_ship_that_fires_bullets.md create mode 100644 src/project_i_alien_invasion/alien_invasion.py create mode 100644 src/project_i_alien_invasion/bullet.py create mode 100644 src/project_i_alien_invasion/settings.py create mode 100644 src/project_i_alien_invasion/ship.bmp create mode 100644 src/project_i_alien_invasion/ship.py diff --git a/chap12_a_ship_that_fires_bullets.md b/chap12_a_ship_that_fires_bullets.md new file mode 100644 index 0000000..52e407c --- /dev/null +++ b/chap12_a_ship_that_fires_bullets.md @@ -0,0 +1,589 @@ +# Chapter 12. A Ship that Fires Bullets + +## Starting the Game Project + +```python +import sys +import pygame + +class AlienInvasion: + """Overall class to manage game assets and behaviour""" + + def __init__(self): + """Initialize the game, and create game resources.""" + pygame.init() + + self.screen = pygame.display.set_mode((1200, 800)) + pygame.display.set_caption("Alien Invasion") + + def run_game(self): + """Start the main loop for the game""" + while True: + + # event loop, watch for mouse/keyboard events + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + + # Make the most recently draw screen visible + pygame.display.flip() + +if __name__ == '__main__': + ai = AlienInvasion() + ai.run_game() +``` + +* `pygame.display.set_mode()` assigned a **surface** to `self.screen`, which is a part of the screen where a game element can be displayed. + * When we activate the game's animation loop, this surface will be redrawn every time. +* Game is contolled by `run_game()` method. containing + * event loop block + * code that manages screen updates. +* To access the events that Pygame detects, use `pygame.event.get()` function. In each loop, any keyboard/mouse event will cause the `for` loop to run. + + +### Creating a Settings Class + +In order to manage the setting more efficiently, we move setting into a separate module `settings.py` + +```python +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) +``` + +`alien_invasion.py` will also be modified to fit it. + +## Adding The Ship Image + +* Verify it's free licensed image + +### Creating the Ship Class + +Create this `ship.py` module + +```python +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.screen_rect = ai_game.screen.get_rect() + + # Load the ship image and get its rect + self.image = pygame.image.load('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 + + def blitme(self): + """Draw the ship at its current location""" + self.screen.blit(self.image,self.rect) +``` + +* Pygame treat all game elements like rectangles (rects). So, collision of objects can be calculated via `get_rect()` method +* `pygame.image.load()` is called to load the image +* When the image is loaded, we call `get_rect()` to access ship surface's `rect` attribute + +How to work with a `rect` object: +* Use x,y coord of top, bottom, left and right edges of the rect. +* We can set any of these values to establish the current position of the rect. +* When working center of a game element, use `center`, `centerx`, or `centery` attribute of a rect. +* When working with edge of the screen: work with `top`, `bottom`, `left` or `right` attributes. +* When adjust horizontal/vertical placement of rect, just use `x` & `y` attributes. +* `(0,0)` is the top-left corner of the screen. + +### Drawing the Ship to the Screen + +Modify `alien_invasion.py` by: + +1. Add ship object in `__init__` +2. Add `self.ship.blitme()` in screen redraw code block + +## Refactoring: The _check_events() and _update_screen() methods + +Refactor code: +* simplifies the structure +* Make it easier to build on. + +Here we refactor the code by break the `run_game()`: +* Move code for managing events to `_check_events()` +* Move code for updating the screen to `_update_screen()` + +**helper method**: +* is a method in class but isn't meant to be called through an instance. +* syntax: a single leading underscore + +### `_check_events()` Method + +Create function in `AlienInvasion` and replace original code snippet in while loop + +```python +def _check_events(self): + """Respond to keypresses and mouse events.""" + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() +``` + +### `_update_screen()` Method + +Create function `_update_screen()` and replace original code snippet + +```python +def _update_screen(self): + """Update images on the screen, and flip to the new screen""" + self.screen.fill(self.settings.bg_color) + self.ship.blitme() +``` + +```python +def run_game(self): + """Start the main loop for the game""" + while True: + + self._check_events() + self._update_screen() + +``` + +After replacing lengthy codes with helper function, `run_game` now become simpler. This is a way of refactoring the code + +## Piloting the Ship + +Now write code that responds when player presses the right or left arrow key. + +### Responding to a Keypress + +* Whenever the player presses a key, the keypress is registered in Pygame as an event. +* Each event is picked by `pygame.event.get()` method. +* We can specify in `_check_events()` method what kind of events we want the game to check for. +* Each keypress is registered as a `KEYDOWN` event +* If a `K_RIGHT` event is found, move the ship to right by increasing the value `self.ship.rect.x` by 1 + +```python + 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: + if event.key == pygame.K_RIGHT: + self.ship.rect.x += 1 +``` + +### Allowing Continuous Movement + +* We can detect whether the key is released using `KEYUP` event. +* We can use `KEYDOWN` & `KEYUP` events together with a flag called `moving_right` to implement continuous motion + * When `moving_right` flag is `False`, the ship will be motionless. + * Create an attribute called `moving_right` and `update()` method to check the status of the `moving_right` flag. + +Change `alien_invasion.py`: + +```python + def run_game(self): + """Start the main loop for the game""" + while True: + + self._check_events() + self.ship.update() + self._update_screen() + + 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: + if event.key == pygame.K_RIGHT: + self.ship.moving_right = True + elif event.type == pygame.KEYUP: + if event.key == pygame.K_RIGHT: + self.ship.moving_right = False +``` + +Change `ship.py`: + +```python + def __init__(self, ai_game): + """Initialize the ship and set its starting position""" + self.screen = ai_game.screen + self.screen_rect = ai_game.screen.get_rect() + + # Load the ship image and get its rect + self.image = pygame.image.load('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 + + # Movement flag + self.moving_right = False + + def update(self): + """Update the ship's position based on movement flag""" + if self.moving_right: + self.rect.x += 1 +``` + +### Moving Both Left and Right + +Apply the same logic on left movement + +### Adjusting the Ship's Speed + +1. Add `ship_speed` attribute to the `Settings` class +2. Modify `update()` & `__init__` method of `Ship`, so it move by defined speed + +```python +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 +``` + +Modify `ship.py` + +```python + def __init__(self, ai_game): + """Initialize the ship and set its starting position""" + self.screen = ai_game.screen + self.screen_rect = ai_game.screen.get_rect() + self.settings = ai_game.settings + + # Load the ship image and get its rect + self.image = pygame.image.load('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 decimal value for the ship's horizontal position + self.x = float(self.rect.x) + + # Movement flag + self.moving_right = False + self.moving_left = False + + def update(self): + """Update the ship's position based on movement flag""" + + # Update the ship's x value, not the rect + if self.moving_right: + self.rect.x += self.settings.ship_speed + if self.moving_left: + self.rect.x -= self.settings.ship_speed +``` + +### Limiting the Ship's Range + +Add limit of ship's range + +```python + def update(self): + """Update the ship's position based on movement flag""" + + # Update the ship's x value, not the rect + if self.moving_right and self.rect.right < self.screen_rect.right: + self.rect.x += self.settings.ship_speed + if self.moving_left and self.rect.left > 0: + self.rect.x -= self.settings.ship_speed +``` + +### Refactoring _check_events() + +As `_check_events()` is becoming lengthy, we should break/refactor it. + +```python + 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 + + def _check_keyup_events(self, event): + """Respond to key releases""" + if event.key == pygame.K_RIGHT: + self.ship.moving_right = False + elif event.key == pygame.K_LEFT: + self.ship.moving_left = False +``` + +### Pressing Q to Quit + +* use `pygame.K_q` key event (q) to exit program + + +```python + 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 + elif event.key == pygame.K_q: + sys.exit() +``` + +### Running the Game in Fullscreen Mode + +To run the game in fullscreen mode + +```python +class AlienInvasion: + """Overall class to manage game assets and behaviour""" + + def __init__(self): + """Initialize the game, and create game resources.""" + pygame.init() + + self.settings = Settings() + + # self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) + 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") + + self.ship = Ship(self) +``` + +## Shooting Bullets + +Write code that fires a bullet, which is represented by a small rectangle, when the player presses the spacebar. + +### Adding the Bullet Settings + +```python + def __init__(self): + """Initialize the game's settings.""" + + # Screen settings + ... + # Ship settings + ... + # Bullet settings + self.bullet_speed = 1.0 + self.bullet_width = 3 + self.bullet_height = 15 + self.bullet_color = (60,60,60) +``` + +`bullet.py` module + +```python +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 decimal value + self.y = float(self.rect.y) +``` + +* `self.rect = ...` created bullet's `rect` attribute. + * First, initialize at `(0,0)`, with width/height stored in `self.settings` + * Then, move it to the correct location via `self.rect.midtop`, as the bullet's position depends on the ship's position + +Then add `update()` & `draw_bullet()` for Bullet class + +```python + def update(self): + """Move the bullet up the screen""" + + # Update the decimal position of the bullet + self.y -= self.settings.bullet_speed + # Update the rect position + self.rect.y = self.y # When a bullet is fired, it moves up the screen + + def draw_bullet(self): + """Draw the bullet to the screen""" + pygame.draw.rect(self.screen, self.color, self.rect) +``` + +### Storing Bullets in a Group + +After defining the `Bullet` class and its necessary settings, we can write code to fire a bullet each time the player presses the spacebar. + +First, we'll create a group in `AlienInvasion` to store all the live bullets, so we can manage the bullets that have already been fired. + +`pygame.sprite.Group` class behave like a list with some extra functionality that's helpful when building games. So we directly draw the group (instead of individual bullets) to the screen. + +`alien_invasion.py` + +```python +def __init__(self): + ... + + self.ship = Ship(self) + self.bullets = pygame.sprite.Group() + +... + +def run_game(self): + """Start the main loop for the game""" + while True: + self._check_events() + self.ship.update() + self.bullets.update() # initiate the bullets group + self._update_screen() +``` + +### Firing Bullets + +1. Add key press event in `_check_keydown_events(self,event)`, which will trigger `_fire_bullet()` by `K_SPACE` +2. Create function `_fire_bullet(self)` +3. Add bullets group's drawing action in `_update_screen()` + +```python +from bullet import Bullet + +class AlienInvasion: + + def _check_keydown_events(self, event): + """Respond to keypresses""" + ... + elif event.key == pygame.K_q: + sys.exit() + elif event.key == pygame.K_SPACE: + self._fire_bullet() + + def _check_keyup_events(self, event): + ... + + def _fire_bullet(self): + """Create a new bullet and add it to the bullets group""" + new_bullet = Bullet(self) + self.bullets.add(new_bullet) + + def _update_screen(self): + """Update images on the screen, and flip to the new screen""" + ... + for bullet in self.bullets.sprites(): + bullet.draw_bullet() + + pygame.display.flip() +``` + +### Deleting Old Bullets + +Currently, bullets don't delete themselves after moving out of frame. Only their y-coordinate values grow increasingly negative. + +* Con: bullets continue to consume memory and processing power. + +Solution: need recycle these objects during running the game + +```python + def run_game(self): + """Start the main loop for the game""" + while True: + ... + + # Delete bullets out of screen + for bullet in self.bullets.copy(): + if bullet.rect.bottom <= 0: + self.bullets.remove(bullet) + print(len(self.bullets)) +``` + +### Limiting the Number of Bullets + +To limit the number of bullets. + +```python +class Settings: + """A class to store all settings for Alien Invasion""" + + def __init__(self): + """Initialize the game's settings.""" + ... + self.bullet_allowed = 3 +``` + +Modify `AlienInvasion` class as shown below + +```python +class AlienInvasion: + """Overall class to manage game assets and behaviour""" + ... + + def _fire_bullet(self): + """Create a new bullet and add it to the bullets group""" + if len(self.bullets) < self.settings.bullets_allowed: + new_bullet = Bullet(self) + self.bullets.add(new_bullet) +``` + +### Creating the _update_bullets() Method + +Need refactor `run_game` function in `AlienInvasion` class again, to make bullets update and deletion independent of `run_game()` + +```python +... +class AlienInvasion: + """Overall class to manage game assets and behaviour""" + def run_game(self): + """Start the main loop for the game""" + while True: + + self._check_events() + self.ship.update() + self._update_bullets() + + self._update_screen() + + def _update_bullets(self): + """Update position of bullets and get rid of old bullets""" + # Update bullet positions. + self.bullets.update() + + # Delete bullets out of screen + for bullet in self.bullets.copy(): + if bullet.rect.bottom <= 0: + self.bullets.remove(bullet) + print(len(self.bullets)) +... +``` \ No newline at end of file diff --git a/src/project_i_alien_invasion/alien_invasion.py b/src/project_i_alien_invasion/alien_invasion.py new file mode 100644 index 0000000..a026276 --- /dev/null +++ b/src/project_i_alien_invasion/alien_invasion.py @@ -0,0 +1,92 @@ +import sys +import pygame +from settings import Settings +from ship import Ship +from bullet import Bullet + +class AlienInvasion: + """Overall class to manage game assets and behaviour""" + + def __init__(self): + """Initialize the game, and create game resources.""" + pygame.init() + + self.settings = Settings() + + self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height)) # Use default screen width/height + # self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN) # Use full screen + + self.settings.screen_width = self.screen.get_rect().width + self.settings.screen_height = self.screen.get_rect().height + pygame.display.set_caption("Alien Invasion") + + self.ship = Ship(self) + self.bullets = pygame.sprite.Group() # + + def run_game(self): + """Start the main loop for the game""" + while True: + + self._check_events() + self.ship.update() + self._update_bullets() + + self._update_screen() + + 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 + 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 key 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.bullets_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() + + # Delete bullets out of screen + for bullet in self.bullets.copy(): + if bullet.rect.bottom <= 0: + self.bullets.remove(bullet) + print(len(self.bullets)) + + def _update_screen(self): + """Update images on the screen, and flip to the new screen""" + self.screen.fill(self.settings.bg_color) + self.ship.blitme() + for bullet in self.bullets.sprites(): + bullet.draw_bullet() + + pygame.display.flip() + +if __name__ == '__main__': + ai = AlienInvasion() + ai.run_game() \ No newline at end of file diff --git a/src/project_i_alien_invasion/bullet.py b/src/project_i_alien_invasion/bullet.py new file mode 100644 index 0000000..42377ff --- /dev/null +++ b/src/project_i_alien_invasion/bullet.py @@ -0,0 +1,34 @@ +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 decimal value + self.y = float(self.rect.y) + + def update(self): + """Move the bullet up the screen""" + + # Update the decimal position of the bullet + self.y -= self.settings.bullet_speed + # Update the rect position + self.rect.y = self.y # When a bullet is fired, it moves up the screen + + def draw_bullet(self): + """Draw the bullet to the screen""" + pygame.draw.rect(self.screen, self.color, self.rect) \ No newline at end of file diff --git a/src/project_i_alien_invasion/settings.py b/src/project_i_alien_invasion/settings.py new file mode 100644 index 0000000..9f1e1fc --- /dev/null +++ b/src/project_i_alien_invasion/settings.py @@ -0,0 +1,20 @@ +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 + + # Bullet settings + self.bullet_speed = 1.0 + self.bullet_width = 3 + self.bullet_height = 15 + self.bullet_color = (60,60,60) + self.bullets_allowed = 3 \ No newline at end of file diff --git a/src/project_i_alien_invasion/ship.bmp b/src/project_i_alien_invasion/ship.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9a94402a63dc937a1b6531c937cde4e254db31f1 GIT binary patch literal 8694 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 Ostv5N`pV)t0{$Os)sF7~ literal 0 HcmV?d00001 diff --git a/src/project_i_alien_invasion/ship.py b/src/project_i_alien_invasion/ship.py new file mode 100644 index 0000000..9d27b08 --- /dev/null +++ b/src/project_i_alien_invasion/ship.py @@ -0,0 +1,36 @@ +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.screen_rect = ai_game.screen.get_rect() + self.settings = ai_game.settings + + # Load the ship image and get its rect + self.image = pygame.image.load('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 decimal value for the ship's horizontal position + self.x = float(self.rect.x) + + # Movement flag + self.moving_right = False + self.moving_left = False + + def update(self): + """Update the ship's position based on movement flag""" + + # Update the ship's x value, not the rect + if self.moving_right and self.rect.right < self.screen_rect.right: + self.rect.x += self.settings.ship_speed + if self.moving_left and self.rect.left > 0: + self.rect.x -= self.settings.ship_speed + + def blitme(self): + """Draw the ship at its current location""" + self.screen.blit(self.image,self.rect) \ No newline at end of file