跳转至

被诅咒的章鱼-renpy小技巧

作者知乎主页

其他可能用到的案例

如何在RenPy中做一个飞镖小游戏? - 经验教程 - RenPy中文空间 - Powered by Discuz!

神秘角色

https://zhuanlan.zhihu.com/p/360046885

init python:

    renpy.register_shader("charactorshader.gradient", variables="""
        uniform vec4 u_gradient_top;
        uniform vec4 u_gradient_bottom;
        uniform vec2 u_model_size;
        varying float v_gradient_done;
    """, vertex_300="""
        v_gradient_done = a_position.y / u_model_size.y;

    """, fragment_300="""
        if(v_gradient_done < 0.5)
        {
            gl_FragColor *= mix(u_gradient_top, u_gradient_bottom, 0.0);
        }
        else
        {
            gl_FragColor *= mix(u_gradient_top, u_gradient_bottom, (v_gradient_done-0.5)*2);
        }
    """)

transform secret_gradient:
    align (0.5, 0.5)
    shader "charactorshader.gradient"
    u_gradient_top (0.0, 0.0, 0.0, 1.0)
    u_gradient_bottom (1.0, 1.0, 1.0, 1.0)

label start:
    show eileen happy at secret_gradient

我的版本

# ---神秘角色
init python:

    renpy.register_shader("charactorshader.gradient", variables="""
        uniform vec4 u_gradient_top;
        uniform vec4 u_gradient_bottom;
        uniform vec2 u_model_size;
        varying float v_gradient_done;
    """, vertex_300="""
        v_gradient_done = a_position.y / u_model_size.y;

    """, fragment_300="""
        if(v_gradient_done < 0.5)
        {
            gl_FragColor *= mix(u_gradient_top, u_gradient_bottom, 0.0);
        }
        else
        {
            gl_FragColor *= mix(u_gradient_top, u_gradient_bottom, (v_gradient_done-0.5)*2);
        }
    """)

transform secret_gradient:
    # 人物放下面中间,其他位置再调整,比如左边右边的神秘角色
    align (0.5,1.0)
    shader "charactorshader.gradient"
    u_gradient_top (0.0, 0.0, 0.0, 1.0)
    u_gradient_bottom (1.0, 1.0, 1.0, 1.0)

# ---

开场 点击继续

Ren'Py引擎从入门到放弃(支线1·续)——自定义开场画面 - 知乎 (zhihu.com)

# 开场 点击继续

## 自定义的图片闪烁变换(transform)
transform transform_blink:
    linear 1.0 alpha 0.2
    linear 1.0 alpha 1.0
    pause 1.0
    repeat

## 自定义界面(screen)部分
screen press_to_start():
    # 这步确保任何其他菜单界面都会被替换
    tag menu
    add "menu/background.png"
    add "menu/press_to_start.png" xalign 0.5 yalign 0.7 at transform_blink

    imagemap:
        # 透明点击图片
        ground "menu/transparent.png"
        # 热点点击,播放音效,返回main_menu
        hotspot (0, 0, 1920, 1080) focus_mask None action(Play("sound", "audio/start.wav"), Return())

还有问题 #TODO

ctc对话点击继续提示

Ren'Py引擎从入门到放弃(3)——成功定义一个角色 - 知乎 (zhihu.com)

## 对话结束提示点击进入下一句的符号
# image ctc = "images/ctc.png"
image ctc:
    "images/ctc.png"
    # 设置为对话字体大小,小一点点
    # gui.rpy 里面的define gui.text_size = 33
    # size (33, 33)
    # xalign 0.5
    # yalign 0.5
    parallel:
        #  旋转
        rotate 0
        ease_circ 1 rotate -360
        repeat
    parallel:
        alpha 1.0
        ease_circ 1 alpha 0
        pause 2.0
        # alpha 0
        ease_circ 1 alpha 1.0
        # ease_circ alpha 1.0
        repeat

quick菜单隐藏

Ren'Py引擎从入门到放弃(16)——GUI定制化之快捷菜单 - 知乎 (zhihu.com)

## screnn.rpy
## 隐藏快捷菜单
screen quick_menu_show_area():
    # 在quick_menu上层,保证比quick_menu更早获取鼠标位置
    zorder 101

    mousearea:
        area (0, 1000, 1.0, 90)
        hovered Show("quick_menu")
        unhovered Hide("quick_menu")

## 快捷菜单界面 ######################################################################
##
## 快捷菜单显示于游戏内,以便于访问游戏外的菜单。

