一、入门
https://eyehere.net/2011/python-pygame-novice-professional-1/
1、安装
pip install pygame
补充说明,如果新建一个项目的话,以前安装的模块就不可以用了,可以这样设置:File -> settings -> Project:... -> project interpreter -> 找到以前的解析器即可
2、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 就是我们在学的这个东西了……
# pygame.rect 管理矩形区域
# pygame.sndarray 操作声音数据
# pygame.sprite 操作移动图像
# pygame.surface 管理图像和屏幕
# pygame.surfarray 管理点阵图像数据
# pygame.time 管理时间和帧信息
# pygame.transform 缩放和移动图像
View Code
3、关于导入:
from pygame.locals import *
# pygame.locals Pygame 定义的常量。
# 这个模块包含了 Pygame 定义的各种常量。它的内容会被自动放入到 Pygame 模块的名字空间中。你可以使用
# from pygame.locals import *将所有的 Pygame 常量导入。
# 各个常量的详细描述记录在 Pygame 各个模块的相关文档中。比如 pygame.display.set_mode() 方法用到的 HWSURFACE 常量,
# 你就可以在 display 模块的文档中找到详细的说明;事件类型在 event 模块的文档中可以找到;当产生 KEYDOWN 或 KEYUP 事件时,
# key 属性描述具体哪个按键被按下,该值是以 K_ 开头的常量(MOD_ 开头的常量表示各种组合键被按下),在 key 模块的文档中可以找到;
# 最后,TIME_RESOLUTION 被定义在 time 模块中
二、display (显示模块)
import pygame, sys
from pygame.locals import *
pygame.init()
print(pygame.display.list_modes())
# 查看你的电脑支持哪些尺寸
# [(1920, 1080), (1680, 1050), (1680, 1050), (1600, 1200),...
SCREEN_SIZE = (640, 480)
screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32)
print(screen) # <Surface(640x480x32 SW)>
# 参数:
# 1、tuple 屏幕尺寸
# 2、int 标志位:窗口模式
# pygame.FULLSCREEN:全屏
# pygame.DOUBLEBUF:双缓冲
# pygame.HWSURFACE:硬件加速,必须跟全屏一起使用:FULLSCREEN|HWSURFACE
# pygame.OPENGL:opengl渲染
# pygame.RESIZABLE:可改变大小
# pygame.NOFRAME:没边框
# 0:标准,默认
# 3、int 色深 一般都是32位
# 创建窗口
# 注意:如果你的程序有什么问题,很可能进入了全屏模式就不太容易退出来了,所以最好先用窗口模式调试好,再改为全屏模式
# 这些显示模式,未必所有的操作系统都支持(放心windows、各种比较流行的Linux发行版都是没问题的),一般来说窗口就用0全屏就用FULLSCREEN,这两个总是OK的。
# 如果你想创建一个硬件显示(surface会存放在显存里,从而有着更高的速度),你必须和全屏一起使用:
# screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE | FULLSCREEN, 32)
# 当然你完全可以把双缓冲(更快)DOUBLEBUF也加上,这就是一个很棒的游戏显示了,不过记得你要使用pygame.display.flip()
# 来刷新显示。pygame.display.update()
# 是将数据画到前面显示,而这个是交替显示的意思。
# 稍微说一下双缓冲的意思,可以做一个比喻:我的任务就是出黑板报,如果只有一块黑板,那我得不停的写,全部写完了稍微Show一下就要擦掉重写,
# 这样一来别人看的基本都是我在写黑板报的过程,看到的都是不完整的黑板报;如果我有两块黑板,那么可以挂一块给别人看,我自己在底下写另一块,
# 写好了把原来的换下来换上新的,这样一来别人基本总是看到完整的内容了。双缓冲就是这样维护两个显示区域,快速的往屏幕上换内容,而不是每次都慢慢地重画。
# 还有OPENGL模式,这是一个得到广泛应用的3D加速显示模式。不过一旦使用了这个模式,pygame中的2D图像函数就不能用了,我们会在以后讲详细的内容。
pygame.display.set_caption("Hello, World!")
# 设置窗口标题
background = pygame.image.load('images/sushiplate.jpg').convert()
# 背景图片
Fullscreen = False
while True:
event = pygame.event.wait()
if event.type == QUIT:
exit()
if event.type == KEYDOWN:
# 全屏,
if event.key == K_f:
Fullscreen = not Fullscreen
if Fullscreen:
print('f')
screen = pygame.display.set_mode((640, 480), FULLSCREEN, 32)
else:
screen = pygame.display.set_mode((640, 480), 0, 32)
# 拖动边框改变尺寸:
if event.type == VIDEORESIZE:
SCREEN_SIZE = event.size
screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32)
pygame.display.set_caption("Window resized to " + str(event.size))
screen_width, screen_height = SCREEN_SIZE
# 这里需要重新填满窗口
for y in range(0, screen_height, background.get_height()):
for x in range(0, screen_width, background.get_width()):
screen.blit(background, (x, y))
# screen.blit(background, (0, 0))
pygame.display.update()
# 刷新一下画面,不然整个画面黑漆漆的
三、event (事件模块)
# 事件 pygame.event
# 需要把代码放置到while True:里面才能不停的捕捉到事件
# 1 get() 非阻塞,返回一个list,没有事件发生时返回[]
# 2 wait() 阻塞,有事件发生时返回一个Event对象, 没有事件时程序挂起
# 3 poll() 非阻塞,随时都返回一个Event,但是,没有事件发生时event.type等于0
for event in pygame.event.get():
if event.type == pygame.QUIT:
# 接收到退出事件后退出程序
sys.exit()
even = pygame.event.wait()
print(even)
even = pygame.event.poll()
if even.type != 0:
print(even)
# 事件 产生途径 参数
# QUIT 用户按下关闭按钮 none
# ATIVEEVENT Pygame被激活或者隐藏 gain, state
# KEYDOWN 键盘被按下 unicode, key, mod
# KEYUP 键盘被放开 key, mod
# MOUSEMOTION 鼠标移动 pos, rel, buttons
# MOUSEBUTTONDOWN 鼠标按下 pos, button
# MOUSEBUTTONUP 鼠标放开 pos, button
# JOYAXISMOTION 游戏手柄(Joystick or pad)移动 joy, axis, value
# JOYBALLMOTION 游戏球(Joy ball)?移动 joy, axis, value
# JOYHATMOTION 游戏手柄(Joystick)?移动 joy, axis, value
# JOYBUTTONDOWN 游戏手柄按下 joy, button
# JOYBUTTONUP 游戏手柄放开 joy, button
# VIDEORESIZE Pygame窗口缩放 size, w, h
# VIDEOEXPOSE Pygame窗口部分公开(expose)? none
# USEREVENT 触发了一个用户事件 code
# 窗口事件
if event.type == pygame.QUIT:
# 关闭窗口
sys.exit()
# 鼠标事件:
if event.type == pygame.MOUSEBUTTONDOWN:
print(event, event.pos, event.button)
# <Event(1025-MouseButtonDown {'pos': (440, 160), 'button': 1, 'window': None})> (440, 160) 1
print(event.pos, event.button) # (328, 151) 1
# 键盘事件
if event.type == pygame.KEYDOWN:
print(event)
# <Event(768-KeyDown {'unicode': '', 'key': 1073742048, 'mod': 4160, 'scancode': 224, 'window': None})>
print(event.mod) # 用于测试组合键
# ctrl键:4160 shift键:4097 alt键:4352 不用死记,用的时候测试一下就行了,
print(event.mod & pygame.KMOD_CTRL, event.mod & pygame.KMOD_SHIFT, event.mod & pygame.KMOD_ALT)
# 64 0 0 0 1 0 0 0 256 只要值不为0 说明对应的按键已按下
print(event.key == 114) # 按下什到键, 可以用数值,也可以用常量,不用死记,测试一下就知道值是多少
print(event.key == pygame.K_DOWN)
# 值可以这些等:114,1073741904,pygame.K_0, pygame.K_a, pygame.K_LEFT
# 事件过滤
# 并不是所有的事件都需要处理的,就好像不是所有登门造访的人都是我们欢迎的一样。比如,俄罗斯方块就无视你的鼠标,而在游戏场景切换的时候,
# 你按什么都是徒劳的。我们应该有一个方法来过滤掉一些我们不感兴趣的事件(当然我们可以不处理这些没兴趣的事件,但最好的方法还是让它们根本
# 不进入我们的事件队列,就好像在门上贴着“XXX免进”一样),我们使用pygame.event.set_blocked(事件名)来完成。如果有好多事件需要过滤
# ,可以传递一个列表,比如pygame.event.set_blocked([KEYDOWN, KEYUP]),如果你设置参数None,那么所有的事件有被打开了。与之相对的,
# 我们使用pygame.event.set_allowed()来设定允许的事件。
# 自定义事件
my_event = pygame.event.Event(pygame.KEYDOWN, key=pygame.K_SPACE, mod=0, unicode=u' ')
# 你也可以像下面这样写,看起来比较清晰(但字变多了……)
my_event = pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_SPACE, "mod": 0, "unicode": u' '})
pygame.event.post(my_event)
补充:
定时(重复)发送事件
pygame.time.set_timer(USEREVENT+10,10000),参数10000是发送事件的间隔时间,单位毫秒
接收事件:
for event in pygame.event.get():
elif event.type == USEREVENT+10:
print('tangjun')
取消事件:把时间间隔设置为0
pygame.time.set_timer(USEREVENT+10, 0)
如果只发送一次事件:
pygame.event.post(pygame.event.Event(USEREVENT + 10))
四、字体
pygame.font
# !/usr/bin/env python
# x, y = pygame.mouse.get_pos()
# # 获得鼠标位置
# 加载图片
# pygame.image.load(background_image_filename)
# background = pygame.image.load(background_image_filename).convert()
# mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()
# 加载图片 , 上面三个方法都会返回一个Surface对象,区别在于convert函数是将图像数据都转化为Surface对象,
# 每次加载完图像以后就应该做这件事件(事实上因为 它太常用了,如果你不写pygame也会帮你做);convert_alpha相比convert,
# 保留了Alpha 通道信息(可以简单理解为透明的部分),这样我们的光标才可以是不规则的形状。
# screen.blit(background, (0,0))
# #将背景图画上去
# screen.fill((200, 200, 200))
# 填充颜色
# screen.blit(background, (0, 0))
# 把图像画到屏幕上
# 参数:
# 1、Surface对象
# 2、tuple 位置
# pygame.display.update()
# # 刷新一下画面,不然整个画面黑漆漆的
# -------------------------------------------------
#==================================================================================
import pygame
from pygame.locals import *
pygame.init()
print(pygame.font.get_fonts())
# ['arial', 'arialblack', 'bahnschrift', 'calibri', 'cambriacambriamath', 'cambria', 'candara', 'comicsansms',
# ...
# '华文楷体', '华文隶书', '华文宋体', '华文细黑', '华文行楷', '华文新魏', '华文中宋', 'extra']
# print(pygame.font.get_default_font()) # freesansbold.ttf
# 设置字体:
# 1 使用系统字体
my_font = pygame.font.SysFont('华文行楷', 46)
# print(my_font) # <pygame.font.Font object at 0x0000024AE1DBCD70>
# 2 使用外部字体:
# 这个语句使用了一个叫做“my_font.ttf”,这个方法之所以好是因为你可以把字体文件随游戏一起分发,避免用户机器上没有需要的字体
my_font = pygame.font.Font('C:\Windows\Fonts\STXINGKA.TTF', 30)
# 说明:怎么找到相应字体的文件名:在C:\Windows\Fonts\找到需要的字体,然后右键属性,就可以看到字体文件名,比如,华文行楷的文件名就是:STXINGKA.TTF
fontface = my_font.render('你好,我是一个好人,how are you ', True, (0, 0, 0), (255, 255, 255))
# 第一个参数是写的文字;第二个参数是个布尔值,以为这是否开启抗锯齿,就是说True的话字体会比较平滑,不过相应的速度有一点点影响;
# 第三个参数是字体的颜色;第四个是背景色,如果你想没有背景色(也就是透明),那么可以不加这第四个参数
# 说明,如果是中文的话,一定要选对字符,否则显示不出或乱码
print(fontface) # <Surface(403x28x8 SW)>
# 注意:fontface是一个Surface对象,接下来知道该怎么做了吧!
pygame.image.save(fontface, 'font.png') # 可以把字体保存为一张图片
五、surface 模块 管理图像和屏幕
补充:
newsur = sur.subsurface(select_rect).copy()
获取一部分子曲面
什么时候需要cop()
如果在自己上面blit的话需要copy(),如:
sur.blit(newsur,rect)
如果是在别的曲面上blit,就不需要copy(),但是copy了也没错,所以还是建议copy吧
screen.blit(newsur,rect)
import pygame, time
pygame.init()
# 怎样生成Curface对象:
# 方法一:应用程序界面
surf1 = pygame.display.set_mode((1000, 1000))
print(surf1) # <Surface(1000x1000x32 SW)>
# 方法二:加载图像
surf2 = pygame.image.load('images/fugu.png').convert()
print(surf2) # <Surface(150x122x32 SW)>
# 方法三:渲染字休
surf3 = pygame.font.SysFont('隶书', 46).render(u'大家好', True, (255, 255, 255), (0, 0, 0))
print(surf3) # <Surface(138x46x8 SW)>
# 方法四:用构造函数返回
surf4 = pygame.surface.Surface((100, 100), depth=24) # 在上面绘制的图形如果超出它的边界,超出部分不会显示
# 如果需要透明通道需要:flags 设置为SRCALPHA,depth深度设置为32位,4*8 等于 32,如果不设置的话,会自动设置为主控台的深度
surf4 = pygame.surface.Surface((100, 100), flags=SRCALPHA, depth=32)
print(surf4) # <Surface(100x100x24 SW)>
# --------------------------------------------------------
# surface的重要方法:
# 1 填充颜色:
surf1.fill((140, 140, 40)) # 填充屏幕,通常用于清屏
# screen.fill((0,222,0),(100,200,200,200)) # 也可以绘制一个矩形
# 2 在surface上面绘制surface:
surf1.blit(surf2, (100, 100))
# 3 设置像素颜色:
for x in range(10):
for y in range(10):
surf1.set_at((500 + x, 500 + y), (222, 222, 222))
# screen.get_at((10, 23))
# 不过记住get_at在对hardware surface操作的时候很慢,而全屏的时候总是hardware的,所以慎用这个方法!
# screen.set_clip(20,20,600,440) # 参数:x, y, width, height
# # 通常游戏的时候你只需要绘制屏幕的一部分。比如魔兽上面是菜单,下面是操作面板,中间的小兵和英雄打的不可开交时候,上下的部分也是保持相对不动的。
# # 为了实现这一点,surface就有了一种叫裁剪区域(clipping area)的东西,也是一个矩形,定义了哪部分会被绘制,也就是说一旦定义了这个区域,
# # 那么只有这个区域内的像素会被修改,其他的位置保持不变,默认情况下,这个区域是所有地方。我们可以使用set_clip来设定,使用get_clip来获得这个区域。
# print(screen.get_clip()) # <rect(20, 20, 600, 440)>
# screen.lock() #很快你就会知道这两句lock和unlock的意思了
# 这里开始绘制屏幕
# screen.unlock()
# 当Pygame往surface上画东西的时候,首先会把surface锁住,以保证不会有其它的进程来干扰,画完之后再解锁。锁和解锁时自动发生的,
# 所以有时候可能不那么有效率,比如上面的例子,每次画100个点,那么就得锁解锁100次,现在我们把两句注释去掉,再执行看看是不是更快了
# (好吧,其实我没感觉出来,因为现在的机器性能都不错,这么点的差异还不太感觉的出来。不过请相信我~复杂的情况下会影响效率的)?
# 当你手动加锁的时候,一定不要忘记解锁,否则pygame有可能会失去响应。虽然上面的例子可能没问题,但是隐含的bug是我们一定要避免的事情
pygame.display.update()
time.sleep(5)
例 1
# 打印一个16*16的色彩方块图片
import pygame
pygame.init()
all_colors = pygame.Surface((4096, 4096), depth=24)
print(all_colors) # <Surface(4096x4096x24 SW)>
for r in range(256):
break # 把这一行代码去掉就可以生成图片
# print(r & 15) # 0 1 2 3....15...
x = (r & 15) * 256
# print(x) 0 256 512 3840 0 512...
y = (r >> 4) * 256
# print(r >> 4) 0*16 1*16 15*16
for g in range(256):
for b in range(256):
all_colors.set_at((x + g, y + b), (r, g, b))
pygame.image.save(all_colors, "allcolors.jpg")
View Code
例 2
# !/usr/bin/env python
import pygame
from pygame.locals import *
from sys import exit
pygame.init() # 初始化
screen = pygame.display.set_mode((640, 480), 0, 32) # 创建主控台
# 创建红、绿、蓝三个surface
def create_scales(height):
red_scale_surface = pygame.surface.Surface((640, height)) # 红surface
green_scale_surface = pygame.surface.Surface((640, height)) # 绿surface
blue_scale_surface = pygame.surface.Surface((640, height)) # 蓝surface
# 为每个surface画640条垂直的线,最终形成颜色渐变的效果
for x in range(640):
c = int((x / 640) * 255) # 宽是640 把640分成255个像素。
red = (c, 0, 0)
green = (0, c, 0)
blue = (0, 0, c)
# line_rect = Rect(x, 0, 1, height) # 画宽度为1,高度为height为的矩形,其实就是画一条垂直的线
# pygame.draw.rect(red_scale_surface, red, line_rect)
# pygame.draw.rect(green_scale_surface, green, line_rect)
# pygame.draw.rect(blue_scale_surface, blue, line_rect)
# 等效于下面三条语句
pygame.draw.line(red_scale_surface, red, (x, 0), (x, height))
pygame.draw.line(green_scale_surface, green, (x, 0), (x, height))
pygame.draw.line(blue_scale_surface, blue, (x, 0), (x, height))
return red_scale_surface, green_scale_surface, blue_scale_surface
red_scale, green_scale, blue_scale = create_scales(80)
color = [127, 127, 127] # 矩形大方块的初始颜色
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
# screen.fill((0, 0, 0))
screen.blit(red_scale, (0, 00))
screen.blit(green_scale, (0, 80))
screen.blit(blue_scale, (0, 160))
x, y = pygame.mouse.get_pos()
# 根据鼠标按下的位置修改 color = [127, 127, 127] 的值
if pygame.mouse.get_pressed()[0]: # 按下鼠标左键
for component in range(3):
if y > component * 80 and y < (component + 1) * 80:
color[component] = int((x / 640) * 255)
pygame.display.set_caption("PyGame Color Test - " + str(tuple(color)))
# 绘制三个圆
for component in range(3):
pos = (int((color[component] / 255.) * 639), component * 80 + 40)
pygame.draw.circle(screen, (255, 255, 255), pos, 20)
pygame.draw.rect(screen, tuple(color), (0, 240, 640, 240))
pygame.display.update()
View Code