Files
Fishdice/FishDice/Docs/Architecture/lucky-dice-core-flow-architecture-blueprint.md
2026-06-23 14:13:08 +08:00

18 KiB
Raw Blame History

Lucky Dice 核心流程架构蓝图

文档信息

本文档定义 Lucky Dice 第一版核心闭环的架构蓝图。它不替代需求或接口规格,而是回答后续实现时最容易失控的几个问题:

  • 普通 Roll、组合规则、Lucky Dice、目标玩法入口之间怎么分层。
  • 哪些模块可以通过配置扩展,哪些模块才需要新增代码。
  • 数据从玩家 Roll 到最终玩法模式如何流动。
  • 追踪、兜底、测试和 Unity 落地目录应如何组织。

当前项目尚未落地生产 C# 玩法模块,因此本文档描述的是第一版建议架构,而不是对现有代码的复盘。

架构定位

Lucky Dice 不是普通骰子的二次 Roll也不是一个独立短玩法本体。它在架构中承担的是“特殊玩法入口选择器”职责

普通 Roll 负责产出组合事实
组合规则负责决定业务行为
Lucky Dice 负责从特殊结果池中选择目标入口
目标玩法模块负责执行 Slap Down / Treasure Heist 等具体内容

第一版架构应优先保证三件事:

  1. 可扩展:新增组合、骰子数量、特殊结果、目标玩法模式时优先改配置。
  2. 可测试:组合事实、规则匹配、筛选、权重随机都能用确定性输入测试。
  3. 可追踪:一次 Roll 的所有关键决策能用同一个 RollSessionId 串起来。

架构原则

两套骰子集合隔离

普通骰子集合只表达主循环 Roll 的结果,例如 2 / 3 / 4 / 5 / 6 / clover。Lucky Dice 特殊结果集合只表达后续玩法入口,例如 rocket / thief

两者不能混用同一套裸字符串,也不能让普通组合匹配器直接处理 Lucky Dice 特殊结果。

规则匹配先于行为执行

普通 Roll 结果必须先转换成结构化 ComboFacts,再交给规则匹配器。业务行为不应读取“左骰 / 右骰”这类固定位置状态。

DiceRollResult
→ ComboFacts
→ ComboRule
→ RollActionSpec
→ IRollActionExecutor

配置增长优先于代码增长

组合数量增加时,应增加 ComboRule 配置。只有出现新的通用匹配能力时才新增 matcher只有出现新的通用业务行为时才新增 executor。

禁止形成下面这种随组合数量膨胀的结构:

clover_clover -> TriggerLuckyDice()
2_clover      -> GrantCloverReward()
3_clover      -> GrantCloverReward3()
...

流程服务不依赖表现层

核心流程可以产出展示所需数据,例如 Lucky Dice 标题、倍率、结果槽和目标玩法反馈,但不能依赖 UI 动画完成时序来做核心决策。

第一版表现层只消费核心结果,不参与筛选、随机和模式解析。

总览图

flowchart TD
    Player["玩家触发 Roll"] --> RollService["DiceRollService"]
    RollService --> FactBuilder["ComboFactBuilder"]
    FactBuilder --> RuleMatcher["ComboRuleMatcher"]
    RuleMatcher --> ActionDispatcher["RollActionDispatcher"]

    ActionDispatcher --> Reward["RewardExecutor"]
    ActionDispatcher --> Multiplier["MultiplierExecutor"]
    ActionDispatcher --> LuckyTrigger["TriggerLuckyDiceExecutor"]

    LuckyTrigger --> LuckyFlow["LuckyDiceFlowService"]
    LuckyFlow --> CandidateFilters["CandidateFilters"]
    CandidateFilters --> WeightedPicker["WeightedPicker"]
    WeightedPicker --> SlotBuilder["ResultSlotBuilder"]
    SlotBuilder --> ModeResolver["TargetModeResolver"]
    ModeResolver --> Launcher["TargetModeLauncher"]
    Launcher --> TargetGameplay["Slap Down / Treasure Heist"]
    TargetGameplay --> MainLoop["回到主循环"]

    RollService -.-> Trace["RollTrace"]
    RuleMatcher -.-> Trace
    ActionDispatcher -.-> Trace
    LuckyFlow -.-> Trace
    ModeResolver -.-> Trace

分层架构

