狀態機(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創意程式設計碎片