Python 之 pygame 学习(入门知识)

Pygame 致力于 2D 游戏的开发
参考pygame菜鸟入门指南

文章目录

    • 一、下载安装 Pygame
    • 二、Pygame 常用模块
    • 三、常用模块的常用方法
    • 四、知道什么是surface
    • 五、使用surface.convert()
    • 六、脏矩形动画
    • 七、硬件surface弊大于利
    • 八、不要纠缠于细枝末节
    • 九、Rect是你的好朋友
    • 十、不要对像素级的碰撞检测费心
    • 十一、管理好事件子系统
    • 十二、色键 vs Alpha
    • 十三、简单示例
    • 十四、补充解释

一、下载安装 Pygame

参考:下载安装步骤

安装过程中出现了两个问题:

  1. You are using pip version 9.0.1, however version 10.0.1 is available.
    You should consider upgrading via the ‘python -m pip install --upgrade pip’ command.

    原因:这个问题主要是版本没有更新的意思,更新一下就好了
    解决方法:输入python -m pip install --upgrade pip命令更新一下就好了

  2. pygame-1.9.3-cp36-cp36m-win_amd64.whl is not a supported wheel on this platform.
    原因:版本不对应
    解决方案:检查是否下载了正确的对应:python版本,32还是64位

二、Pygame 常用模块

模块名 功能
pygame.cdrom 访问光驱
pygame.cursors 加载光标
pygame.display 访问显示设备
pygame.draw 绘制形状、线和点
pygame.event 管理事件
pygame.font 使用字体
pygame.image 加载和存储图片
pygame.joystick 使用游戏手柄或者类似的东西
pygame.key 读取键盘按键
pygame.mixer 声音
pygame.mouse 鼠标
pygame.movie 播放视频
pygame.music 播放音频
pygame.overlay 访问高级视频叠加
pygame.rect 管理矩形区域
pygame.scrap 本地剪贴板访问
pygame.sndarray 操作声音数据
pygame.sprite 操作移动图像
pygame.surface 管理图像和屏幕
pygame.surfarray 管理点阵图像数据
pygame.time 管理时间和帧信息
pygame.transform 缩放和移动图像

三、常用模块的常用方法

Display 模块的常用方法

方法名 功能
pygame.display.init() 初始化display模块
pygame.display.quit() 结束display模块
pygame.display.get_init() 如果display模块已经被初始化,则返回True
pygame.display.set_mode() 初始化一个准备显示的界面
pygame.display.get_surface() 获取当前的Surface对象
pygame.display.flip() 更新整个待显示的Surface对象到屏幕上
pygame.display.update() 更新部分内容显示到屏幕上,如果没有参数,则与flip功能相同(上一条)

Surface 对象的常用方法
Python 之 pygame 学习(入门知识)_第1张图片

四、知道什么是surface

Pygame的最重要部分是surface。就把surface 想当成一张白纸吧。你要用对一个surface做许多的事——你可以在它上面画线,给它的部分填充颜色,把图像拷进去或者拷出来,设置或者读取它上面的某个单独的像素的颜色值。一个surface可以是任何大小(可以理解)并且你要多少就有多少(也可以理解)。有一个surface是特别的——你用 pygame.display.set_mode()创建的那一个。这个display surface代表了屏幕;你对它做的任何事情都会呈现在用户屏幕上。你只能有一个这玩意——这是SDL的一个限制,而不是pygame的。
但是你怎么创建surface呢?正如上所说,可以用 pygame.display_set_mode()来创建特殊的display surface。你可以用image.load()来创建一个包含了图像的surface,或者你可以用font.render()来创建一个包含了文字的surface。甚至你可以用一个Surface()来创建一个什么都没有的surface。
大部分的surface函数都不是至关紧要的。只要学习blit(),fill(),set_at()和get_at()就够用了。

五、使用surface.convert()

当我第一次阅读surface.convert()的文档时,我并没有意识到这是我要注意的。“我只使用png格式,既然我用的都是同一个格式,所以我不需要convert()”。它证明了我是非常,非常错的。
Convert()所指的“格式”并非指文件格式工(如 png,jpeg,gif),它是所谓的“像素格式”。它代表了一个surface记录一个特定像素的颜色的方法。如果surface格式跟显示格式不一样,那SDL就要在每次blit的时候去转化它——这是个相当费时的过程。不用关心解释,只要注意到如果想在blit之外获得速度,那你就需要 convert()。
那怎么转换呢?只需在用image.load()函数创建了一个surface后调用它。
不使用: surface = pygame.image.load('foo.png')
用:

surface = pygame.image.load('foo.png').convert()

