制作你的第一部视觉小说
使用 Pixi’VN 创建视觉小说的分步指南,涵盖项目设置、叙事、素材和交互性。
本教程将指导你完成创建第一部视觉小说的过程。
为了测试的目的,在本指南中我们将使用 Pixi’VN 重新创建视觉小说 Breakdown。 Breakdown 是一个短篇故事,具备了视觉小说应有的所有特性。 Breakdown 的创作者 Josh Powlison 已经允许我们出于教育目的使用他的叙事内容❤️。
由于 Pixi’VN 允许你通过选择一种或多种可用的叙事语言来编写自己的叙事内容,因此在每个开发步骤中都会为当前每种可用的语言提供示例。
创建新项目
在开始之前,请确保你已经安装了前置条件。
第一步是创建一个新项目。 你可以通过运行以下命令来完成:
npm create pixi-vn@latest然后选择 Visual Novel 模板。
VS Code
如果你使用 VS Code 打开项目,系统会要求你同意运行一些工作区任务,并安装项目推荐的扩展。 强烈建议你接受这两项。
你可以在这里找到有关模板、任务和扩展的更多信息。
创建角色
现在我们将定义这个故事中的角色。 为此,我们将在 /content/characters.ts 文件中定义我们将要使用的角色。
有关如何创建和使用角色的更多信息,请参阅:Characters
import Character from "@/models/Character";
import { RegisteredCharacters } from "@drincs/pixi-vn";
export const mc = new Character("mc", {
name: "Me",
});
export const james = new Character("james", {
name: "James",
color: "#0084ac",
});
export const steph = new Character("steph", {
name: "Steph",
color: "#ac5900",
});
export const sly = new Character("sly", {
name: "Sly",
color: "#6d00ac",
});
RegisteredCharacters.add([mc, james, steph, sly]);叙事初稿
Markup
所有模板都支持Markdown和Tailwind CSS,因此我们将在叙事中使用它。
现在我们可以开始编写视觉小说叙事的"初稿"了。
我们将创建第一个名为 start 的叙事节点(label),它将是游戏的开始。 之后我们可以编写视觉小说中接下来的对话。
这是示例:
=== start ===
james: You're my roommate's replacement, huh?
james: Don't worry, you don't have much to live up to. Just don't use heroin like the last guy, and you' fine!
mc: ...
He thrusts out his hand.
james: James!
mc: ...Peter.
I take his hand and shake.
james: Ooh, Peter! Nice, firm handshake! The last quy always gave me the dead fish. I already think we'r gonna get along fine.
james: Come on in and...
james: ...
james: I know you're both watching, come on out already!
sly: I just wanted to see what the new guy was like. Hey, you, Peter- be nice to our little brother, or you'll have to deal with *us*.
mc: ...
james: Peter, this is Sly. Yes, that is her real name.
I put out my hand.
sly: I'm not shakin' your hand until I decide you're an all-right dude. Sorry, policy.
mc: Fair enough, I'm a pretty scary guy, or so l've been told.
james: The redhead behind her is Stephanie.
// Example of using Tailwind CSS
steph: <span class="inline-block motion-translate-y-loop-25">Hey</span>! Everyone calls me Steph. I'll shake your hand.
// ...
-> DONE将叙事拆分为多个叙事节点(labels)
不建议创建非常长的叙事节点(labels)(即使对于线性视觉小说也是如此),而建议创建多个小的叙事节点(labels),并在需要时使用叙事流程控制功能来"调用"它们。
因此,尽管在我们的案例中故事是线性的,它仍将被分为两个叙事节点(labels),第一个是我们刚刚创建的那个(start),第二个将被称为 second_part。
这是示例:
=== start ===
james: You're my roommate's replacement, huh?
james: Don't worry, you don't have much to live up to. Just don't use heroin like the last guy, and you' fine!
mc: ...
He thrusts out his hand.
james: James!
mc: ...Peter.
// ...
-> second_part
=== second_part ===
She enters my room before I'VE even had a chance to. \\n\\n...I could've just come back and gotten the platter later...
She sets it on a desk. I throw my two paper bags down beside the empty bed.
steph: They got you a new mattress, right? That last guy was a druggie, did James tell you that?
sly: *We're* the reason he got expelled!
steph: Sly! If word gets out about that... well, actually, it wouldn't matter, *he's* the one who shot himself up.
I'm fumbling for a new subject.
// ...
-> DONE选择菜单
现在我们将询问玩家是否想继续视觉小说的第二部分。
To do this, we will set narration.choices to an array of choice options created with newChoiceOption (to jump or call a label) and newCloseChoiceOption (to close the choice menu without navigating anywhere).
你可以在这里找到有关选择菜单的更多信息。
这是示例:
=== start ===
// ...
You want continue to the next part?
* Yes, I want to continue
-> second_part
* No, I want to stop here
-> END
=== second_part ===
// ...
-> DONE编辑角色信息并将其用作变量
现在我将赋予玩家更改 mc 名字的能力。
为此,我将要求玩家使用 Pixi’VN 的功能填写一个输入框。
获取输入值后,你可以使用获得的值来设置角色名称。
在 ink 中,_input_value_ 是一个特殊变量,用于保存玩家输入的值。 你可以在这里阅读更多相关信息。
这是示例:
=== start ===
// ...
He thrusts out his hand.
# request input type string default Peter
What is your name?
# rename mc { _input_value_ }
// ...
-> DONE现在我们可以在对话中使用角色名称。
这是示例:
VAR steph_fullname = "Stephanie"
=== start ===
// ...
sly: I just wanted to see what the new guy was like. Hey, you, [mc]- be nice to our little brother, or you'll have to deal with *us*.
mc: ...
james: [mc], this is [sly]. Yes, that is her real name.
I put out my hand.
sly: I'm not shakin' your hand until I decide you're an all-right dude. Sorry, policy.
mc: Fair enough, I'm a pretty scary guy, or so l've been told.
james: The redhead behind her is [steph_fullname].
steph: Hey! Everyone calls me [steph]. I'll shake your hand.
She puts out her hand, and I take it.
mc: Thanks, good to meet you, [steph_fullname].
steph: WOW, that is, like, the most perfect handshake I've ever had! Firm, but also gentle. [sly], you *gotta* shake his hand!
// ...
-> DONE定义并加载素材
要加载和操作素材(图片、gif、视频……), ,您需要使用 Assets。 Assets 是一个功能丰富的类,来自 PixiJS 库,如果你想了解更多信息,请阅读这里。
最初的步骤之一是选择在哪里保存你的视觉小说素材:你可以使用在线托管服务,也可以将它们保存在项目本地。 你可以在这里找到有关这两种选项的更多信息。
如果你选择将素材保存在本地,只需将它们放置在 src/assets 中:模板会自动使用 AssetPack 来分析文件,并将它们定义在 src/assets/manifest.gen.json 中。 如果你选择将它们保存在线上,则需要手动编辑 assets/index.ts 来注册它们。
无论哪种情况,每个素材都会被分配一个或多个别名(alias),之后你可以在代码中使用它来引用该素材。
{
"bundles": [
{
"name": "images",
"assets": [
{
"alias": [
"images_icon",
"icon"
],
"src": [
"images/[email protected]",
"images/icon-VA-NtA.webp",
"images/[email protected]",
"images/icon-E_ZC2g.png"
],
"data": {
"tags": {}
}
}
]
}
]
}添加背景和角色图片
现在也该考虑视觉部分了。 我们将把背景和角色图片添加到视觉小说的画布中。
在我们的案例中,每个角色由 3 个 sprite 组成:身体、眼睛和嘴巴。 我们使用ImageContainer——一个将 Sprite 子元素分组的 Container——将它们组合在一起以构成角色。 你可以在这份文档中找到有关如何添加画布组件的更多信息。
这是示例:
=== start ===
# show image bg bg01-hallway
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-neutral01] xAlign 0.5 yAlign 1
james: You're my roommate's replacement, huh?
# show imagecontainer james [m01-body m01-eyes-grin m01-mouth-smile01]
james: Don't worry, you don't have much to live up to. Just don't use heroin like the last guy, and you'll be fine!
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-grin00]
mc: ...
// ...
-> DONE声音和音乐
要为你的视觉小说添加声音和音乐,你可以使用 sound 工具。
你可以定义声音频道来管理不同类型的声音(例如 BGM、SFX),并控制它们的音量、暂停、恢复等。 建议在游戏开始时定义这些频道,例如在 Game.init 函数的 then 回调中。
在这个示例中,我定义了两个频道:一个用于背景音乐(BGM),一个用于音效(SFX)。 我还将 defaultChannelAlias 设置为 SFX 频道,这样如果我在播放声音时没有指定频道,它将默认在 SFX 频道中播放。 为了定义背景频道,我将 background 属性设置为 true,这样与其他频道不同,声音不会在每个叙事步骤(step)结束时停止,而是会持续播放,直到被暂停或停止。
import { Game, sound } from "@drincs/pixi-vn";
import { BGM_CHANNEL_NAME, SFX_CHANNEL_NAME } from "@/constans";
Game.init(body, {
// ...
}).then(() => {
sound.channels.add(BGM_CHANNEL_NAME, { background: true });
sound.channels.add(SFX_CHANNEL_NAME);
sound.defaultChannelAlias = SFX_CHANNEL_NAME;
});最后,你可以在叙事节点(labels)中使用 sound.play 函数播放声音和音乐。 你还可以使用频道来管理不同类型的声音(例如 BGM、SFX),并控制它们的音量、暂停、恢复等。
你可以在这里找到有关如何使用它的更多信息。
=== start ===
# show image bg bg01-hallway
# play sound sfx_whoosh delay 0.1
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-neutral01] xAlign 0.5 yAlign 1 with movein direction right ease circInOut type spring
james: You're my roommate's replacement, huh?
# play sound sfx_whoosh channel bgm loop true
# show imagecontainer james [m01-body m01-eyes-grin m01-mouth-smile01]
james: Don't worry, you don't have much to live up to. Just don't use heroin like the last guy, and you'll be fine!
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-grin00]
mc: ...
// ...
# pause all sounds
# show imagecontainer steph [fm02-body fm02-eyes-smile fm02-mouth-smile00]
# play sound sfx_whoosh delay 0.1
# remove image james with moveout direction right ease circInOut type spring duration 0.5 delay 0.05
# remove image sly with moveout direction right ease anticipate duration 0.5
# remove image steph with moveout direction left ease easeInOut duration 0.5 delay 0.1
You want continue to the next part?<># continue
* Yes, I want to continue
-> second_part
* No, I want to stop here
-> END
=== second_part ===
# show text bg "(A few minutes later...)" style { fontFamily: "Arial", dropShadow: { alpha: 0.8, angle: 2.1, blur: 4, color: "0x111111", distance: 10, }, fill: "\#ffffff", stroke: { color: "\#004620", width: 12, join: "round" }, fontSize: 60, fontWeight: "lighter" } with fade
# edit text bg align 0.5
# pause
# resume all sounds
# show image bg bg02-dorm align 0 with fade
# play sound sfx_whoosh delay 0.4
// ...
She enters my room before I'VE even had a chance to.
// ...
-> DONE
结语
好了,现在你已经知道如何使用 Pixi’VN 创建视觉小说了。 能力越大,责任越大,所以请明智地使用它,创作一个精彩的故事吧! 🚀