基于Pygame的python游戏开发——外星人入侵

指导书籍:《Python编程:从入门到实践》【美】Eric Matthes 著 【中】袁国忠 译

集成环境:Pycharm

游戏《外星人入侵》描述:

  你说得对,但是《外星人入侵》是一款简单的2D射击小游戏,在游戏中你将操控出现在屏幕底部中央的飞船,玩家可以使用箭头键左右移动飞船,并可以用空格键进行攻击。游戏开始时,一群外星人出现在天空中,它们在屏幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有的外星人都消灭后,将出现一群新的外星人,它们的移动速度更快。只要外星人撞到了玩家的飞船或者触碰到了屏幕底部,玩家就损失一艘飞船。当玩家损失的三艘飞船,游戏结束。

Log-2023/4/26----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

安装Pygame

下载地址:https://pypi.org/project/pygame/#files

 

我是win64-python3.9,下载对应的版本py39

 

下载后将.whl文件放到项目文件夹下,打开终端准备安装

安装命令:python -m pip install --user 文件名

 

Enter运行

 

安装成功!

查看是否将Pygame纳入环境:settings--project:alien_invasion--Python interpreter,发现并没有pygame,原因是当前环境不是我用编译器所在的环境。因为安装了anaconda来管理环境,输入

conda info --env查看已有的python环境:

 

打开Anaconda Navigator看看;

 

出师不利!pygame的确是装到了base环境下,但是包不在。

 

解决方法找到了,就是不要用conda的包管理,把它点掉!

 

点击应用,pygame安装成功!

 Log-2023/4/27----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

创建游戏窗口

alien_invasion.py

import sys
import pygame


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    screen = pygame.display.set_mode((1200, 800))
    pygame.display.set_caption("Alien Invasion")

    # 开始游戏的主循环
    while True:
# 监视键盘和鼠标时间 for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # 让最近绘制的屏幕可见 pygame.display.flip() run_game()

 

定义一个函数入口,进入函数后初

 

始化背景设置,让Pygame能正常工作。

pygame.display.set_mode((1200, 800))创建一个名为screen的尺寸(1200,800)的显示窗口,游戏的所有元素都在这个窗口里显示。

pygame.display.set_caption("Alien Invasion")将窗口抬头设置为“Alien Invasion”。

创建一个循环来监视鼠标和键盘事件,循环以while实现,检测以for...in实现,pygame.event.get()获取事件,当检测到用户点击退出,将调用sys.exit()关闭窗口

pygame.display.flip()在每次while循环后都绘制一个空屏幕并撤去旧屏幕,使得仅新屏幕可见。当我们移动游戏元素时,这行代码将不断更新屏幕以显示新元素位置并擦去旧元素,达到平滑移动的效果。

alien_invaison.py

import sys
import pygame


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    screen = pygame.display.set_mode((1200, 800))
    pygame.display.set_caption("Alien Invasion")

    # 设置背景色
    bg_color = (230, 230, 230)
    # 开始游戏的主循环
    while True:

        # 监视键盘和鼠标时间
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # 每次循环时都重绘屏幕
        screen.fill(bg_color)
        # 让最近绘制的屏幕可见
        pygame.display.flip()


run_game()

设置背景色(230,230,230)并加入到循环中,用screen.fill()接收背景色,这时屏幕变成了灰白。

创建设置类

settings.py

class Settings(object):
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
        # 屏幕设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

相应地,之前的代码修改后如下:

alien_invasion.py

import sys
import pygame
from settings import Settings


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 开始游戏的主循环
    while True:

        # 监视键盘和鼠标时间
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # 每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
        # 让最近绘制的屏幕可见
        pygame.display.flip()


run_game()

调用了Settings类里的属性。

添加飞船图像

Pygame默认加载位图.bmp,选择素材时务必注意许可。可以去https://pixabay.com/下载。

在主项目文件里创建images文件夹,并将图片保存在里面。

创建ship类

import pygame


