?? 游戲中的事件機制.txt
字號:
游戲中的事件機制
上一篇 / 下一篇 2007-11-14 19:25:45
查看( 32 ) / 評論( 0 ) / 評分( 0 / 0 )
【轉載】 作者:Jack H Hansen
事件機制在很多高級程序設計語言中都有支持。譬如VB、C#(delegate)、C++Builder(并不屬于C++的范疇。C++Builder中的事件處理器必須用關鍵字closure<閉包>修飾)等等,甚至在HTML中也可以見到它的身影。事件機制的引入使軟件系統變得更加易于理解——它使一種語言(平臺)更加接近于這個世界的真相。事情的發展變得像現實世界中那樣順理成章。某一事件的產生引發了一系列其他事件的產生,這些事件要么是結果要么又會引發一系列事件的產生……如此這般,信息才得以在事件的新陳代謝中延續,世界才得以向前發展。在某些游戲設計過程中的一項重要任務就是模擬現實世界的某些特征,以期實現機器與用戶的更加親密的溝通。事件機制就是很好的一例。我們需要事件來使我們的系統更加人性化。
我想,在我繼續進行下面對討論之前,先簡單介紹一下“事件”這個東東。
1. 游戲中的事件機制
聯系是普遍存在的。事事有聯系、時時有聯系,整個世界是一個相互聯系的統一整體。一個人的行為、物的狀態的改變或事的進展過程的某一階段可以引發一個事件。一個事件的發生或許會引發另外的事件——通過人的感知、大腦的反映,然后作出決策,付諸行動——也或許,就這么蒸發掉,無人知曉。但無論如何,在這一過程中,我們總能抽象出一些實質性的東西來。就像下面的圖示:
在游戲中:
事件源——表示任何可以引發事件的對象。譬如,一個“人”、“坦克”、“建筑物”、“地面”。
事件——表示任何可以處理的事件。譬如,“感冒”、“射擊”、“倒塌”、“有對象經過”。
響應者——表示任何對某事件感興趣的對象。
響應器——表示對某事件感興趣的對象對某一確定事件作出的反應。
特別的,對于過程:
通知——發生在事件與響應者之間。我們把它分為兩種方式:有限聽眾式、廣播式。對事件感興趣的對象(響應者)只有確定的有限個(只有一個的情況下,可以叫做點對點式)的情況就是有限聽眾式。而對于廣播式,事件并不知道會有哪些(個)對象對自己感興趣。它向所有可以接收事件通知的對象廣播事件。
觸發——響應者發現自己對特定事件需要做出相應的行動時就會觸發事件處理器,并同時傳遞需要的事件信息給它。對于響應者,它也可以選擇沉默——自己了解事件但并不作出行動。因此這個過程的決定權在響應者手上。
2. 萬事之鼻祖 Event
我們需要一個類來表示所有事件的普遍性質。
public class Event {
// 屬性
public string Name { get;set; }// 獲取或設置事件的名稱
public string Message { get;set; }// 獲取或設置事件的簡單描述
EventTypes EventType { get;set; }// 獲取或設置事件類型(枚舉EventTypes)
ListenerCollection Listeners { get; } // 獲取響應者的集合
public bool PoolEvent { get;set; }// 獲取或設置事件的簡單描述
// 方法
void RaiseEvent(); // 通知響應者事件的發生
void AbandonListener( int index ); // 拋棄一個事件響應者,并把它從 Listeners 中移除。
void AbandonListener(); // 拋棄所有的事件響應者
}
3. 枚舉類型 EventTypes
這個枚舉類型指示事件通知過程的類型:有限聽眾式、廣播式。
public enum EventTypes {
LimitedListener ,
Broadcast
}
4. 響應者接口 IListener
該接口只有唯一的方法 EventArrived() 。事件發生時會調用這個方法并傳遞相關參數。這個參數必須是 EventArgs 或由它派生而來。
public interface IListener {
// 通知一個響應者事件的到達。
void EventArrived( EventArgs args );
}
5. EventPool
一個事件池。當且僅當需要事件廣播時我們才需要它。需要注意的是 AddEvent 方法。它把一個事件添加到池中,第二個參數指定是否將該事件已經指定的響應者亦添加到廣播的響應者中。事件添加后,其 Event::EventType 屬性會被設置為 EventTypes.Broadcast。
public class EventPool {
// 屬性
public ArrayList Events { get; }// 獲取池中所有的事件的集合
public ListnerCollection Listners { get; }// 獲取池中所有的響應者的集合
// 方法
void AddEvent( Event obj ,bool copyListners ); // 添加一個事件并把它作為廣播式事件
void RemoveEventAt( int index ); // 將一個事件從列表中移除
void RemoveEvent( Event listener ); // 將一個事件從列表中移除
void Broadcast( Event event ); // 向列表中的所有響應者廣播指定事件(可以是非池中的事件)
void BroadcastItemAt( int index ); // 向列表中的所有響應者廣播池中的指定事件
}
6. EventArgs
public class EventArgs {
public Event Event { get; } // 獲取傳遞這個參數的事件
public object Sender { get; } // 獲取事件源
}
7. UML Diagram
8. 響應者行為
響應者實現 IListener 接口后就可以響應事件了。在 EventArrived() 方法中,你可以直接處理事件,抑或是調用其它的事件處理器(響應器)。C#中有很好的解決方案——委托——替代函數指針的最有效的方法。在C++中也可以用虛擬函數表來模擬委托機制??傊?,在響應器上的解決方案是很靈活的。在實際開發中,可以根據不同的環境做出不同的選擇。
9. 擴展機制
在一個游戲中,除了已經定義好的事件外,其劇情或功能可能會要求玩家自行定義一些事件。這就需要一種可擴展的方案。我們引入了 CustomEvent 類——繼承自 Event,以及 Condition 類。
public class CustomEvent : Event {
public CustomEvent( Condition condition ) {
_Condition = condition;
}
public Condition TestCondition { get{ return _Condition; } }
Condition _Condition = null;
}
public abstract class Condition {
public Condition() {}
bool abstract Test();
}
初始化一個 CustomEvent 類時必須同時傳入一個 Condition 類。Condition 類必須被繼承。Test()方法在適當的時候被調用以檢測是否可以引發這個事件。
10. 后記
以上談到的只是一個簡單的模型,是否實用還要等待實踐的檢驗。歡迎讀者的批評與建議。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -