找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 40|回复: 0

[经验] 【项目管理其三】叙事类型

[复制链接]
发表于 6 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 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笑
  • 架构困难——前期需要先搭好调度器,对逻辑能力有较高要求,需要基础代码能力
  • 中长项目——更重趣味性,有变量管理




事件驱动在项目管理上的优势很大
加新事件、改旧内容、多人协作——都比线性叙事舒服得多。
而且,事件驱动向下兼容线性分支,完全可以用事件驱动的代码结构(三区格式 + 调度器),写一个线性的故事。
事实上,一个功能强大,可玩性高,且故事平滑的游戏往往是多种叙事结构的“混合架构”

例如 事件驱动 嵌套 事件驱动,由第一个调度器分配地点或时间,再由时间或地点的调度器分配事件,层层分包
又例如 线性分支 嵌套 事件驱动,组合成 故事段自由探索段

这里给出的是基础的叙事结构、思考方向,而不是定论。

但有一个相当反直觉的点
很多人会觉得  线性分支  更适合新手,恰恰相反,线性分支适合的往往是老手。
(或是看了这篇文章仍理解不了事件驱动、调度器结构的极致小白)

因为线性分支适合的是短篇故事,而不是适合新手
新手往往怀揣热情且东一榔头西一棒槌,导致文件结构混乱,代码乱飞乱造。
用事件驱动天然的就能让新手更好的学习项目管理,文件结构以及了解引擎。

线性分支则需要很强的自控能力项目管理能力

事件驱动是在管理上天然比线性分支更优的,线性分支的优点比起简单,更重要的是效率

在一个老手制作人,对项目有极强的提前规划能力自控能力时,使用线性分支的叙事结构,也就是想到哪,写到哪往往能将小型项目的推进时间压缩到一个极短的地步
当然,如果你是一个新手,你保证,你的项目不会因为你的一时兴起而打算大量修改,并且你对项目的操作、机制没有任何想法,只想深耕于文学之中,那么线性分支确实是一个很好的选择。





您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号 )

GMT+8, 2026-3-29 19:04 , Processed in 0.018092 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表