第一个简单的脚本
# 第一个简单的脚本
警告!
本文编辑时的 DDLC 中文 Mod 模板可能与教程有极大出入,您应该在文档和模板完善后再来查看本文档。
本节及后续章节均默认您使用 Future 分支。
在 Ren'Py 和 DDLC 的世界里,剧本与视觉内容都围绕着代码展开 —— 但不必慌张! Ren'Py 的语法已经做到了极致的简约,不用太多理解成本,上手即可。
# 从 script.rpy 开始
开始之前,你应该先来看一下 script.rpy
,里面包含了串联起整个游戏的逻辑代码。它带有注释,可以看看。
## script.rpy
# This is the main script that Ren'Py calls upon to start
# your mod's story!
label start:
# 该变量控制一周目之后的反作弊 ID。
# 不建议变更本行代码。请考虑在你的游戏剧情脚本里使用下方代码:
# $ persistent.anticheat = renpy.random.randint(X, Y)
# X - 最小值 | Y - 最大值
$ anticheat = persistent.anticheat
# 该变量将章节数字设置为 0,以供模组使用。
$ chapter = 0
# 该变量控制玩家是否能在游戏期间跳过暂停时刻。
$ _dismiss_pause = config.developer
## 角色命名
# 这些变量控制游戏内角色的命名。
# 如需添加新角色,请参考下方代码示例:
# $ mi_name = "Mike"
# 不要忘记在 definitions.rpy 添加相应角色!
$ s_name = "???"
$ m_name = "女孩 3"
$ n_name = "女孩 2"
$ y_name = "女孩 1"
# 该变量控制文本框展示时是否显示底部文字菜单,以及是否允许使用 Esc 显示菜单。
$ quick_menu = True
# 该变量控制文本框内的对话文字样式风格,可定义为常规(style.normal)或干扰(style.edited)风格。
# 如需干扰风格,您可以使用 style.edited
$ style.say_dialogue = style.normal
# 控制纱世里是否去世的变量。
# 一般不建议修改此项。
$ in_sayori_kill = None
# 这些变量控制是否允许玩家跳过 / 快进对话或转场。
$ allow_skipping = True
$ config.allow_skipping = True
## 脚本文件主要部分
# This is where your script code is called!
# 'persistent.playthrough' controls the playthrough number the player is on i.e (Act 1, 2, 3, 4)
# 当你写好故事脚本文件后,再删除下面这一行,然后在此调用(call)您的脚本
call screen dialog(message="您似乎在尝试直接将 Mod 模板作为无故事剧情的新游戏运行。\n这是一个模组模板,而不是一个实际的模组。请为您的模组编写故事,并在“script.rpy”中调用(call)脚本后重试。", ok_action=MainMenu(confirm=False))
## 下方是原版 DDLC 游戏的 call 语句示例。
# if persistent.playthrough == 0:
# # This variable sets the chapter number to X depending on the chapter
# # your player is experiencing ATM.
# $ chapter = 0
# # This call statement calls your script label to be played.
# call ch0_main
# # This call statement calls the poem mini-game to be played.
# call poem
# ## Day 1
# $ chapter = 1
# call ch1_main
# # This call statement calls the poem sharing minigame to be played.
# call poemresponse_start
# call ch1_end
# call poem
# ## Day 2
# $ chapter = 2
# call ch2_main
# call poemresponse_start
# call ch2_end
# call poem
# ## Day 3
# $ chapter = 3
# call ch3_main
# call poemresponse_start
# call ch3_end
# ## Day 4
# $ chapter = 4
# call ch4_main
# # This python statement writes a file from within the game to the game folder
# # or to the Android/data/[modname]/files/game folder.
# python:
# if renpy.android:
# try: renpy.file(os.environ['ANDROID_PUBLIC'] + "/hxppy thxughts.png")
# except IOError: open(os.environ['ANDROID_PUBLIC'] + "/hxppy thxughts.png", "wb").write(renpy.file("hxppy thxughts.png").read())
# else:
# try: renpy.file(config.basedir + "/hxppy thxughts.png")
# except IOError: open(config.basedir + "/hxppy thxughts.png", "wb").write(renpy.file("hxppy thxughts.png").read())
# ## Day 5
# $ chapter = 5
# call ch5_main
# # This call statement ends the game but doesn't play the credits.
# call endgame
# return
# elif persistent.playthrough == 1:
# $ chapter = 0
# call ch10_main
# # This jump statement jumps over to Act 2 from Act 1.
# jump playthrough2
# elif persistent.playthrough == 2:
# ## Day 1 - Act 2
# $ chapter = 0
# call ch20_main
# label playthrough2:
# call poem
# python:
# if renpy.android:
# try: renpy.file(os.environ['ANDROID_PUBLIC'] + "/CAN YOU HEAR ME.txt")
# except IOError: open(os.environ['ANDROID_PUBLIC'] + "/CAN YOU HEAR ME.txt", "wb").write(renpy.file("CAN YOU HEAR ME.txt").read())
# else:
# try: renpy.file(config.basedir + "/CAN YOU HEAR ME.txt")
# except IOError: open(config.basedir + "/CAN YOU HEAR ME.txt", "wb").write(renpy.file("CAN YOU HEAR ME.txt").read())
# ## Day 2 - Act 2
# $ chapter = 1
# call ch21_main
# call poemresponse_start
# call ch21_end
# # This call statement calls the poem mini-game with no transition.
# call poem(False)
# python:
# if renpy.android:
# try: renpy.file(os.environ['ANDROID_PUBLIC'] + "/iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.txt")
# except IOError: open(os.environ['ANDROID_PUBLIC'] + "/iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.txt", "wb").write(renpy.file("iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.txt").read())
# else:
# try: renpy.file(config.basedir + "/iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.txt")
# except IOError: open(config.basedir + "/iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.txt", "wb").write(renpy.file("iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.txt").read())
# ## Day 3 - Act 2
# $ chapter = 2
# call ch22_main
# call poemresponse_start
# call ch22_end
# call poem(False)
# ## Day 4 - Act 2
# $ chapter = 3
# call ch23_main
# # This if statement calls either a special poem response game or play
# # as normal.
# if y_appeal >= 3:
# call poemresponse_start2
# else:
# call poemresponse_start
# # This if statement is leftover code from DDLC where if your game is
# # a demo that it ends the game fully.
# if persistent.demo:
# stop music fadeout 2.0
# scene black with dissolve_cg
# "End of demo"
# return
# call ch23_end
# return
# elif persistent.playthrough == 3:
# jump ch30_main
# elif persistent.playthrough == 4:
# ## 四周目 - 第一天
# $ chapter = 0
# call ch40_main
# jump credits
# 该 label 定义了一周目的“END”画面。
label endgame(pause_length=4.0):
$ quick_menu = False
stop music fadeout 2.0
scene black
show end
with dissolve_scene_full
pause pause_length
$ quick_menu = True
return
# 着手创建你的第一个 Label
# Future 分支
将 script.rpy
中:
call screen dialog(message="您似乎在尝试直接将 Mod 模板作为无故事剧情的新游戏运行。\n这是一个模组模板,而不是一个实际的模组。请为您的模组编写故事,并在“script.rpy”中调用(call)脚本后重试。", ok_action=MainMenu(confirm=False))
改为:
call ch1_start
然后再创建文件 script-ch1.rpy
(包含后缀)。在新文件中输入:
label ch1_start:
这样,您就创建了您的第一个 Label。
# Next 分支
如果你使用 Next 分支,那么现在您不必再理会 script.rpy
文件,直接打开 script-ch1.rpy
,然后保留文件第一行的 label ch1_start:
,删除其他内容即可。
# 旧分支
如果你使用旧分支,那么准备工作会复杂一点点。将 script.rpy
中被注释包围的几行改成:
call ch1_start
提示
此处是为了与 Next 分支模板保持一致。
然后再创建文件 script-ch1.rpy
(包含后缀)。在新文件中输入:
label ch1_start:
这样,您就创建了您的第一个 Label。
# 开始让角色说话
没有文字的视觉小说可能连小说都算不上,它总得有角色说话。而对于 DDLC,总得有角色出演吧。
现在,我们要来添加旁白和 Monika 的对话。另起一行,按 4 个空格(一些编辑器可以直接按 Tab 键),输入:
"今天是学园祭的第一天。"
m "好了,各位!我们开始准备吧!"
注意!
包住文字的引号必须使用英文符号 ""
。
接下来,运行工程,你应该可以看到两句对话,一句是旁白,一句来自 Monika。这就是一段最基础的对话。
理解了这些基础内容后,我们可以添加更多对话与角色,让 Mod 更加热闹一点。在这里,我先来教你 Ren'Py 的对话基础语法:
<角色> "内容"
在 DDLC 的演出里,<角色>
的地方可以填以下几个:
<角色> | 代表的角色 |
---|---|
m | Monika |
s | Sayori |
n | Natsuki |
y | Yuri |
mc | 主角(你) |
空白 | 旁白 |
现在,尝试理解下面的代码,并运行一下:
"今天是学园祭的第一天。"
m "好了,各位!我们开始准备吧!"
s "耶!小饼干!"
n "要不也来尝下我的纸杯蛋糕?"
y "...其实都可以了。"
mc "总之,以后也要多多关照!"
# 添加 BGM
现在,Mod 已经拥有了对话,但还缺了很多东西,比如 BGM 与背景。这里我们先从 BGM 开始。
本节将以 t1
(Main Theme)为例,为你刚刚写的剧本增加 BGM。
在 label 行与对话之间先空一行,然后输入:
play music t1
现在运行 Mod 并进入游戏,你应该会发现在对话前游戏(非常生硬地)播放了背景音乐。
如果您需要播放更多 BGM,您可以参阅 BGM 列表(相应文档暂未制作)。
# 加个背景
这一步,我们来向剧本加入视觉内容。首先,让我们从展示背景图片开始。假设我想使用部室作为背景,那么在 label 行与刚刚增加的背景音乐行间空一行,输入:
stop music fadeout 2.0
scene bg club_day with dissolve_scene_full
运行工程,现在的过渡更舒服了,同时我们也终于有了背景图片。
如果您需要更多背景图片,您可以参阅 背景图片列表(相应文档暂未制作)。
扩展知识
此处,scene
的作用是清空当前图层,而 with
则是过场动画。
具体的介绍将在 角色立绘的那些事 一节中介绍。
# 添加角色立绘
在这一步,我们将使剧本展示角色立绘。
将刚刚的对话部分修改为以下内容:
"今天是学园祭的第一天。"
show monika 2 at t42
m "好了,各位!我们开始准备吧!"
show sayori 4r at t43
s "耶!小饼干!"
show natsuki 2l at t41
n "要不也来尝下我的纸杯蛋糕?"
show yuri 3m at t44
y "...其实都可以了。"
mc "总之,以后也要多多关照!"
再次运行 Mod,你可能会发现角色立绘会有部分角色的立绘不太靠近或过于靠近屏幕。
这时候,就可以用 zorder
:
"今天是学园祭的第一天。"
show monika 2 at t42 zorder 2
m "好了,各位!我们开始准备吧!"
show sayori 4r at t43 zorder 2
s "耶!小饼干!"
show natsuki 2l at t41 zorder 2
n "要不也来尝下我的纸杯蛋糕?"
show yuri 3m at t44 zorder 2
y "...其实都可以了。"
mc "总之,以后也要多多关照!"
运行 Mod。你会发现这时候角色立绘不会过于的靠近屏幕。
zorder
的作用就是控制图像在图层 Z 轴上的位置。
提示:
zorder
后面的数值越大,图像越远,反之数值越小则越近。
与 show
相反,hide
的作用就是隐藏。用法如下:
hide <之前在屏幕上显示的任何图像的名称>
提示
definitions.rpy
为各个角色定义了许多个表情。
如果你需要使用自带的其他表情,它可以作为参考。这方面以后会展开来讲。