class Ship(object):
    """创建ship类"""
    def __init__(self, screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen

        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()

        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

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

Ship方法__init__接收两个参数:引用self和screen,后者指定将飞船绘制到哪个地方。

pygame.image.load()用来加载图像,它返回一个表示飞船的surface,我们将这个surface存储在了self.image。

加载图像后,get_rect()获取image的surface的属性rect,python能像处理矩形一样处理游戏元素。处理rect对象时,可以使用矩形的四个角和中心的坐标(x,y)。通过设置这些值来指定矩形的位置。

补充:

要将游戏元素居中:设置相应rect属性中的center、centerx、centery。

要让游戏元素与屏幕边缘对齐:top、bottom、left/right。

调整游戏元素的水平或垂直位置:x/y。它们分别是相应矩形左上角的x/y坐标。

在pygame中,原点(0,0)在屏幕左上角,1200×800的屏幕右下角为(1200,800)。

self.screen_rect = screen.get_rect()将屏幕矩形存储在self.screen_rect中,再将飞船的中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性(self.screen_rect.centerx)。并将飞船下边缘的y坐标(self.rect.bottom)设置为表示屏幕的矩形的属性bottom(self.screen_rect.bottom)。经过以上操作,使得飞船图像与屏幕下边缘对齐并水平居中,随意画了个草图表示一下。

在屏幕上绘制飞船

更新alien_invasion.py,使其创建一艘飞船,并调用其方法blitme()。

alien_invasion.py

import sys
import pygame
from settings import Settings
from ship import Ship


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 创建一艘飞船
    ship = Ship(screen)
    
    # 开始游戏的主循环
    while True:

        # 监视键盘和鼠标时间
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # 每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
        ship.blitme()
        # 让最近绘制的屏幕可见
        pygame.display.flip()


run_game()

导入Ship类,在创建屏幕后创建一个名为ship的Ship实例。要在While之前创建它,否则每次循环都会创建一艘飞船。填充背景后,调用ship.blitme()将飞船绘制到屏幕上,确保它出现在背景前面。

 Log-2023/4-28-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

重构模块,方便管理。建立game_functions.py

game_functions.py

import sys
import pygame

def check_events(): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit()

重构后的alien_invasion.py

import game_functions as gf
...
# 开始游戏的主循环
    while True:

        # 监视键盘和鼠标时间
        gf.check_events()

        # 每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
        ship.blitme()
        # 让最近绘制的屏幕可见
        pygame.display.flip()
...

重构run_game中更新屏幕的代码,在game_functions.py中添加:

...
def
update_screen(ai_settings, screen, ship): """更新屏幕上的图像,并切换到新屏幕""" # 每次循环时都重绘屏幕 screen.fill(ai_settings.bg_color) ship.blitme() # 让最近绘制的屏幕可见 pygame.display.flip()
...

alien_invasion.py:

...
    while True:

        # 监视键盘和鼠标时间
        gf.check_events()

        # 每次循环时都重绘屏幕;让最近绘制的屏幕可见
        gf.update_screen(ai_settings, screen, ship)
...

接下来编写控制飞船上下左右移动的代码:

game_functions.py

...
def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                # 向右移动飞船
                ship.rect.centerx += 1
...

alien_invasion.py

...
gf.check_events(ship)
...

在检测事件环节加入了检测右键按下,传入形参ship,更新。这是一个开端,但并不是高效的方法,因为按下一次移动1像素,但持续按住并不会移动。

允许不断移动:设置标志moving_right,飞船不动时,moving_right = False,玩家按下右键时,变为True,松开后又设置为False。为ship类添加moving_right属性和update()方法,每次调整飞船位置时都调用这个方法。

ship.py

...
 # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

        # 移动标志
        self.moving_right = False

    def update(self):
        """根据移动标志调整飞船的位置"""
        if self.moving_right:
            self.rect.centerx += 1
...

接下来修改check_events(),使其在玩家按下右键时将moving_right设置为True,松开时设置为False

game_functions.py

...
def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                # 向右移动飞船
                ship.moving_right = True

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False
...

下面修改alien_invasion.py中的while循环,使其每次循环时都调用update():

alien_invasion.py

...
 while True:

        # 监视键盘和鼠标时间
        gf.check_events(ship)
        ship.update()
        # 每次循环时都重绘屏幕;让最近绘制的屏幕可见
        gf.update_screen(ai_settings, screen, ship)
...

能向右持续移动了,现在尝试一下自己添加向左移动的逻辑。

ship.py

...
     # 移动标志
        self.moving_right = False
        self.moving_left = False

    def update_right(self):
        """根据移动标志调整飞船向右"""
        if self.moving_right:
            self.rect.centerx += 1

    def update_left(self):
        """根据移动标志调整飞船向左"""
        if self.moving_left:
            self.rect.centerx -= 1
...

game_funcitons.py

...
def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                # 向右移动飞船
                ship.moving_right = True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                # 向左移动飞船
                ship.moving_left = True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                ship.moving_left = False
...

alien_invaison.py

...
    while True:

        # 监视键盘和鼠标时间
        gf.check_events(ship)
        ship.update_right()
        ship.update_left()
        # 每次循环时都重绘屏幕;让最近绘制的屏幕可见
        gf.update_screen(ai_settings, screen, ship)
...

运行后发现按下左键并不能移动。

书本的答案是:

ship.py

...
    def update(self):
        """根据移动标志调整飞船向右"""
        if self.moving_right:
            self.rect.centerx += 1
        """根据移动标志调整飞船向左"""
        if self.moving_left:
            self.rect.centerx -= 1
...

game_functions.py

...
def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                # 向右移动飞船
                ship.moving_right = True
            elif event.key == pygame.K_LEFT:
                # 向左移动飞船
                ship.moving_left = True

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False
            elif event.key == pygame.K_LEFT:
                ship.moving_left = False
...

修改后能够左右移动了,同时按住左右按键可以停止不动。接下来调整飞船的速度和移动范围,避免移动过快和移动到屏幕外面去。

调整飞船速度

settings.py

class Settings(object):
    """存储《外星人入侵》的所有设置的类"""
    def __init__(self):
        """初始化游戏的设置"""
        # 屏幕设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 255)

        self.ship_speed_factor = 1.5

添加新属性,为使飞船一次移动1.5个像素点。然而rect的centerx属性只能存储整数值,因此做以下修改

ship.py

...
class Ship(object):
"""创建ship类"""
def __init__(self, ai_settings, screen):
"""初始化飞船并设置其初始位置"""
self.screen = screen
self.ai_settings = ai_settings

# 加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()

# 将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom

# 在飞船的属性 center中存储小数值
self.center = float(self.rect.centerx)

# 移动标志
self.moving_right = False
self.moving_left = False

def update(self):
"""根据移动标志调整飞船向右"""
# 更新飞船的center值,而不是rect
if self.moving_right:
self.center += self.ai_settings.ship_speed_factor
"""根据移动标志调整飞船向左"""
if self.moving_left:
self.center -= self.ai_settings.ship_speed_factor

# 根据self.center更新rect对象
self.rect.centerx = self.center
...

alien_invasion.py

给ship实例添加上ai_settings实参。

...
ship = Ship(ai_settings, screen)
...

限制飞船活动范围

让飞船到达屏幕边缘后停止移动。

ship.py

...
    def update(self):
        """根据移动标志调整飞船向右"""
        # 更新飞船的center值,而不是rect
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        """根据移动标志调整飞船向左"""
        if self.moving_left and self.rect.left > 0:
            self.center -= self.ai_settings.ship_speed_factor
...

仅当飞船在屏幕内时才调整self.center的值,此时运行alien_invasion.py,飞船在触碰到左右边缘后会停止运动。

 Log-2023/4/29--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来重构check_event(),因为随着开发进度的深入,check_event()会变得越来越长。将部分代码放到两个函数里,一个是处理KEYDOWN事件,一个是处理KEYUP事件。

game_functions.py

...
def check_keydown_events(event, ship):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True


def check_keyup_events(event, ship):
    """响应松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False


def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ship)

        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)

...

新的两个函数是从check_event中复制过来的。check_event()变得更加清晰了。

添加射击功能

描述:按空格键发射子弹,子弹在屏幕中向上穿行,抵达屏幕上边缘后消失。

首先,更新settings.py

settings.py

...
    # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_width = 15
        self.bullet_color = 60, 60, 60
...

创建Bullet类

bullet.py

import pygame
from pygame.sprite import Sprite


class Bullet(Sprite):
    """一个对飞船发射的子弹进行管理的类"""
    def __init__(self, ai_settings, screen, ship):
        """在飞船所处的位置创建一个子弹对象"""
        super(Bullet, self).__init__()
        self.screen = screen

        # 在(0,0)处创建一个表示子弹的矩形,再设置正确的位置
        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width,
                                ai_settings.bullet_height)
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top
        # 存储用小数表示的子弹位置
        self.y = float(self.rect.y)

        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor
  def update(self):
   """向上移动子弹"""
  # 更新表示子弹位置的小数值
   self.y -= self.speed_factor
   # 更新表示子弹的rect的位置
   self.rect.y = self.y

  def draw_bullet(self):
   """在屏幕上绘制子弹"""
   pygame.draw.rect(self.screen, self.color, self.rect)

将子弹存储到编组中

alien_invasion.py

import pygame
from settings import Settings
from ship import Ship
from pygame.sprite import Group
import game_functions as gf


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # 创建一艘飞船
    ship = Ship(ai_settings, screen)

    # 创建一个用于存储子弹的编组
    bullets = Group()

    # 开始游戏的主循环
    while True:
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()
        # 每次循环时都重绘屏幕;让最近绘制的屏幕可见
        gf.update_screen(ai_settings, screen, ship, bullets)


run_game()

开火

game_functions.py

import sys
import pygame
from bullet import Bullet


def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key == pygame.K_SPACE:
        # 创建一颗子弹,并将其加入到编组bullets中
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)


def check_keyup_events(event, ship):
    """响应松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False


def check_events(ai_settings, screen, ship, bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, screen, ship, bullets)

        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)


