Pygame 是一组用来开发游戏软件的 Python 程序模块,Pygame 在 SDL(Simple DirectMedia Layer) 的基础上开发而成。它提供了诸多操作模块,比如图像模块(image)、声音模块(mixer)、输入/输出(鼠标、键盘、显示屏)模块,擅长开发 2D 游戏,Python 也提供了开发 3D 游戏的软件包,比如 Pyglet、Panda3D、PyOgre 等。Pygame 是一个高可移植性的模块可以支持多个操作系统。用它来开发小游戏非常适合。官网https://www.pygame.org/news
若使用pygame模块(python的2D游戏开发库),这是第三方(扩展)模块,若未预先装,需要在CMD中,使用 pip install pygame 先装入,可能需要花点时间。
查看是否电脑上是否有pygame,在CMD中,使用pip list命令,参见下图
若没有 pygame,需要在CMD中,使用 pip install pygame 命令安装,参见下图:
【关于安装安装第三方库的更多情况,可参见:https://blog.csdn.net/cnds123/article/details/104393385】
验证是否成功安装
法一、在CMD中,使用python -m pygame.examples.aliens命令验证,参见下图:
法二、进入Python的交互界面,输入以下命令验证是否成功安装,参见下图:
不报错,则说明安装成功。
Pygame的坐标原点(0,0)点位于左上角,X轴自左向右,Y轴自上向下,单位为像素,参见下图:
在游戏中,所有可见的元素都是以矩形区域来描述位置的;
pygame专门提供了一个类Rect 用于 描述矩形区域,格式:
Rect(x,y,width,height)
Pygame的Rect对象,表示的区域必须位于一个 Surface 对象之上,比如游戏的主窗口(screen)。上述方法由四个关键参数值构成,分别是 left、top、width、height
pygame. Rect(left, top, width, height)
display 用于创建、管理游戏窗口
pygame.display.set_mode() #初始化游戏显示窗口
pygame.dispaly.update() #刷新屏幕内容显示,稍后使用
为了做到游戏程序启动后,不会立即退出,通常会在游戏程序中使用一个游戏循环。
pygame开发游戏的大体框架结构如下:
import pygame
# 初始化pygame
pygame.init()
# 创建游戏的窗口 350 * 573(宽,高);注意:此窗口的大小是依据游戏的背景图片而设定的
screen=pygame.display.set_mode((350,600))
# 游戏循环
while True:
pass
示例代码如下:
# 导入所需的模块
import pygame
import sys
# 导入所有pygame.locals里的变量(比如下面大写的QUIT变量)
from pygame.locals import *
# 初始化pygame
pygame.init()
# 设置窗口的大小,单位为像素
screen = pygame.display.set_mode((450, 300))
# 设置窗口标题
pygame.display.set_caption('Hello World')
# 程序主循环
while True:
# 获取事件
for event in pygame.event.get():
# 判断事件是否为退出事件
if event.type == QUIT:
# 退出pygame
pygame.quit()
# 退出系统
sys.exit()
# 绘制屏幕内容
pygame.display.update()
运行之,显示如下:
#设置窗口图标,下面两句
icon = pygame.image.load("./images/xyicon.png") # 加载图像数据
pygame.display.set_icon(icon) #设置图标
#设置窗口背景图片,下面三两句
bg = pygame.image.load("./images/background.png") # 加载图像数据
screen.blit(bg,(0,0)) #绘制图像 blit(图像,位置);
pygame.display.update() #更新屏幕显示
在前面示例代码,设置窗口标题之后,添加设置窗口图标和设置窗口背景图片:
运行之,如下图所示:
有两个方法:
☆播放特效声音:
pygame.mixer.Sound(filename)
该方法返回一个Sound对象,调用它的.play( )方法,即可播放较短的音频文件(如游戏中的枪炮声等);
☆播放背景音乐:
pygame.mixer.music.load(filename)
该方法用来加载背景音乐,之后调用pygame.mixer.music.play( )方法就可以播放背景音乐(在同一个时刻只允许加载一个背景音乐)
# 加载并播放一个特效音频文件)
sound = pygame.mixer.Sound('./music/alter.mp3')
sound.play()
# 加载背景音乐文件
pygame.mixer.music.load('./music/bgmusic.mp3')
# 播放背景音乐,第一个参数为播放的次数(-1表示无限循环),第二个参数是设置播放的起点(单位为秒)
pygame.mixer.music.play(-1, 0.0)
可以在适当的地方,作为测试在背景图片之后插入播放音频代码段,当然音频文件需要准备好。
Pygame绘制图形的常用的方法:
☆ pygame.draw.line(Surface, color, start_pos, end_pos, width)此方法用于绘制一条线段
☆ pygame.draw.aaline(Surface, color, start_pos, end_pos, blend)此方法用于绘制一条抗锯齿的线
☆ pygame.draw.lines(Surface, color, closed, pointlist, width)此方法用于绘制一条折线
☆ pygame.draw.rect(Surface, color, Rect)此方法用于绘制一个矩形
☆ pygame.draw.rect(Surface, color, Rect, width)此方法用于绘制一个矩形框
☆ pygame.draw.ellipse(Surface, color, Rect)此方法用于绘制一个椭圆
☆ pygame.draw.ellipse(Surface, color, Rect, width)此方法用于绘制一个椭圆框
☆ pygame.draw.polygon(Surface, color, pointlist, width)此方法用于绘制一个多边形
☆ pygame.draw.arc(Surface, color, Rect, start_angle, stop_angle, width)此方法用于绘制一条弧线
☆ pygame.draw.circle(Surface, color, Rect, radius)此方法用于绘制一个圆
示例代码
# 导入需要的模块
import pygame, sys
from pygame.locals import *
from math import pi
# 初始化pygame
pygame.init()
# 设置窗口的大小,单位为像素
screen = pygame.display.set_mode((400,300))
# 设置窗口标题
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)
# 设置背景颜色
screen.fill(WHITE)
# 绘制一条线
pygame.draw.line(screen, GREEN, [10, 10], [50,30], 5)
# 绘制一条抗锯齿的线
pygame.draw.aaline(screen, GREEN, [10, 50],[50, 80],True)
# 绘制一条折线
pygame.draw.lines(screen, BLACK, False,[[10, 80], [50, 90], [200, 80], [220, 30]], 5)
# 绘制一个空心矩形
pygame.draw.rect(screen, BLACK, [75, 10, 50, 20], 2)
# 绘制一个矩形
pygame.draw.rect(screen, BLACK, [150, 10, 50, 20])
# 绘制一个空心椭圆
pygame.draw.ellipse(screen, RED, [225, 10, 50, 20], 2)
# 绘制一个椭圆
pygame.draw.ellipse(screen, RED, [300, 10, 50, 20])
# 绘制多边形
pygame.draw.polygon(screen, BLACK, [[100, 100], [0, 200], [200, 200]], 5)
# 绘制多条弧线
pygame.draw.arc(screen, BLACK,[210, 75, 150, 125], 0, pi/2, 2)
pygame.draw.arc(screen, GREEN,[210, 75, 150, 125], pi/2, pi, 2)
pygame.draw.arc(screen, BLUE, [210, 75, 150, 125], pi,3*pi/2, 2)
pygame.draw.arc(screen, RED, [210, 75, 150, 125], 3*pi/2, 2*pi, 2)
# 绘制一个圆
pygame.draw.circle(screen, BLUE, [60, 250], 40)
# 程序主循环
while True:
# 获取事件
for event in pygame.event.get():
# 判断事件是否为退出事件
if event.type == QUIT:
# 退出pygame
pygame.quit()
# 退出系统
sys.exit()
# 绘制屏幕内容
pygame.display.update()
运行之,显示如下:
Pygame不仅可以在屏幕上绘制形状,还可以将文本绘制到屏幕上。Pygame 提供了一些非常简单易用的函数,可以创建字体和文本。字体(font)是字体类型的一种描述,表示按照统一风格绘制的一整套的字母、数字、符号和字符,例如 宋体 和 Times New Roman 都是字体。
☆font.SysFont()函数来创建一个 Font 对象,这个函数有两个参数,第 1 个参数是字体名称,第 2 个参数是字体大小(以像素点为单位)
☆font.render()函数的参数:第 1 个参数是要绘制的文本的字符串;第 2 个参数指定是否想要抗锯齿的一个 Boolean 值,如果是 True,文本看上去更加平滑一些;第 3 个参数是用来渲染文本的颜色,这个例子中使用的是白色。
☆blit() 函数,将像素从一个 Surface 复制到另一个 Surface 之上。常用两个参数:第一个参数是某矩形图像(Surface实例),第二个参数用于指定绘制的位置。
示例代码
import pygame
import sys
from pygame.locals import *
pygame.init()
wS=pygame.display.set_mode((400,300)) #创建画布名称wS
WHITE=(255,255,255)
myString="Hello World!"
font = pygame.font.SysFont("Times New Roman", 48) #来创建 Font对象
text = font.render(myString, True, WHITE)
wS.blit(text, (100,150)) #将text复制到指定位置(100,150)
# 程序主循环
while True:
# 获取事件
for event in pygame.event.get():
# 判断事件是否为退出事件
if event.type == QUIT:
# 退出pygame
pygame.quit()
# 退出系统
sys.exit()
# 绘制屏幕内容
pygame.display.update()
运行之,显示如下:
Pygame实现动画
由于人类眼睛的特殊生理结构,当所看画面的帧率高于24的时候,就会认为是连贯的。
帧率(Frame rate)是用于测量显示帧数的量度,所谓的测量单位为每秒显示帧数(Frames per Second,简称:FPS)。
在原有坐标系的基础上添加偏移量,再重新绘制,依次一张一张的循环绘制下去,就会得到我们想要的物体移动的效果。
Pygame实现动画主要用到的方法:
☆ pygame.image.load(filename) 加载一张图片
☆pygame.Surface.blit(source, dest, area=None, special_flags = 0) 将图片绘制到屏幕相应坐标上(后面两个参数默认,可以不传)
☆pygame.time.Clock() 获得pygame的时钟
☆pygame.time.Clock.tick(FPS) 设置pygame时钟的间隔时间
示例代码如下:
# 导入需要的模块
import pygame, sys
from pygame.locals import *
# 初始化pygame
pygame.init()
# 设置帧率(屏幕每秒刷新的次数)
FPS = 30
# 获得pygame的时钟
fpsClock = pygame.time.Clock()
# 设置窗口大小
screen = pygame.display.set_mode((500, 400), 0, 32)
# 设置标题
pygame.display.set_caption('Animation')
# 定义颜色
WHITE = (255, 255, 255)
# 加载一张图片
img = pygame.image.load('./images/bird.png')
# 初始化图片的位置
imgx = 10
imgy = 10
# 初始化图片的移动方向
direction = 'right'
# 程序主循环
while True:
# 每次都要重新绘制背景白色
screen.fill(WHITE)
# 判断移动的方向,并对相应的坐标做加减
if direction == 'right':
imgx += 5
if imgx == 380:
direction = 'down'
elif direction == 'down':
imgy += 5
if imgy == 300:
direction = 'left'
elif direction == 'left':
imgx -= 5
if imgx == 10:
direction = 'up'
elif direction == 'up':
imgy -= 5
if imgy == 10:
direction = 'right'
# 该方法将用于图片绘制到相应的坐标中
screen.blit(img, (imgx, imgy))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# 刷新屏幕
pygame.display.update()
# 设置pygame时钟的间隔时间
fpsClock.tick(FPS)
pygame事件可以处理游戏中的各种事情,事件是一个概念,比如点击鼠标左键,按下一个键盘的按键,关闭窗口等等都是一个事件,pygame提供了一个函数来获取游戏的事件,并把他们存放在一个队列中,程序通过读取整个事件队列,来获取当前发生的时间,并作出响应。
Pygame常用的事件如下表:
事件 |
产生途径 |
参数 |
QUIT |
用户按下关闭按钮 |
none |
ACTIVEEVENT |
Pygame被激活或者隐藏 |
gain, state |
KEYDOWN |
键盘被按下 |
unicode, key, mod |
KEYUP |
键盘被放开 |
key, mod |
MOUSEMOTION |
鼠标移动 |
pos, rel, buttons |
MOUSEBUTTONDOWN |
鼠标按下 |
pos, button |
MOUSEBUTTONUP |
鼠标放开 |
pos, button |
VIDEORESIZE |
Pygame窗口缩放 |
size, w, h |
示例代码:
# 导入需要的模块
import pygame, sys
from pygame.locals import *
# 定义颜色
WHITE = (255, 255, 255)
# 初始化pygame
pygame.init()
# 设置窗口的大小,单位为像素
screen = pygame.display.set_mode((500,400), 0, 32)
# 设置窗口的标题
pygame.display.set_caption('Event')
# 设置背景
screen.fill(WHITE)
# 程序主循环
while True:
# 获取事件
for event in pygame.event.get():
# 判断事件是否为退出事件
if event.type == QUIT:
# 退出pygame
pygame.quit()
# 退出系统
sys.exit()
# 获得鼠标按下的位置
if event.type ==MOUSEBUTTONDOWN:
print("鼠标按下:",event.pos)
# 获得鼠标抬起的位置
if event.type ==MOUSEBUTTONUP:
print("鼠标抬起:",event.pos)
# 获得键盘方向键按下的事件
if event.type == KEYDOWN:
if(event.key==K_UP or event.key==K_w):
print("上")
if(event.key==K_DOWN or event.key==K_s):
print("下")
if(event.key==K_LEFT or event.key==K_a):
print("左")
if(event.key==K_RIGHT or event.key==K_d):
print("右")
# 按下键盘的Esc键退出
if(event.key==K_ESCAPE):
# 退出pygame
pygame.quit()
# 退出系统
sys.exit()
# 绘制屏幕内容
pygame.display.update()
效果图如下:
下面给出综合例子
需要两个素材文件:小猫图片 cat.png 和背景图片bg1.jpg,为简便将它们 和源码程序文件,放在同一文件夹中。
源码如下:
import sys
import pygame
from pygame.locals import *
def control_ball(event):
speed = [x,y] =[0,0]# 设置相对位移
speed_offset = 1 #小球的速度
# 如果事件的类型是 键盘输入,就根据方向键来求出速度的方向(默认是从左往右为1,从上往下为1)
if event.type == KEYDOWN:
if event.key == pygame.K_LEFT:
speed[0] -= speed_offset
event.key
if event.key == pygame.K_RIGHT:
speed[0] = speed_offset
event.key
if event.key == pygame.K_UP:
speed[1] -= speed_offset
event.key
if event.key == pygame.K_DOWN:
speed[1] = speed_offset
event.key
#如果没有方向键的输入,则速度为0,小球不动
if event.type in (pygame.K_UP, pygame.K_LEFT, pygame.K_RIGHT, pygame.K_DOWN):
speed = [0,0]
return speed
#定义函数
def play_ball():
pygame.init()#初始化
window_size = Rect(0,0,600,440)#设置窗口的大小
screen = pygame.display.set_mode(window_size.size)#设置窗口模式
pygame.display.set_caption('hello cat')#设置窗口标题
ball_image = pygame.image.load('cat.png')#载入小球图片
back_image = pygame.image.load('bg1.jpg')#载入背景图片
ball_rect = ball_image.get_rect()# 获取小球图片所在的区域
while True:
#退出事件的处理
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
control_speed = control_ball(event)#获取到小球的方向
ball_rect = ball_rect.move(control_speed).clamp(window_size)#小球按照方向移动,并且不会移出窗口。
screen.blit(back_image,(0,0))#设置窗口背景,位于(0,0)处,窗口左上角。
screen.blit(ball_image, ball_rect)#把小球绘制到背景surface上。
pygame.display.flip()#更新窗口内容
#调用函数
play_ball()
加载一张图片分块打乱,用户用鼠标单击两块图片则两块图片交换位置,拼图完成后提示成功。确保图像文件存在并与代码中指定的路径相匹配——我这里图片名是player.png,和源码文件放在同一文件夹中。
源码如下:
import random
from sys import exit # 使用sys模块的exit函数来退出游戏
import pygame
from pygame.locals import * # 导入一些常用的函数和常量
from tkinter import messagebox #导入messagebox模块
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
ROW = 0
COL = 0
CELL_WIDTH = 0 #SCREEN_WIDTH / COL
CELL_HEIGHT = 0 #SCREEN_HEIGHT / ROW
firstClickCell = None
# 标记是否游戏成功
successFlag = False
gamePictures='player.png' ##游戏图片##
pygame.init() # 初始化pygame
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) # 创建了一个窗口 大小是(宽,高)
pygame.display.set_caption('单击交换式拼图') # 设置窗口标题
scaled_image =pygame.transform.scale(pygame.image.load(gamePictures), (SCREEN_WIDTH//6, SCREEN_HEIGHT//6)) #加载原始图片缩小
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
myfont = pygame.font.SysFont('KaiTi', 60) #None系统默认字体,楷体:KaiTi
textImage1 = myfont.render("难度级别 1", True , GREEN)
textImage2 = myfont.render("难度级别 2", True , BLUE)
textImage3 = myfont.render("难度级别 3", True , RED)
myfont = pygame.font.SysFont('SimHei', 20) #黑体:SimHei
textImage4 = myfont.render("选取上面难度等级进入游戏;用户用鼠标单击", True , WHITE)
textImage5 = myfont.render("两个已打乱顺序的小图块则这两块交换位置。", True , WHITE)
screen.fill((0,0,0))
screen.blit(scaled_image, (0, 0))
screen.blit(textImage1, (150, 100))
screen.blit(textImage2, (150, 250))
screen.blit(textImage3, (150, 400))
screen.blit(textImage4, (100, 500))
screen.blit(textImage5, (100, 530))
pygame.display.update()
class PartPicture:
# 初始化一个图片分块,保存图像和图像的位置, id代表原图第几块的位置
def __init__(self, img, x, y, id):
self.img = img
self.x = x
self.y = y
self.id = id
# 判断是否在图片内
def isOver(self):
w, h = self.img.get_size()
point_x, point_y = pygame.mouse.get_pos() # 返回鼠标当前坐标
in_x = self.x < point_x < self.x + w
in_y = self.y < point_y < self.y + h
return in_x and in_y
# 检测移动
def isPressed(self, pictureList):
# print('is_pressed')
if self.isOver():
b1, b2, b3 = pygame.mouse.get_pressed()
if b1 == 1:
global firstClickCell
global successFlag
if firstClickCell is None:
firstClickCell = self
print('id为{}的块被点击'.format(firstClickCell.getId()))
else:
print('交换{}与{}的坐标'.format(firstClickCell.getId(), self.getId()))
self.pictureSwitch(firstClickCell, self)
if self.isFinish(pictureList):
successFlag = True
print('成功!')
else:
successFlag = False
firstClickCell = None
return True
return False
# 判断拼图完成
def isFinish(self, pictureList):
for cell in pictureList:
nowp_x, nowp_y = cell.getXY()
p_x = cell.getId() % COL * CELL_WIDTH
p_y = (cell.getId() // COL) * CELL_HEIGHT
print("id{} nowx{}与nowy{}的坐标 本来的坐标x{}y{}".format(cell.getId(), nowp_x, nowp_y, p_x, p_y))
if nowp_x != p_x or nowp_y != p_y:
return False
return True
def pictureSwitch(self, cell1, cell2):
tempX = cell1.getX()
tempY = cell1.getY()
cell1.setXY(cell2.getX(), cell2.getY())
cell2.setXY(tempX, tempY)
def render(self, screen):
screen.blit(self.img, (self.x, self.y))
# get和set方法
def getX(self):
return self.x;
def getY(self):
return self.y
# 获取图片坐标
def getXY(self):
return self.x, self.y
# 修改图片坐标
def setXY(self,x, y):
self.x = x
self.y = y
# 获取id
def getId(self):
return self.id
# 图片分块
def devide(imgSrc):
# 切割原来的图片
pictures = []
ScPictures = []
id = 0 # 给每个分块的图片设置下标
for i in range(ROW):
for j in range(COL):
# 提取部分图片
partOfPicutre = imgSrc.subsurface(j * CELL_WIDTH, i * CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT)
# 保存第一组图片
tempPicture = PartPicture(partOfPicutre, j * CELL_WIDTH, i * CELL_HEIGHT, id)
pictures.append(tempPicture)
# 保存第二组图片
tempPicture = PartPicture(partOfPicutre, j * CELL_WIDTH, i * CELL_HEIGHT, id)
ScPictures.append(tempPicture)
id += 1
random.shuffle(pictures)
# 开始利用第二组图片来打乱原来的图片
for i in range(len(pictures)):
pictures[i].setXY(ScPictures[i].getX(), ScPictures[i].getY())
return pictures # 把打乱的图片返回
setInit = False
while True:
if setInit:
break
for event in pygame.event.get():
if event.type == QUIT:
exit() # 接收到退出事件后退出程序
elif event.type == MOUSEBUTTONDOWN:
b1, b2, b3 = pygame.mouse.get_pressed()
if b1 == 1:
point_x, point_y = pygame.mouse.get_pos() # 返回鼠标当前坐标
if 100 < point_y < 200:
ROW = 3
COL = 3
setInit = True
elif 250 < point_y < 350:
ROW = 4
COL = 4
setInit = True
elif 350 < point_y < 450:
ROW = 5
COL = 5
setInit = True
CELL_WIDTH = SCREEN_WIDTH / COL
CELL_HEIGHT = SCREEN_HEIGHT / ROW
# 原图 提高 blit 的速度 convert_alpha相对于convert,保留了图像的Alpha 通道信息,可以认为是保留了透明的部分,实现了透明转换
imgSrc = pygame.image.load(gamePictures).convert() ##加载游戏图片
PictureList = devide(imgSrc) #图片分块
while True: # 游戏主循环
for event in pygame.event.get():
if event.type == QUIT:
exit() # 接收到退出事件后退出程序
elif event.type == MOUSEBUTTONDOWN:
for cell in PictureList:
# 检测按键按下,并且交换图片位置
if cell.isPressed(PictureList):
break
for partPicture in PictureList:
partPicture.render(screen)
if not successFlag:
# # Sta 绘制分割线
for i in range(1, COL):
pygame.draw.lines(screen, GREEN, 0, [(i * SCREEN_WIDTH // COL, 0), (i * SCREEN_WIDTH // COL, SCREEN_HEIGHT)], 1)
for i in range(1, ROW):
pygame.draw.lines(screen, GREEN, 0, [(0, i * SCREEN_HEIGHT // ROW), (SCREEN_WIDTH, i * SCREEN_HEIGHT // ROW)], 1)
# End 绘制分割线
pygame.display.update() # 刷新一下画面
if successFlag:
messagebox.showinfo("祝贺你!", "成功完成!")
break
效果示意图如下:
下面给出拼图游戏的另一种实现
功能:窗体上有两个按钮,单击"open"按钮加载的游戏图片,单击"start"按钮将加载的游戏图片分割成9小块打乱排列。游戏规则:将一个小块拖动到另一小块时,两小块图片将交换位置,当恢复原样时,提示"success!"。
源码如下:
import pygame
import random
import sys
import tkinter as tk
from tkinter import filedialog
# 初始化Tkinter
root = tk.Tk()
root.withdraw()
# 初始化游戏
pygame.init()
# 设置游戏窗口尺寸
WIDTH = 600
HEIGHT = 600
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("拼图游戏:拖动交换小块图")
# 设置颜色
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
# 设置字体
font = pygame.font.Font(None, 36)
# 设置拼图块的尺寸和间隙
BLOCK_SIZE = 200
BLOCK_MARGIN = 5
# 设置拼图块的初始位置
block_positions = [(j * (BLOCK_SIZE + BLOCK_MARGIN), i * (BLOCK_SIZE + BLOCK_MARGIN))
for i in range(3) for j in range(3)]
# 拼图块的当前位置
current_positions = block_positions.copy()
# 游戏状态
game_started = False
puzzle_completed = False
# 选中的拼图块
selected_block = None
selected_block_index = None
# 加载游戏图片
original_image = None
block_images = []
def load_image():
global original_image, block_images
# 选择并加载计算机中的图片
file_path = filedialog.askopenfilename()
if file_path:
try:
original_image = pygame.image.load(file_path)
original_image = pygame.transform.scale(original_image, (BLOCK_SIZE * 3, BLOCK_SIZE * 3))
# 分割原始图片为拼图块
block_images = []
for i in range(3):
for j in range(3):
block_images.append(original_image.subsurface(pygame.Rect(j * BLOCK_SIZE, i * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)))
# 重置游戏状态
global game_started, puzzle_completed
game_started = False
puzzle_completed = False
except pygame.error:
print("加载图片失败!")
sys.exit(1)
# 检查拼图是否完成
def check_puzzle_completed():
global puzzle_completed
puzzle_completed = all(current_positions[i] == block_positions[i] for i in range(len(current_positions)))
# "start"按钮的点击事件处理
def start_game():
global game_started, puzzle_completed, current_positions
if original_image is not None:
current_positions = block_positions.copy()
random.shuffle(current_positions)
game_started = True
puzzle_completed = False
# 交换拼图块位置
def swap_pieces(pos1, pos2):
current_positions[pos1], current_positions[pos2] = current_positions[pos2], current_positions[pos1]
# 获取拼图块索引
def get_block_index(position):
for i, pos in enumerate(current_positions):
if pos == position:
return i
return None
# 游戏主循环
running = True
clock = pygame.time.Clock()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if 50 <= event.pos[0] <= 180 and 50 <= event.pos[1] <= 80:
load_image() # 当鼠标在"open"按钮区域内按下时,调用load_image()函数加载图片
elif game_started and not puzzle_completed:
for i, position in enumerate(current_positions):
block_rect = pygame.Rect((position[0], position[1]), (BLOCK_SIZE, BLOCK_SIZE))
if block_rect.collidepoint(event.pos):
selected_block = position # 记录选中的拼图块的位置
selected_block_index = i # 记录选中的拼图块的索引
break
elif 200 <= event.pos[0] <= 330 and 50 <= event.pos[1] <= 80:
start_game() # 当鼠标在"start"按钮区域内按下时,调用start_game()函数开始游戏
elif event.type == pygame.MOUSEBUTTONUP:
if game_started and not puzzle_completed and selected_block is not None:
for i, position in enumerate(current_positions):
block_rect = pygame.Rect((position[0], position[1]), (BLOCK_SIZE, BLOCK_SIZE))
if block_rect.collidepoint(event.pos) and selected_block != position:
swap_pieces(selected_block_index, i) # 交换选中的拼图块与鼠标释放位置上的拼图块的位置
break
selected_block = None
selected_block_index = None
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
if original_image is not None:
random.shuffle(current_positions) # 按下"r"键后,随机洗牌拼图块的位置
game_started = True
puzzle_completed = False
elif event.key == pygame.K_q:
running = False # 按下"q"键后,退出游戏循环
window.fill(WHITE) # 填充窗口背景颜色为白色
if original_image is not None:
for i in range(len(current_positions)):
position = current_positions[i]
pygame.draw.rect(window, BLUE, (position[0], position[1], BLOCK_SIZE, BLOCK_SIZE)) # 绘制拼图块的矩形边框
window.blit(block_images[i], position) # 在拼图块的位置上绘制对应的图像
pygame.draw.rect(window, BLUE, (50, 50, 130, 30)) # 绘制"open"按钮的矩形边框
open_text = font.render("open", True, WHITE) # 创建渲染的"open"文本图像
window.blit(open_text, (90, 55)) # 在窗口上绘制"open"文本图像
pygame.draw.rect(window, BLUE, (200, 50, 130, 30)) # 绘制"start"按钮的矩形边框
start_text = font.render("start", True, WHITE) # 创建渲染的"start"文本图像
window.blit(start_text, (235, 55)) # 在窗口上绘制"start"文本图像
check_puzzle_completed() # 检查拼图是否完成
if puzzle_completed:
success_text = font.render("success!", True, WHITE) # 创建渲染的"success!"文本图像
success_text_rect = success_text.get_rect(center=(WIDTH // 2, HEIGHT // 2)) # 计算"success!"文本图像的中心位置
window.blit(success_text, success_text_rect) # 在窗口上绘制"success!"文本图像
pygame.display.flip() # 更新窗口显示
clock.tick(60) # 控制游戏循环的帧率为60帧每秒
pygame.quit()
在此就不给出效果示意图了,你可以试试。
python和pygame实现烟花特效 https://blog.csdn.net/cnds123/article/details/134974517
python和pygame实现捉小兔游戏 https://blog.csdn.net/cnds123/article/details/134996061
附录
Pygame官方文档https://www.pygame.org/docs/