嗨喽~小伙伴们早上好,中午好,晚上好呀,
前面(点击跳转)我们学习了第一个Pygame程序,接着,我们来了解Pygame中最为常用的几个对象。学完今天的内容,我们就可以开发一个稍微复杂一点的游戏了。
Surface对象表示的是一个矩形的2D图像,这个图像,实际上是由非常多的像素点组成的,比如一个(300px , 400px)的Surface对象,这个矩形2D图像由300*400个像素点组成。可以通过调用Pygame的绘制函数,来对一个Surface对象的部分像素点进行修改,从而达到整个图像修改的效果。需要注意的是,窗口的边框,标题栏,按钮并不属于Surface对象的一部分。
我们把通过pygame.display.set_mode() 函数返回的Surface对象,称为显示Surface对象,绘制到显示Surface对象上的任何内容,在调用pygame.display.update()函数后,都会显示到窗口上。
游戏制作中,经常要将几个不同的内容绘制到一个Surface对象中,在游戏的某一次循环里,调用pygame.display.update()函数后,这个Surface对象包含的所有内容都会绘制到屏幕上,这个矩形2D图像(可以更新)被显示到屏幕上的次数,称为帧数。一般而言,计算机的运行速度很快,游戏中一般采用每秒30帧,即30FPS,这叫作帧速率,简称帧率。此处30 FPS的含义是,1s内,游戏界面会被更新30次。
颜色在游戏设计中是必不可少的一部分。在计算机显示器上使用的是光,光有三原色-----红,绿,蓝。通过将这三种颜色按不同比例混合,可以形成任何其他的颜色。
在Pygame中,我们使用三个整数的元组 (a,b,c) 来表示任何一种颜色,第一个整数a表示颜色中有多少红色,取值为0~255,同理,第二个整数对应绿色,第三个整数对应蓝色。如(0,0,255)表示蓝色,(0,255,0)表示绿色,(128,128,128)三色混合后会得到灰色。
Pygame中创建颜色主要有两种方式。一是利用3个0~255的整数组成的元组,二是利用pygame.Color对象。
我们用代码来演示一下:
import sys,pygame
from pygame.locals import *
#color R G B
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = pygame.Color( 0, 0, 255) # 与前面的颜色创建方式效果相同
GREEN = pygame.Color( 0, 255, 0)
pygame.init()
DISPLAY_SURF = pygame.display.set_mode((400,300))
# 将整个显示Surface对象填充为白色
DISPLAY_SURF.fill(WHITE)
# 在Surface对象上,从左上角坐标(0,0)开始,绘制一个往右100px,往下100px的矩形2D区域并填充为黑色
pygame.draw.rect(DISPLAY_SURF, BLACK,(0,0,100,100))
# 在Surface对象上,从左上角坐标(100,0)开始,绘制一个往右100px,往下100px的矩形2D区域并填充为绿色
pygame.draw.rect(DISPLAY_SURF,GREEN,(100,0,100,100))
pygame.display.set_caption("Hello World~")
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
运行结果:
上面用到了一个绘制函数pygame.draw.rect(Surface,Color,(x,y,a,b)),它会在Surface对象上绘制一个矩形区域,第三个参数是一个四元元组,其前两个参数表明矩形区域左上角的坐标(pygame中的XOY坐标,以窗口左上角为原点(0,0),向右为x坐标,向下为y坐标),后两个参数表明矩形区域的大小,单位为像素px。
此外,颜色除了各种颜色的混合比例之外,还有一个特征,那就是颜色的透明度。Pygame允许在颜色的三元元组基础之上加入第四个整数(alpha value,范围0~255),用于表示颜色的透明度。0表示完全透明,255表示完全不透明。
为了能够使用透明颜色来进行绘制,Pygame要求必须使用convert_alpha()方法来创建一个Surface对象。也就是说,能够使用透明颜色来进行绘制的Surface对象,必须用convert_alpha()方法创建。比如上面的代码,我们加上一句:
Alpha_Surface = DISPLAY_SURF.convert_alpha()
则表明可以在Alpha_Surface对象上可以使用透明颜色绘制,而DISPLAY_SURF上不行。
与颜色一样,Pygame提供两种方式创建一个矩形区域。
一是利用四元元组(x,y,a,b) :
二是创建pygame.Rect对象,如:
rect = pygame.Rect(100,200,100,200) # 等同于 rect = (100,200,100,200)
与颜色类似,上述“等同”的含义是,如果有一个针对矩形区域的函数参数的话,那么既可以传入一个四元元组,也可以传入一个pygame.Rect对象(底层实现是重载) 。
pygame.Rect对象有许多属性,现将常用的列举如下:
属性 | 含义(返回值) |
Rect.left | 矩形区域最左边的X坐标(int类型) |
Rect.right | 矩形区域最右边的X坐标(int类型) |
Rect.top | 矩形区域顶部的Y坐标(int类型) |
Rect.bottom | 矩形区域底部的Y坐标(int类型) |
Rect.centerx | 矩形区域中央的X坐标(int类型) |
Rect.centery | 矩形区域中央的Y坐标(int类型) |
Rect.width | 矩形区域的宽度(int类型) |
Rect.height | 矩形区域的高度(int类型) |
Rect.size | 矩形区域大小的元组:(width,height) |
Rect.topleft | 元组(int类型):(top,left) |
Rect.topright | 元组(int类型):(top,right) |
Rect.bottomleft | 元组(int类型):(bottom,left) |
Rect.bottomright | 元组(int类型):(bottom,right) |
Rect.midleft | 元组(int类型):(left,centery) |
Rect.midright | 元组(int类型):(right,centery) |
Rect.midtop | 元组(int类型):(centerx,top) |
Rect.midbottom | 元组(int类型):(centerx,bottom) |
有了前面知识的铺垫,我们就可以用Pygame提供的绘制函数来绘制我们想要绘制的图形了。
先上代码:
import pygame, sys
from pygame.locals import *
# 初始化pygame库
pygame.init()
# 创建窗口
DISPLAY_SURF = pygame.display.set_mode((400, 300), 0, 32)
pygame.display.set_caption('Drawing')
# 设置颜色
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
# 绘制图形
# 填充为白色
DISPLAY_SURF.fill(WHITE)
'''
pygame.draw.polygon(Surface,Color,pointlist,thickness)
在DISPLAY_SURF上,画一个多边形,
第三个参数为多边形顶点坐标顺次构成的元组,
最后的1(px)为多边形的粗细,忽略此参数或设为0,Pygame会视为用颜色填充此多边形
'''
pygame.draw.polygon(DISPLAY_SURF, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)),1)
'''
pygame.draw.line(Surface,Color,start_point,end_point,thickness)
在DISPLAY_SURF上,画一条线,
(60,60),(120,60)分别为线的起始坐标,终点坐标,
4(px)为线条粗细,单位为像素
'''
pygame.draw.line(DISPLAY_SURF, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(DISPLAY_SURF, BLUE, (120, 60), (60, 120), 1)
pygame.draw.line(DISPLAY_SURF, BLUE, (60, 120), (120, 120), 4)
'''
pygame.draw.circle(Surface,Color,center_point,radius,thickness)
在DISPLAY_SURF上,
画一个圆,
(300,50)为圆心的坐标,
20(px)为圆的半径,
0(px)指明圆的粗细,忽略此参数或者设为0,则Pygame默认将圆用颜色填充
'''
pygame.draw.circle(DISPLAY_SURF, BLUE, (300, 50), 20, 0)
'''
pygame.draw.ellipse(Surface,Color,bounding_rectangle,thickness)
画一个椭圆,由于一个矩形可以唯一的确定一个椭圆,
故将其绑定一个Rect对象的矩形区域,
1(px)为椭圆的粗细,忽略此参数或者设为0,则Pygame默认将椭圆用颜色填充
'''
pygame.draw.ellipse(DISPLAY_SURF, RED, (300, 200, 40, 80), 1)
'''
pygame.draw.rect(Surface,Color,rectangle_tuple,thickness)
画一个矩形,
(200, 150, 100, 50)表示矩形左上角坐标为(200,150),宽度100px,高度50px
'''
pygame.draw.rect(DISPLAY_SURF, RED, (200, 150, 100, 50))
# run the game loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
运行结果:
代码中对每个绘制函数都有详细的注释,此处就不再赘述了。
动画是一个游戏的灵魂,如何产生一幅动的画面呢?
前面聊过,while循环里如果设置帧率为30FPS,则屏幕上的画面会每秒更新30次,也就是说,Pygame会将Surface对象以每秒30次的速度不断重新绘制到屏幕上。这样,如果在每次绘制到屏幕上之前,对Surface对象上的内容做略微修改,这样不就产生“动的”画面了吗?
现在的问题是,我们如何控制Surface对象以每秒30次的速度绘制到屏幕上去呢?
这个完全不用我们考虑,Pygame提供了一个专门用于控制帧率的类 pygame.time.Clock。由于计算机运行速度极快,容易导致游戏速度过快,玩家的体验不太好,因而这个对象可以确保游戏以某一个最大的帧率运行,但实际上,如果是大型游戏,代码量过多,以至于无法正常运行来频繁的绘制到屏幕上,会使得帧率下降。
Clock对象的用法,首先,创建Clock对象:
fpsClock = pygame.time.Clock()
Clock对象会在游戏的每一次循环上都设置一个小小的暂停,确保游戏不会运行的太快。
每次循坏的最后,调用pygame.display.update()方法将Surface对象绘制到屏幕上之后,应调用Clock对象的 tick() 方法:
# run the game loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
# 调用tick(),传入30FPS
fpsClock.tick(FPS)
这样便能保证,游戏以最大帧率30FPS运行。
借invpy官网的一段代码,我们演示一个简单的“猫移动”的动画,我们需要一张“猫”的图片:
代码如下:
import pygame, sys
from pygame.locals import *
pygame.init()
# 帧率设置为30FPS
FPS = 30
# 创建Clock对象
fpsClock = pygame.time.Clock()
# 创建窗口
DISPLAY_SURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Animation')
WHITE = (255, 255, 255)
# 加载图片
catImg = pygame.image.load('cat.png')
CAT_X = 10
CAT_Y = 10
# 设置猫初始往右移动
direction = 'right'
while True: # main game loop
DISPLAY_SURF.fill(WHITE)
# 简单地方向控制
if direction == 'right':
CAT_X += 5
if CAT_X == 280:
direction = 'down'
elif direction == 'down':
CAT_Y += 5
if CAT_Y == 220:
direction = 'left'
elif direction == 'left':
CAT_X -= 5
if CAT_X == 10:
direction = 'up'
elif direction == 'up':
CAT_Y -= 5
if CAT_Y == 10:
direction = 'right'
# 利用blit()方法将图片复制到Surface对象中
# 图片的左上角位于(CAT_X, CAT_Y)
DISPLAY_SURF.blit(catImg, (CAT_X, CAT_Y))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# 将Surface对象绘制到屏幕上
pygame.display.update()
# 设置帧率:30FPS
fpsClock.tick(FPS)
运行结果:
到这,Pygame的基础知识就介绍完了,内容也许比较多,小伙伴们多多理解和实践。下一章,我们开始动手做一个稍微复杂一点的游戏:
Python不能做游戏?游戏实战之-----《ink spill》
最后,喜欢的小伙伴们点个赞鼓励支持一下吧~