正文写作
一行就是一段、缩进不算数、标点随你写;加粗变色换字体,以及把悬念停在句子中间。
写正文是 Kiny 里最不需要学的部分:一行普通文字,就是读者会读到的一段话。
雾从港口涌上来,遮住了路灯。
你站在码头边,鞋底能感到木板在动。
远处传来一声汽笛。
-> END
没有任何标记。这一篇讲的是在这个基础上还能做什么。
行与段落
- 每一行非控制行 = 一段输出,行末自动换行。
- 空行会被忽略。源文件里想怎么空行就怎么空——分隔场景、隔开长段落,对输出没有任何影响。
- 行首行末的空白会被裁掉。
想让两段之间不换行(下一段直接贴上来),在前一段末尾加 <>,见选项与分支那一篇末尾的「粘连」。
哪些行不是正文
一行只要以下面这些符号起首,就是控制结构而非正文:
| 起首 | 是什么 |
|---|---|
=== / = |
节点 / 子节点声明 |
* / + |
选项 |
> |
分支体层级 |
-> |
跳转 |
~ / ~~~ |
JS 逻辑 |
@if / @elif / @else |
条件块 |
@名字(...) |
命令 |
// / /* */ |
注释 |
除此之外的一切都是正文。
缩进不算数
普通正文里的缩进没有任何语义,纯粹是给你自己看的。下面三段完全等价:
你打开门。
你走进去。
你打开门。
你走进去。
你打开门。
你走进去。
分支的层级不靠缩进,靠行首的
>(见选项与分支)。所以你可以放心地用缩进排版,不会改变故事的结构。
标点随你写
引号、书名号、破折号、省略号——全都是普通字符,没有任何特殊含义。中英文标点想怎么混用就怎么混用:
「想要点什么?」老板问。
'Hello,' he said.
"我累了,"我说,"明天再聊吧。"
——他没有回答。
-> END
Kiny 不管你的标点风格。
需要转义的符号
少数符号在正文里有含义,想输出它们本身就在前面加一个反斜杠 \。
任何位置都要转义(这几个在行内随处都有意义):
| 写 | 得到 | 不转义会被当成 |
|---|---|---|
\{ \} |
{ } |
表达式插值 |
\< |
< |
富文本标签 / 粘连 |
\/ |
/ |
注释(写网址时用得上:http:\//…) |
\\ |
\ |
转义符自己 |
只有在行首才要转义(它们只在行首是控制标记):
\= \* \+ \> \~ \@ \->
数学里 3 \< 5,集合写作 \{1, 2, 3\}。
\* 这一行以星号开头,但它是正文,不是选项。
-> END
只在选项行里要转义:选项文字里要出现字面的 [ ] ( ) 时写 \[ \] \( \)。
注释
写给自己看、不给读者看的话:
// 这一整行是注释
这一行会显示。 // 行尾也可以,从 // 到行末都不输出
/* 跨行的注释
写草稿、临时关掉一段的时候好用 */
-> END
//到行末为止。正文行与选项行的行尾同样生效——这正是上面那张表里\/要在任何位置转义的原因:网址里的//会被当成注释起头,得写成http:\//example.com。/* */可以跨行。- 注释不进输出,也不进存档。
有两处不能写行尾注释,写了会报出指不到根因的错:
@sleep(100) // ✗ 报「命令实参缺少右括号」
-> 客栈 // ✗ 会把「客栈 // …」整个当成节点名
命令行与跳转行要注释就另起一行写。~ 逻辑行和 ~~~ 块里的 // 归 JS 自己管,照常有效。
加粗、变色、换字体
正文和选项文字里都可以用一组行内标签:
| 标签 | 作用 | 取值 |
|---|---|---|
<b>…</b> |
粗体 | — |
<i>…</i> |
斜体 | — |
<u>…</u> |
下划线 | — |
<s>…</s> |
删除线 | — |
<color=值>…</color> |
颜色 | #c00 / #cc0000 / red 这类具名色 |
<size=倍数>…</size> |
字号 | 相对当前正文的倍数,如 1.5、0.8 |
<font=名>…</font> |
字体 | 项目里自动注册的字体族名,或系统字体名 |
<class=名>…</class> |
语义类 | 交给作品 CSS 去定义样式 |
<br> |
换行 | 自闭合 |
她说:<b>别回头</b>,然后<color=#c00>消失在<i>雾</i>里</color>。
这个词<size=1.5>很大</size>,这个<size=0.8>很小</size>。
第一行<br>第二行。
-> END
几条规则:
- 可以嵌套,样式叠加:
<b><color=red>红色的粗体</color></b>。同类标签嵌套时内层覆盖外层。 <class>是累积的——外层<class=letter>和内层<class=old>会同时挂上。类名渲染时会加kin-前缀交给作品 CSS,所以它是「把样式钩子从正文交给样式表」的正规方式。- 取值是受限的:
color只认颜色、size只认正数、font只认单个族名。标签里写不了任意 CSS——那是有意的,防止经正文注入样式。要精细控制就用<class=名>加作品 CSS,那边完全开放。 <font>找不到字体会回退到正文默认字体,不影响阅读。- 裸的
<一般没事:只有当它后面正好构成一个合法标签时才会被当标签,否则按字面输出(1 < 2照常显示)。想保险就写\<。 - 插值出来的内容不会被二次解析:
{...}求值的结果按纯文本插入,里面的<…>不当标签。
换行符 = <br>
文本里的换行符和 <br> 完全等价。这一条主要在插值上用得着——JS 里拼出来的多行字符串插进正文,就是按行显示的,不必把 <br> 塞进 JS 字符串:
~ let bag = { 绳子: 2, 火折子: 0, 干粮: 3 }
~~~
let list = ""
for (const [item, n] of Object.entries(bag)) {
if (n > 0) list += `- ${item}:${n}\n`
}
~~~
{list}
换行不做修剪:写几个就换几行,首尾也一样。上面每项末尾都有 \n,所以最后会多空一行——不想要就给最后一项别加,或者改用 join("\n")。
把悬念停在句子中间
<pause> 让打字揭示在句子中间停住。两档:
凶手就是……<pause>你自己。
门开了一条缝<pause=2000>,什么都没有。
-> END
- 点击档
<pause>:前半句显示后停住、推进提示三角亮起,读者点一下才续显后半句。 - 毫秒档
<pause=2000>:停满 2 秒自动续显,三角不亮(没在等读者),等待期间点击完全无效。
运行上面那段试试两种的差别。
几条细节:
- 行内任意位置、想放几个放几个;可以落在
<b>/<color>的范围里,不影响样式。 - 停顿跳不过去:点击档在打字过程中点击只会让当前这一段立刻显示,仍然停在标记处,再点一次才续下一段。
- 连着写多个标记只停一次,档位取最后一个(
<pause><pause=500>是 500 毫秒档)。写在行尾的会被忽略——行尾本来就是边界。 - 只对正文行有效。选项列表里的标记不产生停顿(选项是一次性呈现的按钮,没有揭示过程);选中之后印进正文的那部分文字照常生效。
- 读者开了「减弱动态效果」时每段瞬显,但停顿仍然保留——它是叙事节奏,不是动画效果。
- 毫秒档的取值必须是正整数:
<pause=0.5>/<pause=0>/<pause=-1>都会报错。 - 自闭合写法
<pause/>/<pause=500/>等价。要输出字面的<pause>写\<pause>。
想在行与行之间留白,用
@sleep(毫秒),见后面的「演出命令」一篇。<pause>只管句子中间。
台词着色
在项目根声明角色之后,台词行开头标出说话人,那一行就会按角色着色:
<克里斯托弗> 我今天出门了。
阿黎娅:外面在下雨。
两种写法可以混用,只有声明过的名字才会触发——正文里的「他说:」「时间:三点二十」不受任何影响。完整写法与配色见后面的「角色与台词着色」一篇。
写坏了会怎样
标签没闭合、颜色值不合法、字号写了负数——运行期都不会崩:未闭合的标签自动闭到这一段末尾,不合法的取值就是不应用样式。同时编辑器底部会报错标红,方便你修。
接下来
正文之外,故事需要结构——下一篇讲节点与跳转。