2

我所说的“实用方式”是指它不会妨碍游戏的速度。

我正在编写一个 RPG,正如你们许多人所知,它们充满了事件:

  1. 你走进一个触发事件的新房间

  2. 一些谈话恰好发展了chars和npc之间的故事

  3. 但是当你重新进入那个房间时,什么也没有发生。

我对我的游戏进行了编码,以便每次地图更改(假设它转到地图 6)时,它会在 EventReg.hx 类中检查 Bool 数组,如果值(在本例中为 6)为真,并将其复制到PlayState.hx 中的变量eventAvailable

然后在更新函数中,它根据地图编号运行事件,但我觉得这种方法需要大量代码以开关的形式挤在 PlayState.hx 中(以当前地图的值作为参考)。

有谁知道一些处理这个的开源 RPG、示例或教程?蒂亚!

例子:

//Event Helper
private var eventAvailable:Bool;
private var currentMap:Int = MapReg.currentMap;

override public function create():Void
{
    //Checks if there's an even in the current map.
    eventAvailable = EventReg.isEvent[currentMap];
    if (eventAvailable)
        setupEvent();
}

private function setupEvent():Void
{
    switch(currentMap)
    {
        case 0: //code
        case 1: //code
    }
}
4

1 回答 1

0

处理事件的一种有效方法可能是观察者模式。我已经将这种模式的变体用于我正在使用的平台游戏库,观察者将订阅某个事件。它对我来说非常好,所以我想我可以分享它。

这是我的基类:

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 实现事件订阅者的唯一原因是为了支持复杂的事件,这样一旦它们的先决条件(其他事件)得到满足,它们就会触发。但是,如果这对您来说太过分了,您可以删除此功能并简化您的代码。

于 2016-04-05T19:08:10.287 回答