相当简单,你只需要当从硬盘中加载图像的时候,对每个surface调用它一次。你会对结果满意的;我发现使用convert()时,blit的速度有了6倍的提高。
你不需要使用convert()的唯一情况,就是当你真的想对图像的内部格式有绝对的控制权的时候——比如说你正在写一个图像转换程序或者其它的,但是你要保证输出文件和输入文件有相同的像素格式。如果你在写一个游戏,你需要速度(界面显示速度也就是游戏的流畅度),就用convert()。

六、脏矩形动画

Pygame程序中导致帧率不足的的最常见原因就是误用了 pygame.display.update()函数。在pygame中,仅仅把东西画到display surface中并不会让它显示在屏幕上——你要调用pygame.display.update()。有三种方法去调用它:
pygame.display.update()——更新整个窗口(或者在全屏显示下是整个屏幕)。
Pygame.display.flip()——这个干了相同的活,只是如果你同时使用了双缓冲硬件加速时它也会帮你该做的事,但如果你没有,那当什么都没发生过……
Pygame.display.update(一个矩形或者矩形列表) ——这个只更新屏幕上你指定矩形区域。
很多图形编程的新丁使用第一个选择——他们在每一帧里更新整个屏幕。问题是这样做对大多数人来说慢得不能忍受。在我的电脑上调用update()花费35毫秒,听起来不多,除非你看到一秒最多有1000/35=28 帧,并且还是没有任何游戏逻辑,没有blit,没有输入,没有人工智能,什么都没有。我只是坐在凳子上去更新屏幕,而28fps就是我的最高帧率。啊!
解决方法叫做“脏矩形动画”。替换每帧更新整个屏幕,而只更新自上一帧已经改变过的部分。我是通过用一个列表来跟踪这些矩形,然后在帧结束时调用update(the_dirty_rectangles)来实现的。
比如说对于一个移动中的精灵,我:在背景上blit精灵所在的位置,擦掉它。
把精灵的当前区域矩形加到一个叫dirty_rects的列表中去。
移动这个精灵。
在新的区域画这个精灵。
把精灵的新区域加到我的dirty_rects中去。
调用display.update(dirty_rects)
想想Solarwolf有大量持续移动的精灵亦平滑更新,并且还有时间去显示一个视差粒子效果,并且也被更新。
有两种情况是用不上这种技术的。其一是当整个窗口或者屏幕的确需要在每一帧被更新——考虑一下一个平滑滚动的引擎,像一个实时战略游戏或者一个边滚动条。那在这种情况下你应该用什么?呃,简单的答案就是不要在 pygame中写这种游戏。而详细的答案是一个步骤里滚动数个像素,试要企图做出绝对平滑的滚动。玩你游戏的人会喜欢一个滚动得快的游戏,而不太会注意到背景的跳跃。
最后一点——不是所有的游戏都需要高帧率。一个策略战争游戏在一秒钟内只需要更新几次就足够了——在这种情况下,脏矩形动画带来的复杂性是多余的。

七、硬件surface弊大于利

如果你已经看过可以用在pygame.display.set_mode()中可以使用的众多标志值时,你可能会这样想:“嘿,HWSURFACE!嗯,我需要它——谁不喜欢硬件加速?噢……DOUBLEBUF;嗯,听起来挺快的,我看我也需要它!”这不是你的错,我们经受过多年的3D游戏训练,已经默认了把硬件加速是优秀的,而软件加速是缓慢的。很不幸,硬件渲染天生就有一长串的缺陷:
它只在能在某些平台上运行。在Windows上,你通常可以得到硬件surface。大多数的其它平台却不能。比如说Linux,它也许会提供硬件 surface,如果安装了X4,如果DGA2正常运行起来,如果moons也被正确对齐了。如果不能给你硬件surface,SDL会悄悄地给你一个软件surface。
它只能在全屏模式下工作.
它复杂化了每个像素的访问。如果你有一个硬件surface,你必须在读写单独的像素值的时候锁定屏幕。如果你不这样做,就会坏了大事。接着你又要赶在系统被搞蒙并且开始抱怨之前马上解锁。这些过程都是pygame自动为你做的,但是它值得注意。
你没有了屏幕光标。如果你指定要HWSURFACE(并且真的拿到手了),你的光标通常会消失掉(更严重的是只剩下半个在一闪一闪的)。你只有创建一个精灵来扮演鼠标光标的角色,并且还要承担光标加速和敏感度的责任。真烦人。
它有可能变得更慢。许多驱动程序并不会加速我们作图类型,并且所有东西都必须通过视频总线来blit(除非你能用源surface来充满显存),结果就是比软件访问更慢。
硬件渲染有它存在的理由。在Windows下它运行地相当可靠。所以如果你不关于跨平台的性能时,它可以给你带来看得见的速度提升。不过,它也有代价——更多的头疼和复杂性。除非你知道你在干什么,否则最好就是坚持使用较好可信赖的的SWSURFACE。

