鱼C论坛

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

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

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

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

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

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

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



好久不见~

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

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

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

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

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

(请看vcr





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

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


# 颜色常量
WHITE = (255,255,255)
BLACK = (0,0,0)

# 常量
WHITE = 255, 255, 255

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

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

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

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

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

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

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

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

# 背景颜色设定
bg_color = BLACK

running = True

while running:

    # 设定帧数
    clock.tick(60)

    # 延时计时器刷新
    if delay == 0:
        delay = 60

    delay -= 1

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

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

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

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

        # 按键按下事件
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
                
            #F11切换全屏
            if event.key == K_F11:
                full_bool[0] = not full_bool[0]
                full_bool[1] = True

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


    # 窗口背景
    window.fill(WHITE)
    
    #屏幕背景
    screen.fill(BLACK)
    

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

    # 刷新xxx

    # 调整绘制大小,使之填充窗口
    screen_rect = screen.get_rect()
    window_rect = window.get_rect()
    r1, r2 = abs(window_rect[2]/screen_rect[2]), abs(window_rect[3]/screen_rect[3])
    if r1 < r2:
        ratio = r1
        displayed_screen = pygame.transform.smoothscale(screen,
                                                        (ratio*size[0], ratio*size[1]))
    elif r1 > r2:
        ratio = r2
        displayed_screen = pygame.transform.smoothscale(screen,
                                                        (ratio*size[0], ratio*size[1]))
    else:
        ratio = 1
        displayed_screen = screen
    displayed_screen_size = displayed_screen.get_rect()
    displayed_screen_size.center = window_rect.center
    window.blit(displayed_screen,displayed_screen_size)
    

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





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

首先看这段修改和检测窗口拖动与全屏模式的函数【约24-39行】
# 检测和修正全屏
# noinspection PyShadowingNames
def check_full(window, full_bool, size):  # -> None
    """接受参数 fullscreen [bool, bool] 
        [窗口现在是否应该处于全屏状态,是否需要修正窗口状态]
        用于修改窗口状态(大小以及是否全屏,会自动修改full_bool的值)"""
    if full_bool[0] and full_bool[1]:  # 现在窗口应该处于全屏状态,并且没有处于全屏状态
        window = pygame.display.set_mode(pygame.display.list_modes()[0], FULLSCREEN)  # 全屏窗口
        full_bool[1] = False
    elif full_bool[1]:  # 不处于全屏状态
        window = pygame.display.set_mode(size, RESIZABLE)  # 自由拖拽窗口
        full_bool[1] = False
    return window

# 是否全屏
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行】
# 得到鼠标位置
# noinspection PyShadowingNames
def get_pos(offset, ratio):
    """接受两个参数(屏幕绘制的偏移,屏幕缩放比例)"""
    pos = pygame.mouse.get_pos()
    pos = [(pos[0]-offset[0])/ratio,
           (pos[1]-offset[1])/ratio]
    return pos

# 设置鼠标位置
# noinspection PyShadowingNames
def set_pos(pos, offset, ratio):
    """接受三个参数(鼠标位置,屏幕绘制的偏移,屏幕缩放比例)"""
    pos = [pos[0]*ratio+offset[0],
           pos[1]*ratio+offset[1]]
    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】:
# 调整绘制大小,使之填充窗口
    screen_rect = screen.get_rect()
    window_rect = window.get_rect()
    r1, r2 = abs(window_rect[2]/screen_rect[2]), abs(window_rect[3]/screen_rect[3])
    if r1 < r2:
        ratio = r1
        displayed_screen = pygame.transform.smoothscale(screen,
                                                        (ratio*size[0], ratio*size[1]))
    elif r1 > r2:
        ratio = r2
        displayed_screen = pygame.transform.smoothscale(screen,
                                                        (ratio*size[0], ratio*size[1]))
    else:
        ratio = 1
        displayed_screen = screen
    displayed_screen_size = displayed_screen.get_rect()
    displayed_screen_size.center = window_rect.center
    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 怎么样




       



评分

参与人数 13荣誉 +53 鱼币 +43 贡献 +26 收起 理由
陶远航 + 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有你更精彩^_^
中英文泡椒 + 5 + 5 + 3 鱼C有你更精彩^_^
小甲鱼 + 2 无条件支持楼主!

查看全部评分

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2024-8-11 11:40:29 | 显示全部楼层
妹人吗
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

没人看喔,浅浅@一下
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

来了来了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-12 06:40:46 | 显示全部楼层
支持一下~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

来啦来啦
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-13 07:30:17 | 显示全部楼层
不错,好极了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-13 16:09:56 | 显示全部楼层
就需要有这种探索精神的鱼油
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-16 16:20:14 | 显示全部楼层
\
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-8-19 13:49:22 | 显示全部楼层
感谢分享
想知道小甲鱼最近在做啥?请访问 -> 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有你更精彩^_^

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-21 20:57:55 | 显示全部楼层
支持
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

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

已经支持,评分+淘帖
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-22 09:17:25 | 显示全部楼层
蛮有意思的,后续来学习这块
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-29 13:46:24 | 显示全部楼层
终于军训回来了,支持
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-9-19 09:09

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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