资讯专栏INFORMATION COLUMN

Python学习之路11-武装飞船

李昌杰 / 2252人阅读

摘要:和标志,用于表示飞船是否正在移动,用于实现飞船在不松开按键下连续移动。重写了函数,用于绘制飞船模块该模块主要是集中处理游戏中发生的各种事件。函数用于监听游戏的事件,比如,它表示游戏推出事件和分别表示键盘按下与松开事件。

《Python编程:从入门到实践》笔记。
本章主要学习如何使用pygame编写一个简单的小飞机打外星人的游戏,由于本人对用python写游戏并不是特别感兴趣,所以主要是看代码的构建和一些编程规范,代码会有所简略。
1. 准备工作

Python标准库中并没有自带pygame模块,所以需要自行安装,可以在控制台(Windows下是cmd)上使用命令行安装:pip install pygame。如果你是用的PyCharm,也可以在设置中安装:

点击右边的加号,在弹出的窗口中输入pygame,然后安装即可。

该项目中需要使用一些书中的图片,这些图片都可以在 http://www.ituring.com.cn/boo... 中下载到。

2. 游戏基本内容

首先需要新建一个项目,笔者取名为“alien_invasion”,并在该项目的根目录下新建一个images文件夹,用于存放项目中用到的图片。在本节中,我们将先创建4个文件:

alien_invasion.py:游戏主程序

settings.py:游戏的配置文件

game_functions.py:存放游戏的控制函数,比如响应鼠标、键盘等

ship.py:飞船类

2.1 alien_invasion.py模块:

该模块经过重构后的代码如下:

import pygame

import game_functions as gf
from settings import Settings
from ship import Ship

def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()   # 初始化背景设置,让pygame能正常工作
    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)   # 实例化一个飞船类,传入了参数ai_settings和屏幕对象screen

    # 开始游戏的主循环
    while True:
        gf.check_events(ship)  # 用于响应游戏事件
        ship.update()   # 更新飞船状态
        gf.update_screen(ai_settings, screen, ship)  # 重绘screen

run_game()

①代码第1行导入pygame模块,它包含开发游戏所需的基本功能;

②代码3到5行导入的是自行编写且经过重构的模块;

③第9行代码执行游戏的初始化工作,比如初始化游戏背景等;

④第10行实例化一个游戏配置类,用于配置游戏参数,该类的具体实现见本篇后面的内容;

⑤代码第12-13行用于生成一个名为screen的显示窗口,长宽从配置对象ai_settings中读出;display.set_mode()返回的是一个surface,在pygame中,surface是屏幕的一部分,用于显示游戏元素,这里的screen表示的是整个游戏窗口。我们激活游戏的循环后,每经过一次循环pygame都将重绘这个screen

⑥代码第20行的check_events()函数用于响应游戏中发生的时间,比如鼠标,键盘,关闭窗口等。

⑦代码第21行用于更新飞船的信息,如飞船位置

⑧最后一行用于启动游戏,即初始化游戏,并开始主循环。

2.2 settings.py模块

该文件主要是游戏的配置信息,存放游戏的各种参数。

class Settings:
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        """初始化游戏的设置"""
        # 屏幕设置
        self.screen_width = 1200   # 游戏窗口宽度
        self.screen_height = 800   # 游戏窗口高度
        self.bg_color = (230, 230, 230)  # 游戏背景颜色
        self.ship_speed_factor = 1.5  # 飞船的移动速度

这里故意将飞船的速度设置为浮点数,也可以是整数。在设置游戏元素的位置时,如果直接使用浮点数,则只会截取整数部分。

2.3 ship.py模块

该模块描述了一个飞船类的基本内容:

import pygame

class 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

        # 自定义一个能存储浮点数的临时变量,x坐标
        self.center = float(self.rect.centerx)
        
        # 标志,用于表示是否正在向某个方向移动
        self.moving_right = False
        self.moving_left = False

    def update(self):
        """根据移动标志调整飞船的位置"""
        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

        # 用临时变量更新rect的centerx,截取截取整数部分
        self.rect.centerx = self.center

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

__init__()中的self.center属性,代码将self.rect.centerx即飞船的中心x坐标转换成浮点数,并将其存储在self.center中。之所以转换成浮点数,是因为在settings.py文件中,我们将飞船移动速度设置成了浮点数。