八、不要纠缠于细枝末节

有时候,游戏编程新人在某些对游戏的成功并非紧要的地方花了太多时间。把将要的要素做“对”是可理解的,但是在创作游戏的早期,你并不知道哪些是重要的问题,更不要说你应该选择的答案了。结果就是带来一堆的借口。
举例说,思考一下怎么样组织你的图形文件的问题。是每一帧有它自己的图像文件好呢,还是每个精灵都有?或者把所有的图像都打包成一个压缩包?许多项目的许多时间被浪费在在邮件列表提问,争论问题的答案,比较,等等等等。这些都是次要矛盾;花在争论上的时间原本都应该用到编码实战游戏中去。
这里的主旨就是说,一个已经实现了的“恰当地好”的解决方案,要远远优于一个没有开始动手的完美的解决方案。

九、Rect是你的好朋友

Pete Shinners的封闭可能有很酷的alpha效果,和快速的blit速度,但是我不得不说我最喜欢pygame部分是底层的Rect类。一个rect就是一个矩形——由它左上角的位置,它的宽度,它的高度定义。Pygame的许多函数都用rect作参数,也接受“矩形形式”,一个跟rect有相同值的序列。因此,如果我需要一个位于10, 20和40, 50之间的区域时,我可以做以下几个中的一个:
rect = pygame.Rect(10, 20, 30, 30) rect = pygame.Rect((10, 20, 30, 30)) rect = pygame.Rect((10, 20), (30, 30)) rect = (10, 20, 30, 30) rect = ((10, 20, 30, 30))
如果你使用开头三个中的任何一个,你就可以使用rect的实用函数。它们包括移动,收缩和膨胀矩形,找出两个矩形的并集,和一堆的碰撞检测函数。
例如,假设我想得到包含了点(x, y)——可能用户点击了这里,也可能是子弹的当前位置——的所有精灵。如果每个精灵都有一个.rect成员,事情就会很简单——我只消:
sprites_clicked = [sprite for sprite in all_my_sprites_list if sprite.rect.collidepoint(x, y)]
除了能用rect作为参数,rect 跟surface和图像函数没别的瓜葛。你也可以把它们用到跟图形没啥关系,却又需要矩形的地方去。我几乎在每个工程里都发现几个需要使用rect的地方,而我从来没想到我也要在这里用上它们。

十、不要对像素级的碰撞检测费心

至此你已经让你的精灵动了起来,你需要知道他们到底会不会撞上别人。像下面这样做是很有诱惑性的:
看看矩形是不是碰撞了,如果不是,忽略它们。
对于重叠区域的每一个像素,检查它在每个精灵对应的像素是不是不透明的,如果的确是这样,则它们碰撞了。
有很多方法实现这个想法,对精灵进行“与运算”等,但无论如何,它都会很慢。甚至对多数游戏来,更恰当的是做“子矩形碰撞”——对每个精灵做一个比其真实图像略小的矩形,用它来进行碰撞。这样会快得多,并且大数多情况下玩家不会注意到这个不精确的做法。

十一、管理好事件子系统