screen quick_menu():

    ## 确保该菜单出现在其他界面之上,
    zorder 100

    if quick_menu:

        hbox:
            style_prefix "quick"

            xalign 0.5
            yalign 1.0

            textbutton _("回退") action Rollback()
            textbutton _("历史") action ShowMenu('history')
            textbutton _("快进") action Skip() alternate Skip(fast=True, confirm=True)
            textbutton _("自动") action Preference("auto-forward", "toggle")
            textbutton _("保存") action ShowMenu('save')
            textbutton _("快存") action QuickSave()
            textbutton _("快读") action QuickLoad()
            textbutton _("设置") action ShowMenu('preferences')

            at quick_menu_buttons_transform

## 此语句确保只要玩家没有明确隐藏界面,就会在游戏中显示“quick_menu”界面。
init python:
    config.overlay_screens.append("quick_menu_show_area")
##自己的transform.rpy中
# quick菜单隐藏变换
transform quick_menu_buttons_transform:
    #背景图高度36,所以比36稍微多几个像素值
    # yoffset 
    on show:
        # linear 0.5 yoffset -10
        pass
    on hide:
        # linear 0.5 yoffset 5
        pass

transform quick_button_base_fade:
    alpha 0.0
    on show:
        linear 0.5 alpha 1.0
    on hide:
        linear 0.5 alpha 0.0

小特效

Ren'Py引擎从入门到放弃(支线9)——使用CDD播放序列帧动画 - 知乎 (zhihu.com)

滚动文字

init python:
    test = [('-000000-', '111111'), ('-222222-', '333333'), ('-444444-', '555555'), ('-666666-', '777777'),
               ('-888888-', 'aaaaaa'), ('-bbbbbb-', 'cccccc'), ('-dddddd-', 'eeeeee'), ('-ffffff-', 'gggggg'),
               ('-hhhhhh-', 'jjjjjj\nkkkkkk'), ('-llllll-', 'mmmmmm'), ('-nnnnnn-', 'ooooooo\npppppp')
              ]
    test_s = "{size=80}test\n\n"
    c1 = ''
    for c in test:
        if not c1==c[0]:
            test_s += "\n{size=40}" + c[0] + "\n"
        test_s += "{size=60}" + c[1] + "\n"
        c1=c[0]
    test_s += "\n{size=40}-制作引擎-\n{size=60}" + renpy.version() ## 可根据需要进行声明(比如你的制作引擎

init:
    # image play = Movie(play="main_menu_bg_1.mpg", mask="main_menu_bg_1.mpg") ## 可以选择同时配上一段视频
    image list = Text(test_s, text_align=0.5) ## 自定义文本样式,例如‘font’
    image end = Text("{size=80}FIN.", text_align=0.5) 
    image thanks = Text("{size=80}Thank you for your support!")

label test:
    $ test_speed = 20 ## 滚动速度/s
    scene black ## 自定义你的背景
    with dissolve
    show end:
        yanchor 0.5 ypos 0.5
        xanchor 0.5 xpos 0.5
    with dissolve
    with Pause(2.0)
    hide end
    with dissolve
    # show play
    # play voice "/audio/1.mp3" ## 播放音效(个人建议音效建议使用视频自带的,这样就能用'renpy.pause'来实现禁止玩家跳过的同时还能保证音频正常播放,仅个人观点,还请多多包涵)
    show list at Move((0.5, 5.0), (0.5, 0.0), test_speed, repeat=False, bounce=False, xanchor="center", yanchor="bottom")
    with fade
    with Pause(test_speed)
    # hide play
    # with fade
    show thanks:
        yanchor 0.5 ypos 0.5
        xanchor 0.5 xpos 0.5
    with Dissolve(1.0)
    with Pause(2)
    hide thanks
    with dissolve
    with Pause(0.5)

    return

我的版本

init python:
    # 要滚动的字幕
    # eg.美术 Badbrain
    subtitle_list = [('-剧本-', 'Badbrain\n若曈溪'), ('-美术-', 'Badbrain'), ('-程序-', 'Badbrain'), ('-音乐-', '开源音效\n《魔法少女小圆OST》'),
            ('-PV-', '星酱'), ('-后期-', '星酱'),('-特别鸣谢-','《白夜极光》\n《多娜多娜》\n被诅咒的章鱼\nRenpy中文空间\nGitHub'),
            ('-特别申明-\n本游戏没有角色在制作过程中受伤或死亡,制作组也没有虐待角色的行为','')
            ]
    subtitle_image = "{size=80}制作人员名单\n\n"
    c1 = ''
    for c in subtitle_list:
        if not c1==c[0]:
            subtitle_image += "\n{size=40}" + c[0] + "\n"
        subtitle_image += "{size=60}" + c[1] + "\n"
        c1=c[0]
    subtitle_image += "\n{size=40}-制作引擎-\n{size=60}" + renpy.version() ## 可根据需要进行声明(比如你的制作引擎

