Python Learning Road 12 - Alien

This series is a compilation of notes for the introductory book "Python Programming: From Getting Started to Practice", which belongs to the primary content. The title sequence follows the title of the book.
This chapter is mainly a continuation of the previous one, adding "alien" and the interaction between "alien" and the spacecraft.

1. Review the project

When working on a larger project, it's important to review the development plan before entering each development phase, and figure out what features are going to be implemented in code next. This article will design the following content:

  • Research the code as it is and determine if the code needs to be refactored before implementing new features
  • Add an alien in the upper left corner of the screen and specify appropriate margins
  • Calculate how many aliens can fit on the screen based on the first alien's margins and screen size. Write a loop to fill the top half of the screen
  • Move the alien fleet to the sides and down until the aliens are all shot down, or an alien hits the ship, or an alien reaches the bottom of the screen. If all aliens are shot down, create another batch of aliens. If an alien hits the ship or reaches the bottom of the screen, destroy the ship and create another swarm of aliens.
  • Limit the number of planes available to the player, and the game ends when they are exhausted

I hope you didn't delete the code in the previous post. Before starting the new code, let's check_keydown_events()add the code for "ending the game with the shortcut key Q" in the previous function:

def check_keydown_event(event, ship, ai_settings, screen, bullets):
    -- snip --
    elif event.key == pygame.K_q:
        sys.exit()

2. Create Aliens

First we need to program an alien Alienhuman. Create a new alien.pymodule and add the following code to it:

import pygame
from pygame.sprite import Sprite

class Alien(Sprite):
    """表示单个外星人的类"""
    def __init__(self, ai_settings, screen):
        """初始化外星人并设置其起始位置"""
        super(Alien, self).__init__()
        self.screen = screen
        self.ai_settings = ai_settings

        # 加载外星人图像,并设置其rect属性
        self.image = pygame.image.load("images/alien.bmp")
        self.rect = self.image.get_rect()

        # 每个外星人最初都在屏幕左上角附近
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height

        # 存储外星人的准确位置
        self.x = float(self.rect.x)

    def blitme(self):
        """在指定位置绘制外星人"""
        self.screen.blit(self.image, self.rect)

It Bulletinherits from classes just like Spriteclasses. Now start creating multiline aliens.

2.1 Modify the game_functions.py module

First add a function to the game_functions.pymodule to create the alien fleet:create_fleet()

def create_fleet(ai_settings, screen, ship, aliens):
    """创建外星舰队"""
    alien = Alien(ai_settings, screen)
    # 计算每行能放多少个
    number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
    # 计算能放多少行
    number_rows = get_number_rows(ai_settings, ship.rect.height,
                                  alien.rect.height)

    # 嵌套循环创建外星舰队
    for row_number in range(number_rows):
        for alien_number in range(number_aliens_x):
            # 创建外星人并将其加入舰队
            create_alien(ai_settings, screen, aliens, alien_number, row_number)

Then we add the following three functions in turn (note the parameters of each function), which are also located game_functions.pyin:

get_number_aliens_x(): Calculate how many aliens can fit in a line

def get_number_aliens_x(ai_settings, alien_width):
    """计算每行可容纳多少个外星人"""
    # 左右两侧留出一个外星人的宽度
    available_space_x = ai_settings.screen_width - 2 * alien_width
    # 列间距为一个外星人宽度
    number_aliens_x = int(available_space_x / (2 * alien_width))
    return number_aliens_x

get_number_rows(): Calculate how many lines of aliens can be put

def get_number_rows(ai_settings, ship_height, alien_height):
    """计算屏幕可容纳多少行外星人"""
    # 可用高度 = 窗口高度 - 上方一个外星人高度 - 下方一个飞船高度 - 两个外星人高度作为缓冲空间
    available_space_y = (ai_settings.screen_height - 3 * alien_height - ship_height)
    # 行距为一个外星人高度
    number_rows = int(available_space_y / (2 * alien_height))
    return number_rows

create_alien(): Create Alien

def create_alien(ai_settings, screen, aliens, alien_number, row_number):
    """创建一个外星人并将其放在当前行"""
    alien = Alien(ai_settings, screen)
    # 下面就是根据上面的公式计算每一个外星人在窗口中的位置(这是左上角的坐标)
    alien.x = alien.rect.width * (1 + 2 * alien_number)
    alien.rect.x = alien.x
    alien.rect.y = alien.rect.height * (1 + 2 * row_number)
    aliens.add(alien)

Now we also need to modify the update_screen()function:

def update_screen(ai_settings, screen, ship, bullets, aliens):
    -- snip --
    # 绘制外星人,放在绘制子弹的代码后面,让外星人的图像覆盖掉子弹的图像
    aliens.draw(screen)
    -- snip --

