Переделал менеджер событий EventManager ver2, исправлены некоторые ошибки(отложенная отправка проваливалась если на событие не было подписчиков).
Приемущества от предыдущей версии:
- Весь код засунут в конструктор
- отсюда, возможно создать не ограниченное количество менеджеров, например для Gui, System, GameEvents и других назначений.
Минусы:
- отладка!, если что то пошло не так, отладить или отлавливать логическую ошибку в МОРЕ событий это просто АД. В код я засунул по возможности где можно выводы в лог отладчика, это хоть как то да помогает найти проблемные места.
//Site: MidaDev.ru
//данные будут хранится в виде структуры
enum evum
{
_id =0, //айди объекта
_func =1, //функция которую он просит у себя вызвать
_name_object =2, //"тип" объекта, используется в основном для отладки когда включен debug
_name_is =3 //имя объекта, используется в основном для отладки когда включен debug
}
#macro EMMACRO "EventManager(" + my_name_is +"): "
//*******************************************************************
function Events(_name) constructor
{
#region [Constructor]
my_name_is = _name; //имя, назначение класса
events_array = {}; //глобальная структура событий
debug = true; //вкл режим отладки, в структуру помещается доп инфа об объекте
obj_alarm = noone; //объект, используется как alarm для отсчёта шагов при отложенной отправке писем
queue_index=-1; //индекс объекта на котором остановилась отправка отложки
queue_array=undefined; //массив выделенных подписчиков по определённому EVENTу
//массивы/пул сообщений, которые отправляются подписчикам строго по очереди
pool_event_array=[]; pool_data_array=[];
_log("=================================================================================");
trace("Сосздан глобальный Менеджер событий типа = ", my_name_is);
_log("=================================================================================");
#endregion
#region [Methods]
//*******************************************************************
//МЕТОДЫ
//*******************************************************************
static trace_debug = function(a="",b="",c="",d="",e="",f="",g=""){ if debug then trace(EMMACRO, a,b,c,d,e,f,g);};
static get_my_name_is = function(){ try{ return other.my_name_is}catch(m){ return ""} };
static get_obj_name = function(){ return object_get_name(other.object_index); };
static get_obj_full_name = function(){ return get_obj_name() + "(" + get_my_name_is() + ")"; };
//*******************************************************************
static get_all_info_events_array = function(_arr) { var _str=""; for(i=0; i < (array_length(_arr)); i++){_str+= "[" + _arr[i][evum._name_object] + "(" + _arr[i][evum._name_is] + ")] " ;} return _str; }
//*******************************************************************
//подписка на событие, т.е. добавить объект/подписчика в массив событий
static subscribe = function(_event, _func)
{ if _event == "" then{ show_message(EMMACRO + "subscribe() - Попытка подписаться на пустой эвент = ``"); return;};
//если такого эвента еще не создано, то создаём ехо
if (is_undefined(events_array[$ _event])) events_array[$ _event] = [];
//если он уже подписан, то выходим из функции
else if (is_subscribe(other.id, _event) != -1) then{show_message(EMMACRO + "subscribe() - попытка подписаться дважды на одно и тоже событие"); return;}
trace_debug("Объект=", get_obj_full_name(), ", подписался на событие=", _event);
array_push(events_array[$ _event], [other.id, _func, get_obj_name(), get_my_name_is() ]); //добавляем в массив нашего слушателя
}
//*******************************************************************
//проверка не подписан ли уже объект на данное событие
static is_subscribe = function(_id, _event)
{ //бежим по всему списку эвентов
for (var i = 0; i < array_length(events_array[$ _event]); i += 1)
if (events_array[$ _event][i][evum._id] == _id) then return i; //есть такой, возвращаем его индекс
//нету такого, возвращаем отрицательный ответ
return -1;
}
//*******************************************************************
//отправка сообщения одному слушателю
//внутренняя функция, не для вызова из вне
static publishing = function(_index, _array, _data)
{ trace_debug("============================publishing непосредственно отправка==========================");
//если объект существует то отправляем ему сообщение
if (instance_exists(_array[_index][evum._id])) then { _array[_index][evum._func](_data); return true; }
else //иначе удаляем этого товарища из массива к едрени
{ trace_debug(" объедок выбыл, удаляем = ", _array[_index][evum._name_object], " , ", _array[_index][evum._id], " , ", _array[_index][evum._name_is]);
array_delete(_array, _index, 1);
return false;
}
}
//*******************************************************************
//отправляется сообщение всем подписанным на него объектам/подписчикам
static publish = function(_event, _data)
{ trace_debug("============================publish==============================");
trace_debug("Объект=", get_obj_full_name(), ", шлёт событие=", _event, " с данными=", _data);
//получаем массив подписчиков на данный EVENT
var _listens_array = events_array[$ _event];
//если слушателей нет то выходим
if (is_undefined(_listens_array)) then {trace_debug("слушателей нету"); return;}
//пробегаемся по массиву слушателей, двигаемся обязательно с конца к началу
for (var i = (array_length(_listens_array) - 1); i >= 0; i -= 1)
{ trace_debug(" отправляем событие: ", _event, " , подписчику: ", _listens_array[i][evum._name_object], "(", _listens_array[i][evum._name_is], ")", "данные: ", _data);
publishing(i, _listens_array, _data);
}
}
//*******************************************************************
//инициализация необходимых переменных для отложенной отправки alarm[0]
static queue_initial = function()
{ trace_debug("============================queue_initial alarm[0]=======================");
trace_debug("Ожидающих событий:", array_length(pool_event_array), " , очередь событий: ", array_to_string(pool_event_array) );
_event = array_get(pool_event_array, 0);
trace_debug(" будем отправлять событие: ", _event,", с данными=", array_get(pool_data_array, 0) );
//получаем массив подписчиков на данный EVENT
queue_array = events_array[$ _event];
//если слушателей нет в априоре вообще, то выходим
if (is_undefined(queue_array)) then
{ trace_debug("Слушателей почемуто тютю");
return false;}
//получаем количество подписчиков
queue_index = array_length(queue_array) - 1;
trace_debug("Количество слушателей: ", queue_index+1, " список: ", get_all_info_events_array(queue_array));
return true;
}
//*******************************************************************
//выполняется ОТЛОЖЕННАЯ отправка через таймер(объекта), сообщения всем объектам/подписчикам
static queue_publish = function(_event, _data)
{ trace_debug("============================queue_publish==============================");
trace_debug("Обект = ", get_obj_full_name(), " , шлёт QUEUE событие=", _event, " с данными=", _data);
//если индекс объекта в массиве = -1 значит это первая инициализация работы отложки
if queue_index <= -1 then
{ //создание вспомогательного объекта если его еще нет
if instance_exists(obj_alarm)==false then {obj_alarm = instance_create_depth(0,0,0,obj_EM2); obj_alarm.eventa = self;}
obj_alarm.alarm[0] = 1; //включаем таймер #0(иниц переменных), т.е., ТОЛЬКО на следующем шаге начнётся отложенная отправка
}
//сохраняем данные отправки(событие, данные), будут использоваться в функции отложенной отправки
array_push(pool_event_array, _event); array_push(pool_data_array, _data);
trace_debug("Итого уже отложенных событий:", array_length(pool_event_array), " , события в массиве: ", array_to_string(pool_event_array) );
}
//*******************************************************************
//вызывается из объекта из таймера[1] каждый шаг
//ОТЛОЖЕННАЯ отправка сообщения всем объектам/подписчикам
//в массиве queque_array уже содержатся все отобранные слушатели для нужного события
//******************************************************************* alarm[1]
static queue_step = function()
{ trace_debug("============================queue_step alarm[1]=========================");
//делаем первую посылку
//проверяем кооректность отправки посылки объету, если всё норм то брэкуем цикл while
while(queue_index >= 0)
{
if publishing(queue_index--, queue_array, array_get(pool_data_array,0)) then break;
/*var _r= publishing(queue_index--, queue_array, array_get(pool_data_array,0));
if _r then trace_debug("отправка состоялась");
if !_r then trace_debug("отправка НЕсостоялась");
if (_r==true) then break;*/
}
obj_alarm.alarm[1] = 1;
if queue_index <= -1 then //если список подписчиков для отправки закончился
{ array_pool_delete(0); //очищаем (верний/этот) эвент
queue_array = undefined; //чистим массив объектов
obj_alarm.alarm[1] = -1; //отключаем таймер
//если массив/пул/очередь сообщений еще не пуст, значит есть еще сообщения для отправки
if array_length(pool_event_array) >= 1 then
{
obj_alarm.alarm[1] = 1; //запускаем таймер и начинаем по новой но уже для следующего EVENTа
//цикл если queue_initial()=false
while(!queue_initial(array_get(pool_event_array,0)))
{ //видимо нет желающих получать сообщения(нет подписчиков)
array_pool_delete(0);
//если так неудачно весь массив пула испробовали, то, выкл таймер и брекуем из цикла while
if array_length(pool_event_array) <=0 then { obj_alarm.alarm[1] = -1;break; }
//иначе идём опять на цикл к следующему элементу из пула
}
}
}
}
//*******************************************************************
//очистка пула по передаваемому индексу
static array_pool_delete = function(_i=0){ array_delete(pool_data_array, _i, 1); array_delete(pool_event_array, _i, 1); };
//*******************************************************************
//отписать объект/подписчик по его айди от события
static unsubscribe_id = function(_id, _event)
{ if (is_undefined(events_array[$ _event])) return; //если такого эвента не существует то выходим
var _index = is_subscribe(_id, _event); //получаем позицию объекта в массиве
if (_index != -1) then array_delete(events_array[$ _event], _index, 1); //удаляем его если нашелся
}
//*******************************************************************
//отписать объект/подписчик от всех событий
static unsubscribe_all_id = function(_id=id)
{ var _keys_array = variable_struct_get_names(events_array); //получаем все значения имён массива
for (var i = (array_length(_keys_array) - 1); i >= 0; i -= 1) unsubscribe_id(_id, _keys_array[i]);
}
//*******************************************************************
//удаляем все позиции/подписчиков с данным эвентом
static unsubscribe_event = function(_event) { if (variable_struct_exists(events_array, _event)) then variable_struct_remove(events_array, _event); }
//*******************************************************************
//удаляем полностью массив сообщений и подписчиков
static events_remove_all_events = function(){ delete events_array; events_array = {}; }
//*******************************************************************
//очистка массива от не существующих объектов/подписчиков
static unsubscribe_dead_instances = function()
{ var _keys_array = variable_struct_get_names(events_array);
for (var i = 0; i < array_length(_keys_array); i += 1)
{ var _keys_array_subs = events_array[$ _keys_array[i]];
for (var j = (array_length(_keys_array_subs) - 1); j >= 0; j -= 1) if (!instance_exists(_keys_array_subs[j][0])) array_delete(events_array[$ _keys_array[i]], j, 1);
}
}
#endregion
//конец батьки
}
//*******************************************************************
globalvar EVENTGUI;
globalvar EVENTDATA;
EVENTGUI = new Events("GuiEvents");
EVENTDATA= new Events("DataEvents");
//****************************************************************
В конце скрипта создаются две глобальных переменных на различные события.
В событии создания объектов можно выполнить подписку на нужные события:
/// @description test01
//Site: MidaDev.ru
//Author: Dimusikus
EVENTGUI.subscribe("Click", function(_data)
{
if is_array(_data) then
{
trace("получил click = [0] ", _data[0]);
trace("получил click = [1] ", _data[1]);
}
else trace("получил click = ", _data);
});
EVENTDATA.subscribe("PUT", function(_data)
{
trace("получил PUT = ", _data);
});
Любой объект может отправить событие в менеджер командами:
//прямая/сразу отправка сообщения
EVENTGUI.publish("Click", "mouse2");
//отложенная отправка, т.е. на следующих шагах игры
EVENTGUI.queue_publish("Click", "mouse2");