找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 546|回复: 3

[经验] 利用drag特性实现长按与鼠标焦点获取功能(renpy进阶学习经验三)

[复制链接]
发表于 2024-5-28 20:30:01 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

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

×
本帖最后由 烈林凤 于 2024-5-29 11:06 编辑

最近我更新的频率稍微有些快啊,这竟然是本月的第二篇帖子!(笑)
这是“renpy进阶学习经验”系列的第三篇,非常感谢renpy群友的支持!(虽然和系列没什么关系,但还是想感谢一下))))

这次给各位带来的是“利用drag(拖放事件)特性实现长按与鼠标获取功能”

老样子,尽量不使用太难懂的python内容,只要懂renpy并且会一点python基础知识都可以看懂。
关于教程里的很多名词的命名都无所谓,你想怎么命名都可以,我这么命名只是我听着顺耳。
这次将很多讲解的部分放在了代码注释里,请自行查看。
如果你觉得看文章看不懂想直接抄代码,可以直接复制“抄代码环节”里的代码。

感谢广大群友提供的灵感,因为renpy没有原生的长按按钮事件,因此只能手搓一个出来了。

注意!本篇教程使用的是8.2.1版本的renpy,虽然说用低版本不一定会报错,但还是建议换成最新版本,谢谢配合!


接下来进入正题——

这一功能存在一大缺点——如果长按后不进行移动,那么就不会触发drag事件,导致松手后会被判定为点按,之后我可能会再出一篇制作优化后的版本。
关于drag事件的所有功能可以查看文档:拖放组件 — Ren'Py 中文文档 (renpy.cn)

No.1
我们需要先对变量进行定义——

[RenPy] 纯文本查看 复制代码
default num_test_1 = 0 # 变量1
default num_test_2 = 0 # 变量2
default mouse_pos = (0,0) # 变量3,鼠标焦点位置


因为要做三项测试,因此需要三个变量来获取数值

No.2
接下来需要写screen主体部分——

[RenPy] 纯文本查看 复制代码
screen drag_test():
    # 用于直观显示变量1的改变
    text "[num_test_1]":
        color "#fff"
        xycenter(0.3, 0.3)
    frame:
        xycenter(0.3, 0.5)
        xysize(200, 100)
        drag:
            xysize(200, 200) # 将组件大小和frame区域大小设置成一致,防止组件因为拖动而跑动
            clicked SetVariable("num_test_1",num_test_1+1) # 当鼠标左键单击时,使变量1数值+1
            dragged drag_test_1 # 当鼠标拖拽组件时,调用drag_test_1函数,输入两个入参
            dropped drag_test_1 # 当鼠标释放组件时,调用drag_test_1函数,输入两个入参
            add "#ffffff"
            text "单点+1,长按拖动并落下+10" xycenter(0.5, 0.5) color "#000000" size 20
   
    # 用于直观显示变量2的改变
    text "[num_test_2]":
        color "#fff"
        xycenter(0.7, 0.3)
    frame:
        xycenter(0.7, 0.5)
        xysize(200, 100)
        drag:
            xysize(200, 200)
            clicked SetVariable("num_test_2",num_test_2+1) # 当鼠标左键单击时,使变量2数值+1
            dragging drag_test_2 # 当鼠标正在拖拽组件时,调用drag_test_2函数,输入一个入参
            add "#ffffff"
            text "单点+1,长按拖动+10" xycenter(0.5, 0.5) color "#000000" size 20

    # 用于直观显示变量3的改变
    text "[mouse_pos]":
        color "#fff"
        xycenter(0.5, 0.5)
    frame:
        xycenter(0.5, 0.7)
        xysize(500, 300)
        drag:
            xysize(500, 300)
            dragging drag_test_3 # 当鼠标正在拖拽组件时,调用drag_test_3函数,输入一个入参
            add "#ffffff"
            text "鼠标焦点" xycenter(0.5, 0.5) color "#000000" size 30


