Pygame 游戏项目:外星人入侵

  项目计划

前言

项目来源
Python编程:从入门到实践_第1版_Eric Matthes

前置知识

  1. 掌握 Python 基础知识
  2. 了解 Python 库、包、模块的概念,并会使用 Python 模块
  3. 掌握 Pygame 的基础知识 (基础的图形编程概念)
  4. 良好的英文读写能力

参考文档

  1. Pygame 官网文档: https://www.pygame.org/docs/
  2. Pygame 学习(C语言中文网):http://c.biancheng.net/pygame/
  3. 参考视频:B站:我是杨同学杨同学,Python系列。和书中是同一个游戏,从面向过程延伸到面向对象,通俗易懂,逻辑性很好。

项目规范

简介
在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船,还可使用空格键进行射击。游戏开始时,一群外星人出现在天空中,他们在屏幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有外星人都消灭干净后,将出现一群新的外星人,他们移动的速度更快。只要有外星人撞到了玩家的飞船或到达了屏幕底部,玩家就损失一艘飞船。玩家损失三艘飞船后,游戏结束。

设计模块
飞船、子弹、外星人、统计游戏信息、玩家交互。

  项目进度

Pygame

1. 基础概念

Python Pygame 是一款专门为开发和设计 2D 电子游戏而生的软件包,是入门级游戏开发库。

它支 Windows、Linux、Mac OS 等操作系统,具有良好的跨平台性。Pygame 由 Pete Shinners 于 2000 年开发而成,是一款免费开源的的软件包。

Pygame 是在 SDL(Simple DirectMedia Layer,使用 C语言编写的多媒体开发库) 的基础上开发而成,它提供了诸多操作模块,比如图像模块(image)、声音模块(mixer)、输入/输出(鼠标、键盘、显示屏)模块等。相比于开发 3D 游戏而言,Pygame 更擅长开发 2D 游戏,比如于飞机大战、贪吃蛇、扫雷等游戏。

pygame.init()

pygame.init() 的作用是自动检测 Pygame 软件包是否正常可用。它会完成 Pygame 中所有模块的初始化操作,比如 display(显示模块)、font(字体模块)、mixer(声音控制模块)、cursors(光标控制模块)等。

2. pygame.display

Pygame 使用 pygame.display 模块控制显示窗口和屏幕。

初始化(创建)要显示的窗口或屏幕:

pygame.display.set_mode()
 # set_mode(size=(0, 0), flags=0, depth=0, display=0, vsync=0) -> Surface

set_mode()函数将创建一个 Surface,传入的参数是对 Surface 的设置。注意!!!调用这个函数会默认地初始化 pygame.display,也就是具有 pygame.display.init() 的作用。

3. pygame.Surface

掌握 Pygame 的编程逻辑,首先要理解 Surface 对象的概念,它是 Pygame 中最重要的部分。

Surface 译为表面,图像。在 Pygame 中,可以把 surface 对象理解成一张“白纸”,通过这张纸可以做许多的事情,比如在纸上添加文字、填充颜色、添加图片以及绘制任意形状的图形。既然是“纸”就有规格尺寸,Pygame 允许您创建任意大小的纸。


创建 Surface 对象的方法:

screen_surface = pygame.display.set_mode() # 创建的主窗口即 Surface
text_surface = pygame.font.Font.render()   # Pygame 无法直接在其他 Surface 上添加文字,当时可以创建一个 Surface,把文字渲染在此 Surface 上面
image_surface = pygame.image.load()        # 加载的图片即为一个 Surface
draw_surface = pygame.draw.rect()          # 绘制一个矩形作为 Surface

Surface 相关的方法:

pygame.Surface.get_rect()
# get the rectangular area of the Surface

pygame.Surface.blit()
# draw one image onto another

4. pygame.event

什么是 event

事件(Event)是 Pygame 的重要模块之一,它是构建整个游戏程序的核心,比如鼠标点击、键盘敲击、游戏窗口移动、调整窗口大小、触发特定的情节、退出游戏等等,这些都可以看做是“事件”,Pygame 会接受用户产生的各种操作(或事件),这些操作随时产生,并且操作量可大可小。

详细内容,参考:https://www.pygame.org/docs/ref/event.html


event 类型

Pygame 定义了一个专门用来处理事件的结构,即事件队列,队列遵循 “先到先处理” 的基本原则。通过事件队列,我们可以有序的、逐一的处理用户的操作(触发事件)。

事件类型用全大写字母的常量名表示,如 QUIT (用户按下窗口的关闭按钮)。

Pygame 制作游戏时,最常处理的两类事件是鼠标事件和键盘事件。

if event.type == pygame.KEYDOWN:   # 判断事件类型
	if event.key == pygame.K_RIGHT:   # 判断判断 KEYDOWN 事件的属性
		ship.rect.centerx += 1    # 飞船向右移动 1 个像素

event 方法

pygame.event 模块提供了处理事件队列的常用方法,比如 pygame.event.get(),从事件队列中获取一个事件,并从队列中删除该事件。

当我们使用 Pygame 处理事件时,逻辑一般都是相似的。首先是判断事件的类型,然后根据不同的事件操作,执行不同的游戏操作。因此这种情况非常适合使用 if ... else 语句,比如:

