- Home /
Messaging System In Unity
I am trying to create a messaging system in Unity in order to communicate things that happen in the game with events. For now what I want to do is call an event when there is collision between the player's bullets and the enemies. While this actually works, the problem I am having is that since the script is using a messaging system, the exact same event is shared and so does the health for both enemies. So when I shoot on fore example, they all lose the same amount of health. Do you know any way to prevent that?
Code for Event Manager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[System.Serializable]
public class TypedEvent : UnityEvent<object> { }
public class EventManager : MonoBehaviour
{
private Dictionary<string, UnityEvent> eventDictionary;
private static EventManager eventManager;
// Start is called before the first frame update
public static EventManager instance
{
get
{
if(!eventManager){
eventManager = FindObjectOfType(typeof(EventManager)) as EventManager;
if (!eventManager)
{
Debug.LogError("There needs to be one active EventManager script on a GameObject in the scene");
}
else
{
eventManager.Init();
}
}
return eventManager;
}
}
private void Init()
{
if(eventDictionary == null)
{
eventDictionary = new Dictionary<string, UnityEvent>();
}
}
// Unity Action allows for dynamically calling multiple functions(they must have no arguments)
public static void StartListening(string eventName,UnityAction listener)
{
UnityEvent thisEvent = null;
if(instance.eventDictionary.TryGetValue(eventName,out thisEvent))
{
thisEvent.AddListener(listener);
}
else
{
thisEvent = new UnityEvent();
thisEvent.AddListener(listener);
instance.eventDictionary.Add(eventName, thisEvent);
}
}
public static void StopListening(string eventName,UnityAction listener)
{
if (eventManager == null)
return;
UnityEvent thisEvent = null;
if(instance.eventDictionary.TryGetValue(eventName,out thisEvent))
{
thisEvent.RemoveListener(listener);
}
}
public static void TriggerEvent(string eventName)
{
UnityEvent thisEvent = null;
if(instance.eventDictionary.TryGetValue(eventName,out thisEvent))
{
thisEvent.Invoke();
}
}
}
Code for Enemy Health
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
public class enemyDepleteHeatlh : MonoBehaviour
{
private int enemyDamage;
[SerializeField]
private float maxHealth;
[SerializeField]
private float curHealth;
private GameObject gun;
[SerializeField]
Slider healthBar;
private UnityAction onTakeDamage;
//public float damageToEnemy;
// Start is called before the first frame update
private void Awake()
{
gun = GameObject.FindGameObjectWithTag("gun");
enemyDamage = gun.GetComponent<playerShooting>().damage;
onTakeDamage = new UnityAction(OnTakeDamage);
GameManager.enemyHealth = maxHealth;
maxHealth = curHealth;
curHealth = healthBar.value;
}
private void OnEnable()
{
EventManager.StartListening("TakeDamage", onTakeDamage);
}
private void OnDisable()
{
EventManager.StopListening("TakeDamage", onTakeDamage);
}
// Update is called once per frame
void Update()
{
healthBar.value = this.curHealth;
if(curHealth <= 0)
{
Destroy(this.gameObject);
}
if(curHealth > maxHealth)
{
this.curHealth = maxHealth;
}
}
private void OnTakeDamage()
{
Debug.Log("Bullet has collided");
this.curHealth -= enemyDamage;
}
Enemy Health Emitter
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class enmyHeatlhdepleteEmitter : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Enemy")
{
EventManager.TriggerEvent("TakeDamage");
}
}
}
Answer by CodesCove · Feb 09 at 04:55 PM
Here is one quite simple way to do it:
You can make two different categories of events. For example Scene scoped and Local scoped events. Difference would be that the Local scoped event have instance id number included int the event name. Then you can basically target that individual gameobject using the event name and instance number. So event could be "TakeDamage3123" where 3123 is the listening objects InstanceId. Instance id is not persistent but this will not matter because you are doing the registrations always when the objcet is instantiated. When you fire the event you need to get the targets instanceId to formulate the "fully qualified" event name.
https://docs.unity3d.com/ScriptReference/Object.GetInstanceID.html
There are other options to achieve this and also ready made frameworks available, but this one is quite simple to implement and use..