遊戲程式設計模式 命令模式

2022-08-24 10:06:08 字數 2774 閱讀 6184

最近深感**設計對於軟體開發過程中的重要性,所以重新拾起了設計模式,以前學的比較鬆散,理解不夠,這一次本著learning,try,teaching的精神,重新認識和學習設計模式。這一次參考robert nystrom 著的《遊戲程式設計模式》一書,與原先的gof所著的24種設計模式不同,但思想是相通的,讀者若是想對本文的設計模式追根溯源,可自行購買參照。

gof這樣表述命令模式:將乙個請求(request)封裝成乙個物件,從而允許你使用不同的請求、佇列或日誌將客戶端引數化,同時支援請求操作的撤銷和恢復。

其實gof還有乙個更簡單的描述:命令就是物件導向化的**。

對於這兩種描述,相信讀者一開始都會覺得比較的抽象,我們接下來將會舉例說明命令模式的應用場景。

設想我們早期的遊戲機,我們使用手柄作為輸入,手柄上有幾個按鍵,比如「a」,「b」,「c」等,每當我們按下其中乙個按鍵時,遊戲中的角色就會做相應的乙個動作。如果我們要實現這個過程,相信我們很容易寫出這樣的實現**:

void

inputhandler::handlinput()

else

if(ispressed(button_b))

else

//do otherthing

}

這種方式是可以執行的,也可以達到我們的目的,但很明顯,這種硬編碼的風格非常的不靈活,而且如果我們想對按鈕和其對映的行為進行配置的話是無能為力的。這個時候我們就可以使用命令模式了。

在命令模式中,我們首先定義乙個基類來代表命令:

class

command

virtual

void excute() = 0

;};

然後為不同的命令建立子類:

class jumpcommand : public

command

};class firecommand : public

command

};

在輸入處理類中為每乙個按鍵儲存乙個命令指標,然後輸入處理便通過這些指標進行**:

class

inputhandler

void

handleinput()

else

if(ispressed(button_b))

else

}private

: command*button_a;

command*button_b;

};

完成這寫步驟之後,**還不能立刻執行,還需要為inputhandler的左右按鍵配置相應的命令。

inputhandler.setbuttoncommand(button_a,new jumpcammand);

這樣通過為每輸入的處理新增乙個間接呼叫實現了按鍵與命令的解耦,極大的方便了後續關於按鍵處理的修改。這就是命令模式,它的優點是顯而易見的。

但在上述的例子中我們並沒有判斷命令為空的情況,事實上我們可以定義乙個空命令,這個命令不做任何的事情,每乙個按鍵的預設命令就是空命令,這便是空值物件模式,這種模式在很多情況下可以簡化我們的**邏輯。

除此之外,這個例子還有一點不足。通常在遊戲中有很多的角色,相同的型別角色都可以執行相同的命令(這種情況可能沒有想象中的那麼普遍),那在inputhandler如何分辨那個角色執行命令了?我們可以把角色傳入命令中,然後命令使用這個角色來執行對應的指令,比如:

class firecommand : public

command

};

cammand* inputhandler::handleinput()

cammand* cmd =inputhandler.handleinput();

if (cmd !=nullptr)

除了上述的應用場景,我們還可以考慮另乙個應用場景——ai。在遊戲中,我們通常會有非常多的非玩家控制的角色,這些角色的行為都是由ai系統控制的,如果都是用硬編碼的形式來編寫,最後的**會給你帶來地獄般的體驗。這個時候,使用命令模式將帶來極大的便利性。例如ai系統想構建乙個具有侵略性的敵人,那只需要在ai系統中插入一段生成侵略性指令的**即可。ai系統負責生產命令,而命令的執行則由目標角色呼叫。再進一步的思考,命令產生後,角色需要順序執行命令,那就需要乙個佇列來儲存未執行的命令,這種情況就好比乙個命令流,通過命令流我們就是實現了命令生產端和消費端的解耦。

命令模式還有另乙個常用的場景——撤銷和重做。現代社會基本的編輯類應用都會提供這樣的操作(想象一下你在編輯乙個文件,不小心按下刪除按鈕整段內容刪除卻不能撤銷這個操作的情況將是多麼可怕),使用命令模式會非常方便的實現這個功能。修改一下之前的命令類,新增撤銷和重做的方法。

class

command

virtual

void excute(gameactor& actor) = 0

;

virtual

void undo() = 0

;  //重做

};

之後再維護乙個已執行命令和已撤銷命令的棧就能輕鬆實現撤銷和重做功能。

在這裡,我們使用了類來定義命令,主要鑑於c++中閉包支援有限(c++11中閉包需要手動管理記憶體,比較麻煩)。其實命令模式從某些方面看來是某些沒有閉包的語言模擬閉包的乙個方式。在支援閉包的語言中,如js,c#,果斷推薦使用函式來定義命令。

顯而易見,命令模式簡單理解就是命令物件導向化的**。把乙個個行為、請求封裝為乙個乙個命令物件,使得命令的生產和命令的呼叫解耦,避免硬編碼的壞味道。

遊戲程式設計模式 命令模式 Part II

我們剛剛定義的命令類適用於前面的例子,但是它們有很大的侷限性。問題在於,它們假設了存在著jump firegun 等等這樣的隱含地知道如何找到玩家的角色並像操縱傀儡般操縱它的頂層函式。這種假定的耦合限制了這些命令的用途。jumpcommand可以使其跳躍的東西只有玩家。讓我們來放寬這個限制。我們將我...

遊戲程式設計模式 雙緩衝模式

我們先看乙個典型的例子,每個遊戲引擎都要處理的問題 渲染。當引擎渲染出使用者看到的世界時,在同一時間它只渲染一塊 遠處山峰 欺負的丘陵 樹木,這些輪流渲染 假如使用者也逐步的觀察視窗的渲染過程,那麼看到的將是破碎的世界。這是我們不能接受的,場景比如平滑快速的更新,每一幀必須被完整的顯示。如何解決這個...

遊戲程式設計模式 框架 效能 遊戲

恢復內容開始 好的設計意味著當我們做出乙個改動時,就好像整個程式都在期待它一樣。我們可以呼叫少量可選的函式來完美地解決乙個問題,而不會為軟體帶來其他的多餘的 只管寫我們自己的 框架會幫我們收拾一切!關鍵部分 框架意味著變化。衡量乙個設計好壞的方法就是看它應對變化的靈活性。好的改變,是在下乙個人在新增...