这里利用了drag事件的clicked、dragged、dropped、dragging方法,具体详细的功能请去查看文档,我这里只简单说明一下。
clicked:相当于button里的action,可以调用各类action行为(界面行为(action)、值(value)和函数 — Ren'Py 中文文档 (renpy.cn)),这里使用了SetVariable行为用于改变数值。
dragged、dropped:利用该方法可以调用函数,并向函数输入两个入参,分别在组件被拖拽时和掉落时触发,dropped方法需要在dragged被触发后才能被触发。
dragging:利用该方法可以调用函数,并向函数输入一个入参,在组件正在被拖拽时触发。

NO.3
然后我们需要定义各类函数——

[RenPy] 纯文本查看 复制代码
init -10 python:
    def drag_test_1(drags, drop):
        # 如果drop(第二个入参)为为None,则使变量1的值+10
        if not drop:
            renpy.run(SetVariable("num_test_1",num_test_1+10))
        return

    def drag_test_2(drags):
        # 如果调用了函数,则使变量2的值+10
        renpy.run(SetVariable("num_test_2",num_test_2+10))
        return

    def drag_test_3(drags):
        # 如果调用了函数,则使变量3的值变为鼠标焦点位置
        renpy.run(SetVariable("mouse_pos",renpy.get_mouse_pos()))


这里需要一点点python基础内容——def定义函数,不会的可以去自行学习一下,并不是很难)
这些函数就是NO.2里调用的那些函数,我使用了一个python等效行为——renpy.run,可以直接调用action行为,并调用了SetVariable行为用于改变变量,具体请参考文档https://doc.renpy.cn/zh-CN/screen_python.html#renpy.run
除此之外,我还使用了一个renpy.get_mouse_pos()函数来获取鼠标的焦点位置,当玩家拖动组件时就能不断地获取参数,具体请参考文档https://doc.renpy.cn/zh-CN/other.html#renpy.get_mouse_pos

抄代码环节——

[RenPy] 纯文本查看 复制代码
init -10 python:
    def drag_test_1(drags, drop):
        # 如果drop(第二个入参)为为None,则使变量1的值+10
        if not drop:
            renpy.run(SetVariable("num_test_1",num_test_1+10))
        return

    def drag_test_2(drags):
        # 如果调用了函数,则使变量2的值+10
        renpy.run(SetVariable("num_test_2",num_test_2+10))
        return

    def drag_test_3(drags):
        # 如果调用了函数,则使变量3的值变为鼠标焦点位置
        renpy.run(SetVariable("mouse_pos",renpy.get_mouse_pos()))
        return

default num_test_1 = 0 # 变量1
default num_test_2 = 0 # 变量2
default mouse_pos = (0,0) # 变量3,鼠标焦点位置

screen drag_test():
    # 用于直观显示变量1的改变
    text "[num_test_1]":
        color "#fff"
        xycenter(0.3, 0.3)
    frame:
        xycenter(0.3, 0.5)
        xysize(200, 100)
        drag:
            xysize(200, 200)
            clicked SetVariable("num_test_1",num_test_1+1) # 当鼠标左键单击时,使变量1数值+1
            dragged drag_test_1 # 当鼠标拖拽组件时,调用drag_test_1函数,输入两个入参
            dropped drag_test_1 # 当鼠标释放组件时,调用drag_test_1函数,输入两个入参
            add "#ffffff"
            text "单点+1,长按拖动并落下+10" xycenter(0.5, 0.5) color "#000000" size 20
   
    # 用于直观显示变量2的改变
    text "[num_test_2]":
        color "#fff"
        xycenter(0.7, 0.3)
    frame:
        xycenter(0.7, 0.5)
        xysize(200, 100)
        drag:
            xysize(200, 200)
            clicked SetVariable("num_test_2",num_test_2+1) # 当鼠标左键单击时,使变量2数值+1
            dragging drag_test_2 # 当鼠标正在拖拽组件时,调用drag_test_2函数,输入一个入参
            add "#ffffff"
            text "单点+1,长按拖动+10" xycenter(0.5, 0.5) color "#000000" size 20

    # 用于直观显示变量3的改变
    text "[mouse_pos]":
        color "#fff"
        xycenter(0.5, 0.5)
    frame:
        xycenter(0.5, 0.7)
        xysize(500, 300)
        drag:
            xysize(500, 300)
            dragging drag_test_3 # 当鼠标正在拖拽组件时,调用drag_test_3函数,输入一个入参
            add "#ffffff"
            text "鼠标焦点" xycenter(0.5, 0.5) color "#000000" size 30



NEXT
这样,所有功能都准备完毕,我们试着运行一下——

[RenPy] 纯文本查看 复制代码
label start:
    call screen drag_test


试着分别拖拽屏幕上的那三块组件,可以发现成功实现了所需要的功能,并且支持点击和长按拖动两种功能。

                               
登录/注册后可看大图

这一功能可以应用的场景非常广泛,就不一一举例了。

希望能帮助到各位,我们下次再见!

QQ截图20240528202014.png

评分

参与人数 2活力 +600 干货 +6 收起 理由
被诅咒的章鱼 + 300 + 3 感谢分享!
blackpineapple + 300 + 3 感谢分享!

查看全部评分

本帖被以下淘专辑推荐:

发表于 2024-5-30 15:21:44 | 显示全部楼层
本帖最后由 被诅咒的章鱼 于 2024-5-30 15:23 编辑

由于按钮长按的需求还挺常见,所以花时间研究了一下其他实现方法。



参考了两个Lemma论坛的帖子:
https://lemmasoft.renai.us/forums/viewtopic.php?p=566450

https://lemmasoft.renai.us/forums/viewtopic.php?p=556570



