状态机(State Machine)是一个抽象概念,是一个逻辑严谨的数学抽象。它的这种概念在现实生活中处处都有应用,或者说现实世界就充满状态机。要讨论状态机,就涉及到相关概念,比如:State 状态,Event 事件,Action 动作,Transition 转换。状态机是计算机科学的重要基础概念之一,也可以说是一种总结归纳问题的思想,应用范围非常广泛。[1]
如果要便于理解,那么举灯开关现象的例子是最合适不过的!灯有两种状态:开和关,而状态间的转换是瞬间完成的,由开关动作触发转换,而整个触发转换的过程就是事件,如图:
那么上述就是灯的“状态机”。它描述了灯运行时的情况,有了这个机,那么用程序逻辑就可以很好地解释它,模拟它,不然太过于具象了,很难用逻辑去表示它。
当然状态机还分类别,不同的事物抽象出来的状态机都不尽相同,而且结构都不一样。当然,它也有共通性[2]:
(1)可用“状态”来描述事物,并且任一时刻,事物总是处于一种状态
(2)事物拥有的状态总数是有限的,所以学名成为“有限状态机”(Finite-state machine, FSM)
(3)通过触发事物的某些行为,可以导致事物从一种状态过渡到另一种状态(自动机制)
(4)事物状态变化是有规则的,例如:A状态可以变换到B,B可以变换到C,A却不一定能变换到C
(5)同一种行为,可以将事物从多种状态变成同种状态,但是不能从同种状态变成多种状态
总之,它是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。看一些比较复杂的状态机模型:
<来自百度图片搜索>
笔者才疏学浅,就此对状态机介绍到这,读者感兴趣还想了解可自行查阅相关文档。
既然状态机很重要,那么在Processing的应用中也不可避免地会使用到相关概念和技术。早在2017年笔者就以状态量
概念探讨了在Processing程序中的应用,见下文:
https://blog.csdn.net/fddxsyf123/article/details/62848357 ---Processing 状态量控制动画技巧
当然那没有机(系统)的概念。这次就以最简单的一种形式来初探-----线性的状态切换的情况。
情景设计:
【一个装置有三盏灯组成,依次标为A、B、C。只有一个按钮用于转换装置状态,该装置可通过按钮或者自行的触发器依次循环点亮A、B、C三盏灯。】
我们画一张状态机示例:
代码实现
State类:
class State
{
boolean canChange = true;
int temp = 0;
void Enter() {
temp = 0;
println(this.getClass().getName()+" - Enter !");
}
void Update() {
//println(this.getClass().getName()+" - Update !");
temp ++;
}
void Exit() {
println(this.getClass().getName()+" - Exit !");
}
}
设了Enter
、Undate
、Exit
三个方法模拟状态的进入、执行(保持)、退出的过程,还有一个temp
变量,只是临时为了调试方便设计的,方便查看Update
方法的执行与否。这里其实可以设为抽象类,更科学。
StateMachine类:
class StateMachine
{
State currentState;
State lastState;
StateMachine(State initState)
{
SwitchTo(initState);
}
public void SwitchTo(State newState)
{
if (currentState!=null)
{
this.lastState = currentState;
lastState.Exit();
}
currentState = newState;
currentState.Enter();
}
public void Update()
{
this.currentState.Update();
}
}
存有当前状态和旧时状态,并赋予SwitchTo()
、Update()
两个方法,依次分别是切换状态方法和执行状态方法。
Operator类:
class Operator
{
public StateMachine stateMachine;
private ArrayList<State> states;
private boolean mousePressedThisFrame = false;
boolean ischecked = false;
Operator()
{
states = new ArrayList<State>();
this.states.add(new StageA());
this.states.add(new StageB());
this.states.add(new StageC());
stateMachine = new StateMachine(states.get(0));
}
void NextStage()
{
if (states.size() > 0)
{
State firstStage = states.remove(0);
states.add(firstStage);
stateMachine.SwitchTo(states.get(0));
}
}
void setChecked()
{
ischecked = true;
}
void Update()
{
if ((mousePressed && !mousePressedThisFrame) || ischecked)
{
mousePressedThisFrame = true;
ischecked = false;
if (stateMachine.currentState.canChange)
{
println("try Change To Next Stage.");
this.NextStage();
}
}
this.stateMachine.Update();
if (!mousePressed && mousePressedThisFrame)
{
mousePressedThisFrame = false;
}
}
}
这是操控的类,顾名思义是操作运行创建好的状态机。成员变量有状态链表、状态机等。还有转换下一个状态NextStage()
的具体方法和实时检测刷新的方法Update()
。
Light类:
class Light {
int cc;
PVector pos;
int alpha = 0;
boolean isopen;
Light(int _cc, int posx, int posy)
{
cc = _cc; //固定颜色
isopen = false;//默认关的状态
pos = new PVector(posx, posy);
}
//开关
public void update(boolean b)
{
isopen = b;
if( isopen == false ) //如果关了,亮度归位
alpha = 0;
}
//运行时
public void working()
{
if (isopen)
alpha += 1; //如果开着,慢慢变量
alpha = constrain(alpha, 0, 255);
//println(alpha);
}
public void draw()
{
if (isopen)//如果开,慢慢量
{
fill(cc, alpha);
} else//如果关,黑色的
{
fill(20, 250);
}
push();
translate(pos.x, pos.y);
rect(0, 0, 80, 150);//灯的表现形式
pop();
}
}
下面是三个state实现类:
class StageA extends State
{
StageA()
{
}
void Enter()
{
super.Enter();
light1.update(true);
}
void Update()
{
super.Update();
if (temp == 1)
println(this.getClass().getName()+" - Update !");
light1.working();
}
void Exit() {
println(this.getClass().getName()+" - Exit !");
light1.update(false);
}
}
class StageB extends State
{
StageB()
{
}
void Enter()
{
super.Enter();
light2.update(true);
}
void Update()
{
super.Update();
if (temp == 1)
println(this.getClass().getName()+" - Update !");
light2.working();
}
void Exit() {
println(this.getClass().getName()+" - Exit !");
light2.update(false);
}
}
class StageC extends State
{
StageC()
{
}
void Enter()
{
super.Enter();
light3.update(true);
}
void Update()
{
super.Update();
if (temp == 1)
println(this.getClass().getName()+" - Update !");
light3.working();
}
void Exit() {
println(this.getClass().getName()+" - Exit !");
light3.update(false);
}
}
最后是主程序:
Operator operSystem;
float timePassed;
float nextCheckTime;
float lastCheckTime;
float timeLapse = 1500;
Light light1;
Light light2;
Light light3;
void setup()
{
size(600,500);
timePassed = 0;
lastCheckTime = 0;
nextCheckTime = timeLapse*2; //首次计时长一点
light1 = new Light(color(240,20,20),100,100);
light2 = new Light(color(20,240,20),250,100);
light3 = new Light(color(20,20,240),400,100);
operSystem = new Operator();
}
void draw()
{
timePassed = millis() - lastCheckTime;
operSystem.Update(); //“操作系统” 实时运行
light1.draw();
light2.draw();
light3.draw();
//计时器 - 触发(转换)
if(timePassed > nextCheckTime)
{
//println("Check!");
lastCheckTime = millis();
//nextCheckTime = lastCheckTime + timeLapse;
nextCheckTime = timeLapse;
operSystem.setChecked();
}
}
void mouseClicked()
{
}
void keyPressed()
{
}
类的关系图参考:
总结
这是一个很好的有限状态机入门案例,一种线性的状态切换场景。下回我们来看看进一步的状态机,也就是离散的状态切换,其实有点像学数据结构的知识,那个状态机链表我们可以随意设计并调用。相信读者看到这,对状态机有个初步的认识了,并能在实际开发中尝试使用它,希望一切顺利,感谢您的阅读😘。
参考:
[1] https://zhuanlan.zhihu.com/p/47434856 ---- 什么是状态机
[2] https://www.zhihu.com/question/22363777/answer/652758029 -----什么是状态机?
[3] https://zhuanlan.zhihu.com/p/101020131 一生万物——Processing创意编程碎片