for event in pygame.event.get():
	if event.type == pygame.QUIT:
		sys.exit()

5. pygame.sprite

精灵(Sprite),在一个游戏程序中,精灵本质指的是一张张小尺寸的图片。

比如,游戏中的各种道具、人物、场景装饰,它们都可以看成一张张小的“精灵”图。因此,人物的移动可以看做是一系列小精灵图构成的序列(按帧组成的序列),如下图所示:

image
图示 动作逐帧分解图

如果将逐帧分解动作图,按照一定的频率播放,那么就形成了精灵动画,你将会看到雄鹰展翅高飞、人在策马奔腾、运动员奋力跳远。

游戏中的精灵之间可以进行交互,也称为碰撞,而碰撞检测,指的就是检测两个精灵之间是否发生了碰撞。

比如,在贪吃蛇游戏中蛇的头部是否与食物发生了碰撞,飞机大战游戏中子弹是否击中了外星人。当检测到碰撞发生后,接下来会触发某些事件,比如子弹击中外星人,外星人消失,玩家得分随之增加,并且在游戏屏幕上又出现一个外星人。

游戏打包

  1. 控制台安装 pyinstaller
    输入命令 pip install pyinstaller。如果出现更新 pip 的警告,则用命令 python -m pip install --upgrade pip 更新 pip。

  2. 进入主程序所在目录

     C:\Users\dell> f:
     F:\> cd F:\1_计算机\3_代码\Python编程:从入门到实践_第1版\python_work\Project_1 Alien Invasion\AlienInvasion_test_file
    
  3. 用 pyinstaller 打包所有的 .py 文件

     F:\1_计算机\3_代码\Python编程:从入门到实践_第1版\python_work\Project_1 Alien Invasion\AlienInvasion_test_file> pyinstaller alien_invasion.py -p settings.py -p game_stats.py -p ship.py -p game_functions.py -p alien.py -p bullet.py
    

    注意!!!第1个文件是主文件,会生成 exe 文件。.py 文件之间用-p 连接。

  4. 终端显示打包完成之后,打开主程序目录,可以看到新增了两个文件夹:Build 和 dist。

  5. 将程序中用到的图片资源文件拷贝一份到 dist 文件夹下与 .exe 文件所在目录的同级目录下。

  6. 将 dist 文件夹下的 文件打包发送给好友,好友执行 .exe文件即可。

  项目总结

边思考学习和埋头写代码

《Python编程:从入门到实践》虽然是一本入门书,但是只看书中的知识和代码,按书中逻辑写完项目的所有代码是达不到用 Python 处理事情的效果的。比如,跟着书写完 “外星人入侵” 项目,即使这个项目可以运行,但是由于书中只是介绍项目设计逻辑和具体用到的方法和属性(比如 pygame.KEYDOWNpygame.event.get()),关于 Pygame 有哪些主要功能模块,常用模块 Event(事件)中有哪些事件类型,这些事件类型描述了什么,有什么属性,可以调用什么方法,并没有介绍。

如果书中在涉及到 Pygame 中 Event(事件)模块时可以用一小节介绍 Event,不必面面俱到,只是给读者树立一个知识框架,让读者知道:

  • Event 是什么
  • Event 事件类型有主要有哪些
  • 主要有哪些针对 Event的方法
  • Event 主要的属性及属性描述

这些在书中只是三言两语,为了避免这边入门书的冗杂,这样处理可以理解,但作为有追求的 Python 使用者,看到这些云里雾里的知识点时,去查阅了解一下,不求深入探究但求树立知识框架是十分有必要的,不然,在自己要独立设计程序时便会显然茫然的状态。

功能实现的细节忘记了,可以快速查询学习,但是连如何实现的底层框架和逻辑都不了解,细节掌握再好又有何用?不要陷入代码量带来的自我欺骗式的自我愉悦,而是多问问自己“我真的掌握了吗?”

重构代码

根据行业经验,一个函数(方法)处理一个小功能是写程序的好方法。

有时,随着开发的深入,原来的小功能可能集合了两个更小的功能,处理这个小功能的函数也变得冗长难读,这是重构代码是使代码更清晰可读的不二法门。

在“外星人入侵”这个项目中,函数check_events()将越来越长,我们将其部分代码放在两个函数中,一个处理KEYDOWN事件,另一个处理KEYUP事件:

原代码

def check_events(ship):
	"""Respond to keypresses and mouse events."""
	for event in pygame.event.get():
		if event.type == pygame.QUIT:
			sys.exit()

		elif event.type == pygame.KEYDOWN:
			if event.key == pygame.K_RIGHT:
				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
			if event.key == pygame.K_LEFT:
				ship.moving_left = False

重构后的代码

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):
	"""Respond to keypresses and mouse events."""
	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)

模块化和库的使用

显然,这个游戏中,作者始终在传递两个重要的忠告:
(1)模块化让代码清晰明了,方便维护。如果有一天你再次看自己代码的时候会想吐,那么会是一件极度糟糕的事情。
(2)为了造车,我们不需要知道怎么造轮子,但是得学会什么轮子配什么车是适合的,以及如何安装上去。

posted @ 2022-07-06 13:04  BodhiLeaf  阅读(360)  评论(0)    收藏  举报