Create event without arguments:
//event with no arguments (wouldn't pass anything to handler function) public UnityEvent myEvent=new UnityEvent(); //you must initializeFor custom Event with one to max four arguments that you can pass to handler function when event happen, you need to subclass one of 4 abstract classes public abstract class UnityEvent<t0>.
public abstract class UnityEvent<t0, t1>.
public abstract class UnityEvent<t0, t1, t2 >.
public abstract class UnityEvent<t0, t1, t2,t3>.
[Serializable]//<-- don't foget this or you event wouldn't be drawn in Editor public class SequenceEvent:UnityEvent<int> // You subclass from UnityEvent<t1> where t1 is int cos you want to send "data" is type int }Define custom events:
//events public SequenceEvent OnStart = new SequenceEvent (); public SequenceEvent OnEnd = new SequenceEvent ();Invoke/trigger/dispatch event:
OnStart.Invoke(55555);//data=5555 that will be send to event handler function myEvent.Invoke();// no data will be sentcreate c# delegate like syntax sugar:
public event UnityAction<sequence> MyCSharpLikeEvent { add { onStart.AddListener (value); } remove { onStart.RemoveListener (value); } }Now you can bind event handler:
MyCSharpLikeEvent += MyOnStartEventHandler;or in standard way:
OnStart.AddListener (MyOnStartEventHandler);//you put your event handler function to listen for event happeningTo remove handler => not to listen any more of event happening:
MyCSharpLikeEvent -= MyOnStartEventHandler; //OR OnStart.RemoveListener (MyOnStartEventHandler);Example:
using UnityEngine.Events; using UnityEditor; using System; using UnityEngine; public class Sample : MonoBehaviour { public int data; public UnityEvent myEvent=new UnityEvent(); //custom event with int argument public SequenceEvent OnStart = new SequenceEvent (); public SequenceEvent OnEnd = new SequenceEvent ();//another event defined void Awake(){ //register handler to your events (what function will be execute when event happen) OnStart.AddListener (MyOnStartEventHandler); } // Use this for initialization void Start () { } void MyOnStartEventHandler(int receive){//handler function should take one argument of type int cos it is handling event which dispatch argument "ready" of type int Debug.Log ("Event happen data=" + receive); } // Update is called once per frame void Update () { OnStart.Invoke(data);//make the event happen !!! do this somewhere else cos this will dispatch events every update } void OnDestroy() { OnStart.RemoveListener(MyOnStartEventHandler); } }
How you can use events to produced decoupled code?
For years Unity's top evangelists thought people doing tight couple coding and referencing. That is so wrong!!! In Survival Shooter Tutorial (Nightmare project) you can see tight couple Slider reference with Player health component.
UI, Slider in this case, shouldn't know what have triggered changes(player health) or how changes are produced, and should just listen for change event and know how to display them. Player shouldn't contain reference to Slider or to know about slider that is displaying health change, but just to inform system/world/application/game manager (dispatch event that player health was changed). Fastest solution is to create custom event subclassed from UnityEvent as variable in PlayerHealth: public PlayerChangeEvent onChange=new PlayerChangeEvent(); and trigger event inside PlayerHealth process loop. onChange.Invoke(currentHealthValue). Next what I saw was that Enemy keep hardcoded reference of Player’s object component.
There is sometimes when hard coupling is OK, that is when you do fast prototype or bottleneck that critically slow down your game. Not to teach people as common pattern.
Updates and extension as game development progress would require substantial amount of work, when code is structured like that.
and control Player taking damage from Enemy.
What seem logical is that Enemy script(Weapon script) should just do the Attack (move sword or shoot bullets) and shouldn't care of producing damage directly, by call other Entity PlayerHealth component script. Here comes events too. EnemyScript dispatch event that something is hit and the damage should be applied. It doesn't care how that damage will be applied or how will be displayed....
....and so on. (see video where Survival shooter tutorial is redone in this manner)
DO IT RIGHT!
....and so on. (see video where Survival shooter tutorial is redone in this manner)
DO IT RIGHT!
It is interesting that all Enemies and Player are subscribed to onHit event. So when player gun dispatch event of hitting something all Enemies and player itself would take damage. Not what we desire. This can be handle with "not for me pass" logic, but seem better solution to trigger event on target.
Extend UnityEvent so we can implement custom function InvokeOnTarget (invoke on handler subscribed from component in particular target=gameobject). Now only target hit will take damage.
///INFO: In EditorWindow or custom drawing scenario, you can’t use one UnityEventDrawer for many event’s instances. You need to create drawer instances for every UnityEvent instance. UnityEventBase keeps 2 lists of UnityActions(calls), one persistent list (what you see in editor when you add handler) and runtime list - formed from persistent (Runtime and Editor+Runtime) moved to runtime + that dynamically assigned thru code with AddListener./// Unity event extended no args. /// [Serializable] public abstract class UnityEventEx:UnityEvent { public delegate void PersistantCallDelegate(); //substitute for m_Calls which is private in UnityEventBase private Dictionary<int,UnityAction> m_Calls; //substitute for m_PersistentCalls which is private in UnityEventBase private PersistantCallDelegate[] m_PersistentCallsDelegate; // // Constructors // public UnityEventEx():base(){ m_PersistentCallsDelegate = new PersistantCallDelegate[this.GetPersistentEventCount()]; m_Calls=new Dictionary<int, UnityAction>(); } public new void AddListener (UnityAction call) { int id = (call.Target as MonoBehaviour).gameObject.GetInstanceID (); m_Calls [id] = call; base.AddListener (call); } public void InvokeOnTarget(GameObject target){ UnityAction call; int id = target.GetInstanceID (); //try for runtime added listeners if(m_Calls.TryGetValue(id,out call)){ call.Invoke(); }else{//try persistant(editor) added listeners int listenerNumber = this.GetPersistentEventCount(); if(listenerNumber!=m_PersistentCallsDelegate.Length) m_PersistentCallsDelegate=new PersistantCallDelegate[listenerNumber]; MonoBehaviour listener; for (int i=0; i<listenerNumber; i++) { listener=this.GetPersistentTarget(i) as MonoBehaviour; if(listener.gameObject==target){ PersistantCallDelegate callDelegate=m_PersistentCallsDelegate[i]; if(callDelegate==null){ MethodInfo methodInfo=listener.GetType().GetMethod(this.GetPersistentMethodName(i)); //Call thru reflection is slow //methodInfo.Invoke(listener,new object[]{damage,null}); m_PersistentCallsDelegate[i]=callDelegate=System.Delegate.CreateDelegate(typeof(PersistantCallDelegate),listener,methodInfo) as PersistantCallDelegate; } callDelegate.Invoke(); } } } } public new void RemoveAllListeners (){ m_Calls.Clear (); base.RemoveAllListeners (); } public new void RemoveListener (UnityAction call) { int id = (call.Target as UnityEngine.Object).GetInstanceID (); if (m_Calls.ContainsKey (id)) m_Calls.Remove (id); base.RemoveListener (call); } }
look very interessing you can help me to understand
ReplyDeleteSure. Ask?
ReplyDeletenot sure how call the event my fake try
ReplyDeleteusing UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Events;
using UnityEditor;
using UnityEditor.Events;
using System;
using System.Linq;
using System.Reflection;
public class fake : MonoBehaviour {
public UnityEvent myEvent= new UnityEvent(); //you must initialize
public int ready;
[Serializable]//<-- don't foget this or you event wouldn't be drawn in Editor
public class SequenceEvent:UnityEvent
{
public fake OnStart = new fake.SequenceEvent ();
public fake OnEnd = new fake.SequenceEvent ();
}
public event UnityAction SequenceNodeStart {
add {
onStart.AddListener (value);
}
remove {
onStart.RemoveListener (value);
}
//sequence.onStart += onSequenceStart;
}
/*
public void onStart(){
}*/
// Use this for initialization
void Start () {
// OnStart.Invoke(Sequence);
}
//sequence.OnEnd.RemoveListener (onSequenceStart );
//sequence.OnEnd.AddListener (onSequenceStart );
// Update is called once per frame
void Update () {
myEvent.Invoke(ready);
}
}
cool
ReplyDeleteOnStart.AddListener (MyOnStartEventHandler);
OnStart.Invoke(ready);
resquest the int value
yes i have missing somethink's about int value
ReplyDeleteyeah, the problem with editor displaying source code stripping < and > Sorry!
ReplyDelete[Serializable]//<-- don't foget this or you event wouldn't be drawn in Editor
public class SequenceEvent:UnityEvent <int>// You subclass from UnityEvent<int> where T1 is int cos you want to send "ready" which is int
{//if you want Event with 2 arguments => you will subclass from UnityEvent<T1,T2> T1 and T2 will be your types example <string,int>
}
using UnityEngine.Events;
ReplyDeleteusing UnityEditor;
using System;
using UnityEngine;
public class fake : MonoBehaviour {
public int ready;
//
public SequenceEvent OnStart = new SequenceEvent ();
public SequenceEvent OnEnd = new SequenceEvent ();//another event defined
[Serializable]//<-- don't foget this or you event wouldn't be drawn in Editor
public class SequenceEvent:UnityEvent<int>// You subclass from UnityEvent <int>where t1 is int cos you want to send "ready" which is int
{
}
void Awake(){
//register handler to your events (what function will be execute when event happen)
OnStart.AddListener (MyOnStartEventHandler);
}
// Use this for initialization
void Start () {
}
void MyOnStartEventHandler(int data){//handler function should take one argument of type int cos it is handling event which dispatch argument "ready" of type int
Debug.Log ("Event happen data=" + data);
}
// Update is called once per frame
void Update () {
OnStart.Invoke(ready);//make the event happen !!! do this somehere else cos this will dispatch events every update
}
}