层级 主要职责 允许依赖 禁止依赖
配置层 提供骰子集合、规则、候选结果、目标模式映射 Unity 资源、本地 JSON、静态配置 UI 动画状态、运行时随机结果
Roll 层 根据 DiceSet 生成普通骰子结果 配置层、随机源 规则、奖励、玩法入口
组合事实层 把普通 Roll 结果转为统计事实 Roll 层数据 Lucky Dice 特殊结果、UI
规则匹配层 按 matcher 和优先级命中规则 组合事实、规则配置 行为执行器、目标玩法
行为执行层 按 action type 分发通用行为 规则结果、上下文服务 组合 key 到独立方法映射
Lucky Dice 层 候选池、筛选、权重随机、结果槽 候选配置、上下文、随机源 普通组合 matcher
模式解析层 ResultKey -> TargetKey -> ModeKey Lucky Dice 结果、上下文、模式配置 短玩法内部实现
表现层 展示 Lucky Dice 标题、倍率、结果槽、跳转反馈 核心流程产物 核心规则决策
目标玩法层 执行 Slap Down / Treasure Heist 等玩法 TargetModeEntry Lucky Dice 随机细节
追踪层 汇总关键决策链路 所有核心层事件 改变玩法结果

依赖方向应从上游输入流向下游结果。追踪层可以被各层写入,但不应反向影响流程决策。

组件职责

DiceSetConfigProvider

负责提供骰子集合配置包括普通骰子面、普通骰子数量、Lucky Dice 特殊结果面、默认结果槽数量。

第一版可以使用 ScriptableObject、JSON 或硬编码本地配置启动,但调用方必须只通过 provider 读取,不把 2 颗骰子或 3 个结果槽写死到流程逻辑中。

DiceRollService

负责按骰子集合配置生成 Roll 结果。普通 Roll 会产出多个普通骰子面Lucky Dice 第一版不需要真的 Roll 三颗特殊骰,而是由候选选择结果再生成展示槽。

该服务只负责“随机出可见骰子面”,不负责奖励、倍率或玩法入口。

ComboFactBuilder

负责把普通 Roll 结果转成 ComboFacts

  • 原始面列表
  • 面数量统计
  • 数字列表
  • 点数和
  • Clover 数量
  • 无序归一化 key
  • 有序 key

它必须支持 N 颗骰子。2 + cloverclover + 2 的无序 key 必须一致。

ComboRuleMatcher

负责把 ComboFacts 与规则配置匹配。matcher 按类型注册,例如 ExactComboAllNumbersContainsFaceFaceCountRange

第一版按优先级从高到低匹配,默认命中高优先级规则后停止继续匹配,除非规则配置声明可以继续匹配。

RollActionDispatcher

负责把命中规则中的 RollActionSpec 分发给对应 executor。

dispatcher 只能维护 ActionType -> Executor 注册表,不能维护组合 key 到方法的映射。

LuckyDiceFlowService

负责 Lucky Dice 的核心入口选择流程:

构建候选池
→ 筛选候选池
→ 处理空池兜底
→ 权重随机
→ 生成展示结果槽

它不直接启动 Slap Down 或 Treasure Heist而是把选中结果交给模式解析层。

TargetModeResolver

负责把 Lucky Dice 结果解析到具体目标玩法模式。

第一版映射:

ResultKey TargetKey ModeKey
rocket slap_down slap_down_normal
thief treasure_heist treasure_heist_normal

后续教程模式、bonus 模式应通过 mode rule 扩展,而不是在 Lucky Dice 随机代码里写分支。

TargetModeLauncher

负责统一启动目标玩法。它接收 TargetModeEntry,把 Lucky Dice 的结果、倍率、触发来源、RollSessionId 传给目标玩法入口。

目标玩法结束后,通过明确的 completion 回调或流程事件回到主循环。

RollTrace

负责记录一次 Roll 的关键路径。追踪不是埋点管线本身,第一版可以是本地结构化日志或调试面板数据。

最小字段应覆盖:

  • 原始普通骰子结果
  • 组合事实和归一化 key
  • 命中规则和执行行为
  • Lucky Dice 初始候选、过滤过程、兜底类型
  • 权重随机输入和选中结果
  • 解析出的 TargetKey 和 ModeKey
  • 最终结果

核心数据流

普通 Roll

sequenceDiagram
    participant P as Player
    participant R as DiceRollService
    participant F as ComboFactBuilder
    participant M as ComboRuleMatcher
    participant A as RollActionDispatcher
    participant T as RollTrace

    P->>R: Roll(normal_main)
    R->>T: record normal faces
    R->>F: DiceRollResult
    F->>T: record ComboFacts
    F->>M: ComboFacts
    M->>T: record matched rule
    M->>A: RollActionSpec list
    A->>T: record executed actions