self.moving_rightself.moving_left标志,用于表示飞船是否正在移动,用于实现飞船在不松开按键下连续移动。

udpate()方法,用于增减飞船的中心位置x坐标(因为飞船只能在底部移动,所以不用改y坐标),并防止飞船移动出游戏窗口。

④重写了blitme()函数,用于绘制飞船

2.4 game_functions.py模块

该模块主要是集中处理游戏中发生的各种事件。

import sys
import pygame

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

def check_keyup_event(event, ship):
    """响应松开按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    if 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_event(event, ship)

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

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

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

①在pygame中,用K_RIGHTK_LEFT表示方向按键,其实键盘上每个键在pygame中都有所对于,以K_开头。check_keydown_event()函数和check_keyup_event()函数都是对下面的check_event()的进一步简化,这两个函数的代码均可以放在check_event()中,但这样代码将会很臃肿,结构不清晰。

check_event()函数用于监听游戏的事件,比如pygame.QUIT,它表示游戏推出事件;pygame.KEYDOWNpygame.KEYUP分别表示键盘按下与松开事件。本次大循环中(外层的while循环)发生的所有事件都存储在pygame.event中,我们使用get()方法获得这些事件。

③在update_screen()函数中,我们使用screenfill()方法填充窗体的背景色,调用blitme()方法来在窗体中绘制飞船,最后,调用pygame.display.flip()方法让最近的绘制在窗体中可见。

2.5 运行游戏

现在我们运行alien_invasion.py文件,我们将得到如下窗体:

目前功能还比较简单,只能实现飞船的左右移动。

3. 添加射击功能

为了添加射击功能,需要先添加一个子弹类。

3.1 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           # 从飞机的顶部射出

        # 存储用浮点数表示的子弹位置,因为子弹只在y轴上运动,所以不需要x坐标
        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)

首先我们需要导入pygame模块以及其中的Sprite类(直译的话叫做“精灵类”,然而这名字叫的真的很尴尬),它可以让我们在后面方便批量处理相同类型的同一操作,子弹类继承自Sprite类。该子弹类并没有使用图片,而是直接在screen上绘制矩形用于表示子弹。update()方法用于更新子弹的位置。pygame.draw.rect()用于在screen上绘制子弹。

3.2 修改settings.py

在该模块中添加子弹类的参数:

class Settings:
    def __init__(self):
        -- snip --
        # 子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        # 表示窗口中最多允许存在的子弹数,当然你也可以将其去掉
        self.bullets_allowed = 3   
3.3 修改game_functions.py

游戏中我们按空格键发射子弹,并发射子弹的过程多带带写在一个函数fire_bullet()中。为了响应空格键,需要修改check_event()函数和check_keydown_event()函数,前者只修改了参数,后者在判断结构中添加了一个判断。有了子弹类,那我们还需要在screen中绘制子弹,所以还需要修改update_screen()函数,而子弹自身信息(比如子弹的移动)的修改则放在了一个新的函数update_bullets()中。

import sys
import pygame
from Bullet import Bullet

# 新增函数!
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)

# 修改了参数!
def check_keydown_event(event, ship, ai_settings, screen, bullets):
    -- snip --
    # 按空格键发射子弹
    elif event.key == pygame.K_SPACE:
        fire_bullet(ai_settings, screen, ship, bullets)

# 修改了参数!
def check_events(ai_settings, screen, ship, bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        -- snip -- 
        elif event.type == pygame.KEYDOWN:
            # 增加了参数
            check_keydown_event(event, ship, ai_settings, screen, bullets)
        -- snip --

# 修改了函数!
def update_screen(ai_settings, screen, ship, bullets):
    -- snip --
    # 绘制子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    -- snip --

# 新增函数
def update_bullets(bullets):
    """更新子弹的位置,并删除已消失的子弹"""
    # 更新子弹的位置
    # bullets是个Group对象,调用一次update()就会调用其中所有Sprite对象的update()
    # 相当于你不用自己写for循环了
    bullets.update()

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

当子弹从窗口中消失时,它并没有从内存中消失,如果对于已经从屏幕中消失的子弹不做处理的话,时间一长,子弹数一多,光子弹一项的内存占用就会越来越多(土豪请忽略),虽然只是线性增长,但作为一个合格的程序员,应该避免这种无谓的浪费。

3.4 修改alien_invation.py

最后,我们修改主程序,在其中添加一个pygame.sprite中的Group对象用于表示子弹集合,以及对该对象的操作代码。

import pygame
from pygame.sprite import Group   # 导入一个新类

import game_functions as gf
from settings import Settings
from ship import Ship

def run_game():
    -- snip --
    bullets = Group()

    # 开始游戏的主循环
    while True:
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_screen(ai_settings, screen, ship, bullets)
3.5运行新代码

以下是运行截图:

4. 小结

自此,我们创建了一个能开火的小飞机,在下一篇文章中我们将向其中添加外星人。

本篇中的代码都是经过了重构后的代码,但是,当我们自己在编程时,如果对某一框架还是小白,搞不清楚该如何组织代码,那就把所有代码都写在一个或几个文件里(虽然这种习惯很不好),也暂时不用考虑代码结构之类的问题,因为你的任务是造东西,而不是写漂亮代码,用精巧结构,用别人没看过的语法。两者能兼备当然更好,但每个人都有当小白的时期,有一定熟练度后,再来考虑代码重构的问题。


迎大家关注我的微信公众号"代码港" & 个人网站 www.vpointer.net ~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41790.html

相关文章

  • Python学习之路12-外星人

    摘要:现在开始创建多行外星人。小结本篇讲述了如何在游戏中添加大量相同的元素如何用嵌套循环来创建元素网格如何控制对象在屏幕上移动的方向以及响应事件如何检测和响应元素碰撞如何在游戏中跟踪统计信息如何使用标志来判断游戏是否结束。 《Python编程:从入门到实践》笔记。本章主要是对上一篇的继续,添加外星人,外星人与飞船的交互。 1. 回顾项目 开发较大的项目时,进入每个开发阶段前回顾一下开发计划,...

    chemzqm 评论0 收藏0
  • Python学习之路13-记分

    摘要:之所以这里要添加这四行代码,其实是为了当你重新开始也就是第二次及以后点击按钮游戏时,计分板能正确显示。当第一运行游戏时,没有这四行也能正确显示计分板。 《Python编程:从入门到实践》笔记。本篇是Python小游戏《外星人入侵》的最后一篇。 1. 前言 本篇我们将结束Pygame小游戏《外星人入侵》的开发。在本篇中,我们将添加如下内容: 添加一个Play按钮,用于根据需要启动游戏以...

    tommego 评论0 收藏0
  • 前端开发-从入门到Offer - 收藏集 - 掘金

    摘要:一些知识点有哪些方法方法前端从入门菜鸟到实践老司机所需要的资料与指南合集前端掘金前端从入门菜鸟到实践老司机所需要的资料与指南合集归属于笔者的前端入门与最佳实践。 工欲善其事必先利其器-前端实习简历篇 - 掘金 有幸认识很多在大厂工作的学长,在春招正式开始前为我提供很多内部推荐的机会,非常感谢他们对我的帮助。现在就要去北京了,对第一份正式的实习工作也充满期待,也希望把自己遇到的一些问题和...

    sf_wangchong 评论0 收藏0
  • 阿里云推企业云安全架构 11层防护武装到“牙齿”

    摘要:年,阿里云在全球范围内率先发起数据保护倡议。借助阿里云的网络溯源,警方最终成功抓捕到名犯罪嫌疑人,将黑客组织一网打尽。过去两年,阿里云已陆续协助警方破获案件数十起攻击相关案件,抓捕百余人次。9月28日,阿里云正式发布首个企业云安全架构和《2017阿里云安全白皮书》(以下简称白皮书),企业可参考架构指南和白皮书构建安全、稳固的信息化架构。白皮书将用户隐私和数据安全列为第一原则,并于2015年全...

    Kaede 评论0 收藏0
  • 关于开发Python项目的心得总结

    摘要:最近照着编程从入门到实践这本书上的内容,开发了第一个完整的项目。之前都是用写一些脚本什么的,这是第一次开发一个完整的项目,现将在开发过程中的一些心得总结如下。 最近照着《Python编程从入门到实践》这本书上的内容,开发了第一个完整的Python项目。之前都是用Python写一些脚本什么的,这是第一次开发一个完整的项目,现将在开发过程中的一些心得总结如下。 这个Python项目是一个小...

    zhoutao 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<