Broadcast с помощью EventSystem

Части и запчасти к проектам, дополнительное программное обеспечение.

Broadcast с помощью EventSystem

Сообщение Syberex 26 май 2016, 02:38

Потребовалось в текущем проекте рассылать события, но подписчиков может быть несколько.
Как оказалось с помощью EventSystem можно послать уведомление только одному конкретному геймобъекту :-o

Поискал, далеко ходить не пришлось, на хабре лежит статья, автор все грамотно расписал, казалось бы все готово бери и используй...
Подключил и вот облом, на каждый геймобъект, куда ты хочешь повесить SomethingHappenedInputModule (как предлагает автор) садится еще и EventSystem 8()
Ладно думаю, сейчас переделаю 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);
                }
        }
}


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...");
        //}
}


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);
                }
        }
}


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);
        }
}
 


BroadcastReceiverInventoryUpdate.cs
Синтаксис:
Используется csharp
using UnityEngine;
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);
}
 
Аватара пользователя
Syberex
Адепт
 
Сообщения: 2292
Зарегистрирован: 14 янв 2011, 20:35
Откуда: Кострома
  • Сайт

Re: Broadcast с помощью EventSystem

Сообщение Syberex 26 май 2016, 02:55

Особенности использования:
BroadcastExecuteEvents.cs - статический класс, вешать никуда не надо, вызываем в скриптах функции этого класса,
чтобы известить подписчиков о произошедшем событии. Функции добавляем по необходимости в соответствии с интерфейсом.

BroadcastInputModule.cs - просто навешиваем на геймобъект EventSystem.

BroadcastReceivers.cs - статический класс, который хранит списки подписчиков. Навешивать никуда не надо, вызывать тоже.

BroadcastReceiver.cs - абстрактный класс, от него нужно образовать новый класс BroadcastReceiverЧтоСлучилось.

BroadcastReceiverInventoryUpdate.cs - пример класса BroadcastReceiverЧтоСлучилось, образованного от BroadcastReceiver. Затем навешивается на каждый геймобъект, который является подписчиком на событие данного интерфейса.

IEventHandlerInventoryUpdate.cs - пример интерфейса IEventHandlerЧтоСлучилось, описывающего обработчик события IEventHandler. Нужно добавлять к своим скриптам и реализовывать методы обработки события. Методы придумываем сами. Интерфейс может быть один, а методов/событий несколько. Можно группировать, но в каждом классе поддерживающем интерфейс, придется реализовывать все методы, так что запихивать все в один не слишком хорошо.

---
Старался описать понятно и для тех кто только начинает писать свои майнкрафты и инвентари :-B
Что непонятно спрашивайте.
В чем я не прав поправьте. Спасибо за внимание.
Аватара пользователя
Syberex
Адепт
 
Сообщения: 2292
Зарегистрирован: 14 янв 2011, 20:35
Откуда: Кострома
  • Сайт

Re: Broadcast с помощью EventSystem

Сообщение samana 26 май 2016, 17:01

А я постоянно пользовался обычными делегатами-событиями C#, вроде всё было хорошо. А тут вижу у Unity есть ещё и своя какая-то специальная система, неожиданно :-ss
Аватара пользователя
samana
Адепт
 
Сообщения: 4738
Зарегистрирован: 21 фев 2015, 13:00
Откуда: Днепропетровск


Вернуться в Компоненты (Копилка)

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3