文章目录
前言
开发游戏是学习语言的理想方式。看别人玩你写的游戏能获得满足感,而编写简单的游戏有助于你明白专业级游戏是怎样编写出来的。在阅读本文章的同时,请手动输入每一行代码,以明白各行代码的用处,并尝试不同的值和设置,以对如何改进游戏的交互性有更深的认识。
提示:以下为本文主要内容,共分为六个部分,具体可以参考本文目录。
1、写在前面
本文时基于Python开发的一款小游戏,你必须具备一定的Python基础,这样你写起来才能得心应手。
如果你对Python游戏感兴趣,请接着看下去。
2、对游戏的基本介绍
2.1 游戏界面
这款游戏类似于“飞机大战”,就是一开始你会控制一架飞机,在屏幕的底端,你可以左右移动,发射子弹,然后射杀外星人。随着游戏的进行,游戏的节奏会越来越快,飞船的移动速度加快,外星人的下降速度也会变快。界面大概长这个样子:
2.2 项目规划
开发大型项目时,制定好规划后再动手编写代码很重要。规划可以确保你不偏离轨道,从而提高项目成功的可能性。
这是有关“外星人入侵”游戏的描述,其中大概的描述了游戏的玩法:
游戏开始时,玩家驾驶一架飞船出现在屏幕的底部中央,玩家可以通过方向键控制飞船的左右移动,还可以按空格键发射子弹,用来射杀出现在屏幕顶部的外星人。当所有的外星人都被射杀后,将会出现一群新的外星人,并且移动速度更快,但同时你的飞船移动速度也会变快,子弹飞行速度也会更快。当由外星人碰到飞船或到达屏幕底部时,玩家就损失一架飞船,当所有飞船都损坏时,游戏结束。
3、安装库
该游戏使用到的库有Pygame.
3.1使用pycharm安装
我使用的编译器是pycharm,如果有想了解的可以自行搜索。下面来介绍安装流程。
1.打开pycharm设置(Ctrl+Alt+s)
2.找到Project:(Project name)
3.点击Python Interpreter
4.点击左上角的加号(版本可能不同,找那个小加号就行)
5.搜索Pygame
6.点击Install Package
7.安装完成会弹出一个绿色的条儿
3.2 使用cmd安装
快捷键:
1.win+R
2.输入cmd
3.输入pip install Pygame
安装完成会显示Successfully installed Pygame
4、着手编写程序
做好一切准备工作后,现在来正式编写代码。
首先创建一个Pygame窗口,在这个窗口里我们来显示我们的游戏画面。
4.1 创建Pygame游戏窗口以及响应用户输入
在文本编辑器中新建一个文件,将其保存为alien_invasion.py 在其中输入以下代码:
import sys
import pygame
class AlienInvasion:
"""管理游戏资源和行为的类"""
def __init__(self):
"""初始化游戏并创建游戏资源"""
pygame.init() # 对硬件的初始化(initialize) 可以看下面的链接
self.screen = pygame.display.set_mode((1200, 800)) # 设置窗口大小
pygame.display.set_caption("Alien Invasion") # 设置窗口上的游戏名
def run_game(self):
"""开始游戏的主循环"""
while True:
# 监视键盘和鼠标事件。
for event in pygame.event.get():
if event.type == pygame.QUIT: # 鼠标点击右上角的叉叉时就退出
sys.exit()
# 让最近绘制的屏幕可见
pygame.display.flip()
if __name__ == '__main__': # 见下方链接
# 创建游戏实例并运行游戏
ai = AlienInvasion() # 创建一个AlienInvasion实例
ai.run_game() # 调用run_game方法
if name == ‘main’:链接
首先,导入模块sys和pygame,模块中包含了开发游戏所具有的功能,玩家退出时,就使用了模块sys中的工具退出游戏。
接下来,创建了一个类AlienInvasion,在这个类中调用pygame.display.set_mode来设置窗口大小,并将此赋给属性self.screen,目的是让这个类中的所有方法都可以使用它。
这个游戏由方法run_game控制。该方法包含一个while循环,这个循环包含一个事件循环和管理屏幕更新的代码。事件是用户玩游戏时进行的操作,如按键或移动鼠标。
为访问Pygame检测到的事件,这里使用了pygame.event.get(),并创建了一个循环,在这个循环中,使用if语句来检测并响应特定的事件。例如,当玩家单击右上角的关闭按钮时,将检测到pygame.QUIT事件,进而调用sys.exit()来退出游戏。
在while循环的最后,调用了pygame.display.flip()函数,这个函数在每次执行while循环时,都会被调用。玩家对游戏做出一定操作后,pygame.display.flip()将不断更新屏幕,所以玩家可以看到游戏元素的移动。
在文件的末尾,创建一个游戏实例并调用run_game()。这时如果运行alien_invasion.py,将看到一个空的Pygame 窗口。
4.2 设置背景色
Pygame默认创建一个黑色的屏幕,我们来添加一个属性,改变一下游戏窗口的背景颜色。
在方法__init()__的末尾添加属性bg_color:
self.bg_color = (230, 230, 230) # 这是一种浅灰色
然后在方法run_game()中调用此属性:
def run_game(self):
"""开始游戏的主循环"""
while True:
# 监视键盘和鼠标事件。
for event in pygame.event.get():
if event.type == pygame.QUIT: # 鼠标点击右上角的叉叉时就退出
sys.exit()
# 每次循环时都重新绘制屏幕
self.screen.fill(self.bg_color)
# 让最近绘制的屏幕可见
pygame.display.flip()
如果现在运行代码,将会出现一个灰色的窗口。
4.3 创建一个设置的类
一个游戏通常由很多的设置,下面我们创建一个settings模块,将所有的设置都放在其中。这样,每当需要访问设置时,只需使用一个设置对象。另外,在项目增大时,每次修改设置将变得更加容易。
创建一个新文件,并命名为setting.py。并添加一个类Settings:
class Settings:
"""存储游戏Alien_invasion中所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
# 屏幕设置
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
为了在项目中创建Settings实例并用它来访问设置,需要将alien_invasion.py修改成这样:
首先在主程序文件alien_invasion.py中导入Settings类:
from settings import Settings
然后修改类AlienInvasion:
def __init__(self):
"""初始化游戏并创建游戏资源"""
pygame.init() # 对硬件的初始化(initialize) 可以看下面的链接
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion") # 设置窗口上的游戏名
def run_game(self):
"""开始游戏的主循环"""
while True:
# 监视键盘和鼠标事件。
for event in pygame.event.get():
if event.type == pygame.QUIT: # 鼠标点击右上角的叉叉时就退出
sys.exit()
# 每次循环时都重新绘制屏幕
self.screen.fill(self.settings.bg_color)
# 让最近绘制的屏幕可见
pygame.display.flip()
如果这时运行代码,结果不会有任何不同。
4.4 添加飞船图像
这就是飞船的图像,可右键保存到本地。
4.4.1 创建ship类
创建一个新的文件,命名为ship,并创建Ship类。提前将小飞船的图像和程序放在一个文件夹里。
import pygame
class Ship:
"""管理飞船的类"""
def __init__(self, ai_game):
"""初始化飞船并设置其初始位置"""
self.screen = ai_game.screen
self.screen_rect = ai_game.screen.get_rect()
# 加载飞船图像并获取其外界矩形
self.image = pygame.image.load('ship.png')
self.rect = self.image.get_rect()
# 对于每艘新的飞船,都将其放在屏幕底部的中央
self.rect.midbottom = self.screen_rect.midbottom # 意思是飞船底部中央和屏幕底部中央对齐
def blitme(self):
"""在指定的位置绘制飞船"""
self.screen.blit(self.image, self.rect)
ship的方法__init()__接收两个参数:引用self和指向当前AlienInvasion实例的引用。这让ship可以访问AlienInvasion中的所有资源。在第9行,将屏幕赋给了Ship的一个属性,以便在这个类中的所有方法中轻松访问。在第10行,使用方法get_rect()访问屏幕的属性rect,并将其赋给了self.screen_rect,这让我们能够将飞船放到屏幕上合适的位置。
第13,14行将小飞船的图像加载进来,然后获取小飞船的图像大小,将其赋给self.rect。
最后在方法blitme()中绘制飞船。
注意:
在pygame中,原点(0,0) 位于屏幕左上角。在1200*800的屏幕上,右下角的坐标为(1200,800)。
4.4.2 在屏幕上绘制飞船
下面更新alien_invasion.py,创建一艘飞船并调用其方法blitme():
首先导入ship类:
from ship import Ship
然后在创建屏幕后创建一个ship实例:
self.ship = Ship(self)
调用Ship()时,必须提供一个参数:一个AlienInvasion实例。在这里,self指向的是当前AlienInvasion实例。这个参数让Ship能访问游戏资源,如对象screen。我们将这个实例赋给了self.ship。
在方法run_game()中,调用self.ship.blitme()将飞船绘制到屏幕上。
self.ship.blitme()
修改后的代码:
class AlienInvasion:
"""管理游戏资源和行为的类"""
def __init__(self):
--snip-- # 重复部分省略
pygame.display.set_caption("Alien Invasion") # 设置窗口上的游戏名
self.ship = Ship(self)
def run_game(self):
--snip-- # 重复部分省略
# 每次循环时都重新绘制屏幕
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
--snip--
如果现在运行alien_invasion.py,将看到一艘飞船出现在屏幕底部中央。
4.5重构:方法_check_events()和_update_screen()
在大型项目中,经常需要在添加新代码之前重构既有代码。旨在简化既有代码的结构,使其更容易拓展。本节把越来越长的方法run_game()拆分成两个辅助方法(helper method)。辅助方法在类中执行任务,但并非是通过实例调用的。在python中,辅助方法以单个下划线开头。
4.5.1 新方法_check_events()
我们把管理事件的代码移到一个名为_check_events()的方法中,以简化run_game()并隔离事件管理。通过事件管理循环,可将事件管理与游戏的其他方面分离。
在方法run_game()后新增方法_check_events():
def _check_events(self):
"""响应键盘和鼠标事件。"""
for event in pygame.event.get():
if event.type == pygame.QUIT: # 鼠标点击右上角的叉叉时就退出
sys.exit()
简化后的run_game()方法:
def run_game(self):
"""开始游戏的主循环"""
while True:
# 调用方法 _check_events
self._check_events()
# 每次循环时都重新绘制屏幕
self.screen.fill(self.settings.bg_color)
--snip--
4.5.2 新方法_update_screen()
为进一步简化run_game(),将更新屏幕的代码移到一个名为_update_screen()的方法中。
在方法_check_events后新增方法_update_screen():
def _update_screen(self):
"""更新屏幕上的图像,并切换到新屏幕"""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
pygame.display.flip()
简化后的run_game()方法:
def run_game(self):
"""开始游戏的主循环"""
while True:
self._check_events()
self._update_screen()
经过新增的两个方法的重构,run_game()部分变得简单许多。
在开发大型项目时应该遵循的原则:先编写尽可能简单的代码,等项目越来越复杂后再对其进行重构。
4.6 驾驶飞船
这一部分我们将编写代码来让飞船动起来!
4.6.1 响应按键
当用户在按下方向键时,飞船将做出响应。当一个按键被按下时,都会在Pygame中注册一个事件。事件都是通过方法pygame.event.get()获取的,因此需要在方法_check_events()中指定要检查那些类型的事件。每次按键都被注册为一个KEYDOWN事件。
Pygame检测到KEYDOWN事件时,需要检查按下的是否是触发行动的键。例如,如果玩家按下的是右方向键,就增大飞船的rect.centerx值,将飞船向右移动,在方法_check_events中添加以下代码:
def _check_events(self):
"""# 监视键盘和鼠标事件。"""
for event in pygame.event.get():
if event.type == pygame.QUIT: # 鼠标点击右上角的叉叉时就退出
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
# 向右移动飞船
self.ship.rect.x += 1
在方法_check_events()中,为事件添加了一个elif代码块,以便Pygame检测到KEYDOWN事件时做出响应。我们检查按下键(event.key)是否是右方向键(pygame.K_RIGHT),如果是,就将self.ship.rect.centerx的值加1,从而飞船向右移动。
如果现在运行alien_invasion.py,当你每按下一次右方向键,飞船都向右移动1像素。这是一个开端,但并不是控制飞船的高效方式。我们下面来继续改进,让飞船可以持续移动。
4.6.2 允许持续移动
玩家按住右方向键不放时,我们希望飞船不断向右移动,直至松开为止。我们将让游戏检测pygame.KEYUP事件,以便知道玩家何时松开右方向键。然后结合使用KEYDOWN和KEYUP事件以及一个名为moving_right的标志来实现持续移动。
当标志moving_right为False时,飞船不会移动。玩家按下方向键时,我们将此标志设为True,当玩家松开按键时将该标志设为False。
飞船的属性由ship类控制,因此要给这个类添加一个moving_right的属性,再新增一个方法update()。方法update()检查标志的状态。
修改ship.py,首先添加属性self.moving_right,然后新增方法update():
def __init__(self, ai_game):
--snip--
self.rect.midbottom = self.screen_rect.midbottom # 意思是飞船底部中央和屏幕底部中央对齐
# 移动标志
self.moving_right = False
def update(self):
"""根据移动标志调整飞船的位置"""
if self.moving_right:
self.rect.x += 1
def blitme(self):
--snip--
接下来修改alien_invasion.py中的方法_check_events(),使其在玩家按下方向键时将moving_right设为True,松开时设为False。
def _check_events(self):
"""# 监视键盘和鼠标事件。"""
for event in pygame.event.get():
if event.type == pygame.QUIT: # 鼠标点击右上角的叉叉时就退出
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
最后,修改run_game中的while循环,在每次游戏开始时都能调用方法update()。
def run_game(self):
"""开始游戏的主循环"""
while True:
self._check_events()
self.ship.update()
self._update_screen()
如果现在运行代码并按住右箭头键,飞船将持续向右移动。
4.6.3左右移动
和向右持续移动一样,接下来我们来添加持续向左移动的代码。
修改ship.py:
def __init__(self, ai_game):
--snip--
# 移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船的位置"""
if self.moving_right:
self.rect.x += 1
if self.moving_left:
self.rect.x -= 1
def blitme(self):
--snip--
再修改alien_invasion.py:
def _check_events(self):
"""# 监视键盘和鼠标事件。"""
for event in pygame.event.get():
if event.type == pygame.QUIT: # 鼠标点击右上角的叉叉时就退出
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
if event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right =