Следующая улучшенная версия менеджера событий ver1 подошла к выходу.
1. Решена проблема функциональной атомарности.
2. Вызывать события можно прямо из метода Create любого объекта независимо от его позиции в иерархии создания объектов в комнате. Это не маловажный нюанс, т.к. не обязательно создавать менеджер событий самым первым в комнате, тем самым повышается гибкость использования скрипта. Но вызов события должен быть обязательно "отложенный" т.е. events_queque_publish
Пример:
//метод Create
events_listen("kuku", function(_data)
{
trace("получил данные = ", _data);
});
events_queque_publish("kuku", "отправка всем данных");
Данное сообщение получат все объекты кто подписался на событие kuku
//*******************************************************************
//Site: MidaDev.ru
//Author: Dimusikus
//EMail: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в вашем браузере должен быть включен Javascript.
//*******************************************************************
globalvar events_debug; //вкл режим отладки, в структуру помещается доп инфа об объекте
events_debug = true;
globalvar events_array; //глобальная структура событий
events_array = {}; //создаём структуру
globalvar events_manager_obj; //объект, используется для alarm что бы отправлять отложенные сообщения подписчикам
events_manager_obj = undefined; //
//индекс объекта на котором остановилась отправка отложки
globalvar _events_queque_index;
_events_queque_index=-1;
//массив выделенных подписчиков по определённому сообщению
globalvar _events_queque_listens_array;
_events_queque_listens_array=undefined;
//массив/пул сообщений, которые отправляются подписчикам строго по очереди
globalvar _events_pool_event_array;
_events_pool_event_array=[];
//массив/пул передаваемых данных, которые отправляются подписчикам
globalvar _events_pool_data_array;
_events_pool_data_array=[];
//*******************************************************************
//данные будут хранится в виде структуры
enum evenum
{
_id = 0, //айди объекта
_func = 1, //функция которую он просит у себя вызвать
_name_object =2 //имя объекта, используется в основном для отладки
}
//*******************************************************************
//подписка на событие, т.е. добавить объект/подписчика в массив событий
function events_listen(_event, _func)
{
//если такого эвента еще не создано
if (is_undefined(events_array[$ _event]))
{ //то создаём его
events_array[$ _event] = [];
}
else
//если уже подписан то выходим
if (events_is_listen(id, _event) != -1) then return;
var _str="";
if events_debug then _str = object_get_name(object_index);
//добавляем в массив нашего слушателя
array_push(events_array[$ _event], [id, _func, _str ]);
}
//*******************************************************************
//проверка не подписан ли уже объект на данное событие
function events_is_listen(_id, _event)
{
//бежим по всему списку эвентов
for (var i = 0; i < array_length(events_array[$ _event]); i += 1)
{
//если таковой есть то возвращаем его индекс
if (events_array[$ _event][i][evenum._id] == _id) then return i; //есть такой, возвращаем его индекс
}
//нету такого, возвращаем отрицательный ответ
return -1;
}
//*******************************************************************
//отправка сообщения одному слушателю
//внутренняя функция, не для внешнего вызова
function events_publishing(_index, _array, _data)
{
//если объект существует то отправляем ему сообщение
if (instance_exists(_array[_index][evenum._id])) then
{
_array[_index][evenum._func](_data);
return true;
}
//иначе удаляем этого товарища из массива к едрени
else
{
if events_debug then trace("EVENTMANAGER: объедок выбыл, удаляем = ", _array[_index][evenum._name_object], "_", _array[_index][evenum._id]);
array_delete(_array, _index, 1);
return false;
}
}
//*******************************************************************
//отправляется сообщение всем подписанным на него объектам/подписчикам
function events_publish(_event, _data)
{
//получаем массив подписчиков на данный EVENT
var _listens_array = events_array[$ _event];
//если слушателей нет то выходим
if (is_undefined(_listens_array)) then return;
//пробегаемся по массиву слушателей, двигаемся обязательно с конца к началу
//что бы по ходу "пьессы" удалять не существующих уже слушателей из массива
//и при удалении(массив сдвигается) не пропустить слушателей
for (var i = (array_length(_listens_array) - 1); i >= 0; i -= 1)
{
events_publishing(i, _listens_array, _data)
}
}
//*******************************************************************
//инициализация необходимых переменных для отложенной отправки
function events_queque_initial(_event)
{
//получаем массив подписчиков на данный EVENT
_events_queque_listens_array = events_array[$ _event];
//если слушателей нет в априоре вообще, то выходим
if (is_undefined(_events_queque_listens_array)) then return false;
//получаем количество подписчиков
_events_queque_index = array_length(_events_queque_listens_array) - 1;
return true;
}
//*******************************************************************
//выполняется ОТЛОЖЕННАЯ отправка через таймер(объекта), сообщения всем объектам/подписчикам
function events_queque_publish(_event, _data)
{
//если индекс объекта в массиве = -1 значит это первая инициализация работы отложки
if _events_queque_index <= -1 then
{
//if instance_exists(events_manager_obj)==false then events_manager_obj = instance_create_depth(0,0,0,obj_event_manager);
if is_undefined(events_manager_obj)==true then events_manager_obj = instance_create_depth(0,0,0,obj_event_manager);
//инициализация необходимых переменных для отложенной отправки
//if events_queque_initial(_event) == false then return;
//сохраняем данные отправки(событие, данные), будут использоваться в функции отложенной отправки
array_push(_events_pool_event_array, _event);
array_push(_events_pool_data_array, _data);
//устарело, неактуально
//запускаем начало первой отложенной отправки НЕМЕДЛЕННО
//events_queque_step();
//включаем таймер #1(иниц переменных), т.е., ТОЛЬКО на следующем шаге начнётся отложенная отправка
events_manager_obj.alarm[1] = 1;
}
else
//ага, зафиксирована попытка отправить еще сообщения когда предыдущий сеанс отправки еще не завершен
//помещаем данные в пул, в очередь, пусть ждут своего "часа"
{
array_push(_events_pool_event_array, _event);
array_push(_events_pool_data_array, _data);
}
}
//*******************************************************************
//вызывается из объекта из таймера каждый шаг
//ОТЛОЖЕННАЯ отправка сообщения всем объектам/подписчикам
//в массиве _events_queque_listens_array уже содержатся все отобранные слушатели для нужного события
function events_queque_step()
{
//делаем первую посылку
do
{ //заведомо заранее делаем минусовку индекса
_events_queque_index -= 1;
//проверяем кооректность отправки посылки объету, если всё норм то брэкуем цикл DO
if (events_publishing(_events_queque_index+1, _events_queque_listens_array, array_get(_events_pool_data_array,0))==true) then break;
//нет не всё номано, видимо объект исчес/уничт,
//проверяем индекс в массиве и если мы еще не достигли конца то повторяем посыл уже для следующего объекта в массиве
//до тех пор пока отправка не завершится удачно
}until (_events_queque_index <= -1)
//уменьшаем индекс
//_events_queque_index -= 1;
//запускаем алярм в объекте, который будет вызывать эту же функцию events_queque_publish каждый step шаг
events_manager_obj.alarm[0] = 1;
//если закончили отправлять сообщения
if _events_queque_index <= -1 then
{ //очищаем первое знчение в массиве
events_array_pool_delete(0);
//чистим массив объектов
_events_queque_listens_array = undefined;
//отключаем таймер
events_manager_obj.alarm[0] = -1;
//если массив/пул сообщений еще не пуст, значит есть еще сообщения для отправки
if array_length(_events_pool_event_array) >= 1 then
{
if events_queque_initial(array_get(_events_pool_event_array,0)) == false then
{//нету желающих получать сообщения
events_array_pool_delete(0);
return;
}
//запускаем таймер и начинаем по новой но уже для следующего EVENTа
events_manager_obj.alarm[0] = 1;
}
}
}
//*******************************************************************
//очистка пула по передаваемому индексу
function events_array_pool_delete(_i=0)
{
array_delete(_events_pool_data_array, _i, 1);
array_delete(_events_pool_event_array, _i, 1);
}
//*******************************************************************
//отписать объект/подписчик по его айди от события
function events_unlisten_id(_id, _event)
{
//если такого эвента не существует то выходим
if (is_undefined(events_array[$ _event])) return;
//получаем позицию объекта в массиве
var _index = events_is_listen(_id, _event);
//удаляем его если нашелся
if (_index != -1) then array_delete(events_array[$ _event], _index, 1);
}
//*******************************************************************
//отписать текущий объект/подписчик от события
function events_unlisten(_event) { events_unlisten_id(id, _event); }
//*******************************************************************
//отписать объект/подписчик от всех событий
function events_unlisten_all_id(_id=id)
{
//получаем все значения имён массива
var _keys_array = variable_struct_get_names(events_array);
for (var i = (array_length(_keys_array) - 1); i >= 0; i -= 1)
{
events_unlisten_id(_id, _keys_array[i]);
}
}
//*******************************************************************
//удаляем все позиции/подписчиков с данным эвентом
function events_remove_event(_event)
{
if (variable_struct_exists(events_array, _event)) then variable_struct_remove(events_array, _event);
}
//*******************************************************************
//удаляем полностью массив сообщений и подписчиков
function events_remove_all_events()
{
delete events_array;
events_array = {};
}
//*******************************************************************
//очистка массива от не существующих объектов/подписчиков
function events_remove_dead_instances()
{
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);
}
}
}
}
Данный менеджер тоже не лишен недостатков.
К примеру данный менеджер событий может использоваться только как один для всего происходящего в приложении. А иногда для гибкости и оптимальности бывает необходимы иметь несколько разных менеджеров, например для GUI, для системных надобностей, для юнитов, и для других происходящих групп событий который друг к другу мало чем имеют отношение.
Ну чтож, задача поставлена будем решать в следующих статьях.