跟着元气学编程【20】:状态机与反射
2021/12/07254 浏览综合
上一期几乎是一个月以前了……我也将近一个月没碰Python,连列表的操作都忘了……不过还好接下来有关boss的AI的内容没有太多语法上的难点,只难思路

首先简单地说一下状态机。状态机是表示有有限多个状态在不同条件下相互转换的图。一个对象在同一时间内只会有一个状态,并在当前状态下有指定的行为。状态与状态之间的转换依赖于条件,当特定条件满足后会转换为另一个状态

(完整且最准确的定义,百度百科截图都截不完)
但是我们只需写个简单的状态机,只需要懂基本概念以及工作原理就行了(事实上是因为我就学了这么多),直接头铁开写

首先定义两个枚举,分别为状态机的状态枚举以及条件枚举

(本来不想用Null,想用None更符合,但是Python里面None是关键字,用不了)

状态机是整个状态机系统最重要的部分(我感觉这句话是废话但又不是完全废),整体逻辑比较复杂,这期就先实现状态类的抽象基类
python中的抽象类在我看来有点“奇怪”。想要写一个抽象类,我们需要引入abc模块中的ABCMeta(编写抽象类)和abstractmethod(编写抽象方法)

大致的思路和框架是这样的。在__init__()方法中初始化我们需要的数据

(我决定写注释了,原因后面再说 😐)
里面还出现了一个Init()方法,这个方法用于提供给子类扩展重写。由于所有子类都需要初始化,我们要求所有继承于该类的子类都要重写该方法

(被@abstractmethod标记的方法不被重写就会报错)
既然有了条件列表,那就要有方法往里面放和移除条件。

映射表里装的应该是枚举类型,用于查找条件与状态的对应关系。真正的条件类型会被存储到条件列表中,这样后面遍历所有条件会更方便

(据说真正成熟的代码永远不会报空引用错误。但是我报得最多的错就是空引用😕)
移除就是判断有没有再移除,比添加简单多了

每个状态都有自己独特的行为。父类无法提出共同点,这个时候也适合抽象方法。一些可能会有共同点的方法还是适合虚方法,可以选择重写或不重写

(Python有虚方法……吗?)
假如一个条件成立了,我们需要利用映射表得到这个条件所对应的下一个状态

那么我们怎么知道一个条件成立呢?当然是遍历条件列表,一个一个调用条件基类内部检查自身是否被调用的方法

至此,状态的抽象基类就写完了。看着内容不多也没什么难的语法,但是用了我将近1个小时

其中AddTrigger()方法使用了反射。反射可以理解为动态创建一个对象。相比于用if-elif-else语句一个一个判断传入的条件id再创建条件对象的方法,反射具有更好的代码复用性。后期想要扩展条件也无需更改代码

步骤:1.获取模块名
2.利用模块名导入模块
3.getattr()方法获取模块中指定名称的类的对象
由此可得出文件名和类名的命名都是有规律的,名字写好了事半功倍

本期的测试就不用运行游戏了,需要一个测试类来测试反射能不能正常地跑。但是……这是个抽象基类,意味着我们还要写一个实现类才能调用方法

(先粗略写一个状态实现类进行测试)

(再实例化后传值)

输出没问题。那么这期的任务差不多结束了。下一期写条件抽象基类以及各种条件和状态的实现类。





还有人吗?我好像还没有说决定写注释的原因……之前的我完全没有写注释的习惯,因为我的想法是“不会吧不会吧,不会真的有人连自己写的代码也会忘吧?”

现在我信了。因为学业的关系,这段时间我完全没有看之前的代码。结果就是我现在灵魂三问:
“啊?这是我写的?”
“嗯?这有什么意义吗?”
“草,我在想什么?”

另外不知道因为什么,vs最近好像怪怪的,典型表现就是它现在睁眼说瞎话

(你是怎么在有两个错误的情况下告诉我“未找到相关问题”的??)
我觉得在我Unity学完前这个系列写不完了,正好给我多点时间准备接下来的Unity复刻
