Как оказалось с помощью EventSystem можно послать уведомление только одному конкретному геймобъекту
Поискал, далеко ходить не пришлось, на хабре лежит статья, автор все грамотно расписал, казалось бы все готово бери и используй...
Подключил и вот облом, на каждый геймобъект, куда ты хочешь повесить SomethingHappenedInputModule (как предлагает автор) садится еще и EventSystem
Ладно думаю, сейчас переделаю SomethingHappenedInputModule и повешу на объект где EventSystem, а для регистрации сделаю свой объект и норм.
Но тут опять облом - перестал работать UI, кнопки не нажимаются, ничего не реагирует.
Поискал в нете нашел описание похожих проблем ... Не знаю почему, но появление третьего InputModule на объекте EventSystem вводит в ступор StandaloneInputModule и TouchInputModule. Но этот наш SomethingHappenedInputModule нам особо и не нужен как я понял. В суть не вник, в справке об этом кот наплакал. Видел в нете примеры рабочих InputModule например для кинекта, но как я понимаю StandaloneInputModule и TouchInputModule в этом случае не используется или временно отключается...
Но для рассылки событий с помощью ExecuteEvents.Execute() InputModule не является необходимым элементов, как я понял. Избавился от него, заменил на обычный монобех, чтобы получать ссылку на го EventSystem.
Кроме того решил сделать одно важное для себя упрощение: каждый раз городить функтор мне показалось очень не удобным делом. В итоге функтор прописал внутри класса, а событие производится соответствующей функцией.
Пример: BroadcastExecuteEvents.ExecuteInventoryUpdate_OnAddItem(InventoryItem item)
Функций придется конечно сделать по штуке на событие, но они будут одинаковые как клоны и мне кажется так гораздо удобней, чем каждый раз в коде описывать функтор.
В итоге привожу исправленные рабочие скрипты для широковещательной рассылки, кому надо:
BroadcastExecuteEvents.cs
Синтаксис:
Используется csharp
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
/// <summary>
/// BroadcastExecuteEvents является всего лишь обёрткой над ExecuteEvents
/// и выполняет всего одну задачу — выбор подходящих обработчиков для указанного события и их вызов.
///
/// На основе статьи: https://habrahabr.ru/post/254239/
/// В статье содержатся неточности, код не рабочий.
/// Потому как вместе с компонентом SomethingHappenedInputModule на gameObject навешивается EventSystem, и так на каждый объект, подписавшийся на событие ...
///
/// Переделал под себя:
/// BroadcastReceiver<TEventType> - абстрактный класс подписчика на событие
/// BroadcastReceiverInventoryUpdate - основан на BroadcastReceiver и вешается на объекты подписчики.
///
/// Так как функтор каждый раз определять не удобно и трудно, упростим - разбиваем на отдельные методы.
/// Каждый метод рассылает приемникам свое событие. Функтор определяется внутри метода.
///
/// Произвести широковещательную посылку события можно так:
/// BroadcastExecuteEvents.ExecuteInventoryUpdate_OnAddItem(item);
/// </summary>
public static class BroadcastExecuteEvents
{
// То, что было изначально
//public static void Execute<T>(BaseEventData eventData, ExecuteEvents.EventFunction<T> functor) where T : IEventSystemHandler
//{
// var handlers = BroadcastReceivers.GetHandlersForEvent<T>();
// if (handlers == null)
// return;
// foreach (var handler in handlers)
// {
// ExecuteEvents.Execute<T>(handler, eventData, functor);
// }
//}
public static void ExecuteInventoryUpdate_OnAddItem(InventoryItem item)
{
BaseEventData eventData = new BaseEventData(BroadcastInputModule.eventSystemCurrent);
var handlers = BroadcastReceivers.GetHandlersForEvent<IEventHandlerInventoryUpdate>();
if (handlers == null)
return;
foreach (var handler in handlers)
{
ExecuteEvents.EventFunction<IEventHandlerInventoryUpdate> functor = (i, d) => i.OnAddItem(item);
ExecuteEvents.Execute<IEventHandlerInventoryUpdate>(handler, eventData, functor);
//Debug.Log("ExecuteInventoryUpdate_OnAddItem for handler = " + handler);
}
}
public static void ExecuteInventoryUpdate_OnRemoveItem(InventoryItem item)
{
BaseEventData eventData = new BaseEventData(BroadcastInputModule.eventSystemCurrent);
var handlers = BroadcastReceivers.GetHandlersForEvent<IEventHandlerInventoryUpdate>();
if (handlers == null)
return;
foreach (var handler in handlers)
{
ExecuteEvents.EventFunction<IEventHandlerInventoryUpdate> functor = (i, d) => i.OnRemoveItem(item);
ExecuteEvents.Execute<IEventHandlerInventoryUpdate>(handler, eventData, functor);
//Debug.Log("ExecuteInventoryUpdate_OnRemoveItem for handler = " + handler);
}
}
}
using UnityEngine.EventSystems;
using System.Collections;
/// <summary>
/// BroadcastExecuteEvents является всего лишь обёрткой над ExecuteEvents
/// и выполняет всего одну задачу — выбор подходящих обработчиков для указанного события и их вызов.
///
/// На основе статьи: https://habrahabr.ru/post/254239/
/// В статье содержатся неточности, код не рабочий.
/// Потому как вместе с компонентом SomethingHappenedInputModule на gameObject навешивается EventSystem, и так на каждый объект, подписавшийся на событие ...
///
/// Переделал под себя:
/// BroadcastReceiver<TEventType> - абстрактный класс подписчика на событие
/// BroadcastReceiverInventoryUpdate - основан на BroadcastReceiver и вешается на объекты подписчики.
///
/// Так как функтор каждый раз определять не удобно и трудно, упростим - разбиваем на отдельные методы.
/// Каждый метод рассылает приемникам свое событие. Функтор определяется внутри метода.
///
/// Произвести широковещательную посылку события можно так:
/// BroadcastExecuteEvents.ExecuteInventoryUpdate_OnAddItem(item);
/// </summary>
public static class BroadcastExecuteEvents
{
// То, что было изначально
//public static void Execute<T>(BaseEventData eventData, ExecuteEvents.EventFunction<T> functor) where T : IEventSystemHandler
//{
// var handlers = BroadcastReceivers.GetHandlersForEvent<T>();
// if (handlers == null)
// return;
// foreach (var handler in handlers)
// {
// ExecuteEvents.Execute<T>(handler, eventData, functor);
// }
//}
public static void ExecuteInventoryUpdate_OnAddItem(InventoryItem item)
{
BaseEventData eventData = new BaseEventData(BroadcastInputModule.eventSystemCurrent);
var handlers = BroadcastReceivers.GetHandlersForEvent<IEventHandlerInventoryUpdate>();
if (handlers == null)
return;
foreach (var handler in handlers)
{
ExecuteEvents.EventFunction<IEventHandlerInventoryUpdate> functor = (i, d) => i.OnAddItem(item);
ExecuteEvents.Execute<IEventHandlerInventoryUpdate>(handler, eventData, functor);
//Debug.Log("ExecuteInventoryUpdate_OnAddItem for handler = " + handler);
}
}
public static void ExecuteInventoryUpdate_OnRemoveItem(InventoryItem item)
{
BaseEventData eventData = new BaseEventData(BroadcastInputModule.eventSystemCurrent);
var handlers = BroadcastReceivers.GetHandlersForEvent<IEventHandlerInventoryUpdate>();
if (handlers == null)
return;
foreach (var handler in handlers)
{
ExecuteEvents.EventFunction<IEventHandlerInventoryUpdate> functor = (i, d) => i.OnRemoveItem(item);
ExecuteEvents.Execute<IEventHandlerInventoryUpdate>(handler, eventData, functor);
//Debug.Log("ExecuteInventoryUpdate_OnRemoveItem for handler = " + handler);
}
}
}
BroadcastInputModule.cs
Синтаксис:
Используется csharp
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
/// <summary>
/// Класс BroadcastInputModule вроде бы в нем и нужно делать рассылку событий в методе Process()
/// как показано в инструкции http://docs.unity3d.com/ScriptReference ... odule.html
/// Но если так сделать и включить в сцену он блокирует работу StandaloneInputModule и TouchInputModule.
/// Поэтому перевел его на MonoBehaviour и главная его функция, получить ссылку на EventSystem <img src="./images/smilies/1.gif" alt=":)" title="Happy" /> всего ничего ...
/// Навещивается на gameObject, на котором висит EventSystem.
/// </summary>
public class BroadcastInputModule : MonoBehaviour //BaseInputModule
{
static private EventSystem _eventSystem;
static public EventSystem eventSystemCurrent
{
get { return _eventSystem; }
}
void Awake()
{
_eventSystem = GetComponent<EventSystem>();
}
//public override void Process()
//{
// Debug.Log("Process...");
//}
}
using UnityEngine.EventSystems;
using System.Collections;
/// <summary>
/// Класс BroadcastInputModule вроде бы в нем и нужно делать рассылку событий в методе Process()
/// как показано в инструкции http://docs.unity3d.com/ScriptReference ... odule.html
/// Но если так сделать и включить в сцену он блокирует работу StandaloneInputModule и TouchInputModule.
/// Поэтому перевел его на MonoBehaviour и главная его функция, получить ссылку на EventSystem <img src="./images/smilies/1.gif" alt=":)" title="Happy" /> всего ничего ...
/// Навещивается на gameObject, на котором висит EventSystem.
/// </summary>
public class BroadcastInputModule : MonoBehaviour //BaseInputModule
{
static private EventSystem _eventSystem;
static public EventSystem eventSystemCurrent
{
get { return _eventSystem; }
}
void Awake()
{
_eventSystem = GetComponent<EventSystem>();
}
//public override void Process()
//{
// Debug.Log("Process...");
//}
}
BroadcastReceivers.cs
Синтаксис:
Используется csharp
using UnityEngine;
using UnityEngine.EventSystems;
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Класс хранящий коллекции приемников (обработчиков) событий.
/// </summary>
public static class BroadcastReceivers
{
private static readonly IDictionary<Type, IList<GameObject>> BroadcstReceivers = new Dictionary<Type, IList<GameObject>>();
public static IList<GameObject> GetHandlersForEvent<TEventType>() where TEventType : IEventSystemHandler
{
if (!BroadcstReceivers.ContainsKey(typeof(TEventType)))
{
return null;
}
return BroadcstReceivers[typeof(TEventType)];
}
public static void Register<TEventType>(GameObject go) where TEventType : IEventSystemHandler
{
if (BroadcstReceivers.ContainsKey(typeof(TEventType)))
{
BroadcstReceivers[typeof(TEventType)].Add(go);
}
else
{
BroadcstReceivers.Add(typeof(TEventType), new List<GameObject>());
BroadcstReceivers[typeof(TEventType)].Add(go);
}
}
public static void Unregister<TEventType>(GameObject go)
{
if (BroadcstReceivers.ContainsKey(typeof(TEventType)))
{
BroadcstReceivers[typeof(TEventType)].Remove(go);
}
}
}
using UnityEngine.EventSystems;
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Класс хранящий коллекции приемников (обработчиков) событий.
/// </summary>
public static class BroadcastReceivers
{
private static readonly IDictionary<Type, IList<GameObject>> BroadcstReceivers = new Dictionary<Type, IList<GameObject>>();
public static IList<GameObject> GetHandlersForEvent<TEventType>() where TEventType : IEventSystemHandler
{
if (!BroadcstReceivers.ContainsKey(typeof(TEventType)))
{
return null;
}
return BroadcstReceivers[typeof(TEventType)];
}
public static void Register<TEventType>(GameObject go) where TEventType : IEventSystemHandler
{
if (BroadcstReceivers.ContainsKey(typeof(TEventType)))
{
BroadcstReceivers[typeof(TEventType)].Add(go);
}
else
{
BroadcstReceivers.Add(typeof(TEventType), new List<GameObject>());
BroadcstReceivers[typeof(TEventType)].Add(go);
}
}
public static void Unregister<TEventType>(GameObject go)
{
if (BroadcstReceivers.ContainsKey(typeof(TEventType)))
{
BroadcstReceivers[typeof(TEventType)].Remove(go);
}
}
}
BroadcastReceiver.cs
Синтаксис:
Используется csharp
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
/// <summary>
/// Абстрактный класс приемника событий.
/// Регистрирует объект приемника в коллекции широковещательной рассылки BroadcastReceivers.
/// Также отменяет регистрацию при уничтожении объекта.
/// </summary>
/// <typeparam name="TEventType"></typeparam>
public abstract class BroadcastReceiver<TEventType> : MonoBehaviour where TEventType : IEventSystemHandler
{
void Awake()
{
BroadcastReceivers.Register<TEventType>(gameObject);
}
void OnDestroy()
{
BroadcastReceivers.Unregister<TEventType>(gameObject);
}
}
using UnityEngine.EventSystems;
using System.Collections;
/// <summary>
/// Абстрактный класс приемника событий.
/// Регистрирует объект приемника в коллекции широковещательной рассылки BroadcastReceivers.
/// Также отменяет регистрацию при уничтожении объекта.
/// </summary>
/// <typeparam name="TEventType"></typeparam>
public abstract class BroadcastReceiver<TEventType> : MonoBehaviour where TEventType : IEventSystemHandler
{
void Awake()
{
BroadcastReceivers.Register<TEventType>(gameObject);
}
void OnDestroy()
{
BroadcastReceivers.Unregister<TEventType>(gameObject);
}
}
BroadcastReceiverInventoryUpdate.cs
Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
public class BroadcastReceiverInventoryUpdate : BroadcastReceiver<IEventHandlerInventoryUpdate>
{
}
using System.Collections;
public class BroadcastReceiverInventoryUpdate : BroadcastReceiver<IEventHandlerInventoryUpdate>
{
}
IEventHandlerInventoryUpdate.cs
Синтаксис:
Используется csharp
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
/// <summary>
/// Событие в инвентаре, происходящее в момент добавления или удаления предмета.
/// </summary>
public interface IEventHandlerInventoryUpdate : IEventSystemHandler
{
// How Do I Define A Custom Message?
// http://docs.unity3d.com/ru/current/Manu ... ystem.html
// Functions that can be called via the messaging system
void OnAddItem(InventoryItem item);
void OnRemoveItem(InventoryItem item);
}
using UnityEngine.EventSystems;
using System.Collections;
/// <summary>
/// Событие в инвентаре, происходящее в момент добавления или удаления предмета.
/// </summary>
public interface IEventHandlerInventoryUpdate : IEventSystemHandler
{
// How Do I Define A Custom Message?
// http://docs.unity3d.com/ru/current/Manu ... ystem.html
// Functions that can be called via the messaging system
void OnAddItem(InventoryItem item);
void OnRemoveItem(InventoryItem item);
}