鱼C论坛

 找回密码
 立即注册
查看: 3295|回复: 14

[技术交流] pygame窗口:任意缩放

[复制链接]
发表于 2024-8-10 15:07:49 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 cjjJasonchen 于 2024-8-10 15:07 编辑

Pygame  游戏开发 —— 梦开始的地方



好久不见~

不知道各位在使用pygame自带的全屏功能的时候,有没有感觉到各种不顺手呢~

今天来教大家驯服pygame自带的全屏功能~~

(此教程为进阶pygame教程,萌新退散~~~)

在小甲鱼老师的旧版教程中,曾经提到过“不建议使用窗口的拖动变形功能,因为会导致动画精灵的变形,位置错误等问题”

但这次,我发现里拖动变形的正确打开方式:

(请看vcr





上面的视频展示了拖动窗口、全屏,背景填充,鼠标位置的设定与读取等操作,下面先看看源码:

(大概扫一眼就行,这里放出来主要是给大家运行,玩一下,感受一下和普通窗口的区别,别忘了注意控制台哦。后面我会分段细讲代码)
  1. import pygame
  2. import sys
  3. from pygame.locals import *


  4. # 颜色常量
  5. WHITE = (255,255,255)
  6. BLACK = (0,0,0)

  7. # 常量
  8. WHITE = 255, 255, 255

  9. # 设定窗口
  10. size = width, height = [1300, 700]  # 16*50 : 9*60
  11. window = pygame.display.set_mode(size, RESIZABLE)  # 自由拖动大小
  12. screen = pygame.Surface(size)
  13. pygame.display.set_caption("可拖动窗口")
  14. window_bg = WHITE

  15. clock = pygame.time.Clock() # 时钟

  16. delay = 60  # 延时计时器(1秒)

  17. # 检测和修正全屏
  18. # noinspection PyShadowingNames
  19. def check_full(window, full_bool, size):  # -> None
  20.     """接受参数 fullscreen [bool, bool]
  21.         [窗口现在是否应该处于全屏状态,是否需要修正窗口状态]
  22.         用于修改窗口状态(大小以及是否全屏,会自动修改full_bool的值)"""
  23.     if full_bool[0] and full_bool[1]:  # 现在窗口应该处于全屏状态,并且没有处于全屏状态
  24.         window = pygame.display.set_mode(pygame.display.list_modes()[0], FULLSCREEN)  # 全屏窗口
  25.         full_bool[1] = False
  26.     elif full_bool[1]:  # 不处于全屏状态
  27.         window = pygame.display.set_mode(size, RESIZABLE)  # 自由拖拽窗口
  28.         full_bool[1] = False
  29.     return window

  30. # 是否全屏
  31. full_bool = [False, False]  # [是否全屏,是否需要修正窗口状态]

  32. # 得到鼠标位置
  33. # noinspection PyShadowingNames
  34. def get_pos(offset, ratio):
  35.     """接受两个参数(屏幕绘制的偏移,屏幕缩放比例)"""
  36.     pos = pygame.mouse.get_pos()
  37.     pos = [(pos[0]-offset[0])/ratio,
  38.            (pos[1]-offset[1])/ratio]
  39.     return pos

  40. # 设置鼠标位置
  41. # noinspection PyShadowingNames
  42. def set_pos(pos, offset, ratio):
  43.     """接受三个参数(鼠标位置,屏幕绘制的偏移,屏幕缩放比例)"""
  44.     pos = [pos[0]*ratio+offset[0],
  45.            pos[1]*ratio+offset[1]]
  46.     pygame.mouse.set_pos(pos)

  47. displayed_screen_size = size  # 屏幕的矩形大小
  48. ratio = 1  # 屏幕缩放率

  49. # 背景颜色设定
  50. bg_color = BLACK

  51. running = True

  52. while running:

  53.     # 设定帧数
  54.     clock.tick(60)

  55.     # 延时计时器刷新
  56.     if delay == 0:
  57.         delay = 60

  58.     delay -= 1

  59.     # 检测并修改全屏状态
  60.     window = check_full(window, full_bool, size)

  61.     # 得到鼠标位置
  62.     pos = get_pos([displayed_screen_size[0], displayed_screen_size[1]],
  63.                     ratio)

  64.     # 事件检测
  65.     for event in pygame.event.get():
  66.         if event.type == QUIT:
  67.             pygame.quit()
  68.             sys.exit()

  69.         # 鼠标
  70.         if event.type == MOUSEBUTTONDOWN:
  71.             if event.button == 1: # 左键按下,设置鼠标位置
  72.                 pos = pygame.mouse.get_pos()
  73.                 set_pos(
  74.                     [600,400],
  75.                     [displayed_screen_size[0], displayed_screen_size[1]],
  76.                     ratio)
  77.                 print(f"鼠标位置设定为屏幕[600,400](小白点)处")
  78.             elif event.button == 3:  # 右键按下,显示鼠标位置
  79.                 print(pos)

  80.         # 按键按下事件
  81.         if event.type == KEYDOWN:
  82.             if event.key == K_ESCAPE:
  83.                 pygame.quit()
  84.                 sys.exit()
  85.                
  86.             #F11切换全屏
  87.             if event.key == K_F11:
  88.                 full_bool[0] = not full_bool[0]
  89.                 full_bool[1] = True

  90.         # 按键抬起事件
  91.         if event.type == KEYUP:
  92.             pass


  93.     # 窗口背景
  94.     window.fill(WHITE)
  95.    
  96.     #屏幕背景
  97.     screen.fill(BLACK)
  98.    

  99.     #画 xxxx
  100.     # 一个圆
  101.     pygame.draw.circle(screen, [255,0,0], [300,300], 50, width = 3)
  102.     # 白点
  103.     pygame.draw.rect(screen, rect=[600,400,5,5],color=(255,255,255))
  104.    

  105.     # 刷新xxx

  106.     # 调整绘制大小,使之填充窗口
  107.     screen_rect = screen.get_rect()
  108.     window_rect = window.get_rect()
  109.     r1, r2 = abs(window_rect[2]/screen_rect[2]), abs(window_rect[3]/screen_rect[3])
  110.     if r1 < r2:
  111.         ratio = r1
  112.         displayed_screen = pygame.transform.smoothscale(screen,
  113.                                                         (ratio*size[0], ratio*size[1]))
  114.     elif r1 > r2:
  115.         ratio = r2
  116.         displayed_screen = pygame.transform.smoothscale(screen,
  117.                                                         (ratio*size[0], ratio*size[1]))
  118.     else:
  119.         ratio = 1
  120.         displayed_screen = screen
  121.     displayed_screen_size = displayed_screen.get_rect()
  122.     displayed_screen_size.center = window_rect.center
  123.     window.blit(displayed_screen,displayed_screen_size)
  124.    

  125.     # 刷新界面
  126.     pygame.display.update()


复制代码






好的,下面我们开始逐一讲解代码:
(默认各位都有pygame基础,有很多能网上轻易查到的和注释写的就不细说了)

首先看这段修改和检测窗口拖动与全屏模式的函数【约24-39行】

  1. # 检测和修正全屏
  2. # noinspection PyShadowingNames
  3. def check_full(window, full_bool, size):  # -> None
  4.     """接受参数 fullscreen [bool, bool]
  5.         [窗口现在是否应该处于全屏状态,是否需要修正窗口状态]
  6.         用于修改窗口状态(大小以及是否全屏,会自动修改full_bool的值)"""
  7.     if full_bool[0] and full_bool[1]:  # 现在窗口应该处于全屏状态,并且没有处于全屏状态
  8.         window = pygame.display.set_mode(pygame.display.list_modes()[0], FULLSCREEN)  # 全屏窗口
  9.         full_bool[1] = False
  10.     elif full_bool[1]:  # 不处于全屏状态
  11.         window = pygame.display.set_mode(size, RESIZABLE)  # 自由拖拽窗口
  12.         full_bool[1] = False
  13.     return window

  14. # 是否全屏
  15. full_bool = [False, False]  # [是否全屏,是否需要修正窗口状态]
复制代码


先看第一个参数:window

这里先提一下本文中 window 和 screen 的定义:

小甲鱼曾经说过,pygame 通多 display.set_mode 创建的窗口(同样也是 Surface 对象),称为 window"窗口" 或 screen"屏幕" ,根据小甲鱼老师的习惯,我们一般把它称为 screen

但是,今天,我们把它称作 window , 因为 screen 还有其他任务要完成

所以,window 今天的任务就是成为一个优秀的窗口,而 screen 仅仅只负责“屏幕”(显示区域)

先看图:

上面视频中的窗口,我将他缩小了一些方便演示

上面视频中的窗口,我将他缩小了一些方便演示


这是上面视频和代码中的窗口,这一整个窗口是 "window", 但只有黑色部分属于 "screen"。

让我拖拽改变一下窗口大小:

这是拉长后的窗口

这是拉长后的窗口

我将他拉长了,可以看到,白色的部分从上下两条变成了左右两条,屏幕变大了一些,说明此时窗口的宽度大于屏幕的宽度,高度与屏幕高度相等。
并且上面的红色圆和白色的点位置看起来没有发生变化。

所以,window的xy轴的长度都在能自由变化,而screen只是在比例缩放而已

其他的所有动画精灵等等对象可以像以前一样绘制在 screen 上,再把 screen 绘制再window上就ok辣!

好,现在大家已经知道window是什么了,我们回到代码看下一个参数~

full_bool:
这个参数中传入的就是函数后定义的列表,由两个bool值组成,第一个表示现在window是不是处于全屏状态,第二个是窗口的实际全屏状态是否与第一个布尔值的状态不一致

(在后续事件判定的代码中会判定是否全屏,但只是判定,不是修改,所以需要上面这个函数每帧修改全屏状态。)
(第二个布尔值的存在是为了防止重复重新设置屏幕全屏状态)

第三个参数size:
窗口在解除全屏后应该设置的大小



然后我们看下一段代码吧~

【约41-56行】
  1. # 得到鼠标位置
  2. # noinspection PyShadowingNames
  3. def get_pos(offset, ratio):
  4.     """接受两个参数(屏幕绘制的偏移,屏幕缩放比例)"""
  5.     pos = pygame.mouse.get_pos()
  6.     pos = [(pos[0]-offset[0])/ratio,
  7.            (pos[1]-offset[1])/ratio]
  8.     return pos

  9. # 设置鼠标位置
  10. # noinspection PyShadowingNames
  11. def set_pos(pos, offset, ratio):
  12.     """接受三个参数(鼠标位置,屏幕绘制的偏移,屏幕缩放比例)"""
  13.     pos = [pos[0]*ratio+offset[0],
  14.            pos[1]*ratio+offset[1]]
  15.     pygame.mouse.set_pos(pos)
复制代码


这两个函数差不多,但是可能有的鱼油不知道有什么用,为什么不用pygame默认的鼠标交互函数嘞?

上图:

拉长的窗口(带数值

拉长的窗口(带数值



如果使用pygame自带的鼠标位置计算函数:
得到的鼠标位置是[x1 + x2,  y]

但如果使用上面我提供的函数:
鼠标位置是[x2 * 屏幕缩放比例,y * 屏幕缩放比例]

这问题产生的原因再上面代码讲到window和screen定义时用加粗字表示出来了,没看到的可以爬楼回去看哦~

第一个参数pos:
和pygame的set_pos一样,设置鼠标的位置,但是不同的是我设置鼠标在screen中的位置,而pygame设置了在window中的位置

第二个参数:offset
可以理解为是 x1 的部分

第三个参数:ratio
可以理解为screen缩放的比例

(当然后面两个参数都在每帧代码末尾已经算好了,你们只需要填写第一个参数就可以啦~)

下一段【约134-151】:
  1. # 调整绘制大小,使之填充窗口
  2.     screen_rect = screen.get_rect()
  3.     window_rect = window.get_rect()
  4.     r1, r2 = abs(window_rect[2]/screen_rect[2]), abs(window_rect[3]/screen_rect[3])
  5.     if r1 < r2:
  6.         ratio = r1
  7.         displayed_screen = pygame.transform.smoothscale(screen,
  8.                                                         (ratio*size[0], ratio*size[1]))
  9.     elif r1 > r2:
  10.         ratio = r2
  11.         displayed_screen = pygame.transform.smoothscale(screen,
  12.                                                         (ratio*size[0], ratio*size[1]))
  13.     else:
  14.         ratio = 1
  15.         displayed_screen = screen
  16.     displayed_screen_size = displayed_screen.get_rect()
  17.     displayed_screen_size.center = window_rect.center
  18.     window.blit(displayed_screen,displayed_screen_size)
复制代码


这一段负责调整screen,将其放大或缩小(这里是按window短边比例计算,实际应用时可以灵活调整,按照长边或各计算50%都是可以的)

首先,得到窗口和屏幕的大小,
得到x轴和y轴长度比例,
判断得出短边,将screen大小改变为刚好顶满短边。

然后把screen放在window中间~

好啦,讲解结束~鱼油们学会了吗~~~




通俗来讲呢,上面这套框架的使用方法就是:

将原版游戏中绘制在原本screen上(本文中称window)的任何东西,都绘制在新的screen上,把这个screen当成原来的screen一样用就可以了。

但是要注意鼠标的交互函数,set_pos 和 get_pos 不能再使用原来的,要根据教程使用上面我提供的。

同时,只需要移动,旋转,缩放screen,就可以完成"震动反馈""地图拖动"等等高端操作,

不过话说,这层screen已经有它的工作了,为何不再加一层呢?不如就叫 word 怎么样




       



评分

参与人数 14荣誉 +54 鱼币 +44 贡献 +26 收起 理由
九歌当下 + 1 + 1 鱼C有你更精彩^_^
陶远航 + 5 + 5 + 3
zsy0226 + 2 + 2 鱼C有你更精彩^_^
琅琊王朝 + 5 + 5 看不懂思密达
Ewan-Ahiouy + 5 + 5 感谢楼主无私奉献!
liuhongrun2022 + 5 + 3 无条件支持楼主!
某一个“天” + 3 来晚了~
smallwh + 1 + 2 鱼C有你更精彩^_^
python爱好者. + 5 + 5 + 3 鱼C有你更精彩^_^
中英文泡椒 + 3 鱼C有你更精彩^_^

查看全部评分

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2024-8-11 11:40:29 | 显示全部楼层
妹人吗
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-8-11 11:45:25 | 显示全部楼层
@不二如是 @KeyError @中英文泡椒 @歌者文明清理员 @zhangchenyvn @小甲鱼 @Mike_python小 @学习编程中的Ben @python爱好者. @过默 @凌凌祺 @学习学习在研究 @flyps @某一个“天” @tommyyu @陶远航 @zhangjinxuan @python爱好者. @liuhongrun2022 @zhangchenyvn @Ewan-Ahiouy @琅琊王朝

没人看喔,浅浅@一下
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-11 11:54:57 | 显示全部楼层
cjjJasonchen 发表于 2024-8-11 11:45
@不二如是 @KeyError @中英文泡椒 @歌者文明清理员 @zhangchenyvn @小甲鱼 @Mike_python小 @学习编程中的Be ...

来了来了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-12 06:40:46 | 显示全部楼层
支持一下~
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-12 14:52:13 | 显示全部楼层
cjjJasonchen 发表于 2024-8-11 11:45
@不二如是 @KeyError @中英文泡椒 @歌者文明清理员 @zhangchenyvn @小甲鱼 @Mike_python小 @学习编程中的Be ...

来啦来啦
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-13 07:30:17 | 显示全部楼层
不错,好极了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-13 16:09:56 | 显示全部楼层
就需要有这种探索精神的鱼油
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-16 16:20:14 | 显示全部楼层
\
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-8-19 13:49:22 | 显示全部楼层
感谢分享
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-21 20:29:39 | 显示全部楼层
还需要更多热度  @KeyError @中英文泡椒 @歌者文明清理员 @zhangchenyvn @小甲鱼 @Mike_python小 @学习编程中的Ben @python爱好者. @过默 @凌凌祺 @学习学习在研究 @flyps @某一个“天” @tommyyu @陶远航 @zhangjinxuan @python爱好者. @liuhongrun2022 @zhangchenyvn @Ewan-Ahiouy @琅琊王朝

评分

参与人数 2荣誉 +10 鱼币 +10 贡献 +3 收起 理由
Ewan-Ahiouy + 5 + 5 鱼C有你更精彩^_^
python爱好者. + 5 + 5 + 3 鱼C有你更精彩^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-21 20:57:55 | 显示全部楼层
支持
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-8-21 20:59:20 | 显示全部楼层
不二如是 发表于 2024-8-21 20:29
还需要更多热度  @KeyError @中英文泡椒 @歌者文明清理员 @zhangchenyvn @小甲鱼 @Mike_python小 @学习编程 ...

已经支持,评分+淘帖
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-22 09:17:25 | 显示全部楼层
蛮有意思的,后续来学习这块
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-29 13:46:24 | 显示全部楼层
终于军训回来了,支持
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-8 11:14

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表