# 伪代码文档

### 什么是伪代码?什么是伪代码系统?

广义上, 伪代码是假的代码, 但它往往用来描述了某项工作的工作流程, 所以往往伪代码是面向过程的. 当然每个人写的伪代码习惯/能力不一样, 可能也会面向对象.

但**这里的伪代码系统是面向过程的; 也是面向策划工作人员的**, **所以此伪代码系统语法非常简单**: 基本上是由固定格式的XXX方法名(方法参数列表)构成. (具体语法细节请看文档后面)

**此伪代码存在的意义是: 给策划灵活配置一个个的条件判断与过程而准备的.**

例如一个角色的技能释放过程是这样的: 在角色的前方释放一个火球并飞向敌人, 为敌人造成M点伤害, 并为敌人施加一个负面效果-----燃烧N秒, 每秒造成 P 点伤害.

那么策划配置的伪代码可以这样在Excel表格中填写:

“角色释放飞弹类法术(创建火球(), 获取当前敌人(), 计算技能伤害(), 创建燃烧负面效果(N, P))”

其中:

角色释放飞弹类法术 / 创建火球 / 获取当前敌人 / 计算技能伤害 / 创建燃烧负面效果 这些都是伪代码中的方法, 简称: 伪方法.

N, P 可能是常数, 所以直接填入数字.

M 则不是常数, 而是根据释放技能的角色属性和技能本身决定的所以需要计算, 伪代码中调用: 计算技能伤害() 方法来获得计算值.

创建火球() 类似的方法可以有: 创建冰晶() / 创建水球() / 创建能量球() 等

获取当前敌人() 类似的方法可以有: 随机周围一个敌人() / 随机一名友方() / 自己() 等

创建燃烧负面效果() 类似的方法也可以有很多: 创建诅咒负面效果() / 创建回血正面效果() 等

类似的方法越多, 组合就越丰富, 灵活性越强!

那么策划写出这样的流程代码出来后, 怎么让此代码能够正常工作呢? 怎么让伪代码成为真正的代码呢?

答案是: **程序员需要完成所有”伪”方法对应的”真”方法,** 这样的话伪代码才能被运行.

伪方法: 角色释放飞弹类法术(法术飞弹实体对象, 被攻击对象, 伤害数值, 负面效果) --- 程序员在C# 中实现对应方法名的函数, 此函数接收参数为: 法术飞弹实体对象, 被攻击对象, 伤害数值, 负面效果. 此函数的作用是调用接收到的参数来完成: \[法术飞弹实体对象]飞向\[被攻击对象]的动画, 当飞弹撞击到被攻击对象时, 给\[被攻击对象]造成\[伤害数值]点的伤害, 并且施加\[负面效果].

伪方法: 创建火球() --- 程序员在C# 中实现对应方法名的函数, 此函数用来完成: 调用资源在游戏中生成火球对象.

为什么不让程序员直接按照策划需求来实现所有的技能呢? 还需要策划去写伪代码不是多此一举吗? 当技能数量很多时, 比如超过一百个技能时, 使用伪代码的优势就体现出来了. 程序员不需要按照这一百多个技能的要求一个接一个地去实现出来, 而是技能策划按照程序员提供的接口(也就是伪方法)去灵活设计. 技能策划类似于玩拼图游戏去完成一个个不同的技能.

**工作流参考流程:** 策划先出一些需求, 程序员根据这些需求去拆分成一个个伪方法并在C# 中实现, 再将伪方法和伪方法文档提供给策划, 策划再使用这些伪方法按照伪代码语法编写组合成一个个新的流程出来, 期间可能需要程序员不断补充新的伪方法, 随着伪方法越来越多, 伪代码能够发挥的空间越来越大, 能编写出的效果也越来越灵活. 伪方法**文档能够自动生成.**

伪代码不仅仅只是技能系统中可以使用, 对于RPG游戏来说, 游戏的剧情和任务几乎是必不可少的, 而剧情和任务中又有大量需要配置的条件判断和执行过程, 条件判断和执行过程皆可使用此伪代码系统来实现. **游戏中只要是需要灵活配置条件判断和执行过程的地方都可以使用伪代码系统来实现.**

### 伪代码语法:

* **语句:** 用’;’隔开.每个语句由一个作为”根”的伪方法构成.
* **伪方法**: “方法名(方法参数0, 方法参数1, ...)”, 方法名前后或参数前后的任何空格或换行符或制表符不会对伪方法执行有任何影响.
* **方法参数**: 给方法传入其运行所需的参数, 方法才能被正确执行. 每个方法参数都可以由一个伪方法运行后返回.
* **基本类型**: 整数/浮点数/布尔数/字符串, 对应着c# 中的int / float / bool / string 类型. 伪代码中的整数会被伪代码解析器解析为int类型, 小数会被解析为float类型.
* **空对象**: \[NULL], 用来表示一个 object 类型是 null 对象.
* **代码块方法**: NewAction(伪代码语句) 和 NewFunc(一句有返回值的伪代码语句) , 这两个特殊方法的参数是伪代码语句, 它们封装一个伪代码语句为一个代码块提供给其他需要代码块参数的伪方法作为参数. 比如使用到 IF() 或 While() 等逻辑控制或循环控制的伪方法中.

### 伪代码编辑器:

伪代码可以是以字符串形式填入, 也可以是伪代码对象( PseudoCode对象)的形式存在. PseudoCode对象更加方便在UnityEditor中编辑. 如果是要在Excel表配置, 则只能是填入字符串的伪代码.

PseudoCode对象可以直接在UnityEditor的Inspector中编辑, 配合伪代码的**方法文档窗口**更加方便编写:

![](https://1547393012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F72JBk3dBKE7NYnndIjp7%2Fuploads%2Fw28CEPKv73pJeWxiqLSI%2F0.png?alt=media)

另外PseudoCode对象可以在伪代码编辑器窗口编辑, 这种方式编写比较推荐, 有时候需要两种方式相互配合来编写.

![](https://1547393012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F72JBk3dBKE7NYnndIjp7%2Fuploads%2FqJ5PViuTQSUwIULDC8Cm%2F1.png?alt=media)

![](https://1547393012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F72JBk3dBKE7NYnndIjp7%2Fuploads%2FaXOpDq28lp6t8NbCElL8%2F2.png?alt=media)

### 策划用的伪代码方法文档窗口:

![](https://1547393012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F72JBk3dBKE7NYnndIjp7%2Fuploads%2FEVzgLBrlZ5xsCEYQszJE%2F3.png?alt=media)

**程序员实现伪方法:**

### 伪方法要能够被执行, 还需要程序员在C# 中去实现其执行过程. 例如:

````csharp
```csharp
````

\[PFC\_HelpInfo("获取变量的值",\
typeof(object), "变量的值", *// Return Info*\
typeof(string), "变量的名字"\
)]\
\[PFC\_FindingPath("变量")]\
public static void GetVar(PseudoCode pseudoCode, string funcName, Action\<object> callback, object\[] parameters, object\[] commonParameters) {\
string varName = (string)parameters\[0];

if (pseudoCode.vars.ContainsKey(varName))\
callback?.Invoke(pseudoCode.vars\[varName]);\
else {\
Debug.LogError($"不存在变量：{varName} ---- 调用方法:{funcName}");\
}\
}

\[PFC\_HelpInfo("设置变量的值, 没有变量则新建, 有变量则覆盖已有的值, 并返回变量的值",\
typeof(object), "变量的值", *// Return Info*\
typeof(string), "变量的名字",\
typeof(object), "变量的值"\
)]\
\[PFC\_FindingPath("变量")]\
public static void SetVar(PseudoCode pseudoCode, string funcName, Action\<object> callback, object\[] parameters, object\[] commonParameters) {\
string varName = (string)parameters\[0];\
object varValue = parameters\[1];

pseudoCode.vars\[varName] = varValue;\
callback?.Invoke(varValue);\
}

\`\`\`

所有的伪方法实现都是这种格式:

\[伪方法相关特性] public static void 伪方法名称(PseudoCode pseudoCode, string funcName, Action\<object> callback, object\[] parameters, object\[] commonParameters) {

伪方法获取参数

伪方法过程代码

伪方法返回

}

**伪方法获取参数 :**

伪方法通常有两种参数来源, 一种是伪代码中指定是基本类型的值还是由其他伪代码返回值; 另一种则是由伪代码外调用处传入参数, 这类参数叫做公共域参数, 因为同一个伪代码中所有伪方法**共用**相同的公共域参数. 注意”公共域参数”这个概念很重要, 公共域参数要求不同的两个伪方法往往不能共存于一个伪代码中, 用户使用不对的话, 往往会出现意想不到的运行错误!!!

一个伪方法中, 传入的公共域参数是同一系列, 但由于不同的伪方法使用公共域参数的方式可能不同: 有些伪方法不需要任何的公共域参数, 而有的只用到了传入的一部分公共参数, 那么意味着只要能从公共域参数中不获取或正确获取到自己所需要的参数的伪方法都是可以共存于同一个伪代码中的. 所以**为了提高伪方法的通用性, 往往在实现伪方法前就应该明确规定公共域参数中可以获取到哪些值.**

伪方法参数由 object\[] parameters 传入, 具体传入什么参数就看伪代码怎么写了; 公共域参数则是由 object\[] commonParameters 传入, 公共域参数的内容由调用伪代码处传入.

PseudoCode pseudoCode, string funcName 这两个参数绝大多数伪方法不会用到, 可以暂时忽略.

**伪方法过程代码**

过程代码即伪方法有何作用的体现.

**伪方法返回**

**所有的伪方法都需要在自己的事情处理完后通过调用 callback 返回回去, 无论此伪方法是否具有返回值都必须调用 callback 返回.**

**伪方法相关特性**

* PFClass (同: PseudocodeFuncClass) 被标记的类说明其类中的静态方法可添加到 Pseudocode 系统中去,提供伪方法.
* PFC\_Ignore (同:PseudocodeFuncClass\_Ignore ) 被标记的方法会被Pseudocode 系统忽略, 不提供伪方法.
* PFC\_FindingPath 为伪方法或类指定在选择方法名窗口中的寻找路径, 若伪方法和其所在的类都标记了 PFC\_FindingPath 则优先使用伪方法标记的特性.
* PFC\_CommonParameterField 为伪方法或类指定公共域参数字符串, 若伪方法和其所在的类都标记了 PFC\_CommonParameterField 则优先使用伪方法标记的特性.
* PFC\_HelpInfo 为伪方法指定帮助信息: 伪方法的注释, 返回信息, 各个参数的信息.

注意: **上述所有特性都是可以不使用的, 但如果用得好的话, 可以有效地帮助到伪代码编辑人员.**

### 程序员用的API:

* PseudocodeHelper.AddPseudocodeFuncs 通常使用PFClass 特性为伪代码系统添加伪代码类, 被PFClass 标记过的伪代码类中的伪方法在运行时会被伪代码系统自动添加. 如果不使用PFClass 也可以通过此方法为伪代码系统手动添加伪方法类.
* PseudocodeHelper.SetPseudocodeFuncs 也可以使用此方法一个一个地添加伪方法到伪代码系统中去.
* PseudocodeHelper.Run 伪代码有两种存在形式, 一种是字符串形式存在, 一种是以 PseudoCode 对象的形式存在. 要运行字符串形式的伪代码就需要使用到此方法来运行伪代码. 以 PseudoCode 对象的形式存在的伪代码可以直接调用 PseudoCode.Run 方法来运行.
* PseudocodeHelper.RunCode 如果伪代码是异步的, 可以在协程中等待其执行, 可以使用此方法: yield return PseudocodeHelper.RunCode(...)

### 打包设置:

伪代码实现代码往往不会在c#中有引用, 它的调用都是通过伪代码运行机通过反射来调用运行的. Unity 的 Player 优化设置项 Managed Stripping Level 应该尽量低, 尽可能不要在打包时对代码进行裁剪.

### 调试:

当PseudoCode类型的伪代码运行过后, 会保存最后一次运行使用的公共参数域, 此时的PseudoCode类型的伪代码会多出一个 \[调试运行 (Debug Run)] 按钮, 点击此按钮后能够使用最后一次运行使用的公共参数域再运行一次, 并且在Console中打印出运行结果, 如果报错, 也会在Console 中显示出来.

**注意: 只有在伪代码运行后才会出现 \[调试运行 (Debug Run)] 按钮.**

![](https://1547393012-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F72JBk3dBKE7NYnndIjp7%2Fuploads%2Ff47DGxbtAEWiz8ttgPug%2F4.png?alt=media)
