Как "практично" кодировать игровые события HaxeFlixel?
И под "практическим способом" я подразумеваю так, чтобы это не мешало скорости игры.
Я программирую RPG, и, как многие из вас знают, они ПОЛНЫЕ события:
Вы входите в новую комнату, где происходит событие
какой-то разговор происходит, чтобы развить историю между символами и NPC
но затем, когда вы входите в эту комнату, ничего не происходит.
Я написал свою игру так, чтобы каждый раз, когда карта меняется (скажем, на карту 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, заключается в поддержке сложных событий, так что они будут срабатывать, как только их предпосылки (другие события) будут выполнены. Но если это для вас излишне, вы можете удалить эту функциональность и упростить свой код.