Lucky Dice

sequenceDiagram
    participant A as TriggerLuckyDiceExecutor
    participant L as LuckyDiceFlowService
    participant F as CandidateFilters
    participant W as WeightedPicker
    participant S as ResultSlotBuilder
    participant M as TargetModeResolver
    participant T as RollTrace

    A->>L: LuckyDiceContext
    L->>T: record initial candidates
    L->>F: apply filters
    F->>T: record filter decisions
    F->>W: filtered candidates
    W->>T: record random value and selected result
    W->>S: selected candidate
    S->>T: record result slots
    S->>M: selected candidate + context
    M->>T: record target mode

配置架构

第一版建议至少拆成四类配置:

配置 示例内容 扩展方向
DiceSetConfig normal_mainlucky_dice、骰子面、骰子数量、默认槽数 支持关卡自定义骰子数量
ComboRuleConfig 规则 id、优先级、matcher、actions 策划新增组合语义
LuckyDiceCandidateConfig result、target、weight、enabled、slot count、default 扩展特殊结果与概率
TargetModeConfig target、default mode、tutorial mode、bonus mode 扩展玩法模式

配置读取顺序建议:

本地默认配置
→ 关卡或活动覆盖配置
→ 运行时上下文修正

远端热更不在第一版范围内,但数据结构应避免与 Unity 场景对象强绑定,给后续迁移到远端配置留出空间。

Unity 落地目录建议

如果第一版开始实现 C# 模块,建议按运行时核心、配置、表现、测试分开:

FishDice/Assets/FishDice/
  Runtime/
    LuckyDice/
      Core/
        DiceRollService.cs
        ComboFactBuilder.cs
        ComboRuleMatcher.cs
        RollActionDispatcher.cs
        LuckyDiceFlowService.cs
        TargetModeResolver.cs
      Config/
        DiceSetConfig.cs
        ComboRuleConfig.cs
        LuckyDiceCandidateConfig.cs
        TargetModeConfig.cs
      Actions/
        GrantRewardExecutor.cs
        AddMultiplierExecutor.cs
        TriggerLuckyDiceExecutor.cs
        EnterModeExecutor.cs
      Filters/
        EnabledFilter.cs
        ProgressFilter.cs
        SourceFilter.cs
        CooldownFilter.cs
        TutorialFilter.cs
      Presentation/
        LuckyDicePresenter.cs
        LuckyDiceResultSlotView.cs
      Trace/
        RollTrace.cs
        RollTraceLogger.cs
  Tests/
    EditMode/
      LuckyDice/

第一版核心流程建议尽量放在纯 C# 类里,减少对 MonoBehaviour 的依赖。表现层再通过 Unity 组件消费核心流程产物。

错误处理与兜底

普通组合未命中

处理策略:

  1. 记录 RollSessionId、原始骰子面、NormalizedKey
  2. 走普通奖励兜底,或在调试模式返回明确错误。
  3. 不进入 Lucky Dice。

Lucky Dice 候选池为空

处理策略:

  1. 尝试使用配置的 default result。
  2. default result 不可用时,降级为普通奖励兜底。
  3. 记录每个 filter 过滤前后的候选数量和空池原因。

模式解析失败

处理策略:

  1. 记录 ResultKeyTargetKey 和上下文。
  2. 尝试使用目标玩法默认模式。
  3. 仍失败时降级为普通奖励兜底,并标记为配置错误。

兜底必须可观测,不能静默吞掉。否则后续配置错误会表现成“玩家只是没进 Lucky Dice”很难定位。

测试架构

测试应围绕外部行为和完整链路,而不是私有实现细节。

单元测试

测试对象 重点
ComboFactBuilder N 骰统计、无序 key、有序 key、Clover 数量
ComboRuleMatcher 优先级、matcher 类型、停止继续匹配
RollActionDispatcher 按 action type 分发,不按 combo key 分发
CandidateFilters enabled、progress、source、cooldown、tutorial 过滤
WeightedPicker 固定随机源下结果可预测
TargetModeResolver rocket/thief 映射和默认模式

链路测试

链路测试是第一版最重要的测试口:

给定 Roll 请求
给定 DiceSet 配置
给定 ComboRule 配置
给定 Lucky Dice 候选池
给定固定随机源
断言最终 outcome

