Как "практично" кодировать игровые события HaxeFlixel?

И под "практическим способом" я подразумеваю так, чтобы это не мешало скорости игры.

Я программирую RPG, и, как многие из вас знают, они ПОЛНЫЕ события:

  1. Вы входите в новую комнату, где происходит событие

  2. какой-то разговор происходит, чтобы развить историю между символами и NPC

  3. но затем, когда вы входите в эту комнату, ничего не происходит.

Я написал свою игру так, чтобы каждый раз, когда карта меняется (скажем, на карту 6), она проверяет в классе EventReg.hx массив Bool, если значение (в данном случае 6) равно true, и копируется в Переменная eventAvailable в PlayState.hx.

Затем в функции обновления он запускает событие в соответствии с номером карты, но я чувствую, что этот подход потребовал бы огромного количества кода, ограниченного в 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
    }
}

1 ответ

Эффективным способом обработки событий может быть что-то вроде шаблона наблюдателя. Я использовал вариант этого паттерна для игровой платформы, в которой я работаю, где наблюдатель подпишется на определенное событие. Это работает довольно хорошо для меня, поэтому я подумал, что смогу поделиться этим.

Вот мои базовые классы:

1) BaxEventSubscriber

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 реализует подписчика Event, заключается в поддержке сложных событий, так что они будут срабатывать, как только их предпосылки (другие события) будут выполнены. Но если это для вас излишне, вы можете удалить эту функциональность и упростить свой код.

Другие вопросы по тегам