Note that this function adds a parameter aliens, which is an Groupobject, so the method in the code is the same as draw()the method in the previous article bullets.update(), one line of code updates all objects.

2.2 Modify the alien_invasion.py module

Add the code to create the alien in the main program:

def run_game():
    -- snip --
    gf.create_fleet(ai_settings, screen, ship, aliens)

    while True:
        -- snip --
        # 比之前代码多传入了一个aliens参数
        gf.update_screen(ai_settings, screen, ship, bullets, aliens)

-- snip --

Now we execute the program and we will get the following result:
write picture description here

3. Get the alien fleet moving

We will make the alien fleet move to the right in the window, hit the edge of the screen and descend for a certain distance, and then move in the opposite direction until the alien is eliminated, or the alien hits the spaceship, or there is an alien Person reaches the bottom of the form.

3.1 Supplementary settings.py module

class Settings:
    def __init__(self):
        -- snip --
        self.fleet_drop_speed = 10
        # 外星舰队方向标志:1向右,-1向左
        # 也可以用如left, right之类的标志,但这样会很麻烦
        self.fleet_direction = 1

3.2 Modify the alien.py module

We need Aliento add two methods to the class, one for detecting the edge of the form and one for updating the Alienobject:

class Alien(Sprite):
    -- snip --
    def check_edges(self):
        """如果外星人位于屏幕边缘则返回True"""
        screen_rect = self.screen.get_rect()
        return self.rect.right >= screen_rect.right or self.rect.left <= 0

    def update(self):
        """向右移动外星人"""
        # 以后这样的方式会用的很多
        self.x += self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction
        self.rect.x = self.x

If you use text values ​​to control direction, you need to add if-elsestatements to detect the direction the fleet is moving. Given that there are only two possible directions, we use -1and 1to represent it, which makes it easier to change the coordinates of the alien object.

3.3 Modify the game_functions.py module

First, we add a function to the module that updates the alien fleet update_aliens():

def update_aliens(ai_settings, aliens):
    """检查是否有外星人位于屏幕边缘,并更新外星舰队中所有外星人的位置"""
    check_fleet_edges(ai_settings, aliens)
    aliens.update()  # “一键更新”

check_fleet_edges()The function is used to detect whether the fleet has touched the edge of the form, the code is as follows:

def check_fleet_edges(ai_settings, aliens):
    """有外星人到达边缘时采取相应的措施"""
    # 检测舰队中每一个外星人是否碰到了窗体边缘
    for alien in aliens.sprites():
        if alien.check_edges():
            change_fleet_direction(ai_settings, aliens)
            break

change_fleet_direction()The function is used to change the movement direction of the fleet and let the fleet move down. The code is as follows:

def change_fleet_direction(ai_settings, aliens):
    """将外星舰队下移,并改变它们的方向"""
    for alien in aliens.sprites():
        alien.rect.y += ai_settings.fleet_drop_speed
    ai_settings.fleet_direction *= -1

The above three functions are game_functions.pyall the changes in .

3.4 Modify the alien_invasion.py module

In this module we just need whileto add a line of code to the loop:

# 开始游戏的主循环
    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, bullets, aliens)

Finally, run the main program and get the following effect:
write picture description here

I took a static picture, which is actually dynamic.

4. Kill the aliens

For the current program, if a bullet is fired, the bullet will pass through the alien instead of killing it, let's continue to refine the project so that it can kill the alien. The key to doing this is to detect if the bullet image overlaps the alien image, which means a hit.

4.1 Modify game_functions.py

Why to detect the collision between bullets and satellite people, we need to modify the update_bullets()function, here we add update_bullets()parameters, and also call a new function:

def update_bullets(bullets, aliens, ship, screen, ai_settings):
    -- snip --
    check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)

The function is check_bullet_alien_collisions()used to detect the collision between bullets and aliens. When the aliens are eliminated, the existing bullets are emptied and a new alien fleet is generated. Its code is as follows:

def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
    """检测是否有子弹击中了外星人,如果有,就删除相应的子弹和外星人"""
    # 下一篇中我们将用该变量实现分数统计
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)

    #如果外星人被消灭光,则生成新的外星人舰队
    if len(aliens) == 0:
        # 删除现有的子弹并创建新的舰队
        bullets.empty()
        create_fleet(ai_settings, screen, ship, aliens)

sprite.groupcollide()method is used to detect collisions between objects, it compares bulletseach bullet in 's rectwith alienseach alien's rectin , and returns a dictionary. The dictionary has the object in the first parameter as the key and the key in the second parameter as the value, here, the key that bulletscollided with bulletis the value of the alien(not aliens) it collided with! The third parameter indicates whether to delete the object that collided in the first parameter, and the four parameters indicate whether to delete the object that collided in the second parameter.