最小链路用例:

  • 2 + 3 发普通奖励。
  • 2 + clover 发普通奖励并提供 Clover bonus。
  • clover + clover 触发 Lucky Dice。
  • Lucky Dice 选中 rocket 后进入 slap_down_normal
  • Lucky Dice 选中 thief 后进入 treasure_heist_normal
  • 候选池为空时进入可观测兜底。
  • 普通骰子数量改成 3 后,组合事实和 matcher 仍可工作。

架构决策记录

完整 ADR 存放在 Docs/Decisions。本节只保留摘要,后续决策变更应优先新增或更新独立 ADR 文档。

ADR-001普通骰子和 Lucky Dice 特殊结果分成两套集合

ADR-001

上下文普通骰子负责奖励组合Lucky Dice 负责玩法入口。两者语义不同。

决策:使用 DiceSetType.NormalDiceSetType.Lucky 区分集合,不让普通组合匹配器处理 Lucky Dice 特殊结果。

结果:避免数字结算和玩法入口混用。代价是配置和调试界面需要明确展示集合类型。

ADR-002Roll 结果先转组合事实,再匹配规则

ADR-002

上下文:第一版默认 2 颗骰子,但未来需要支持 3 颗、4 颗或关卡自定义骰子数量。

决策:所有普通 Roll 都先生成 ComboFactsmatcher 只依赖事实统计,不依赖固定位置。

结果:后续扩骰数量时风险更低。代价是第一版需要多写一层事实构建和测试。

ADR-003行为按 action type 注册

ADR-003

上下文:如果每个组合 key 绑定一个独立方法,组合增长会让执行层失控。

决策:规则只声明 action 列表dispatcher 只按 ActionType 找 executor。

结果:新增组合多数情况下只改配置。代价是 action 参数需要有清晰校验。

ADR-004Lucky Dice 随机源可注入

ADR-004

上下文:权重随机必须可测试、可回放。

决策Lucky Dice 权重随机通过 IRandomSource 或等价接口注入随机源。

结果:测试可预测,线上问题可复盘。代价是运行时需要统一管理随机源生命周期。

新功能开发蓝图

新增普通组合

  1. 确认现有 matcher 能否表达该组合。
  2. 能表达时新增 ComboRuleConfig
  3. 需要新业务效果时,优先复用现有 action。
  4. 只有确实出现新的通用行为时,新增 executor。
  5. 补一条规则匹配测试和一条链路测试。

新增 Lucky Dice 特殊结果

  1. 在 Lucky Dice 特殊结果集合中新增 ResultKey
  2. 在候选配置中声明 TargetKey、权重、启用条件、槽数、default 标记。
  3. 在目标模式配置中声明默认 ModeKey
  4. 补候选筛选测试、权重随机测试和目标模式解析测试。

新增目标玩法模式

  1. TargetModeConfig 中增加模式映射。
  2. 如模式选择依赖上下文,新增 mode rule。
  3. 不修改 Lucky Dice 随机逻辑。
  4. TargetModeResolver 测试。

接入表现动画

  1. 核心流程先产出 LuckyDiceResultSlotsTargetModeEntry
  2. 表现层播放标题、倍率、结果槽和跳转反馈。
  3. 动画完成后再调用 launcher或先启动目标玩法再做过场取决于产品节奏。
  4. 动画失败不能改变核心选择结果。

架构治理

实现和评审时重点检查以下规则:

  • 核心流程中没有固定读取第 0/1 颗骰子的业务判断。
  • 普通骰子和 Lucky Dice 特殊结果没有混用同一套集合。
  • 新增组合时没有新增组合 key 到独立方法的 map。
  • Lucky Dice 候选必须先筛选再随机。
  • 候选池为空、模式解析失败、未命中规则都有可观测兜底。
  • RollSessionId 贯穿普通 Roll、规则匹配、Lucky Dice、模式解析、目标玩法启动。
  • 表现层不参与规则匹配、候选筛选和权重随机。

第一版落地顺序

建议按以下顺序实现:

  1. 定义核心数据结构和本地默认配置。
  2. 实现普通 Roll、组合事实和规则匹配。
  3. 实现 action dispatcher 和普通奖励 / Clover bonus / trigger Lucky Dice 执行器。
  4. 实现 Lucky Dice 候选筛选、权重随机、结果槽生成。
  5. 实现 rocket -> slap_down_normalthief -> treasure_heist_normal 模式解析。
  6. 实现最小表现层:标题、倍率、结果槽、目标玩法跳转反馈。
  7. 补齐链路测试和追踪字段。

这个顺序能先把核心闭环跑通,再逐步加表现和更多结果类型,避免一开始被动画和短玩法完整内容拖散。