- Home /
Question about events with arguments and ineritance
I'm finally getting around to using delegates and events in my code, after reading tons on the subject. But I can't seem to accomplish what I'm going for. I have an "Entity" class which has this:
public class Entity : MonoBehaviour {
public delegate void EntityDelegateMessage();
}
One of its child classes has this:
public class AsteroidController : Entity
{
public static event EntityDelegateMessage EventAsteroidDestroyed;
}
I can call "EventAsteroidDestroyed" from within the AstetroidController class and my GameManager script subscribes to it like this:
AsteroidController.EventAsteroidDestroyed += AnotherOneBitesTheDust;
This fires the proper method in GameManager and all is well. Now, I would like to send another type of message from the AsteroidController class with an accompanying integer, who's value depends on the instance of the class sending the message (not all asteroids are worth the same number of points). So I added this in Entity:
public delegate void EntityDelegateScore(int points);
public static event EntityDelegateScore EventScorePoints;
Now, in the AsteroidController, if I try to send the message using:
EventScorePoints(1);
Unity (and Visual Studio) complain with the following: "The event 'Entity.EventScorePoints' can only appear on the left hand side of += or -= when used outside of the type `Entity'".
What am I doing wrong? I know that if I want the instance variable I shouldn't use a static modifier but I don't know how this should be done.
Thanks a lot.
Answer by callen · Jan 24, 2018 at 07:02 PM
Look here for a MSDN article on the subject
One thing you could do is write a method like this, which you should be able to call from AsteroidController:
// defined in Entity.cs
protected static void OnEventScorePoints(int points)
{
if(EventScorePoints != null) EventScorePoints(points);
}
Hope that helps!
Thank you. I've read the article (which is in line with your suggestion). This solution works, but there are two things I don't understand (the second one is more mysterious to me) : (1) Isn't there a more elegant solution than the wrapping of the event in a method? (2) How on Earth does this work correctly with instanced members of a class (I've tried, there's no problem with that, each instance can send its own message), when the event is defined as static. Doesn't the event belong to the class rather than to the instances when marked as static?
No, the point is an event (so a delegate variabe with the event keyword) can only be invoked inside the class it is defined. Since the base class provides the event it's responsible for calling it.
Another solution is to not use an event but simply a delegate. If you declare it like this:
public static EntityDelegateScore EventScorePoints;
It should do what you want. However you loose encapsulation. So everybody can invoke that delegate. Subscribing and unsubscribing works the same way.
After much digging I've abandoned the idea of delegates inside the base Entity class and child classes, because it seemed too fragmented. I've come up with an Event$$anonymous$$anager class on which I can call methods from anywhere and it then handles the messaging globally. Does it seem logical (or conversely, very silly) to do things that way? It works as expected on my end, but I don't know if it's considered good practice (please bear in $$anonymous$$d I've only figured out what delegates do this morning...) Thanks.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Event$$anonymous$$anager : $$anonymous$$onoBehaviour {
public delegate void Event$$anonymous$$essage();
public static event Event$$anonymous$$essage OnAsteroidDestroyed;
public static event Event$$anonymous$$essage OnPlayerDestroyed;
public static event Event$$anonymous$$essage OnLivesEqualsZero;
public static event Event$$anonymous$$essage OnLastAsteroidDestroyed;
public delegate void EventScorePoints(int points);
public static event EventScorePoints OnScorePoints;
// Events for delegate type Event$$anonymous$$essage
public static void $$anonymous$$essageAsteroidDestroyed()
{
if (OnAsteroidDestroyed != null) OnAsteroidDestroyed();
}
public static void $$anonymous$$essageLastAsteroidDestroyed()
{
if (OnLastAsteroidDestroyed != null) OnLastAsteroidDestroyed();
}
public static void $$anonymous$$essagePlayerDestroyed()
{
if (OnPlayerDestroyed != null) OnPlayerDestroyed();
}
public static void $$anonymous$$essageLivesEqualsZero()
{
if (OnLivesEqualsZero != null) OnLivesEqualsZero();
}
// Events for delegate type EventScorePoints
public static void $$anonymous$$essageScorePoints(int points)
{
if (OnScorePoints != null) OnScorePoints(points);
}
}
Answer by Neamtzu · Feb 05, 2018 at 01:21 PM
I started using Action instead of delegates. It does the same thing but you don't have to declare the delegates.
//Declaration
public static event Action NoParamsEvent;
public static event Action<int> IntEvent;
public static event Action<string> StringEvent;
public static event Action<string, int> MultipleParamsEvent;
//Extensions - you can put these methods in a static class
public static void Fire(this Action _action)
{
if(_action != null)
_action();
}
public static void Fire<T>(this Action<T> _action,T _t)
{
if(_action != null)
_action(_t);
}
public static void Fire<T, V>(this Action<T,V> _action,T _t,V _v)
{
if(_action != null)
_action(_t,_v);
}
public static void Fire<T, V, U>(this Action<T,V,U> _action,T _t,V _v,U _u)
{
if(_action != null)
_action(_t,_v,_u);
}
public static void Fire<T, V, U, W>(this Action<T,V,U,W> _action,T _t,V _v,U _u,W _w)
{
if(_action != null)
_action(_t,_v,_u,_w);
}
//Usability
NoParamsEvent.Fire();
IntEvent.Fire(aInt);
StringEvent.Fire(aString);
MultipleParamsEvent.Fire(aString, aInt);
To receive the events your should register the same:
YourClass.NoParamsEvent += handleNoParamsEvent;
YourClass.IntEvent += handleIntEvent;
void handleNoParamsEvent() {
}
void handleIntEvent(int _intParam) {
}
Don't forget to remove the listener before the gameobject is destroyed / deactivated.
YourClass.NoParamsEvent += handleNoParamsEvent;
YourClass.IntEvent += handleIntEvent;
Usually you do += in OnEnable and -= in OnDisable.
I agree, since I discovered it I find it much more concise. Just commented about that yesterday :) https://answers.unity.com/comments/1464175/view.html
Your answer
Follow this Question
Related Questions
An OS design issue: File types associated with their appropriate programs 1 Answer
Interaction script 2 Answers
Inheritance that changes how events are handled. (C#) 1 Answer
How do I show UnityEvent in inspector on Inheritance class of ScrollRect ? 2 Answers
How to serialize fields for both base and derived classes? 0 Answers