Pygame介绍与游戏开发
提供pygame功能介绍的文档:Pygame Front Page — pygame v2.6.0 documentation
基础语法和实现逻辑
与CLI不同,pygame提供了图形化使用界面GUI(graphical user interface)基于图像的界面可以创建一个有图像和颜色的窗口
要让pygame的众多函数可以被调用,首先要调用pygame.init()
然后我们要创建一个surface对象:pygame.display.set_mode((400, 300))
并用pygame.display.set_caption()设置窗口顶部的标题文本
game loop—游戏循环一直在做三件事:处理事件、更新游戏状态、在屏幕上绘制游戏状态
最常见的游戏循环状态就是事件处理(而循环由while True进行)
while True:#main game loop
for event in pygame.event.get():
quit函数是一种与init相反的函数,在调用sys.exit前总是先调用pygame.quit()
而进行这一语句的条件则是if event.type==QUIT:
在在for循环的事件和事件处理条件if结束,游戏状态开始更新,pygame.display.update()
它把pygame.display.set_ mode()所返回的Surface对象绘制到屏幕上。
由此,我们就可以创建一个最简单最无聊的屏幕游戏了
import pygame, sys
from pygame.locals import *
# 初始化Pygame
pygame.init()
# 设置窗口大小和标题
DISPLAYSURF = pygame.display.set_mode((400, 300))
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()
坐标系和图形建立
不管是2D游戏还是3D游戏,我们都需要有空间界定来判断事件发生与否,而这也是所有游戏当中最常见的引发事件方法,为此,我们要建立合适的像素坐标
这里我们选择使用一个笛卡尔坐标系统来表示一个特定的点:值得注意的是,这里的Y轴坐标从最顶部的0开始,然后向下增加,而不是向上增加,这也是在大多数编程语言当中笛卡尔坐标是最有效的
在有了基本的空间界定以后,我们需要对其进行上色和透明度处理,其中颜色由RGB来表示,为了表示透明度,我们在RGB后面再加上一个大小在0~255的元素其中255表示完全不透明,RGB(0,0,0)为黑色RGB(255,255,255)为白色,想要使用透明颜色进行绘制,就必须使用convert_alpha()方法来创建一个Surface对象
anotherSurface=DISPLAYSURF.convert_alpha()
除了用四个整数构造的元组以外,也可以调用pygame.Color()填入参数
Rect对象是一种表示矩形区域的方法
例如,上述代码所表示的是所表示对象左上角位于(10,20)宽度为200像素,高度为300像素
如果想要访问对象最右边的x坐标,就必须要输入spamRect.right,当你对原坐标作出修改,其他坐标会自动算出,相应做出修改
pygame.Rect 提供了丰富的属性,用于描述矩形的位置和尺寸,以下是其主要属性:
基本位置和尺寸属性
• x:矩形左上角的 x 坐标。
• y:矩形左上角的 y 坐标。
• width:矩形的宽度。
• height:矩形的高度。
• size:矩形的尺寸,返回一个元组 (width, height)。
边界属性
• left:矩形左边缘的 x 坐标。
• right:矩形右边缘的 x 坐标。
• top:矩形上边缘的 y 坐标。
• bottom:矩形下边缘的 y 坐标。
中心和角点属性
• center:矩形的中心点坐标,返回一个元组 (centerx, centery)。
• centerx:矩形水平中心的 x 坐标。
• centery:矩形垂直中心的 y 坐标。
• topleft:矩形左上角的坐标。
• bottomleft:矩形左下角的坐标。
• topright:矩形右上角的坐标。
• bottomright:矩形右下角的坐标。
• midtop:矩形上边中点的坐标。
• midleft:矩形左边中点的坐标。
• midbottom:矩形下边中点的坐标。
• midright:矩形右边中点的坐标。
简化属性
• w:与 width 相同,表示矩形的宽度。
• h:与 height 相同,表示矩形的高度。
这些属性不仅可以用于获取矩形的相关信息,还可以通过赋值来修改矩形的位置或尺寸。
pygame的基本绘制函数
import pygame, sys
from pygame.locals import *
pygame.init()
# set up the window
DISPLAYSURF = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Drawing')
# set up the colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# draw on the surface object
DISPLAYSURF.fill(WHITE)
pygame.draw.polygon(DISPLAYSURF, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)))
pygame.draw.line(DISPLAYSURF, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(DISPLAYSURF, BLUE, (120, 60), (60, 120))
pygame.draw.line(DISPLAYSURF, BLUE, (60, 120), (120, 120), 4)
pygame.draw.circle(DISPLAYSURF, BLUE, (300, 50), 20, 0)
pygame.draw.ellipse(DISPLAYSURF, RED, (300, 250, 40, 80), 1)
pygame.draw.rect(DISPLAYSURF, RED, (200, 150, 100, 50))
pixObj = pygame.PixelArray(DISPLAYSURF)
pixObj[480][380] = BLACK
pixObj[482][382] = BLACK
pixObj[484][384] = BLACK
pixObj[486][386] = BLACK
pixObj[488][388] = BLACK
del pixObj
# run the game loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
在编写代码时,我们往往会为每一种颜色设置常变量,这样会使代码更具有可读性,绘制函数则是根据他们的形状来命名,传递给这些函数的参数,则告诉他们它们在哪一个Surface对象上绘制,将形状绘制到何处
部分函数和方法介绍:
fill(color):会使传递给color参数到任何颜色来填充整个Surface对象
pygame.draw.polygon(surface,color,pointlist,width):多边形是由多个平坦的边组成的形状,surface和color参数告诉函数,将多边形绘制到哪一个surface上,以及用什么颜色绘制,其中:pointlist参数是一个元组或者点的列表(也就是说,用于XY 坐标的元组,或者两个整数的元组的列表)。多边形是通过这样的方式来绘制的,即在每个点以及元组中其后续的点之间绘制线条,然后,从最后的点到第一个点绘制一个线条。你也可以传递点的列表,而不是点的元组。
width参数是可选的。如果你漏掉了这个参数,多边形将会绘制为填充的,就像是屏幕上的绿色多边形那样,会用颜色来填充它。如果确实给width参数传递了一个整数值,则只是绘制出多边形的边框。这个整数表示多边形的边框会有多少个像素那么宽。给width参数传递1,将会绘制一个边框细瘦的多边形,而传递4、10或者20,将会绘制一个边框较粗一些的多边形。如果给width参数传入0,多边形将会是填充的(这和完全漏掉了width参数的效果一样)。
pygame.draw.line(surface, color, start_point, end_point, width) ——这个函数在start_point和end_point参数之间绘制一条直线。
pygame.draw.lines(surface, color, closed, pointlist, width) ——这个函数绘制了从一个点到下一个点的一系列的线条,这和pygame.draw.polygon()函数非常相似。唯一的区别在于如果你给closed参数传递了False,将不会有从pointlist中的最后一个点到第一个点的那条直线了。如果你传递了True,将会绘制从最后一个点到第一个点的直线。
pygame.draw.circle(surface, color, center_point, radius, width) ——该函数绘制一个圆。center_point参数指定了圆的圆心。传递给radius的参数为整数,表示的是圆的半径。
pygame.draw.ellipse(surface, color, bounding_rectangle, width) ——该函数绘制一个椭圆形。bounding_rectangle参数可以是一个pygame.Rect对象或者是4个整数的一个元组,就是表示出一个长方体的形状,由函数自动拟出椭圆。
pygame.draw.rect(surface, color, rectangle_tuple, width) ——该函数绘制一个矩形。
使用PixelArray()可以单个或批量设置像素的颜色,值得注意的是,PixelArray会将surface对象锁定,此时不能够使用blit()方法在其上绘制,诸如png,jpg这样的图像,如果想要查看一个对象是否被锁定了,可以使用get_locked()方法(锁定了会返回True),可以通过del语句删除PixelArray对象,以此outlocked
将图像连成动画需要通过多个图像来让人们的眼睛产生幻觉,通过提高帧率来让动画足够逼真
而pygame.time.Clock对象可以帮助我们确保程序以某一个最大的FPS运行。Clock对象将会在每一次迭代完成后都设置一个小小的暂停。调用一个Clock对象的tick方法,则能够确保无论计算机有多快,游戏都会按相同的速度运行。因此,这条代码应该放在循环的末尾。
如果只是想要绘制简单的形状,绘制函数已经很好用了,但是实际上在制作游戏的过程中我们往往需要将PNG、JPG、GIF和BMP等文件格式的图像加载到我们的游戏当中——要加载图像,可以将图像名与后缀传递给pygame.image.load()函数,但是由此返回的surface对象和原来的surface对象并非用一个surface对象并非同一个surface对象,因此我们还需要用到位图复制(Blitting),可以将一个surface的内容复制到另一个surface上面,用的是blit()函数
import pygame, sys
from pygame.locals import *
# 初始化Pygame
pygame.init()
# 设置帧率
FPS = 30
fpsClock = pygame.time.Clock()
# 设置窗口
DISPLAYSURF = pygame.display.set_mode((400, 300), 0, 32)
pygame.display.set_caption('Animation')
# 定义颜色和图像
WHITE = (255, 255, 255)
catImg = pygame.image.load('cat.png')
catx = 10
caty = 10
direction = 'right'
# 主游戏循环
while True:
# 填充背景色
DISPLAYSURF.fill(WHITE)
# 根据方向移动猫
if direction == 'right':
catx += 5
if catx == 280:
direction = 'down'
elif direction == 'down':
caty += 5
if caty == 220:
direction = 'left'
elif direction == 'left':
catx -= 5
if catx == 10:
direction = 'up'
elif direction == 'up':
caty -= 5
if caty == 10:
direction = 'right'
# 绘制猫图像
DISPLAYSURF.blit(catImg, (catx, caty))
# 事件处理
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# 更新显示并控制帧率
pygame.display.update()
fpsClock.tick(FPS)
除此之外,我们还会在编写游戏的过程当中用到文本:
想要把文本绘制到屏幕,一共有六步:
1.创建一个pygame.font.Font对象
2.创建一个Surface对象,通过调用Font对象的render()方法,将文本绘制其上
3.通过调用Surface对象的get_rect()方法,从Surface对象创建一个Rect对象
4.通过修改Rect对象的属性之一,来设置其位置。
5.将带有文本的Surface对象复制到pygame.display.set_mode()所返回的Surface对象上
6.调用pygame.display.update(),使显示Surface出现在屏幕上
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Hello World!')
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)
fontObj = pygame.font.Font('freesansbold.ttf', 32)
textSurfaceObj = fontObj.render('Hello world!', True, GREEN, BLUE)
textRectObj = textSurfaceObj.get_rect()
textRectObj.center = (200, 150)
while True: # main game loop
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(textSurfaceObj, textRectObj)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
render方法中提供了抗锯齿的图形技术,通过给文本和图形的边缘进行模糊化处理,使其看上不不那么块状化,(给render的第二个参数传入True)
用户体验升级:声音和交互按钮
播放声音可以使用pygame.mixer.Sound()构造函数,以此创建pygame.mixer.Sound对象,要播放音乐时可以调用sound对象的play()方式,停止则使用stop()方式,值得注意的是,sound一次只能加载一个音乐文件,因此我们要加载一个背景声音文件,调用pygame.mixer.music.load()函数并且将要加载的声音文件作为一个字符串参数传递。该函数一共有两个参数第一个表示指定循环的次数,第二个参数表示从音频的多少秒处开始播放。
此外作为能实现交互的游戏我们需要对按钮进行制作Pygame本身并没有直接提供一个现成的Button类或按钮交互功能,但可以通过自定义实现按钮交互。以下是实现按钮交互的基本方法:
创建按钮
1. 定义按钮类:通常需要定义一个Button类,包含按钮的矩形区域(pygame.Rect)、颜色、文本等属性。
2. 绘制按钮:使用pygame.draw.rect()绘制按钮的矩形背景,并通过pygame.font渲染文本。
实现按钮交互
1. 监听鼠标事件:在主循环中,通过pygame.event.get()获取事件,检测鼠标点击事件(pygame.MOUSEBUTTONDOWN)。
2. 判断点击位置:使用pygame.Rect.collidepoint()方法判断鼠标点击位置是否在按钮矩形内。
3. 执行操作:如果点击位置在按钮内,则执行相应的操作,例如打印消息或调用函数。
示例代码
以下是一个简单的按钮实现示例:
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
pygame.display.set_caption("Pygame Button Example")
class Button:
def __init__(self, rect, color, text, font):
self.rect = rect
self.color = color
self.text = text
self.font = font
def draw(self, surface):
pygame.draw.rect(surface, self.color, self.rect)
text_surface = self.font.render(self.text, True, (0, 0, 0))
text_rect = text_surface.get_rect(center=self.rect.center)
surface.blit(text_surface, text_rect)
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN and self.rect.collidepoint(event.pos):
print("Button clicked!")
button = Button(pygame.Rect(150, 125, 100, 50), (255, 0, 0), "Click Me", pygame.font.Font(None, 36))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
button.handle_event(event)
screen.fill((255, 255, 255))
button.draw(screen)
pygame.display.flip()
pygame.quit()
此外,也有一些第三方库或项目封装了按钮功能,例如pygame-button-interface-switch,可以方便地集成到Pygame项目中。