被诅咒的章鱼-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.")