init:
    # image play = Movie(play="main_menu_bg_1.mpg", mask="main_menu_bg_1.mpg") ## 可以选择同时配上一段视频
    image list = Text(subtitle_image, text_align=0.5) ## 自定义文本样式,例如‘font’
    image end = Text("{size=80}FIN.", text_align=0.5) 
    image thanks = Text("{size=80}Thank you for your support!")

label rolling_subtitles:
    $ subtitle_imagepeed = 25 ## 滚动速度/s
    scene black ## 自定义你的背景
    with dissolve
    show end:
        yanchor 0.5 ypos 0.5
        xanchor 0.5 xpos 0.5
    with dissolve
    with Pause(1.0)
    hide end
    with dissolve
    # show play
    # play voice "/audio/1.mp3" ## 播放音效(个人建议音效建议使用视频自带的,这样就能用'renpy.pause'来实现禁止玩家跳过的同时还能保证音频正常播放,仅个人观点,还请多多包涵)
    play voice "/audio/God is Madoka - 卡梅·马多卡·卡内卡·卡内·马多卡·卡梅·马多卡·马吉卡.mp3" ## 播放音效(个人建议音效建议使用视频自带的,这样就能用'renpy.pause'来实现禁止玩家跳过的同时还能保证音频正常播放,仅个人观点,还请多多包涵)
    show list at Move((0.5, 4.0), (0.5, -3.0), subtitle_imagepeed, repeat=False, bounce=False, xanchor="center", yanchor="bottom")
    with fade
    with Pause(subtitle_imagepeed)
    # hide play
    # with fade
    show thanks:
        yanchor 0.5 ypos 0.5
        xanchor 0.5 xpos 0.5
    with Dissolve(1.0)
    with Pause(2)
    hide thanks
    with dissolve
    with Pause(0.5)

    return

下雨特效shader实现

## https://www.renpy.cn/thread-900-1-2.html
init python:

    renpy.register_shader("shadertoy.rainonwindow", variables="""
        uniform float u_time;
        uniform vec2 u_model_size;
        uniform float u_rain_amount;
        uniform sampler2D tex0;
    """,
        fragment_functions="""
        vec3 N13(float p)
        {
           vec3 p3 = fract(vec3(p) * vec3(.1031,.11369,.13787));
           p3 += dot(p3, p3.yzx + 19.19);
           return fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
        }
        vec4 N14(float t)
        {
            return fract(sin(t*vec4(123., 1024., 1456., 264.))*vec4(6547., 345., 8799., 1564.));
        }

        float N(float t)
        {
            return fract(sin(t*12345.564)*7658.76);
        }

        float Saw(float b, float t)
        {
            return smoothstep(0., b, t) * smoothstep(1., b, t);
        }

        vec2 DropLayer2(vec2 uv, float t)
        {
            vec2 UV = uv;

            uv.y += t*0.75;
            vec2 a = vec2(6., 1.);
            vec2 grid = a*2.;
            vec2 id = floor(uv*grid);

            float colShift = N(id.x); 
            uv.y += colShift;

            id = floor(uv*grid);
            vec3 n = N13(id.x*35.2+id.y*2376.1);
            vec2 st = fract(uv*grid)-vec2(.5, 0);

            float x = n.x-.5;

            float y = UV.y*20.;
            float wiggle = sin(y+sin(y));
            x += wiggle*(.5-abs(x))*(n.z-.5);
            x *= .7;
            float ti = fract(t+n.z);
            y = (Saw(.85, ti)-.5)*.9+.5;
            vec2 p = vec2(x, y);

            float d = length((st-p)*a.yx);

            float mainDrop = smoothstep(.4, .0, d);

            float r = sqrt(smoothstep(1., y, st.y));
            float cd = abs(st.x-x);
            float trail = smoothstep(.23*r, .15*r*r, cd);
            float trailFront = smoothstep(-.02, .02, st.y-y);
            trail *= trailFront*r*r;

            y = UV.y;
            float trail2 = smoothstep(.2*r, .0, cd);
            float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z;
            y = fract(y*10.)+(st.y-.5);
            float dd = length(st-vec2(x, y));
            droplets = smoothstep(.3, 0., dd);
            float m = mainDrop+droplets*r*trailFront;

            return vec2(m, trail);
        }

        float StaticDrops(vec2 uv, float t)
        {
            uv *= 40.;

            vec2 id = floor(uv);
            uv = fract(uv)-.5;
            vec3 n = N13(id.x*107.45+id.y*3543.654);
            vec2 p = (n.xy-.5)*.7;
            float d = length(uv-p);

            float fade = Saw(.025, fract(t+n.z));
            float c = smoothstep(.3, 0., d)*fract(n.z*10.)*fade;
            return c;
        }

        vec2 Drops(vec2 uv, float t, float l0, float l1, float l2)
        {
            float s = StaticDrops(uv, t)*l0; 
            vec2 m1 = DropLayer2(uv, t)*l1;
            vec2 m2 = DropLayer2(uv*1.85, t)*l2;

            float c = s+m1.x+m2.x;
            c = smoothstep(.3, 1., c);

            return vec2(c, max(m1.y*l0, m2.y*l1));
        }
    """,
        vertex_300="""
        v_tex_coord = a_tex_coord;
    """,

        fragment_300="""
        vec2 uv = (gl_FragCoord.xy - u_model_size.xy*.5) / u_model_size.y;
        vec2 UV = gl_FragCoord.xy/u_model_size.xy;
        float T = u_time + 2.;

        float t = T*.2;

        float rainAmount = u_rain_amount;

        float maxBlur = mix(3., 6., rainAmount);
        float minBlur = 2.;

        float story = 0.;
        float heart = 0.;


        float zoom = 1.0;
        uv *= .7+zoom*.3;

        UV = (UV-.5)*(.9+zoom*.1)+.5;

        float staticDrops = smoothstep(-.5, 1., rainAmount)*2.;
        float layer1 = smoothstep(.25, .75, rainAmount);
        float layer2 = smoothstep(.0, .5, rainAmount);


        vec2 c = Drops(uv, t, staticDrops, layer1, layer2);

        vec2 e = vec2(.001, 0.);
        float cx = Drops(uv+e, t, staticDrops, layer1, layer2).x;
        float cy = Drops(uv+e.yx, t, staticDrops, layer1, layer2).x;
        vec2 n = vec2(cx-c.x, cy-c.x);


        float focus = mix(maxBlur-c.y, minBlur, smoothstep(.1, .2, c.x));
        vec3 col = texture2DLod(tex0, UV+n, focus).rgb;

        gl_FragColor = vec4(col, 1.);
    """)

