马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 Maz马 于 2026-3-29 14:27 编辑
前言:
·仅在renpy环境下讨论
·仅在开发者视角下角讨论
·任何言语不针对任何人及不包含恶劣侮辱意味
本篇涉及的内容包括游戏的立项,制作过程,并不涉及任何代码
是个人对这一两年走的弯路直路的总结
并非是实际的代码或案例,而是形而上学的思维方式或经验
由于本人本职是美术,既非编程也非策划,因此措辞可能会略显小白
也不会引经据典,亦可能有所谬误或缺漏,仅供入门参考
目录:
一 立项与规划
二 工程与管理
三 叙事类型
四 体验反馈
五 杂项总结统筹
叙事类型
这一篇要讲的叙事类型,不和故事的好坏挂钩
而是你的工程文件、你的代码要怎么长在故事上。
“结构是骨架,故事是血肉”
线性分支/树状管控
分支包含结局。
玩家一旦做出选择,就走上了一条不归路,直接走向对应的结局。
[RenPy] 纯文本查看 复制代码 start
│
└── menu(选择点)
├── A ───→ 结局A
│
├── B ───→ menu(选择点)
│ ├── B1 ───→ 结局B1
│ └── B2 ───→ 结局B2
│
└── C ───→ menu(选择点)
├── C1 ───→ 结局C1
└── C2 ───→ 结局C2 具体举例:
假设你有分支 A、B、C、D:
- 分支 A 有自己的小分支 Aa、Ab、Ac
- 那么 A 的内容应该与 Aa、Ab、Ac 放在同一个文件 A.rpy 中
假设你有分支 A、B、C、D,如果分支 Aa 还有次级分支 Aa1、Aa2:
- 那么 Aa 的内容应该与 Aa1、Aa2 放在同一个文件 Aa.rpy 中
- 并且 A 应该升级为文件夹,包含 Aa、Ab、Ac 等子文件
[RenPy] 纯文本查看 复制代码 start
└── A/
├── A.rpy # A 的主干内容
├── Aa.rpy # Aa + Aa1 + Aa2
├── Ab.rpy
└── Ac.rpy 线性分支,树怎么长,文件夹就怎么长。
每个选择点就是一个节点,一个文件只包含一层的嵌套,树状结构,家族节点划分
- 创作流畅——游戏流程情绪连贯,适合打磨文字、塑造氛围
- 管理痛苦——加分支可能影响前后逻辑,分支越多,文件结构越臃肿
- 短小项目——更重文学,传统视觉小说
事件驱动/变量管控
分支不包含结局。
可以无数次遍历各个事件,无论分支多少,都必须回到主干。
[RenPy] 纯文本查看 复制代码 |---A---| |---end1
| | |
start--Manager start--|---B---|-- Manager end--|---end2
| | |
|---C---| |----end3 和线性叙事不同,事件驱动型的文件组织没有固定规则。
为什么?因为事件是独立的。
你只需要考虑,有哪些事件?怎么分文件能让我自己快速查阅?
所以你可以按任何方式划分:
按时间划分:白天事件放一个文件,夜晚事件放一个文件
按角色划分:昊哥相关事件放一个文件,小美相关事件放一个文件
按进度划分:先开发的AC放一个文件,后开发的BD放另一个文件
按规模划分:事件A很庞大,单独一个文件;BCD比较小,合在一个文件
文件怎么分不重要,事件独立才重要。
在事件驱动型叙事里,每个事件必须是一个
独立、完整、自包含的代码区域
这意味着,改一个事件,不会影响其他事件,删一个事件,不影响整个事件流程
我们只需要按照固定结构写代码,用命名来区分开代码的有效范围[RenPy] 纯文本查看 复制代码 #=== 事件A ===
# 【资源区】这个事件用到的资源
image eventA_bg = "images/bg/eventA.jpg"
image eventA_char = "images/char/haoge.png"
# 【变量区】不被其他事件管控的变量
# 注意,独立事件可以修改整个世界存在的变量,比如某个角色的好感,又或是时间
# 需要和这个事件绑定的,不是这个事件管控的变量,而是不被其他事件管控,仅能在这个事件中使用的变量
default eventA_triggered = False
default eventA_choice = 0
# 【事件本体】
label eventA:
#事件A的内容
#事件A的所更改的变量
#例如:
menu:
"选项1":
$ eventA_choice = 1
"选项2":
$ eventA_choice = 2
# 回到调度器
jump Manager
可以直观的感受一下整洁的区块感:
[RenPy] 纯文本查看 复制代码 #=== 事件A ===
image eventA_bg1 = "images/bg/eventA1.jpg"
image eventA_bg2 = "images/bg/eventA2.jpg"
image eventA_bg3 = "images/bg/eventA3.jpg"
image eventA_bg4 = "images/bg/eventA4.jpg"
image eventA_bg5 = "images/bg/eventA5.jpg"
default eventA_triggered1 = False
default eventA_triggered2 = False
default eventA_triggered3 = False
default eventA_triggered4 = False
default eventA_triggered5 = False
transform eventA_move():
transform eventA_jump():
screen eventA_minigame1():
screen eventA_minigame2():
label eventA:
jump Manager
#=== 事件B ===
image eventB_bg1 = "images/bg/eventB1.jpg"
image eventB_bg2 = "images/bg/eventB2.jpg"
image eventB_bg3 = "images/bg/eventB3.jpg"
image eventB_bg4 = "images/bg/eventB4.jpg"
image eventB_bg5 = "images/bg/eventB5.jpg"
default eventB_triggered1 = False
default eventB_triggered2 = False
default eventB_triggered3 = False
default eventB_triggered4 = False
default eventB_triggered5 = False
label eventB:
jump Manager
#=== 事件C ===
image eventC_bg1 = "images/bg/eventC1.jpg"
image eventC_bg2 = "images/bg/eventC2.jpg"
image eventC_bg3 = "images/bg/eventC3.jpg"
image eventC_bg4 = "images/bg/eventC4.jpg"
image eventC_bg5 = "images/bg/eventC5.jpg"
default eventC_triggered1 = False
default eventC_triggered2 = False
default eventC_triggered3 = False
default eventC_triggered4 = False
default eventC_triggered5 = False
label eventC:
jump Manager 注意每个事件结尾的 jump Manager——这是关键。所有事件最终都要回到一个调度器中
所有事件由调度器分配,或者进入结局。
调度器
调度器是事件驱动型叙事的“心脏”
[RenPy] 纯文本查看 复制代码 label start:
# 初始化所有变量
$ day = 1
$ haoge_love = 0
# ...
# 进入调度器
jump Manager
label Manager:
# 版本匹配以及问题修复
# 举例:上个版本中你使用变量hgll作为名字,下个版本你修改成llgh
# 此时,你的玩家因为版本更新,导致变量报错,
# 但你可以在这里,一个玩家每次都必须来到的 【完美原点】 为他匹配
# llgh=hgll,再下个版本时,玩家存储的则是llgh了,hgll可以完全废弃。
if ver == 0.5:
$ llgh = hgll
# 根据变量分配事件
# 根据变量修改变量
# 根据变量分配结局
# 例如
if day >= 30:
jump Manager_end
if day == 1 and not eventA_triggered:
jump eventA
elif haoge_love >= 10 and not eventB_triggered:
jump eventB
else:
"无事发生的一天..."
$ day += 1
jump Manager 事件驱动,兼容性格式,对文件管理可迭代性强。
是现代游戏最典型的管理模式,由一个循环体来分配事件,每个事件独立,不干涉其他事件
- 管理灵活——复用性强,好维护好修改,向下兼容线性叙事
- 动态更新——必须回调度器,那么管控玩家的状态成为可能,可以随意更新发布版本
- 创作割裂——玩家在事件A哭,下一秒事件B笑
- 架构困难——前期需要先搭好调度器,对逻辑能力有较高要求,需要基础代码能力
- 中长项目——更重趣味性,有变量管理
总
事件驱动在项目管理上的优势很大。
加新事件、改旧内容、多人协作——都比线性叙事舒服得多。
而且,事件驱动向下兼容线性分支,完全可以用事件驱动的代码结构(三区格式 + 调度器),写一个线性的故事。
事实上,一个功能强大,可玩性高,且故事平滑的游戏往往是多种叙事结构的“混合架构”
例如 事件驱动 嵌套 事件驱动,由第一个调度器分配地点或时间,再由时间或地点的调度器分配事件,层层分包
又例如 线性分支 嵌套 事件驱动,组合成 故事段 和 自由探索段
这里给出的是基础的叙事结构、思考方向,而不是定论。
但有一个相当反直觉的点
很多人会觉得 线性分支 更适合新手,恰恰相反,线性分支适合的往往是老手。
(或是看了这篇文章仍理解不了事件驱动、调度器结构的极致小白)
因为线性分支适合的是短篇故事,而不是适合新手
新手往往怀揣热情且东一榔头西一棒槌,导致文件结构混乱,代码乱飞乱造。
用事件驱动天然的就能让新手更好的学习项目管理,文件结构以及了解引擎。
线性分支则需要很强的自控能力和项目管理能力。
事件驱动是在管理上天然比线性分支更优的,线性分支的优点比起简单,更重要的是效率。
在一个老手制作人,对项目有极强的提前规划能力和自控能力时,使用线性分支的叙事结构,也就是想到哪,写到哪往往能将小型项目的推进时间压缩到一个极短的地步
当然,如果你是一个新手,你保证,你的项目不会因为你的一时兴起而打算大量修改,并且你对项目的操作、机制没有任何想法,只想深耕于文学之中,那么线性分支确实是一个很好的选择。
|