I wonder if there is any way to configure it to be like this: Class1 → Class2 → Class3 → Class4
Awake() → Awake() → Awake() → Awake()
Start() → Start() → Start() → Start()
UpDate() → UpDate() -->UpDate() -->UpDate()
That is: I need all the Awake () of each Class to be executed before all the Start () of each class.
That’s exactly what happens. If you’re seeing something different, it’s because the second one is being created in a later frame than the first. (Or I guess more specifically, the second one is being created during or after the Start() of the first object runs)
First I was calling the AddSender (IEvent e) function from the EventInstaller :: Awake () and everything was working fine (except for a problem I had with clones) …
So I decided to put the AddSender (IEvent e) function inside EventManager :: Awake () but now nothing works …
It make me think that the order of execution was as I said above. But if not, I don’t know what to think. Any idea what it could be?
public class EventInstaller : MonoBehaviour
{
List<IEvent> iEvent = new List<IEvent>();
//--------------------------------------------------
public void AddSender(IEvent e)
{
iEvent.Add(e);
}
//--------------------------------------------------
public void RemoveSender(IEvent e)
{
iEvent.Remove(e);
}
//--------------------------------------------------
public void AddListener(IEventListener listener)
{
foreach (IEvent e in iEvent)
{
e.OnEvent += listener.Listen;
}
}
//--------------------------------------------------
public void RemoveListener(IEventListener listener)
{
foreach (IEvent e in iEvent)
{
e.OnEvent -= listener.Listen;
}
}
}
//--------------------------------------------------------------
public class EventManager : EventListener, IEvent, IEventInfo
{
public event Action<EventInfo, EntityEvent.Type, EntityEvent.Property> OnEvent;
//--------------------------------------------
public virtual void Awake()
{
eventInstaller.AddSender(this);
}
//--------------------------------------------
public override void OnDestroy()
{
eventInstaller.RemoveSender(this);
base.OnDestroy();
}
//--------------------------------------------
public override void Start()
{
base.Start();
}
//--------------------------------------------
public void SendEvent(EventInfo info, EntityEvent.Type eventType, Property property)
{
OnEvent?.Invoke(info, eventType, property);
}
}
public class EventListener : MonoBehaviour, IEventListener
{
public EventInstaller eventInstaller;
//--------------------------------------------------
public virtual void Start() => eventInstaller.AddListener(this);
//--------------------------------------------------
public virtual void OnDestroy() => eventInstaller.RemoveListener(this);
//--------------------------------------------------
public virtual void Listen(EventInfo receiveInfo, EntityEvent.Type eventType, Property property)
{
}
}
So your Awake methods are just adding a bunch of event listeners, right? That’s fine. I think the issue is that once your last MonoBehavior calls its Awake method, Unity is just going ahead with running the scene’s Start methods, but that doesn’t mean your EventInstallers have all finished their subscriptions.
However, I’m not clear on what kind of situation would cause a problem there. Are you expecting the code that responds to those event listeners to fire before Start does? Because that’s not gonna happen. You might just be a way more advanced programmer than me, but I can’t envision what you’re doing here that needs to be done this way. Is it some kind of object pooling system?
So your Awake methods are just adding a bunch of event listeners, right?
Yes, That is.
Are you expecting the code that responds to those event listeners to fire before Start does?
No, i need it work after Start.
You might just be a way more advanced programmer than me.
I’m not… I’m learned C in the university 20 years ago… then i learned C++ by my self… and now i learning C# and dessing patterns… thats all i know.. I’m a chemical engineer … but I would have liked to study computer engineering. XD
Is it some kind of object pooling system?
I’m using the events for communication among objects. They are really useful and i think all is more easy with them. I use it for almost everything.
The last thing I’m doing is a damage system. I want a server to be in charge of managing the damage of all the characters. A single code for all objects.
The Server:
//-------------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
using EntityEvent;
//-------------------------------------------------
namespace healthsystem
{
//-------------------------------------------------
[Serializable]
public struct Damagable
{
public Transform objectID;
public HealthData healthData;
}
//-------------------------------------------------
public class HealthServer : EventManager
{
[Tag] public string playerTag;
[SerializeField] private Damagable[] clientsList;
Dictionary<Transform,HealthData> clientsMap;
//--------------------------------------------------
public override void Start()
{
base.Start();
clientsMap = new Dictionary<Transform, HealthData>();
foreach (var clients in clientsList)
{
if (clients.healthData.useClon)
{
clientsMap.Add(clients.objectID, clients.healthData.Clone());
}
else
{
clientsMap.Add(clients.objectID, clients.healthData);
}
}
}
//--------------------------------------------------
public override void OnGameStart(EventInfo receiveInfo)
{
foreach (var clients in clientsList)
{
clientsMap[clients.objectID].enableDamage = true;
eventData.Lives = clientsMap[clients.objectID].totalHeath;//clientsMap[clients.objectID].currentLive;
SendEvent(new EventInfo(clients.objectID, this), EntityEvent.Type.lives, EntityEvent.Property.update);
}
}
//--------------------------------------------------
public override void OnRespawnEnd(EventInfo receiveInfo)
{
foreach (var clients in clientsList)
{
clientsMap[clients.objectID].enableDamage = true;
}
}
//--------------------------------------------------
public override void OnHealthGet(EventInfo receiveInfo)
{
if (clientsMap[receiveInfo.sender].curentHeath<clientsMap[receiveInfo.sender].totalHeath)
{
clientsMap[receiveInfo.sender].curentHeath += receiveInfo.GetHealth();
sentInfo.sender = receiveInfo.sender;
eventData.Health = clientsMap[receiveInfo.sender].curentHeath;
SendEvent(EntityEvent.Type.health, EntityEvent.Property.update, receiveInfo);
}
}
//--------------------------------------------------
public override void OnHealthLost(EventInfo receiveInfo)
{
//acutualizar el nivel de salud
if (clientsMap[receiveInfo.sender].enableDamage && clientsMap[receiveInfo.sender].curentHeath > 0)
{
eventData.Health = clientsMap[receiveInfo.sender].curentHeath -= receiveInfo.GetDamage();
SendEvent( new EventInfo(receiveInfo.sender, this), EntityEvent.Type.health, EntityEvent.Property.update);
}
//se ha perdido una vida (restablecer el nivel de salud al maximo)
if (clientsMap[receiveInfo.sender].curentHeath <= 0)
{
clientsMap[receiveInfo.sender].enableDamage = false;
SendEvent(new EventInfo(receiveInfo.sender),EntityEvent.Type.lives, EntityEvent.Property.lost);
eventData.Health = clientsMap[receiveInfo.sender].curentHeath = clientsMap[receiveInfo.sender].totalHeath;
SendEvent(new EventInfo(receiveInfo.sender, this), EntityEvent.Type.health, EntityEvent.Property.update);
}
Debug.Log(this + " -->" + receiveInfo.sender.tag +
" curentHeath-->" + clientsMap[receiveInfo.sender].curentHeath +
" currentLive-->" + clientsMap[receiveInfo.sender].currentLive +
" enableDamage-->" + clientsMap[receiveInfo.sender].enableDamage);
}
//--------------------------------------------------
public override void OnLivesLost(EventInfo receiveInfo)
{
if (clientsMap[receiveInfo.sender].currentLive > 0)
{
//clientsMap[receiveInfo.sender].enableDamage = true;
eventData.Lives = clientsMap[receiveInfo.sender].currentLive -= 1;
SendEvent(new EventInfo(receiveInfo.sender, this), EntityEvent.Type.lives, EntityEvent.Property.update);
SendEvent(new EventInfo(receiveInfo.sender, this), EntityEvent.Type.dead, EntityEvent.Property.start);
}
if (clientsMap[receiveInfo.sender].currentLive <= 0)
{
//clientsMap[receiveInfo.sender].enableDamage = false;
if (receiveInfo.sender.CompareTag(playerTag))
{
SendEvent(new EventInfo(receiveInfo.sender, this), EntityEvent.Type.game, EntityEvent.Property.end);
}
}
}
//--------------------------------------------------
public override void OnLivesGet(EventInfo receiveInfo)
{
if (clientsMap[receiveInfo.sender].currentLive < clientsMap[receiveInfo.sender].totalLives )
{
eventData.Lives = clientsMap[receiveInfo.sender].currentLive += receiveInfo.GetLives();
SendEvent(EntityEvent.Type.lives, EntityEvent.Property.update, receiveInfo);
}
}
}
}
The Client:
//-------------------------------------------------
using UnityEngine;
using EntityEvent;
//-------------------------------------------------
namespace healthsystem
{
public class HealthClient : EventManager
{
public HitersDataBase hitersDataBase;
private int count;
private bool isHurt;
//-------------------------------------------------
public bool CheckHiters(Collider2D collision) => hitersDataBase.hiters.CheckHiters(collision);
//-------------------------------------------------
bool IsWeapon(Collider2D collision)
{
if (collision.gameObject.TryGetComponent(out IWeapon iWeapon))
{
if (iWeapon.OwnerTag == "none" || iWeapon.OwnerTag == transform.tag)
{
cancelCheckCollision = true;
return true;
}
}
return false;
}
//-------------------------------------------------
bool IsHitter(Collider2D collision)
{
if (!isHurt && CheckHiters(collision))//damage
{
isHurt = true;
count = 0;
if (collision.gameObject.TryGetComponent(out IEventInfo iEventInfo))
{
Debug.Log(this + " Hit Event-->" + collision.gameObject.tag + " -->" + iEventInfo.EventData().eventType + " -->" + iEventInfo.EventData().eventProperty);
SendEvent(new EventInfo(transform, iEventInfo, collision), EntityEvent.Type.health, EntityEvent.Property.lost);
}
return true;
}
return false;
}
//-------------------------------------------------
void GetBonus(Collider2D collision)
{
if (collision.gameObject.TryGetComponent(out IEventInfo iEventInfo))
{
//Debug.Log(this + " otro evento-->" + collision.gameObject.tag + " -->" + iEventInfo.EventData().eventType + " -->" + iEventInfo.EventData().eventProperty);
SendEvent(new EventInfo(transform,iEventInfo, collision), iEventInfo.EventData().eventType, iEventInfo.EventData().eventProperty);
}
}
//-------------------------------------------------
public virtual void OnTriggerEnter2D(Collider2D collision)
{
if (IsWeapon(collision)) return;
if (IsHitter(collision)) return;
GetBonus(collision);
}
//-------------------------------------------------
public virtual void OnTriggerExit2D(Collider2D collision)
{
if (isHurt && CheckHiters(collision))
{
isHurt = false;
}
}
//-------------------------------------------------
public void Update()
{
if (isHurt && count == 1) isHurt = false;
if (int.MaxValue == count) count = 2;
count++;
}
//-------------------------------------------------
}
}
It’s not finished yet. But more or less you can see what I’m trying to do.
What do you think about the idea?
Look this video i made… i found that there is some Awake() functions are executing after Start() function… it is a clon…it is the clons fault… they are created after the others so the Awake funtion have a delay… i dont know what can i do to solve this problem ¿some idea?
Ok, get it to work !! I only had to make a few small changes in the EventInstaller … Since the Awake () did not work for me, I had to transfer the responsibility to Update ().
The code is like this:
//--------------------------------------------------
public class EventInstaller : MonoBehaviour
{
List<IEvent> iEvent = new List<IEvent>();
private bool isEventAdded;
private List<IEventListener> listeners = new List<IEventListener>();
//--------------------------------------------------
public void AddSender(IEvent sender)
{
if (!iEvent.Contains(sender))
{
iEvent.Add(sender);
isEventAdded = true;
}
}
//--------------------------------------------------
public void RemoveSender(IEvent sender)
{
if (iEvent.Contains(sender))
{
iEvent.Remove(sender);
}
}
//--------------------------------------------------
public void AddListener(IEventListener listener)
{
listeners.Add(listener);
foreach (IEvent sender in iEvent)
{
sender.OnEvent += listener.Listen;
}
}
//--------------------------------------------------
public void RemoveListener(IEventListener listener)
{
listeners.Remove(listener);
foreach (IEvent sender in iEvent)
{
sender.OnEvent -= listener.Listen;
}
}
//--------------------------------------------------
public void RemoveAllSubscriptions()
{
foreach (IEvent e in iEvent)
{
foreach (IEventListener listener in listeners)
{
e.OnEvent -= listener.Listen;
}
}
}
//--------------------------------------------------
public void UpdateSubscriptions()
{
foreach (IEvent e in iEvent)
{
foreach (IEventListener listener in listeners)
{
e.OnEvent += listener.Listen;
}
}
}
//--------------------------------------------------
void Update()
{
if (isEventAdded)
{
RemoveAllSubscriptions();
UpdateSubscriptions();
isEventAdded = false;
}
}
}
//--------------------------------------------------------------
I no longer have any problem with clones … life is wonderful !! XD
Thank you very much for your help !! very appreciated!!
I haven’t looked at what you want to achieve so there might be better solutions.
But as a rule of thumb. Never have external dependencies from Awake.
In Awake you setup your internal stuff. Then in Start you setup external dependencies. This way you make sure all external dependencies have executed their Awake which setup up event bus, singleton or how you choose to communicate between components.