@xiaoyixy
2020-04-02T07:43:43.000000Z
字数 2209
阅读 997
文档
选择向 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 LRA[剧情片段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。
| 类 | 名称 | 解释 |
|---|---|---|
| Action | 场景类 | 代表一个场景/回合 |
| Component | 组件超类 | |
| Line | 台词类 | 台词组件,角色台词 |
| Selector | 剧情选择器类 | 提供剧情选择器组件,用于提供不同选择从而导向后续分支剧情 |
| Judgement | 数值裁决组件 | 裁决器组件,某些数值达到一定的阈值则触发相关事件或者导向后续分支剧情 |
| GameValues | 数值类 | 在此处代表数值变动,在本场景被演出之后相关数值应该如何变动 |
classDiagramclass Action{// 当前场景的ID+int id// 前一个场景的ID+int previous_action_id// 下一个场景的ID+int next_action_id// 场景背景+Sprite bg// 场景音乐+AudioSource bgmLine lineSelector selectorJudgement judgementGameValues gamevalues}class Component{int idint 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 HPint MPint EXP}Action <|-- ComponentComponent <|-- LineComponent <|-- SelectorComponent <|-- JudgementComponent <|-- GameValues