def update_screen(ai_settings, screen, ship, bullets):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)

    # 在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
        
    ship.blitme()

    # 让最近绘制的屏幕可见
    pygame.display.flip()

优化内存,删除已经消失的子弹,检测子弹的rect的bottom的属性为0,表明子弹已经穿过了屏幕顶端

alien_invaison.py

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

        # 删除已经消失的子弹
        for bullet in bullets.copy():
            if bullet.rect.bottom <= 0:
                bullet.remove(bullet)
        # print(len(bullets))

        # 每次循环时都重绘屏幕;让最近绘制的屏幕可见
        gf.update_screen(ai_settings, screen, ship, bullets)
...

限制子弹数量,防止盲目射击

settings.py

    # 子弹设置
        self.bullet_speed_factor = 0.5
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullet_allowed = 3

game_fucntions.py

...
        # 创建一颗子弹,并将其加入到编组bullets中
        if len(bullets) < ai_settings.bullets_allowed:
            new_bullet = Bullet(ai_settings, screen, ship)
            bullets.add(new_bullet)
...

创建函数update_bullets(),添加到game_funcitons.py末尾

game_functions.py

...
def update_bullets(bullets):
    """更新子弹的位置,并删除已消失的子弹"""
    # 更新子弹的位置
    bullets.update()

    # 删除已经消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

