处理事件的一种有效方法可能是观察者模式。我已经将这种模式的变体用于我正在使用的平台游戏库,观察者将订阅某个事件。它对我来说非常好,所以我想我可以分享它。
这是我的基类:
1) BaxEvent订阅者
interface BaxEventSubscriber {
function getNotified(baxEventKey:String):Void;
}
2) BaxEvent
class BaxEvent implements BaxEventSubscriber
{
public var id:String;
/** Event will be fired when all prequisite events are triggered.
* Once a prequisite is satisfied, it will be removed from this list.
* When the list is empty, the event will trigger */
public var prequisiteEvents:Array<String>;
/**
* If serialExecution is true, the prequisites must be executed in a serialised order (first to last)
* In other words, every prequisite will be satisfied only if it is first in the prequisited list
*
* If serial execution is false (default, the prequisites can be satisfied in any order)
*/
public var serialExecution : Bool = false;
public function new (id:String)
{
if (id == null) {
throw "any Event should have an id";
}
this.id = id;
prequisiteEvents = new Array<String>();
}
public function addPrequisite(key:String):Void {
if (key == null || key.length == 0) {
return;
}
var isUnique:Bool = true;
for (i in 0...prequisiteEvents.length) {
var ss:String = prequisiteEvents[i];
if (ss == key) {
isUnique = false;
}
}
if (isUnique) {
prequisiteEvents.push(key);
BaxEventManager.getManager().addSubscriberForEvent(this, key);
}
}
public function getNotified(baxEventKey:String):Void {
this.eventTriggered(baxEventKey);
}
public function eventTriggered(key:String):Void {
var isPrequisite :Bool = false ;
for ( i in 0...prequisiteEvents.length) {
var ss:String = prequisiteEvents[i];
if (ss == key) {
isPrequisite = true;
}
}
if (isPrequisite) {
var ind:Int = prequisiteEvents.indexOf(key);
// If no serialExecution, checkout this event.
// Else, check it out only if it's the first on the prequisites list
if (! serialExecution || ind == 0 ) {
prequisiteEvents.splice(ind, 1);
}
}
// if all prequisites are satisfied, fite the Event
if (prequisiteEvents.length == 0) {
BaxEventManager.getManager().broadcastEvent(this.id);
}
}
}
3) BaxEventManager
class BaxEventManager
{
/** the names of registered Events */
public var events:Array<BaxEvent>;
/**
* Objects that need to be notified for a certain event
*
* structure:
* [key] = event name (as in events[])
* subscribers : Array<BaxEventSubscriber> : an array of subcribers for this Event
*/
public var eventSubscribers : StringMap<Array<BaxEventSubscriber>>;
private static var eventManager:BaxEventManager;
public static inline function getManager():BaxEventManager {
if (eventManager == null)
return eventManager = new BaxEventManager();
else
return eventManager;
}
private function new() {
trace(" BaxEventManager CONSTRUCT");
events = new Array<BaxEvent>();
eventSubscribers = new StringMap<Array<BaxEventSubscriber>>();
}
/**
* Registers an event's key, and an array with Subscribers for it. FailSafe, as it checks if event already exists
* @param key
*/
public function addEvent(key : String):Void {
//trace("check to add key :"+key);
var alreadyExists :Bool = false;
for ( i in 0...events.length) {
var ss :String = events[i].id;
if ( ss == key ) {
//trace("key " + key + " exists");
alreadyExists = true;
break;
}
}
if (!alreadyExists) {
events.push( new BaxEvent(key) );
var subscribers : Array<BaxEventSubscriber> = new Array<BaxEventSubscriber>();
eventSubscribers.set(key , subscribers);
}
}
public function addSubscriberForEvent(subscriber:BaxEventSubscriber, eventKey:String):Void {
this.addEvent(eventKey);
var subscribers :Array<BaxEventSubscriber> = eventSubscribers.get(eventKey);
subscribers.push(subscriber);
}
public function broadcastEvent(evt:String) : Void {
if (evt == null) {
return;
}
var subscribers :Array<BaxEventSubscriber> = eventSubscribers.get(evt);
if (subscribers != null && subscribers.length > 0) {
for ( i in 0...subscribers.length) {
var sub:BaxEventSubscriber = subscribers[i];
sub.getNotified(evt);
}
}
}
/**
* Clears up the registered Events and their subscribers.
* Make sure to Call this when resseting a level, as the subscribers might be nullified
*/
public function clearEvents():Void {
ArrayUtils.emptyArray(events);
eventSubscribers = null;
eventSubscribers = new StringMap<Array<BaxEventSubscriber>>();
}
}
在我的实际实现中,我还使用了不同类型的事件,这些事件定义了事件是否应该在每个游戏会话或关卡中注册(和触发)一次,或者将被无限触发。现在我正在考虑它,我应该将它包含在 BaxEvent 类中。
不管怎样,关键是有了这个设计
- 您可以更好地控制触发的事件,并且更容易跟踪已触发的事件
- 您不必遍历所有事件或订阅者的更新函数
- 它是可扩展的,您可以将它用于不同类型的事件,如关卡事件(收集所有硬币)、步行事件、对话完成、开关和交互式对象等
事件管理器是一个单例类,尽管您也可以将其设计为静态实用程序类。
BaxEvent 实现事件订阅者的唯一原因是为了支持复杂的事件,这样一旦它们的先决条件(其他事件)得到满足,它们就会触发。但是,如果这对您来说太过分了,您可以删除此功能并简化您的代码。