Python project alien invasion (II): Aliens
In this chapter, we mainly complete the following functions. First, add an alien at the edge of the screen, and then try to generate a group of aliens. We make these aliens move like two hundred years and below, and delete the aliens hit by the bullet. Finally, we show the number of ships the player has and end the game when the player runs out of ships. In this section, due to the need of display, the size of the screen in the Setting class is modified to 1200 * 700, the moving speed of the spacecraft is modified to 1.5, the bullet speed is modified to 1, and the code does not need to be modified elsewhere (mainly to make the number of Aliens displayed on the screen reasonable). The complete code is at the bottom. If you have any questions, you can ask them in the comment area. If you see them, you will reply:
# Settings.py class Settings: """Store all classes related to alien invasion""" def __init__(self): """Initialize game settings""" # screen setting self.screen_width = 1200 # Set window width self.screen_height = 700 # Set window height self.bg_color = (230, 230, 230) # Set background color self.ship_speed = 1.5 # Set the initial value of the speed # Bullet setting self.bullet_speed = 1 # Bullet speed self.bullet_width = 3 # The width of the bullet self.bullet_height = 15 # Bullet height self.bullet_color = (60, 60, 60) # Bullet color self.bullets_allowed = 3 # Limit the number of bullets that do not disappear to 3
Use Q to exit the game
Next, we set the shortcut key to exit the game. When we press Q from the keyboard, the game will exit automatically. Therefore, we need to modify the function check_keydown_events():
# game_function.py def check_keydown_events(event, ai_settings, screen, ship, bullets): """Operate in response to the key""" if event.key == pygame.K_RIGHT: # Determine whether the right arrow is pressed ship.moving_right = True elif event.key == pygame.K_LEFT: # Determine whether the left arrow is pressed ship.moving_left = True elif event.key == pygame.K_SPACE: # Determine whether the space is pressed fire_bullet(ai_settings, screen, ship, bullets) elif event.key == pygame.K_q: # Judge whether Q is pressed. If Q is pressed, quit the game sys.exit()
Create an alien
Preventing aliens on the screen is similar to placing spaceships on the screen. We will create a new Alien class in which we control the behavior of aliens. In order to simplify the program, we use bitmaps to represent aliens. We can not only obtain pictures of aliens from the Internet as in the previous chapter, but also directly use the following pictures. Please be sure to save the pictures in Alien_ In the images folder under the invasion folder.
First, we create the Alien class:
# alien.py import pygame from pygame.sprite import Sprite class Alien(Sprite): def __init__(self, screen, ai_settings): """Initialize the alien and set its starting position""" super.__init__() self.screen = screen self.ai_settings = ai_settings # Load the alien image and set its rect property self.image = pygame.image.load("images/alien.bmp") self.rect = self.image.get_rect() # Let each alien appear near the upper left corner of the screen self.rect.x = self.rect.width # Left margin set to alien width self.rect.y = self.rect.height # The top margin is set to alien height # Store the exact location of aliens self.x = float(self.rect.x) # It is mainly used for back calculation def blitme(self): """Draw aliens at the specified location""" self.screen.blit(self.image, self.rect)
Next, we need to instantiate the Alien class in the main function:
# alien_invasion.py import pygame # It contains the functions required for game development from settings import Settings from ship import Ship import game_function as gf from pygame.sprite import Group from alien import Alien def run_game(): """Initialize the game and create a screen object""" pygame.init() # Initialize and check whether the toolkit is complete ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width, ai_settings.screen_length)) # Create a window with a size of 800 * 600 pygame.display.set_caption("Alien invasion") # Set screen name ship = Ship(ai_settings, screen) # Create a spaceship bullets = Group() # Create a group to store bullets alien = Alien(ai_settings, screen) # Create an alien instance # Start the main cycle of the game while True: gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(bullets) gf.update_screen(ai_settings, screen, ship, alien, bullets) run_game() # Run program
Then we were at the game_function.py to modify the screen update function so that aliens can appear on the screen:
# game_function.py def update_screen(ai_settings, screen, ship, alien, bullets): """Update the image on the screen and switch to a new screen""" screen.fill(ai_settings.bg_color) # Fills the screen with the specified color for bullet in bullets: bullet.draw_bullet() ship.blitme() # Display the spacecraft on the screen alien.blitme() # Let aliens appear on the screen pygame.display.flip() # Displays the most recently drawn screen
After that, when we run the main function, we can see an alien in the upper right corner of the screen. We draw bullets and spacecraft first to ensure that aliens are at the front.
Create a group of Aliens
Determine how many aliens a row can hold
To draw a group of aliens, we first need to determine how many aliens can be accommodated in a row. We need to first calculate the horizontal spacing to determine how many aliens can be accommodated in a row, and then calculate the vertical spacing to determine how many aliens can be accommodated in a column.
First, we calculate how much horizontal free space there is, and the width of the screen is stored in AI_ settings. screen. In width, because we need to leave a space of two alien widths on both sides of the screen, the actual available space in one line is:
available_space_x = ai_settings.screen_width - 2 * alien_width
At the same time, we also need to leave a certain space between aliens, that is, the width of aliens. Therefore, the space required by yo aliens is twice its width. One width is used to place aliens and the other width is the blank on the right of aliens. Therefore, we can divide the space by twice the width of aliens to determine how many aliens can be accommodated in a row:
number_alien_x = available_space_x / (2 * alien_width)
Create a line of Aliens
To create a row of aliens, we first need to create an empty group named aliens in the main function to store all aliens, and then call game_function.py function for creating alien populations:
# alien_invasion.py import pygame # It contains the functions required for game development from settings import Settings from ship import Ship import game_function as gf from pygame.sprite import Group def run_game(): """Initialize the game and create a screen object""" pygame.init() # Initialize and check whether the toolkit is complete ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width, ai_settings.screen_length)) # Create a window with a size of 800 * 600 pygame.display.set_caption("Alien invasion") # Set screen name ship = Ship(ai_settings, screen) # Create a spaceship bullets = Group() # Create a group to store bullets aliens = Group() # Create an alien group gf.create_fleet(ai_settings, screen, aliens) # Start the main cycle of the game while True: gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(bullets) gf.update_screen(ai_settings, screen, ship, aliens, bullets) run_game() # Run program
We are no longer in alien_invasion.py directly creates aliens, but creates an empty group in game_ function. Create of PY_ To generate a group of aliens in fleet (), we modified GF update_ The call in screen () passes the alien group to it. At the same time, we also need to modify the internal call of the function:
# game_function.py def update_screen(ai_settings, screen, ship, aliens, bullets): """Update the image on the screen and switch to a new screen""" screen.fill(ai_settings.bg_color) # Fills the screen with the specified color for bullet in bullets: bullet.draw_bullet() ship.blitme() # Display the spacecraft on the screen aliens.draw(screen) # Let aliens appear on the screen pygame.display.flip() # Displays the most recently drawn screen
When calling the draw() function to draw aliens on each screen, Python will automatically draw each element in the group, and the drawing position is determined by rect.
Then we can play in the game_ function. Write a new function create in PY_ Fleet() to generate a group of aliens. At the same time, because the basic parameters of aliens need to be used to generate aliens, we need to import Alien class here:
# game_function.py def create_fleet(ai_settings, screen, aliens): """Create alien groups""" alien = Alien(ai_settings, screen) # Create an alien alien_width = alien.rect.width # Get the width of an alien # Calculate how many positions there are in a row available_space_x = ai_settings.screen_width - 2 * alien_width # Calculate how many aliens a row can hold number_alien_x = int(available_space_x / (2 * alien_width)) # Create first row for alien_number in range(number_alien_x): # Create an alien and join the current line alien = Alien(ai_settings, screen) # Create an alien alien.x = alien_width + 2 * alien_width * alien_number # Set the initial position of each alien alien.rect.x = alien.x aliens.add(alien) # Adding aliens to a group
In order to place aliens, we need to know their height and width. Therefore, before calculation, we created an alien, but did not put it into the group. Then we obtained the width attribute of aliens and calculated how many aliens can be accommodated in a row.
Next, we convert the calculated number into an integer, because we don't want an alien to display only a part. At the same time, the range() function also needs an integer, which returns a list of integers from 0 to the incoming number minus 1
In the main body of the loop, we create a new alien, add it to the current line by setting the x coordinate, and then set the distance between aliens.
After running, we will see a line of aliens.
Refactoring create_fleet()
Next, we refactor create_ The fleet () function decomposes its function into get that calculates how many aliens a line can hold_ number_ alien_ X () function, and create an alien function_ alien()
# game_function.py def create_alien(ai_settings, screen, aliens, alien_number): """Create an alien and put it in the current line""" alien = Alien(ai_settings, screen) # Create an alien alien_width = alien.rect.width # Get the width of an alien alien.x = alien_width + 2 * alien_width * alien_number # Set the initial position of each alien alien.rect.x = alien.x aliens.add(alien) # Adding aliens to a group def create_fleet(ai_settings, screen, aliens): """Create alien groups""" alien = Alien(ai_settings, screen) # Create an alien number_alien_x = get_number_alien_x(ai_settings, alien.rect.width) # Create first row for alien_number in range(number_alien_x): create_alien(ai_settings, screen, aliens, alien_number)
add rows
To create a swarm of aliens, you need to calculate how many lines the screen can hold and cycle the same number of times for creating a row of aliens. In order to calculate the number of rows that can be accommodated, we need to calculate the available vertical space and subtract the screen height from the height of the spacecraft, the height of aliens, and the distance between the initial spacecraft and aliens.
available_space_y = ai_settings.screen_height-3 * alien_height-ship_height
In this way, a certain blank area will be left at the top of each line to give players time to shoot aliens.
At the same time, a little space is left at the bottom of each row. In order to calculate the number of rows that can be accommodated, we divide the space between us by twice the height of aliens
number_rows = available_space_y / (2 * alien_height)
Once you know how many lines you can hold, you can repeat the code that generates one line:
# game_function.py def get_number_rows(ai_settings, ship_height, alien_height): """Calculate how many lines the screen can hold""" # Calculate how much space is left on the screen available_space_y = ai_settings.screen_height - 3 * alien_height - ship_height # How many lines are there on the calculation screen number_rows = int(available_space_y / (2 * alien_height)) return number_rows def create_alien(ai_settings, screen, aliens, alien_number, row_number): """Create an alien and put it in the current line""" alien = Alien(ai_settings, screen) # Create an alien alien_width = alien.rect.width # Get the width of an alien alien_height = alien.rect.height alien.x = alien_width + 2 * alien_width * alien_number # Set the initial position of each alien alien.rect.x = alien.x # Determine the vertical position of each alien alien.rect.y = alien_height + 2 * alien_height * row_number aliens.add(alien) # Adding aliens to a group def create_fleet(ai_settings, screen, ship, aliens): """Create alien groups""" alien = Alien(ai_settings, screen) # Create an alien number_alien_x = get_number_alien_x(ai_settings, alien.rect.width) number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height) # Create first row for row_number in range(number_rows): for alien_number in range(number_alien_x): create_alien(ai_settings, screen, aliens, alien_number, row_number)
We're getting_ number_ Rows() implements the modification of the two formulas of the previous calculation line. Here, the int() function is also used to avoid incomplete alien lines. At the same time, the range() function needs to pass integers.
Finally, we use a nested loop. The inner loop is used to create a row of aliens, and the outer loop is used to count from 0 to the number of rows to create aliens.
After that, we need to modify create in the main function_ Modification of parameters of the fleet() function:
gf.create_fleet(ai_settings, screen, ship, aliens)
After running, we can display multiple aliens on the screen.
Let the alien crowd move
Next, we let the alien move to the right, hit the edge of the screen, move down a certain distance, and then move to the left. We will continue to move all aliens until all aliens are eliminated, an alien hits a spaceship, or an alien reaches the bottom of the screen
Let the aliens move to the right
Next, let the alien move to the right. First, we set the alien movement speed in the Settings class:
# settings.py # Alien settings self.alien_speed = 0.5 # Aliens move at a speed of 0.5
Then we use this speed to implement update()
# ailen.py def update(self): """Move aliens to the right""" self.x += self.ai_settings.alien_speed self.rect.x = self.x
Borrow it. Let's go to game_function.py write a new function update_alien() to call this method:
# game_function.py def update_aliens(aliens): """Update the location of Aliens""" aliens.update()
Finally, we call the location update function in the while loop of the main function. First, we update the location of the bullet and update the location of the alien.
# alien_invasion.py # Start the main cycle of the game while True: gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(bullets) gf.update_aliens(aliens) gf.update_screen(ai_settings, screen, ship, aliens, bullets)
Create settings that represent alien movement
First, we set the flag for detecting alien collision screen and the speed of moving down in the Settings class:
# settings.py # Alien settings self.alien_speed = 0.5 # Aliens move at a speed of 1 self.fleet_drop_speed = 10 # The speed at which aliens move down # fleet_ When direction is 1, it means to move right, and when direction is - 1, it means to move left self.fleet_direction = 1
Here, instead of using the left or right signs to mark the movement direction of aliens, we use 1 and - 1, because moving to the right increases the coordinate of x and moving to the left decreases the coordinate of x. This setting will be more convenient to calculate.
Detect whether aliens hit the edge of the screen
Now in alien Add function check in py module_ Edge() to detect whether aliens move to the edge of the screen. At the same time, we need to modify the update() function to ensure that each alien moves in the right direction:
# alien.py def check_edge(self): """If the alien is on the edge of the screen, return True""" screen_rect = self.screen.get_rect() # If the edge of the alien is greater than or equal to the right edge of the screen if self.rect.right >= screen_rect.right: return True # If the edge of the alien is less than or equal to the left edge of the screen, i.e. coordinate 0 elif self.rect.left <= 0: return True def update(self): """Move aliens to the right""" self.x += (self.ai_settings.alien_speed * self.ai_settings.fleet_direction) self.rect.x = self.x
In the update() method, set the movement amount to alien speed and fleet_direction product, let aliens translate left and right. If fleet_ If the direction is 1, the alien will be moved to the left, if fleet_ If the direction is - 1, the alien will move to the right.
Move the Alien Swarm down and change direction
When an alien reaches the edge of the screen, you need to move the whole group of aliens down and change their moving direction. To do this, we need to modify the game_function.py, and create the function check_fleet_edges() is used to take certain measures and write change when aliens reach the edge of the screen_ fleet_ Direction () moves the aliens down and changes their direction, and updates them_ Aliens();
# game_function.py def check_fleet_edges(ai_settings, aliens): """When aliens reach the edge, take corresponding measures""" for alien in aliens.sprites(): if alien.check_edge(): change_fleet_direction(ai_settings, aliens) # The reason for using break here is that calling it once will automatically move all grouping members down one line break def change_fleet_direction(ai_settings, aliens): """Move all aliens down and change their direction""" for alien in aliens.sprites(): alien.rect.y += ai_settings.fleet_drop_speed ai_settings.fleet_direction *= -1 def update_aliens(ai_settings, aliens): """Update the location of Aliens""" check_fleet_edges(ai_settings, aliens) aliens.update()
Check in function_ fleet_ In edges (), we traverse the alien colony and call check for each of them_ Edge(), if it is at the edge, it will return the True value. At this time, we know that aliens are at the edge of the screen and need to change the direction of the alien crowd, so we call change_fleet_direction() traverses all aliens and moves them down AI_ settings. fleet_ drop_ The value set in speed, and then set the flag to the opposite number of the previous flag.
Finally in alien_invasion.py, modify update_aliens(), add the formal parameter ai_settings:
while True: gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(bullets) gf.update_aliens(ai_settings, aliens) gf.update_screen(ai_settings, screen, ship, aliens, bullets)
Then we can see the aliens moving back and forth on the screen and down after reaching the edge.
Shoot aliens
We have created spaceships and alien groups. When the odd bullet hits the alien, it will pass through the alien. In order to avoid this situation, we need to check the collision. In the game, the collision is the overlapping of game elements. In order to make the bullet shoot down the alien, we will use sprite Groupcollapse() detects collisions between two member groups.
Detect bullet and alien collisions
After the bullet hits the alien, we need to know immediately so that the alien can disappear in time after the collision, so we will detect the collision immediately after updating the location of the bullet.
Method sprite Groupcollapse () compares the rect of each bullet with the rect of each alien, and returns a dictionary that includes the colliding bullet and alien. Each key is the value of being shot by aliens in the dictionary.
# game_function.py def update_bullets(aliens, bullets): """Update the location of bullets and delete bullets that have disappeared""" bullets.update() for bullet in bullets.copy(): # The number of lists or groups should not be modified in the for loop, which will lead to the loss of traversal if bullet.rect.bottom <= 0: bullets.remove(bullet) # Detect whether any bullets hit aliens. If so, delete bullets and aliens collision = pygame.sprite.groupcollide(bullets, aliens, True, True)
The last line of code traverses each bullet in the grouping bullets, and then traverses each alien in the grouping aliens. Whenever a bullet overlaps with an alien, groupcollide() adds a key value pair to the returned dictionary. The two arguments tell us to delete the bullet and alien in collision, because we call update_bullets() passes the argument aliens, so we need to modify the function call part:
# alien_invasion.py while True: gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(aliens, bullets) gf.update_aliens(ai_settings, aliens) gf.update_screen(ai_settings, screen, ship, aliens, bullets)
Now we can kill aliens by running the main function.
Create a bullet for the test
As long as you run this game, you can test many functions, but some functions are cumbersome under normal circumstances. For example, it takes a long time to shoot down all aliens when you want to test whether the code can correctly handle the empty formation of aliens. We can modify the width of the bullet in the Settings class to make it easier to hit aliens. For example, we can change the bullet_ The width is changed to 300.
Generate a new group of Aliens
Next, we need to write code to make a new group of aliens appear after an alien population is eliminated. To make a new group of aliens appear after aliens are eliminated, we first need to check whether the aliens group is empty. If it is empty, call create again_ The fleet () method, which we will use in update_bullets() to check, because aliens were destroyed here:
# update_bullets.py def update_bullets(ai_settings, screen, ship, aliens, bullets): """Update the location of bullets and delete bullets that have disappeared""" bullets.update() # The number of lists or groups should not be modified in the for loop, which will lead to the loss of traversal for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet) # Detect whether a bullet hit the alien. If so, delete the bullet and alien collision = pygame.sprite.groupcollide(bullets, aliens, True, True) if len(aliens) == 0: # If all aliens are eliminated, delete the existing bullets and regenerate aliens bullets.empty() create_fleet(ai_settings, screen, ship, aliens)
Because we modified update_ The definition of bullets () contains formal parameters, so we need to update update in the main function_ Call of bullets():
# alien_invasion.py while True: gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(ai_settings, screen, ship, aliens, bullets) gf.update_aliens(ai_settings, aliens) gf.update_screen(ai_settings, screen, ship, aliens, bullets)
Now, after we eliminate the aliens on the screen, a new group of aliens will appear.
Refactoring update_bullets()
Next, we refactor update_bullets() function to make it no longer complete so many tasks. We move the code for detecting the collision between bullets and aliens to a separate function:
# game_function.py def update_bullets(ai_settings, screen, ship, aliens, bullets): """Update the location of bullets and delete bullets that have disappeared""" bullets.update() # The number of lists or groups should not be modified in the for loop, which will lead to the loss of traversal for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet) check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets) # print(len(bullets)) # Show how many bullets are left. This is used to test. Deleting this statement at run time can reduce memory def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets): """Respond to the collision between bullets and aliens and generate a new group of aliens after all wasina people died""" # Detect whether a bullet hit the alien. If so, delete the bullet and alien collision = pygame.sprite.groupcollide(bullets, aliens, True, True) if len(aliens) == 0: # If all aliens are eliminated, delete the existing bullets and regenerate aliens bullets.empty() create_fleet(ai_settings, screen, ship, aliens)
End the game
If the player doesn't wipe out the whole group of aliens in enough time, and some aliens hit the ship, the ship will be destroyed. At the same time, we limit the number of ships available to the player. When the aliens reach the bottom of the screen, the ship will also be destroyed. After the player uses up the ship, the game will end.
Detect the collision between aliens and spacecraft
First of all, we check the collision between spacecraft and aliens. After moving the position of each alien, we will immediately detect the collision between spacecraft and Aliens:
# game_function def update_aliens(ai_settings, ship, aliens): """Update the location of Aliens""" check_fleet_edges(ai_settings, aliens) aliens.update() # Detect the collision between aliens and spacecraft if pygame.sprite.spritecollideany(ship, aliens): print("Bang")
The method spritecollideany() accepts two arguments, one object and one group. It checks whether the members of the group collide with the object, stops traversing after finding the collided members, and outputs a prompt message. If there is no collision, the method will return to None. At this time, the if code block will not be executed. If the alien who has found the collision is found, the alien will be returned. At this time, the code behind the if code block will be executed.
In response to the collision between spacecraft and aliens
Now we need to determine what should be done when aliens collide with the spacecraft. Instead of destroying the existing ship instance and creating a new ship instance, we count how many times the spacecraft has been hit by tracking the game and counting the game information, which helps to count the scores in the third part.
Next, write a class to track game statistics, GameStates, and save it as a file game_states.py:
# game_stats.py class GameStats: """Track game statistics""" def __init__(self, ai_settings): self.ai_settings = ai_settings self.reset_stats() def reset_stats(self): """Initialize information that may change during game operation""" # Count the number of non passes remaining in the game self.ships_left = self.ai_settings.ship_limit
During the game, we only need to reset some statistics every time we start the whole game_ Most statistics are initialized in the stats () method.
Currently, there is only one statistic, ship_left, YONGANLI counts the number of remaining available spacecraft
Then we add parameters in the Settings class:
# settings.py # Spacecraft setup self.ship_speed = 1.5 # Set the initial value of the speed self.ship_limit = 3 # Set the maximum number of ships for players
Next, we modify alien_invasion.py, create an instance of GameStats:
# alien_invasion.py def run_game(): """Initialize the game and create a screen object""" pygame.init() # Initialize and check whether the toolkit is complete ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width, ai_settings.screen_height)) # Create a window with a size of 800 * 600 pygame.display.set_caption("Alien invasion") # Set screen name stats = GameStats(ai_settings) ship = Ship(ai_settings, screen) # Create a spaceship bullets = Group() # Create a group to store bullets aliens = Group() # Create an alien group gf.create_fleet(ai_settings, screen, ship, aliens) # Create alien groups # Start the main cycle of the game while True: gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(ai_settings, screen, ship, aliens, bullets) gf.update_aliens(ai_settings, stats, screen,ship, aliens, bullets) gf.update_screen(ai_settings, screen, ship, aliens, bullets)
In the main loop, we modify update in advance_ Aliens parameter, and the parameters stats, screen and ship are added. After an alien collides with a spaceship, we will use these parameters to track how many spaceships the player has and create new aliens.
After an alien collides with a spaceship, we will reduce the number of remaining spaceships by one, create a group of new aliens, and place the spaceship in the bottom center (at the same time, we will pause the game for a period of time to buffer the players)
Next, put the code to realize these functions in ship_hit() and modify update_aliens() function:
from time import sleep def ship_hit(ai_settings, stats, screen, ship, aliens, bullets): """Responding to a spaceship hit by aliens""" # Reduce the number of ships by one stats.ships_left -= 1 # Clear the list of bullets and aliens aliens.empty() bullets.empty() # Create a new group of aliens and put the spacecraft in the center of the screen create_fleet(ai_settings, screen, ship, aliens) # Create new aliens ship.center_ship() # Put the spacecraft in the center of the screen # suspend sleep(0.5) def update_aliens(ai_settings, stats, screen, ship, aliens, bullets): """Update the location of Aliens""" check_fleet_edges(ai_settings, aliens) aliens.update() # Detect the collision between aliens and spacecraft if pygame.sprite.spritecollideany(ship, aliens): ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
First, import the function sleep() from the module time to pause the game and give the player buffer time, ship_ The hit() function responds when the spaceship hits aliens, reduces the number of available spaceships by 1, then empties the bullets and alien groups, regenerates a group of aliens, moves the spaceship back to the center of the screen, then pauses the game so that players can see the collision, then displays the new spaceship and aliens on the screen, and then writes center_ship() function, put the spaceship in the center of the screen:
# ship.py def center_ship(self): """Center the ship on the screen""" self.center = self.screen_rect.centerx
An alien has reached the bottom of the screen
If an alien reaches the bottom of the screen, the response should be the same as that of an alien hitting a spacecraft. We will add a new function named check_aliens_bottom():
# game_function def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets): """Check if aliens have reached the bottom of the screen""" screen_rect = screen.get_rect() # Read the matrix information of the screen for alien in aliens: # If the coordinates of the alien's bottom matrix are larger than the screen, the collision response is performed if alien.rect.bottom >= screen_rect.bottom: ship_hit(ai_settings, stats, screen, ship, aliens, bullets) break def update_aliens(ai_settings, stats, screen, ship, aliens, bullets): """Update the location of Aliens""" check_fleet_edges(ai_settings, aliens) aliens.update() # Detect the collision between aliens and spacecraft if pygame.sprite.spritecollideany(ship, aliens): ship_hit(ai_settings, stats, screen, ship, aliens, bullets) check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)
End the game
At present, although collision has been detected, the game will never end_ The value of left will become negative, and a flag attribute game will be added in GameStats below_ Active so that players can end the game after using up the ship:
# game_function.py def __init__(self, ai_settings): self.ai_settings = ai_settings self.reset_stats() self.game_active = True # The game has just started and is active
Then in ship_ After the game () function is used up, all players will increase the hit function_ Set active to False:
# game_function.py def ship_hit(ai_settings, stats, screen, ship, aliens, bullets): """Responding to a spaceship hit by aliens""" if stats.ships_left > 0: # Reduce the number of ships by one stats.ships_left -= 1 # Clear the list of bullets and aliens aliens.empty() bullets.empty() # Create a new group of aliens and put the spacecraft in the center of the screen create_fleet(ai_settings, screen, ship, aliens) # Create new aliens ship.center_ship() # Move the spacecraft to the center of the screen # suspend sleep(0.5) else: stats.game_active = False
Determine the running part of the game
In alien_ invasion. In py, we need to determine which parts of the game run in any case and which only run in the active state:
while True: gf.check_events(ai_settings, screen, ship, bullets) if stats.game_active: ship.update() gf.update_bullets(ai_settings, screen, ship, aliens, bullets) gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets) gf.update_screen(ai_settings, screen, ship, aliens, bullets)
In any case, we need to detect keyboard and mouse events and update the screen. The rest is as long as the game is active. Now we run the game and the game will stand still after using the spacecraft.
review
Next, we will paste all the files created by the game so far and the code in the file below:
# alien_invasion.py import pygame # It contains the functions required for game development from settings import Settings from ship import Ship import game_function as gf from pygame.sprite import Group from game_stats import GameStats from time import sleep def run_game(): """Initialize the game and create a screen object""" pygame.init() # Initialize and check whether the toolkit is complete ai_settings = Settings() screen = pygame.display.set_mode( (ai_settings.screen_width, ai_settings.screen_height)) # Create a window with a size of 800 * 600 pygame.display.set_caption("Alien invasion") # Set screen name stats = GameStats(ai_settings) ship = Ship(ai_settings, screen) # Create a spaceship bullets = Group() # Create a group to store bullets aliens = Group() # Create an alien group gf.create_fleet(ai_settings, screen, ship, aliens) # Create alien groups # Start the main cycle of the game while True: gf.check_events(ai_settings, screen, ship, bullets) if stats.game_active: ship.update() gf.update_bullets(ai_settings, screen, ship, aliens, bullets) gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets) gf.update_screen(ai_settings, screen, ship, aliens, bullets) run_game() # Run program
# game_stats.py class GameStats: """Track game statistics""" def __init__(self, ai_settings): self.ai_settings = ai_settings self.reset_stats() self.game_active = True # The game has just started and is active def reset_stats(self): """Initialize information that may change during game operation""" # Count the number of ships remaining in the game self.ships_left = self.ai_settings.ship_limit
# alien.py import pygame from pygame.sprite import Sprite class Alien(Sprite): def __init__(self, ai_settings, screen): """Initialize the alien and set its starting position""" super().__init__() self.screen = screen self.ai_settings = ai_settings # Load the alien image and set its rect property self.image = pygame.image.load("images/alien.bmp") self.rect = self.image.get_rect() # Let each alien appear near the upper left corner of the screen self.rect.x = self.rect.width # Left margin set to alien width self.rect.y = self.rect.height # The top margin is set to alien height # Store the exact location of aliens self.x = float(self.rect.x) # It is mainly used for back calculation def blitme(self): """Draw aliens at the specified location""" self.screen.blit(self.image, self.rect) def check_edge(self): """If the alien is on the edge of the screen, return True""" screen_rect = self.screen.get_rect() # If the edge of the alien is greater than or equal to the right edge of the screen if self.rect.right >= screen_rect.right: return True # If the edge of the alien is less than or equal to the left edge of the screen, i.e. coordinate 0 elif self.rect.left <= 0: return True def update(self): """Move aliens to the right""" self.x += (self.ai_settings.alien_speed * self.ai_settings.fleet_direction) self.rect.x = self.x
# settings.py class Settings: """Store all classes related to alien invasion""" def __init__(self): """Initialize game settings""" # screen setting self.screen_width = 1200 # Set window width self.screen_height = 700 # Set window height self.bg_color = (230, 230, 230) # Set background color # Spacecraft setup self.ship_speed = 1.5 # Set the initial value of the speed self.ship_limit = 3 # Set the maximum number of ships for players # Bullet setting self.bullet_speed = 1 # Bullet speed self.bullet_width = 3 # The width of the bullet self.bullet_height = 15 # Bullet height self.bullet_color = (60, 60, 60) # Bullet color self.bullets_allowed = 3 # Limit the number of bullets that do not disappear to 3 # Alien settings self.alien_speed = 0.5 # Aliens move at a speed of 0.5 self.fleet_drop_speed = 10 # The speed at which aliens move down # fleet_ When direction is 1, it means to move right, and when direction is - 1, it means to move left self.fleet_direction = 1
# ship.py import pygame class Ship: def __init__(self, ai_settings,screen): """Initialize the spacecraft and set its initial position""" self.screen = screen self.moving_right = False # Can I move the sign to the right self.moving_left = False # Can I move the sign to the left self.ai_settings = ai_settings # Load the ship image and obtain its circumscribed rectangle self.image = pygame.image.load("images/ship.bmp") self.rect = self.image.get_rect() # Get the size attribute of the image and save it self.screen_rect = screen.get_rect() # Get the size attribute of the screen and save it # Put each new ship in the center of the bottom self.rect.centerx = self.screen_rect.centerx # Obtain the midpoint data of the x-axis of the screen and assign it to rect self.rect.bottom= self.screen_rect.bottom # Get the bottom position data of the screen and assign it to rect # Store decimals in the attribute center of the spacecraft self.center = float(self.rect.centerx) # Set a new property to store decimals def blitme(self): """Draw the spaceship at the specified location""" self.screen.blit(self.image, self.rect) def update(self): """Check the status of the flag, if the flag is True Just move the ship""" # Update the center value of the spacecraft instead of the rect value if self.moving_right and self.rect.right < self.screen_rect.right: self.center += self.ai_settings.ship_speed if self.moving_left and self.rect.left > 0: self.center -= self.ai_settings.ship_speed # According to self The value of center updates self Value of centerx self.rect.centerx = self.center def center_ship(self): """Center the ship on the screen""" self.center = self.screen_rect.centerx
# bullet.py import pygame from pygame.sprite import Sprite class Bullet(Sprite): """A class used to manage the firing of bullets from a spacecraft""" def __init__(self, ai_settings, screen, ship): """Add a bullet object to the position of the ship""" super(Bullet, self).__init__() self.screen = screen # Create a rectangle representing the bullet at (0, 0) and set the correct position. self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height) self.rect.centerx = ship.rect.centerx # Set the centerx of the bullet to the centerx of the ship self.rect.top = ship.rect.top # Set the top of the bullet to the top of the ship # Store bullet position in decimal self.y = float(self.rect.y) # Store the y coordinate of the bullet in decimal places self.color = ai_settings.bullet_color # Sets the color of the bullet self.speed = ai_settings.bullet_speed # Set the speed of the bullet def update(self): """Move the bullet up""" self.y -= self.speed self.rect.y = self.y def draw_bullet(self): """Draw bullets on the screen""" pygame.draw.rect(self.screen, self.color, self.rect)
# game_function.py import sys # Use the sys module to exit the game import pygame # It contains the functions required for game development from bullet import Bullet from alien import Alien from time import sleep def check_keydown_events(event, ai_settings, screen, ship, bullets): """Operate in response to the key""" if event.key == pygame.K_RIGHT: # Determine whether the right arrow is pressed ship.moving_right = True elif event.key == pygame.K_LEFT: # Determine whether the left arrow is pressed ship.moving_left = True elif event.key == pygame.K_SPACE: # Determine whether the space is pressed fire_bullet(ai_settings, screen, ship, bullets) elif event.key == pygame.K_q: # Judge whether Q is pressed. If Q is pressed, quit the game sys.exit() def check_keyup_events(event, ship): """Response release key""" if event.key == pygame.K_RIGHT: # Right arrow when judging the released ship.moving_right = False elif event.key == pygame.K_LEFT: # Judge whether there is an arrow loosened ship.moving_left = False def check_events(ai_settings, screen, ship, bullets): """Respond to keyboard and mouse events""" for event in pygame.event.get(): if event.type == pygame.QUIT: # If the type of event is exit, it is equivalent to mouse click × sys.exit() elif event.type == pygame.KEYDOWN: # Determine whether a key is pressed check_keydown_events(event, ai_settings, screen, ship, bullets) elif event.type == pygame.KEYUP: # Judge whether the key is released check_keyup_events(event, ship) def update_screen(ai_settings, screen, ship, aliens, bullets): """Update the image on the screen and switch to a new screen""" screen.fill(ai_settings.bg_color) # Fills the screen with the specified color for bullet in bullets: bullet.draw_bullet() ship.blitme() # Display the spacecraft on the screen aliens.draw(screen) # Let aliens appear on the screen pygame.display.flip() # Displays the most recently drawn screen def update_bullets(ai_settings, screen, ship, aliens, bullets): """Update the location of bullets and delete bullets that have disappeared""" bullets.update() # The number of lists or groups should not be modified in the for loop, which will lead to the loss of traversal for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet) check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets) # print(len(bullets)) # Show how many bullets there are. This knowledge test. Deleting this statement at run time can reduce memory def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets): """Respond to the collision between bullets and aliens and generate a new group of aliens after all wasina people died""" # Detect whether a bullet hit the alien. If so, delete the bullet and alien collision = pygame.sprite.groupcollide(bullets, aliens, True, True) if len(aliens) == 0: # If all aliens are eliminated, delete the existing bullets and regenerate aliens bullets.empty() create_fleet(ai_settings, screen, ship, aliens) def fire_bullet(ai_settings, screen, ship, bullets): """If the firing limit is not reached, fire a bullet""" if len(bullets) < ai_settings.bullets_allowed: new_bullet = Bullet(ai_settings, screen, ship) # Create an instance of the bullet bullets.add(new_bullet) # Place bullet instances in groups def get_number_rows(ai_settings, ship_height, alien_height): """Calculate how many lines the screen can hold""" # Calculate how much space is left on the screen available_space_y = ai_settings.screen_height - 3 * alien_height - ship_height # How many lines are there on the calculation screen number_rows = int(available_space_y / (2 * alien_height)) return number_rows def get_number_alien_x(ai_settings, alien_width): """Calculate how many aliens each row can hold""" # Calculate how many positions there are in a row available_space_x = ai_settings.screen_width - 2 * alien_width # Calculate how many aliens a row can hold number_alien_x = int(available_space_x / (2 * alien_width)) return number_alien_x def create_alien(ai_settings, screen, aliens, alien_number, row_number): """Create an alien and put it in the current line""" alien = Alien(ai_settings, screen) # Create an alien alien_width = alien.rect.width # Get the width of an alien alien_height = alien.rect.height alien.x = alien_width + 2 * alien_width * alien_number # Set the initial position of each alien alien.rect.x = alien.x # Determine the vertical position of each alien alien.rect.y = alien_height + 2 * alien_height * row_number aliens.add(alien) # Adding aliens to a group def create_fleet(ai_settings, screen, ship, aliens): """Create alien groups""" alien = Alien(ai_settings, screen) # Create an alien number_alien_x = get_number_alien_x(ai_settings, alien.rect.width) number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height) # Create first row for row_number in range(number_rows): for alien_number in range(number_alien_x): create_alien(ai_settings, screen, aliens, alien_number, row_number) def check_fleet_edges(ai_settings, aliens): """When aliens reach the edge, take corresponding measures""" for alien in aliens.sprites(): if alien.check_edge(): change_fleet_direction(ai_settings, aliens) break def change_fleet_direction(ai_settings, aliens): """Move all aliens down and change their direction""" for alien in aliens.sprites(): alien.rect.y += ai_settings.fleet_drop_speed ai_settings.fleet_direction *= -1 def ship_hit(ai_settings, stats, screen, ship, aliens, bullets): """Responding to a spaceship hit by aliens""" if stats.ships_left > 0: # Reduce the number of ships by one stats.ships_left -= 1 # Clear the list of bullets and aliens aliens.empty() bullets.empty() # Create a new group of aliens and put the spacecraft in the center of the screen create_fleet(ai_settings, screen, ship, aliens) # Create new aliens ship.center_ship() # Move the spacecraft to the center of the screen # suspend sleep(0.5) else: stats.game_active = False def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets): """Check if aliens have reached the bottom of the screen""" screen_rect = screen.get_rect() # Read the matrix information of the screen for alien in aliens: # If the coordinates of the alien's bottom matrix are larger than the screen, the collision response is performed if alien.rect.bottom >= screen_rect.bottom: ship_hit(ai_settings, stats, screen, ship, aliens, bullets) break def update_aliens(ai_settings, stats, screen, ship, aliens, bullets): """Update the location of Aliens""" check_fleet_edges(ai_settings, aliens) aliens.update() # Detect the collision between aliens and spacecraft if pygame.sprite.spritecollideany(ship, aliens): ship_hit(ai_settings, stats, screen, ship, aliens, bullets) check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)