alien_invasion.py

...
    while True:
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        # 每次循环时都重绘屏幕;让最近绘制的屏幕可见
        gf.update_screen(ai_settings, screen, ship, bullets)
...

接下来将发射子弹的代码移动到一个独立的函数中。

game_functions.py

...
def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key == pygame.K_SPACE:
        # 创建一颗子弹,并将其加入到编组bullets中
        fire_bullet(ai_settings, screen, ship, bullets)


def fire_bullet(ai_settings, screen, ship, bullets):
    """如果没有达到限制,就发射一颗子弹"""
    # 创建新子弹,并将其加入到编组bullets中
    if len(bullets) < ai_settings.bullets_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)
...

 Log-2023/4/30-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

创建外星人

在此之前,添加一个结束游戏的快捷键:Q

...
def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key == pygame.K_SPACE:
        # 创建一颗子弹,并将其加入到编组bullets中
        fire_bullet(ai_settings, screen, ship, bullets)
    elif event.key == pygame.K_q:
        sys.exit()
...

但是发现按下Q并不能退出,反而飞船不能移动了,需要按下空格才能继续移动飞船。原因是需要按下大写的Q才能退出,而小写的q会引发错误,别的字母没有反应,不知道具体原因。

创建第一个外星人

编写外星人类

alien.py

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)

每个外星人最初都位于屏幕的左上角附近,我们将外星人的左边距都设置为外星人的宽度,将上边距设置为外星人的高度。

创建Alien实例

alien_invasion.py

...
    # 创建一个外星人
    alien = Alien(ai_settings, screen)

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

让外星人出现在屏幕上

game_functions.py

...
def update_screen(ai_settings, screen, ship, alien, bullets):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)

    # 在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()

    ship.blitme()
    alien.blitme()
    
    # 让最近绘制的屏幕可见
    pygame.display.flip()
...

接下来绘制一群外星人

 确定一行能容纳多少外星人:让屏幕两侧留下一定空白,外星人之间也要相隔,因此可用于放置外星人的水平空间为屏幕宽度减去两倍外星人的宽度:available_space_x = ai_settings.screen_width - (2*alien_width)

为确定一行可容纳多少外星人,可以用空间除以两倍外星人的宽度:number_alien_x = available_space_x / (2*alien*width)

首先在alien_invasion.py创建一个名为aliens的空编组,用于存储全部外星人,再调用game_functions.py中创建外星人群的函数

alien_invasion.py


import pygame
from settings import Settings
from ship import Ship
from pygame.sprite import Group
import game_functions as gf

... aliens
= Group() # 创建外星人群 gf.create_fleet(ai_settings, screen, aliens) # 开始游戏的主循环 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() ...

在game_functions.py中修改update_screen,并创建外星人群

game_functions.py

import sys
import pygame
from bullet import Bullet
from alien import Alien
...
def update_screen(ai_settings, screen, ship, aliens, bullets):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)

    # 在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()

    ship.blitme()
    aliens.draw(screen)

    # 让最近绘制的屏幕可见
    pygame.display.flip()
...
def create_fleet(ai_settings, screen, aliens):
    """创建外星人群"""
    # 创建一个外星人,并计算一行可容纳多少个外星人
    # 外星人之间的间距为外星人宽度
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    available_space_x = ai_settings.screen_width - 2*alien_width
    number_aliens_x = int(available_space_x / (2 * alien_width))

    # 创建第一行外星人
    for alien_number in range(number_aliens_x):
        # 创建一个外星人并将其加入当前行
        alien = Alien(ai_settings, screen)
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.rect.x = alien.x
        aliens.add(alien)

 

posted on 2023-04-26 19:23  pingu_1  阅读(126)  评论(0)    收藏  举报