我们通过使用pygame模块,创建一些带有图形和声音的、更有趣的高级游戏。
目录
主要内容
pygame
Windows安装pygame
使用pygame
pygame Hello World
(一)导入模块
(二)初始化pygame
(三)设置pygame窗口
(四)设置颜色变量
(五)将文本写到pygame窗口上
(六)用一种颜色填充一个Surface对象
(七)pygame的绘制函数
绘制一个多边形
绘制直线
绘制圆形
绘制椭圆形
绘制矩形
给像素着色
(八)Surface对象的blit()方法
(九)将Surface对象绘制到屏幕上
(十)事件和游戏循环
以下是主要内容
Pygame是一个利用SDL(Simple DirectMedia Layer)库编写的游戏库,SDL是一位叫做Sam Lantinga的大牛写的,据说他为了让Loki(致力于向Linux上移植Windows的游戏的一家大好人公司,可惜已经倒闭)更有效的工作,创造了这个东东。
SDL是用C写的,不过它也可以使用C++进行开发,当然还有很多其它的语言,Pygame就是Python中使用它的一个库。Pygame已经存在很多时间了,许多优秀的程序员加入其中,把Pygame做得越来越好。
pygame官网提供了不同系统下面安装的相关命令,都是免费的。笔者选择的是Windows。当然,在安装pygame之前,你的Windows里需要安装好Python以及配置好环境变量。注意版本要对应上,笔者电脑里的是64位的Python3.7。
因为笔者之前有安装过anaconda,所以这里使用anaconda进行安装。
2.回车进入页面后,一般选择下载最多的打开
3.复制命令行,进入CMD终端运行。
4.完成安装。可以通过编译环境检测是否安装成功,这里以IDLE为例,输入命令import pygame,出现以下信息,则证明导入成功。
>>> import pygame
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
这里仅以anaconda为例,当然还有其他的安装方法,pip也是可以的。
pygame有很多的模块,下面是一些常用的模块
name | function |
pygame.Color | color representations |
pygame.display | control the display window and screen |
pygame.draw | draw shapes |
pygame.event | interacting with events and queues |
pygame.font | loading and rendering fonts |
pygame.image | image transfer |
pygame.key | work with the keyboard |
pygame.locals | various constants |
pygame.mixer | load and play sounds |
pygame.mouse | pygame.mouse |
pygame.Rect | storing rectangular coordinates |
pygame.surface | representing images |
pygame.time | monitoring time |
pygame.mixer.music | control streamed audio |
有些模块可能在某些平台上不存在,你可以用None来测试一下。
import pygame
if pygame.Color is None:
print('This module is None')
else:
print('This module is in the module')
如果存在的话会有以下运行结果
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
This module is in the module
接下来我们将会用pygame写第一个Hello World程序。
我们将分析pygame Hello World程序的代码,看看它们做些什么事情。
import pygame
import sys
from pygame.locals import *
最后一行导入了pygame.locals模块。这个模块包含了许多将要用在pygame中的常量。
如果在程序中使用from sys import *语句,而不是使用import sys导入语句,那么在代码中要使用exit()调用该函数,而不是使用sys.exit()。但是大多数时候,使用完整的函数名称会更好,因为这样可以知道该函数是在哪个模块中。
# Set up pygame.
pygame.init()
在导入了pygame模块后,并且在调用任何其他的pygame函数之前,所有pygame程序都必须先调用pygame.init()函数。
# Set up the window.
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Hello world!')
通过调用pygame.display模块中的set_mode()方法,创建了一个图形化用户界面(graphical user interface, GUI)。display模块是pygame模块之中的一个模块。这些方法设置了一个窗口,供pygame在其中运行。窗口使用一个坐标系统,但是该窗口的坐标系统是以像素为单位的。
像素是计算机屏幕上的最小的点。屏幕上的单个像素,可以以任何的颜色显示。像素是指由图像的小方格组成的,这些小方块都有一个明确的位置和被分配的色彩数值,小方格颜色和位置就决定该图像所呈现出来的样子。
这里我们使用一个元组,创建了一个500像素宽和400像素高的一个窗口。关于元组的知识,参见...???
pygame.display.set_mode()方法有三个参数,第一个参数就是包含两个整数的一个元组,这两个整数分别代表窗口的宽度和高度,以像素为单位。第二个和第三个参数???
pygame模块中的display模块中的set_caption()方法用来设置窗口标题。
Surface对象
set_mode()函数返回了一个pygame.Surface对象。对象(object)是对拥有方法的数据类型的值的另外一种叫法。例如,字符串就是Python中的对象,因为它们有数据(字符串本身)和方法(诸如lower()和split())。Surface对象表示这个窗口。
变量存储对对象的引用,就像它们存储了对列表和字典的引用一样。
像素有三种主要的:红色、绿色和蓝色。通过组合这三种颜色的不同数量(计算机屏幕就是这么做的),你可以形成任何其他颜色。在pygame中,颜色由三个整数的元组表示。这些被称为RGB颜色值,我们将在程序中使用它们为像素指定颜色。由于我们不希望每次在程序中使用特定颜色时都重写一个三位数的元组,因此我们将创建常量来保存以元组表示的颜色命名的元组:
# Set up the colors.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
元组中的第1个值表示颜色中有多少红色。整数值0表示该颜色中没有红色,而225表示该颜色中红色达到最大值。第二个值表示绿色,第三个值表示蓝色。这三个整数值构成了一个RGB元组。例如:
(0,0,0)表示没有红色、绿色和蓝色。最终的颜色是完全的黑色;
(255,255,255)表示红色、绿色和蓝色都达到最大值,最终得到的颜色就是白色。
常见的颜色及其RGB值如下表:
颜色 | RGB值 |
黑色 | (0,0,0) |
蓝色 | (0,0,255) |
灰色 | (128,128,128) |
绿色 | (0,128,0) |
紫色 | (128,0,128) |
红色 | (255,0,0) |
黄色 | (255,255,0) |
将文本写到一个窗口之上,和我们在基于文本的游戏中只是使用print()函数有点不同。为了将文本写道窗口上,我们需要先做一些设置。
使用字体来样式化文本
字体是以统一风格绘制的一整套的字母、数字、符号和字符。
在之前的游戏中,我们只告诉Python打印文本。用于显示文本的颜色、大小和字体,则完全取决于操作系统。Python程序不能修改字体。但是,pygame可以以计算机上的任何字体来绘制文本。
# Set up the fonts.
basicFont = pygame.font.SysFont(None, 48)
这里使用pygame.font.SysFont()函数以创建一个pygame.font.Font对象。
这个函数包含两个参数。第一个参数是字体的名称,但是我们将传递None值以使用默认的系统字体。第二个参数是字体的大小(以点为单位)。生成一幅文本的字母图像,这叫做渲染(rendering)。
渲染一个Font对象
存储在变量basicFont中的Font对象有一个叫做render()的方法。这个方法将返回一个Surface对象,文本就绘制于其上。render()的第一个参数是要绘制的文本的字符串。第二个参数指定是否想要抗锯齿的一个Boolean值。为文本抗锯齿,会使其看上去略微平滑一些。
第3个参数和第4个参数都是RGB元组。第三个参数是将要用来渲染文本的颜色(在这个例子中是白色),并且第4个参数是文本后的背景颜色(蓝色)。我们将这个Font对象赋值给变量text。
# Set up the text.
text = basicFont.render('Hello world!', True, WHITE, BLUE)
我们需要将设置好的Font对象放置到窗口上的一个位置之中。
使用Rect属性设置文本位置
pygame.Rect数据类型(简称Rect)表示特定大小和位置的矩形区域。我们就是使用它来设置窗口中的对象的位置。
调用函数pygame.Rect()来创建一个新的Rect对象。注意,pygame.Rect()函数和pygame.Rect数据类型的名称相同。与其数据类型的名称相同并且创建其数据类型的对象或值的函数,叫做构造函数(constructor functions)。
pygame.Rect()函数的参数是表示左上角的X坐标和Y坐标的整数,后边跟随着宽度和高度,都是以像素为单位。带有参数的这个函数看上去是这样的:pygame.Rect(left, top, width, height)。当我们创建了Font对象,就已经为其生成了一个Rect对象,因此,我们现在要做的就是访问它。我们在text上使用get_rect()方法,将该Rect赋值给textRect:
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
就像方法是与对象相关的函数一样,属性(attribute)是与对象相关的变量。Rect数据类型有许多属性,用来描述它所表示的矩形。为了设置textRect在窗口上的位置,我们需要将其中心的x值和y值,赋值为窗口上的坐标像素。由于每个Rect对象已经有了存储Rect的中心的x坐标和y坐标的属性,分别名为centerx和centery,我们所需要做的就是赋值这些坐标值。
我们需要将textRect放置到窗口的中央,因此,我们需要获取windowsSurfaceRect,获取其centerx和centery属性,然后将其赋值给textRect的centerx和centery属性。
Rect对象的好处是,如果修改了这些属性中的任意一个,所有其他属性也将自动修改。
在pygame模块中,是font和surface模块,这些模块中,是Font和Surface数据类型。pygame程序员用小写字母大头表示模块,用大写字母开头表示数据类型,以便于区分数据类型和模块。注意,Font对象(存储在text变量中)和Surface对象(存储在WindowsSurface变量中)都有一个名为get_rect()的方法。从技术上讲,这是两种不同的方法,但是pygame的程序员给它们起了相同的名字,因为它们都做了相同的事情,并且分别返回表示Font对象或Surface对象的大小和位置的Rect对象,所以pygame程序员给予他们相同的名称。
对于我们的程序来说,我们想要用白色来填充存储在windowsSurface变量中的整个Surface对象。fill()函数将会使用传递给参数的颜色来填充整个Surface对象。
# Draw the white background onto the surface.
windowSurface.fill(WHITE)
注意,在pygame中,当调用fill()方法或其他任何绘制函数时,屏幕上的窗口都不会改变。相反,这些函数将会改变Surface对象,但是在调用pygame.display.update()函数之前,不会把新的Surface对象绘制到屏幕上。这是因为,在计算机内存中修改Surface对象要比在屏幕上修改图像块。当所有绘制函数完成了对Surface对象的绘制之后,再在屏幕上绘制,这样要高效的很多。
以上内容实现了使用一种颜色填充一个pygame窗口并添加文本,但是pygame还拥有让你能够绘制形状和线条的函数。每一种形状都有其自己的函数,并且可以将这些形状组合到不同的图片中,以用于图形化的游戏。这里主要用到pygame模块中的draw模块。
pygame.draw.polygon()函数可以绘制你所指定的任意多边形,该函数的参数依次为:polygon(surface, color, points, width=0)
# Draw a green polygon onto the surface.
pygame.draw.polygon(windowSurface, GREEN, ((146, 0), (291, 106),
(236, 277), (56, 277), (0, 106)))
以上代码在Surface对象上绘制了一个绿色的五角星。
pygame.draw.line()函数只是从屏幕上的一点到另一点绘制一条直线。pygame.draw.line()参数依次是:
line(surface, color, start_pos, end_pos, width=1)
# Draw some blue lines onto the surface.
pygame.draw.line(windowSurface, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(windowSurface, BLUE, (120, 60), (60, 120))
pygame.draw.line(windowSurface, BLUE, (60, 120), (120, 120), 4)
以上代码调用了三次pygame.draw.line()函数,在Surface对象上绘制一个蓝色的“Z”。
pygame.draw.circle()函数在Surface对象上绘制圆形。其参数如下:circle(surface, color, center, radius, width=0)
# Draw a blue circle onto the surface.
pygame.draw.circle(windowSurface, BLUE, (300, 50), 20, 0)
以上代码绘制了一个蓝色的圆。
pygame.draw.elipse()函数与pygame.draw.circle()函数类似,但是它绘制一个椭圆形,这就像是一个压扁了的圆形。pygame.draw.elipse()函数依次如下:ellipse(surface, color, rect, width=0)
# Draw a red ellipse onto the surface.
pygame.draw.ellipse(windowSurface, RED, (300, 250, 40, 80), 1)
pygame.draw.rect()函数将绘制一个矩形。pygame.draw.rect()函数的参数依次如下:rect(surface, color, rect, width=0)
# Draw the text's background rectangle onto the surface.
pygame.draw.rect(windowSurface, RED, (textRect.left - 20,
textRect.top - 20, textRect.width + 40, textRect.height + 40))
这里要想起来我们之前创建的一个textRect对象。
pygame.PixelArray对象(简称PixelArray对象)。PixelArray对象是颜色元组的一个列表,这些颜色元组表示传递给它的Surface对象。PixelArray对象使得我们能够进行更高的像素级别的控制因此,如果需要在屏幕上绘制非常详细或定制化图像,而不只是较大的图形的时候,PixelArray对象是很好的选择。
我们将使用PixelArray把windowsSurface上的一个像素变为黑色。当你运行pygame的Hello World程序的时候,在窗口的右下角可以看到这个像素。
把windowsSurface作为参数传递给pygame.PiexlArray()函数调用,所以下面把BLACK分配给pixArray[480][380],将会把坐标为(480,380)的像素改变为一个黑色像素。pygame将把这个改变自动更新到windowsSurface对象。
PixelArray对象中的第1个索引是X坐标。第2个索引是Y坐标。PixelArray对象使得在一个PixelArray对象上单独像素设置为特定颜色变得很容易。
# Get a pixel array of the surface.
pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = BLACK
del pixArray
注意,从一个Surface对象创建PixelArray对象,将会锁定这个Surface对象。锁定意味该Surface对象不能调用blit()函数。要解锁这个Surface对象,必须使用del操作符删除PixelArray对象。
如果忘记删除PixelArray对象,就会得到一条错误信息:error: Surfaces must not be locked during blit
bllit()方法将一个Surface对象的内容绘制到另一个Surface对象之上。render()方法所创建的所有文本对象,都存在于自己的Surface对象之上。pygame的绘制方法都能够指定要在其上绘制一个形状或线条的Surface对象,但我们的文本存储在text变量中而不是绘制到windowsSurface上。为了将text绘制到我们想要让其出现的Surface上,必须使用blit()方法:
# Draw the text onto the surface.
windowSurface.blit(text, textRect)
这里将text变量中的'Hello World!'Surface对象绘制到了windowsSurface变量中的Surface对象之上。blit()的第2个参数指定了应该将text surface绘制于windowsSurface上的何处。前面通过调用text.get_rect()而获取的Rect对象,将作为这个参数传递。
在调用pygame.display.update()函数之前,并不会真的将任何内容绘制到屏幕上,该函数显示更新后的Surface对象:
# Draw the window onto the screen.
pygame.display.update()
为了节省内存,我们并不想要在每次调用了绘图函数之后,都更新到屏幕上,只有在所有绘制函数都调用完后,才想要一次性更新屏幕。
在前面的游戏中,所有程序都会立即打印每一项内容,知道它们遇到一个input()函数调用。此时,程序会停止,等待用户输入一些内容并按下回车键。但是,pygame程序不断地运行叫做游戏循环(game loop)地一个循环。在这个程序中,游戏循环中地所有代码行每秒钟都会执行100次左右。
游戏循环是不断地查看新事件、更新窗口的状态并在屏幕上绘制窗口的一个循环。任何时候,当用户按下一个键、点击或移动鼠标或使得其他一些事件发生的时候,pygame都会创建该对象。事件(event)是pygame.event.Event数据类型的对象。
# Run the game loop.
while True:
这里把while语句的条件设置为True,所以它会永远循环。退出循环的唯一方式是,如果有一个事件导致了程序终止。
获取事件对象
调用pygame.event.get()函数检索自从上次调用pygame.event.get()后所生成的任何新的pygame.event.Event对象(简称为Event对象)。这些事件会以Event对象的一个列表的形式返回。所有Event对象都有一个叫做type的属性,它告诉我们事件是什么类型。在这里我们只需要使用QUIT类型的事件,当用户终止程序的时候,该事件会告诉我们。
for event in pygame.event.get():
if event.type == QUIT:
这里我们使用了一个for循环,遍历了pygame.event.get()所返回的列表中的每一个Event对象。如果事件的type属性等于常量QUIT(它位于我们在程序开始处所导入的pygame.locals模块之中),那么我们就知道产生了QUIT事件。
当用户关闭程序的窗口或者当计算机关闭并尝试终止所有运行的程序的时候,pygame模块会产生QUIT事件。接下来,当检测到QUIT事件的时候,我们将告诉程序做什么。
退出程序
如果已经产生了QUIT事件,程序就应该调用pygame.quit()和sys.exit()。
pygame.quit()
sys.exit()
pygame.quit()函数是和init()相对应的一种函数。在退出程序之前,需要调用它。如果忘记了,可能导致IDLE在程序结束之后挂起。
源代码:
import pygame
import sys
from pygame.locals import *
# Set up pygame.
pygame.init()
# Set up the window.
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Hello world!')
# Set up the colors.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# Set up the fonts.
basicFont = pygame.font.SysFont(None, 48)
# Set up the text.
text = basicFont.render('Hello world!', True, WHITE, BLUE)
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
# Draw the white background onto the surface.
windowSurface.fill(WHITE)
# Draw a green polygon onto the surface.
pygame.draw.polygon(windowSurface, GREEN, ((146, 0), (291, 106),
(236, 277), (56, 277), (0, 106)))
# Draw some blue lines onto the surface.
pygame.draw.line(windowSurface, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(windowSurface, BLUE, (120, 60), (60, 120))
pygame.draw.line(windowSurface, BLUE, (60, 120), (120, 120), 4)
# Draw a blue circle onto the surface.
pygame.draw.circle(windowSurface, BLUE, (300, 50), 20, 0)
# Draw a red ellipse onto the surface.
pygame.draw.ellipse(windowSurface, RED, (300, 250, 40, 80), 1)
# Draw the text's background rectangle onto the surface.
pygame.draw.rect(windowSurface, RED, (textRect.left - 20,
textRect.top - 20, textRect.width + 40, textRect.height + 40))
# Get a pixel array of the surface.
pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = BLACK
del pixArray
# Draw the text onto the surface.
windowSurface.blit(text, textRect)
# Draw the window onto the screen.
pygame.display.update()
# Run the game loop.
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
运行结果:
参考: