You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
graph LR;
A[选择卡1] --> B[选择子项1-1分]
A[选择卡1] --> C[选择子项2-2分]
A[选择卡1] --> D[选择子项3-3分]
B --> E[剧情1]
C --> O[结局1]
D --> F[剧情2]
E[剧情1] --> G[选择卡2]
G --> H[选择子项1-1分]
G --> I[选择子项2-2分]
G --> J[选择子项3-3分]
F[剧情2] --> K[选择卡3]
K --> L[选择子项1-1分]
K --> M[选择子项2-2分]
H --> O[结局1-累计2分]
I --> P[结局2-累计3分]
J --> Q[结局3-累计4分]
L --> Q[结局3-累计4分]
M --> R[结局4-累计5分]
graph LR;
A[选择卡1] --> B[选择子项1-1分]
A[选择卡1] --> C[选择子项2-2分]
A[选择卡1] --> D[选择子项3-3分]
B --> E[剧情1]
C -.2分.-> O[结局1]
D --> F[剧情2]
E[剧情1] --> G[选择卡2]
G --> H[选择子项1-1分]
G --> I[选择子项2-2分]
G --> J[选择子项3-3分]
F[剧情2] --> K[选择卡3]
K --> L[选择子项1-1分]
K --> M[选择子项2-2分]
H -.2分.-> P[结局2]
I --3分--> P[结局2]
J --4分--> Q[结局3]
L --4分--> Q[结局3]
M -.5分.-> Q[结局3]
graph LR
A[剧情片段1] -->|数值变动| B{选择1}
B -->|数值变动| D[剧情片段2]
B -->|数值变动| E[剧情片段3]
B -->|数值变动| E[剧情片段3]
D -->|数值变动| F{选择2}
F -->|数值变动| G[剧情片段4]
F -->|数值变动| H[剧情片段5]
E -->|数值变动| I{数值达到一定值}
I -->|满足条件1| H[剧情片段5]
I -->|满足条件2| J[剧情片段6]
G -.->|后续剧情| K[结局1]
H -.->|后续剧情| L[结局2]
J -.->|后续剧情| M[结局3]
问题描述
选择向 RPG 游戏往往通过不同的多个选择(这里称为路线)最终导向不同的结局。
当路线的数目和结局数目比较少的时候可以通过设定数值,最后数值统计的方式来推出下一步的方向和结局。
比如:
graph LR; A[选择卡1] --> B[选择子项1-1分] A[选择卡1] --> C[选择子项2-2分] A[选择卡1] --> D[选择子项3-3分] B --> E[剧情1] C --> O[结局1] D --> F[剧情2] E[剧情1] --> G[选择卡2] G --> H[选择子项1-1分] G --> I[选择子项2-2分] G --> J[选择子项3-3分] F[剧情2] --> K[选择卡3] K --> L[选择子项1-1分] K --> M[选择子项2-2分] H --> O[结局1-累计2分] I --> P[结局2-累计3分] J --> Q[结局3-累计4分] L --> Q[结局3-累计4分] M --> R[结局4-累计5分]但是,一旦路线数和结局过多,中途出现选项合并,比如出现不同分数可以导向同一下一步/结局,或者分数相同时可导向不同的下一步/结局的情况,那么
逻辑就会显得相当混乱,管理非常麻烦且容易出错。graph LR; A[选择卡1] --> B[选择子项1-1分] A[选择卡1] --> C[选择子项2-2分] A[选择卡1] --> D[选择子项3-3分] B --> E[剧情1] C -.2分.-> O[结局1] D --> F[剧情2] E[剧情1] --> G[选择卡2] G --> H[选择子项1-1分] G --> I[选择子项2-2分] G --> J[选择子项3-3分] F[剧情2] --> K[选择卡3] K --> L[选择子项1-1分] K --> M[选择子项2-2分] H -.2分.-> P[结局2] I --3分--> P[结局2] J --4分--> Q[结局3] L --4分--> Q[结局3] M -.5分.-> Q[结局3]如上图这种情况,多个不同的分数得到了同一个结局。
问题分析
上面设计存在的问题在于分数导向结局的过程,当分数出现相同时难以管理。
以上的问题可以通过合理设定分数来避免重复冲突,但是很明显这项工作不仅设计工作量巨大且不利于管理和程序设计,一旦有一点小改动,那么后果是
灾难性的。一个不那么好的优化措施是使用穷举法,通过步伐记录(每一个选择选择了什么)来区分每一种不同的情况,事先保存一系列可能的路线。但是带来的是出现大量重复的问题。
另一个比较好的思路是运用动态规划(线性规划)的思想,不用关注之前的所有选择和结果,而只关注当前的问题和选择。
动态规划
建模思路
跳出积分导向思路,而把某些变量的数值当成动态判断方向的依据,结合面向切面编程思想,目标是实现每一个步骤可插拔,可替换,这样可以实现每一步的操作不会影响之前或者之后的发展。因此选用双向链表的数据结构显然非常合适。
graph LR A[剧情片段1] -->|数值变动| B{选择1} B -->|数值变动| D[剧情片段2] B -->|数值变动| E[剧情片段3] B -->|数值变动| E[剧情片段3] D -->|数值变动| F{选择2} F -->|数值变动| G[剧情片段4] F -->|数值变动| H[剧情片段5] E -->|数值变动| I{数值达到一定值} I -->|满足条件1| H[剧情片段5] I -->|满足条件2| J[剧情片段6] G -.->|后续剧情| K[结局1] H -.->|后续剧情| L[结局2] J -.->|后续剧情| M[结局3]基础类设计
定义一个组件超类
Component,子类:台词类Line,选择器类Selector,条件触发器类Judgement,数值类GameValues。classDiagram class Action{ // 当前场景的ID +int id // 前一个场景的ID +int previous_action_id // 下一个场景的ID +int next_action_id // 场景背景 +Sprite bg // 场景音乐 +AudioSource bgm Line line Selector selector Judgement judgement GameValues gamevalues } class Component{ int id int action_id } class Line{ // 角色台词 +String text // 台词样式 +Style style // 角色语音 +AudioSource voice } class Selector{ // 已选择的值 object selected_value // 子选项列表 List<Option> options } class Judgement{ // 达成触发条件的数值组合 List<GameValues> meetGameValues } class GameValues{ // ... int HP int MP int EXP } Action <|-- Component Component <|-- Line Component <|-- Selector Component <|-- Judgement Component <|-- GameValues