4.2 Modify alien_invasion.py

Just modify the line of code that calls update_bullets()the function and add a few parameters:

gf.update_bullets(bullets, aliens, ship, screen, ai_settings)

Basic functions are basically completed.

4.3 Supplementary test skills

For the above code, we may need to test whether the new fleet can be created correctly after destroying the aliens, etc. If we use the current game settings, that is, the bullet speed is 1 and the bullet width is 3, then the test will be will be painful. At this point, we can modify the parameters of the game, such as modifying the bullet width to 300 and the bullet speed to 3, which is equivalent to fast-forwarding the game. At this time, the running effect of the code is as follows:
write picture description here
But remember to modify the parameters in the end go back.

5. End the game

Next, we implement the code that the alien encounters the spaceship, the alien reaches the bottom of the window, and the number of spaceships runs out, leading to the end of the game.

5.1 Create GameStats class

First we create a GameStatsclass for storing game information and store it in a game_stats.pyfile:

class GameStats:
    """跟踪游戏的统计信息"""
    def __init__(self, ai_settings):
        """初始化统计信息"""
        # 用于控制游戏启动与否
        self.game_active = True
        self.ai_settings = ai_settings
        self.reset_stats()

    def reset_stats(self):
        """初始化在游戏运行期间可能变化的统计信息"""
        # 重置飞船数
        self.ships_left = self.ai_settings.ship_limit

5.2 Modify settings.py

As can be seen from the above code, we need settings.pyto add a piece of information representing the "number of spaceships" in:

class Settings:
    def __init__(self):
        """初始化游戏的设置"""
        # 屏幕设置
        -- snip --
        # 设置飞船数量限制
        self.ship_limit = 3
        -- snip --

5.3 Modify game_functions.py in response to the collision between the spacecraft and aliens

We detect collisions between aliens and the ship immediately after updating each alien's position, and then check to see if the aliens have reached the bottom of the window. Modify the update_aliens()function and use spritethe spritecollideany()method in to detect collision: compare each element in the second parameter with the first parameter, detect whether there is a collision, and return the first collision object in the second parameter, if there is no collision, then returns None:

# 增加了参数和碰撞检测
def update_aliens(ai_settings, aliens, ship, screen, bullets, stats):
    -- snip --
    # 检测外星人和飞船之间的碰撞
    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)

For this we need to add two functions:

ship_hit(): This function is called when the alien collides with the spaceship

-- snip --
from time import sleep

def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
    """响应被外星人撞到的飞船"""
    # 将ship_left减1
    if stats.ships_left > 0:
        stats.ships_left -= 1

        # 清空外星人列表和子弹列表
        aliens.empty()
        bullets.empty()

        # 创建一群新的外星人,并将飞船恢复到初始位置
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()

        # 暂停
        sleep(0.5)
    else:
        stats.game_active = False

As can also be seen from the above code, we also need Shipto add a center_ship()method to the class:

def center_ship(self):
    """让飞船在屏幕上居中"""
    self.center = self.screen_rect.centerx

check_aliens_bottom(): This function is called when the spaceship reaches the bottom of the window

def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
    """检测是否有外星人到达了屏幕底部"""
    screen_rect = screen.get_rect()
    for alien in aliens.sprites():
        if alien.rect.bottom >= screen_rect.bottom:
            # 和飞船被碰撞是的代码没啥区别,故调用同一个函数
            ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
            break

5.4 Modify the main program alien_invasion.py

Modify the looping part of the game:

# 开始游戏的主循环
while True:
    gf.check_events(ai_settings, screen, ship, bullets)

    # 决定程序运行时该执行的部分
    if stats.game_active:
        ship.update()
        gf.update_bullets(bullets, aliens, ship, screen, ai_settings)
        gf.update_aliens(ai_settings, aliens, ship, screen, bullets, stats)

    gf.update_screen(ai_settings, screen, ship, bullets, aliens)

In the main loop, it needs to be called in any case check_events(), even if the game is inactive; also needs to keep updating the screen so that it can be modified while waiting for the player to choose to restart the game; other functions are too needed only when the game is active transfer.

6. Summary

This article describes:

  • How to add a lot of the same elements to the game;
  • How to create a grid of elements with nested loops;
  • How to control the direction in which objects move on the screen and respond to events;
  • how to detect and respond to element collisions;
  • How to track statistics in-game;
  • How to use flags game_activeto tell if the game is over.

In the next article, which is also the last of this project, we will:

  • Add a Play button to allow players to start the game, and start after the game is over;
  • Speed ​​up the game every time the player destroys a group of aliens;
  • Add a points system.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325636269&siteId=291194637