下面的部分还待完善的成果:

1. 首先需要自定义一个组件,来捕获鼠标在组件区域内按下和抬起的两个事件:
[RenPy] 纯文本查看 复制代码
init python:

    import pygame

    class OnPressAction(renpy.Displayable):
        def __init__(self, press_action, release_action, button_dispalyable, **kwargs):
            super().__init__(**kwargs)
            self.press_action = press_action
            self.release_action = release_action
            self.width = 0
            self.height = 0
            self.child = button_dispalyable
        def render(self, width, height, st, at):
            t = Transform(child=self.child)
            cr = renpy.render(t, width, height, st, at)
            self.width, self.height = cr.get_size()
            r = renpy.Render(self.width, self.height)
            r.blit(cr, (0, 0))
            return r
        def event(self, ev, x, y, st):
            if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1:
                if x > 0 and x < self.width and y > 0 and y < self.height:
                    return renpy.run(self.press_action)
            elif ev.type == pygame.MOUSEBUTTONUP and ev.button == 1:
                return renpy.run(self.release_action)


使用自定义组件而不是 keysym 按键映射的原因在于,keysym绑定鼠标事件后,点击整个屏幕都会触发按钮的action。如果之后再根据鼠标位置进行处理反而麻烦了。

这个自定义组件总共有3个入参,分别对应鼠标左键按下的action、鼠标左键抬起的action和显示的图像。入参有两个action的原因后面说。

2. 只包含一个自定义组件的界面,可以当作一个封装的特殊按钮:
[RenPy] 纯文本查看 复制代码
screen button_with_long_press:
    add OnPressAction(press_action, release_action, child_displayable)



3. 新建一个界面,设置一些变量,包括:要调整的值,值的范围,点击等待和重复执行间隔等。
[RenPy] 纯文本查看 复制代码
screen value_tune:
    default is_pressed = False
    default hovered_button = ""
    default value = 0
    default value_max = 10
    default value_min = 0
    default press_time = 0.0
    default press_wait_time = 0.5
    default press_repeat_time = 0.1
    text "[is_pressed]" xalign 0.5 yalign 0.2
    text "[hovered_button]" xalign 0.5 yalign 0.3
    
    hbox:
        align 0.5, 0.5
        spacing 50
        
        use button_with_long_press(button_name="-", press_action=[SetScreenVariable("is_pressed", True), SetScreenVariable("value", max(value_min, value-1)), SetScreenVariable("hovered_button", "-")], release_action=[SetScreenVariable("is_pressed", False), SetScreenVariable("press_time", 0.0), SetScreenVariable("hovered_button", "")], child_displayable=Text("-"))
        
        text "[value]"

        use button_with_long_press(button_name="+", press_action=[SetScreenVariable("is_pressed", True), SetScreenVariable("value", min(value_max, value+1)), SetScreenVariable("hovered_button", "+")], release_action=[SetScreenVariable("is_pressed", False), SetScreenVariable("press_time", 0.0), SetScreenVariable("hovered_button", "")], child_displayable=Text("+"))
        
        
        timer press_repeat_time repeat True:
                if is_pressed:
                    if press_time < press_wait_time:
                        action SetScreenVariable("press_time", press_time + 0.1)
                    else:
                        if hovered_button == "+":
                            action SetScreenVariable("value", min(value_max, value+1))
                        elif hovered_button == "-":
                            action SetScreenVariable("value", max(value_min, value-1))

此时就会用到上面自定义组件的两个不同时间触发的action,按下时调整数值 value,抬起时重置标识 is_pressedhovered_button

这些调整都仅限界面 value_tune 内的标量。如果要调整全局变量,把某些action中的SetScreenVariable改用SetVariable即可。


自定义组件在显示层面还不完善,不像button组件那样有idle、hover、selected等多个状态。
回复 支持 5 抱歉 1

使用道具 举报

发表于 2024-6-1 01:03:05 | 显示全部楼层
我封装了一个新的用户接口,应该可以更好解决你的问题
https://www.renpy.cn/forum.php?mod=viewthread&tid=1540
回复 支持 1 抱歉 0

使用道具 举报

 楼主| 发表于 2024-5-30 17:35:08 | 显示全部楼层
被诅咒的章鱼 发表于 2024-5-30 15:21
由于按钮长按的需求还挺常见,所以花时间研究了一下其他实现方法。

非常好用的功能!谢谢章鱼大大提供的方法!而我设想中的优化方案就显得有些捉襟见肘了(笑)
renpy在按钮操作方面还有所欠缺,明明长按事件非常常见,却没有原生可以直接调用的方法,希望以后能更加完善吧。
回复 支持 抱歉

使用道具 举报

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

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号|苏公网安备 32092302000068号 )

GMT+8, 2024-12-4 01:39 , Processed in 0.158176 second(s), 36 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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