Pygame的事件系统很巧妙。有两种不同的方法找出输入设备(键盘,鼠标或游戏控制杆)在做什么。
第一种方法是直接检查设备的状态。通过调用叫pygame.mouse.get_pos()或pygame.key.get_press()的方法来实现。它们会告诉你当你调用这函数时设备的状态。
第二种方法是使用SDL的事件队列。这个队列是一个事件的列表——当事件发生时就会被加到列表中,如果被读过了,它们就会被删除掉。
这两套系统各有利弊。状态检查(第一套系统)很精确——你准确知道一个输入什么时候发生——如果mouse.get_pressed([0])的值是1,说明那时的鼠标左键点下了。而事件队列仅仅告诉我们在过去某个时间鼠标被点下了;如果你很频繁地检查队列,那没事,但如果你延迟了检查,潜在的输入就会越来越多。状态检查系统的另一个优势就是可以很方便地检测“和音 ”,也就是说,在同一时间里同时生了多种状态。如果你要检查t和f键是不是被同时按下了,只要检查:
if (key.get_pressed[K_t] and key.get_pressed[K_f]):
print “Yup!”
在队列系统中,每个到达队列的按键都作为一个单独的事件,所以你必须记得,在你检查f键的时候,t键被按下了但没有弹起。有点复杂。
但状态系统有一个巨大的缺陷。它只报告被调用的那一刻的设备状态。如果鼠标被按下了,并且在释放之前调用了mouse.get_pressed(),鼠标会返回0——get_pressed()完全错过了鼠标按下事件。这两个事件,MOUSEBUTTONDOWN和MOUSEBUTTONUP,仍然被放到事件队列中去,等待读取和处理。
教训就是:选择最适合你的需要的系统。如果不需要在循环中做太多事——也就是说你只需坐在板凳上,在一个’while 1’循环中待输入,用get_pressed()或者其它状态函数,延迟会比较短。相反,如果每一个按键都很关键,但是延迟不那么重要——比如你的用户在一个编辑框里输入某些东西,就使用事件队列。某些按键会有点延迟,但至少你会一个不缺。
关于event.poll()和wait()对比的注记—— poll()看起来会好点,因为它在等待输入的时候不会阻塞你的程序做事。Wait()则挂起程序,直到接收到一个事件。不过,poll()运行时会花掉所有可用的CPU时间,并且它会用NOEVENTS来塞满事件队列。用set_blocked()来选择你只需要哪些事件类型——你的队列看起来会更好管理。

十二、色键 vs Alpha

这两种技术常常混淆,并且大多来自于对术语的误用。
“色键”告诉pygame,特定图像的具有某种特定颜色的所有像素都被当成透明的。当其它部分的图像被blit的时候,这些透明像素不会被blit,所以不会破坏背景。这就是我们怎么样使得精灵的形状不是矩形的。简单调用surface.set_colorkey(color),其中color是一个RGB元组——像(0, 0, 0)。它会把图像中黑色的像素当作透明的。
“Alpha”则不同,它有两种形式。“图像Alpha”应用于整个图像,这通常是你需要的。如大家所知的“半透明”,alpha让源图像的每个像素只有部分不透明。比如说,你设置了一个surface的alpha为 192,然后把它blit到一个背景上去时,每个像素的3/4颜色值会来自源图像,而1/4来自背景图像。
Alpha用255到0的一个数来衡量,其中0 表示完全透明,255表示完全不透明。注意,颜色键和alpha可以混合——产品就是一部分完全透明另一部分半透明的图像。
“按像素alpha”是alpha的另一种表现,它更加复杂。简单说,源图像上每个像素都有它自己的alpha值,从0到255。
在blit到背景上的时候,每个像素都有一个不同的不透明度。这种类型的alpha不能跟色键混使用,并且要覆盖整个图像alpha。游戏中很少会用到按像素alpha的,如果要用它,你要先用一个图像编辑器让它保留下一个alpha通道。很复杂——不要用它了。

十三、简单示例

import pygame
import sys
# 初始化 pygame
pygame.init()

size = width,height = 800,600
speed = [-2,1]
bg = (255,255,255) # 背景设置为白色

# 创建指定大小的窗口 Surface
screen = pygame.display.set_mode(size)
# 设置窗口的标题
pygame.display.set_caption("初次见面,请大家多多关照")
# 加载图片
chicken = pygame.image.load("chicken.png")
# 获得图像的位置矩形
position = chicken.get_rect()

# 设置为死循环,确保窗口一直显示
while True:
    # 遍历所有的事件
    for event in pygame.event.get():
        # 如果单击关闭窗口,则退出
        if event.type == pygame.QUIT:
            sys.exit()
    # 移动图像
    position = position.move(speed)
    # 反转图像
    if position.left < 0 or position.right > width:
        chicken = pygame.transform.flip(chicken,True,False)
        # 反方向移动
        speed[0] = -speed[0]

    if position.top < 0 or position.bottom > height:
        speed[1] = -speed[1]
    # 填充背景
    screen.fill(bg)
    # 更新图像
    screen.blit(chicken,position)
    # 更新界面
    pygame.display.flip()
    # 延迟 10 毫秒
    pygame.time.delay(10)

效果图

十四、补充解释

set_mode:返回一个 Surface 对象,代表了在桌面上出现的那个窗口,三个参数第一个为元祖,代表分辨率(不可省略);
第二个是标志位如下,如果不用指定什么特性默认为0
Python 之 pygame 学习(入门知识)_第2张图片
第三个是色深,通常最好不要传递深度参数。它将默认为系统的最佳和最快颜色深度。
blit:将一个图像画到另一图像上,作为背景的图像会被模糊化,以使突出显示目标图像的效果

你可能感兴趣的:(Pygame学习)