init python:

    class RainOnWindow(renpy.Displayable):

        def __init__(self, child, width, height, rainamount=1.0, **kwargs):
            super(RainOnWindow, self).__init__(**kwargs)

            self.child = renpy.displayable(child)
            self.width = width
            self.height = height
            self.rainamount = rainamount;

        def render(self, width, height, st, at):
            render = renpy.Render(self.width, self.height)
            render.place(self.child)
            render.add_shader("shadertoy.rainonwindow")
            render.add_uniform("u_time", st)
            render.add_uniform("u_model_size", (self.width, self.height))
            render.add_uniform("u_rain_amount", self.rainamount)
            renpy.redraw(self, 0)
            return render

# 添加背景图片位置,必须要有大小设置
image rainonwindow = RainOnWindow("bg street evening", width = 1920, height = 1080, rainamount = 1.0)
## 注意这个shader使用了背景图片“bgafterwindow”,也可以换成自己需要的其他背景图片。
## CDD中的入参rainamount可以按需求调整。

label Rain:
    # scene bg intersection evening
    show rainonwindow with ease
    pause 20.0
    'rain is rain!'
    show e
    e 'Great'
    # hide rainonwindow with dissolve
    'rain is fin!'

历史对话点击跳转

在Renpy中实现历史记录跳跃效果 - 经验教程 - RenPy中文空间 - Powered by Discuz!

screen history():

    tag menu

    ## Avoid predicting this screen, as it can be very large.
    predict False

    use game_menu(_("History"), scroll=("vpgrid" if gui.history_height else "viewport"), yinitial=1.0):

        style_prefix "history"

        for h in _history_list:

            window:
                ## This lays things out properly if history_height is None.
                has fixed:
                    yfit True

                if h.who:
                    label h.who:
                        style "history_name"
                        substitute False

                        ## Take the color of the who text from the Character, if set.
                        if "color" in h.who_args:
                            text_color h.who_args["color"]

                $ what = renpy.filter_text_tags(h.what, allow=gui.history_allow_tags)
                textbutton what:
                    substitute False
                    style "history_text"
                    action Confirm("跳转到此处?", yes=RollbackToIdentifier(h.rollback_identifier), no=None, confirm_selected=False),

        if not _history_list:
            label _("The dialogue history is empty.")