I have a project with a lot of events called by triggers. I want to be able to select in the inspector certain functions that will be called at the right time. Up to this point would work well with the 4.6 UnityEvent from UnityEngine.Events.
The problem is that UnityEvent allows only one parameter and does not accept any type. I needed something that would show in the inspector any type, exactly how are shown public variables from components. I think I could solve the problem using reflection but Iâm stuck because I do not know how to create a field in the inspector knowing the type of the variable.
So I was actually planning on writing an article over on my website http://jupiterlighthousestudio.com/ some time soon to do with this very subject.
I already have the code for it uploaded in the open version of my framework.
The component side is the âTriggerâ class.
[edit]no longer available at this time[/edit]
/Scenario/Trigger.cs
This class allows configuring targets to trigger on some event. I usually inherit from the âTriggerComponentâ class to define specific Trigger types.
For example here is an IntervalTrigger that triggers on some interval:
[edit]no longer available at this time[/edit]
/Scenario/IntervalTrigger.cs
Or one that triggers OnTriggerEnter:
[edit]no longer available at this time[/edit]
/Scenario/EnterTrigger.cs
They can trigger in 4 possible ways.
âTriggerAllOnTargetâ - triggers all ITriggerableMechanisms on some target GameObject
âTriggerSpecificâ - triggers a specific ITriggerableMechanism on some target GameObject
âSendMessageâ - sends a message to some target GameObject
âCallMethodOnSelectedTargetâ - calls a method on some component of the target GameObject. This is the one youâre interested in
The ITriggerableMechanism interface:
[edit]no longer available at this time[/edit]
/Scenario/ITriggerable.cs
Here is the inspector for it:
[edit]no longer available at this time[/edit]
/Scenario/TriggerInspector.cs
And the PropertyDrawer for the TriggerTarget. This being where I actually have the inspector code for how you want to trigger the target, of which the âCallMethodOnSelectedTargetâ is an option.
[edit]no longer available at this time[/edit]
/Scenario/TriggerTargetPropertyDrawer.cs
Youâll be interested in the method âDrawAdvanced_CallMethodOnSelectedâ.
The result looks like:
I know, sorry, your answer is buried inside of a huge amount of code that does more than you want.
I would break this up further, or be more in depth, but thatâs essentially writing the article. And I donât plan on doing that for a few days as the amount of work required is outside of my means with my current schedule.
Thanks you, I looked at the source code and I found interesting things
First, make your Base class with main method (called Done in this case):
using UnityEngine;
//Base Class Function
public abstract class Function : MonoBehaviour {
//Main Method. Don't need return bool state
public abstract void Done();
}
After, you can create derived classes from Function base class. For example:
using UnityEngine;
[AddComponentMenu("Function/ChangeColorBackground")]
public class ChangeColorBackground : Function {
public Color color;
public override void Done(){
Camera.main.backgroundColor = color;
}
}
To test:
using UnityEngine;
public class TestFunction : MonoBehaviour {
public Function Press1;
public Function Press2;
public Function Press3;
public Function Press4;
public Function Press5;
void Update(){
if(Input.GetKeyDown(KeyCode.Alpha1)) Press1.Done();
if(Input.GetKeyDown(KeyCode.Alpha2)) Press2.Done();
if(Input.GetKeyDown(KeyCode.Alpha3)) Press3.Done();
if(Input.GetKeyDown(KeyCode.Alpha4)) Press4.Done();
if(Input.GetKeyDown(KeyCode.Alpha5)) Press5.Done();
}
}
If you need Action instead of Function. Action return bool state, and tell you if it has finished:
using UnityEngine;
//Base Class
public abstract class Action : MonoBehaviour {
//Main Method. Abstract, you must override in derived classes. Using argument GameObject.
public abstract bool Done(GameObject gameObject);
}
Derived classes GoTo and PlaySound:
using UnityEngine;
using System.Collections;
//Go To position with velocity
[AddComponentMenu("Action/GoTo")]
public class GoTo : Action{
public Vector3 position;
public float velocity;
public override bool Done(GameObject go){
Vector3 vector = (this.position - go.transform.position).normalized;
float move = this.velocity * Time.deltaTime;
float distance = Vector3.Distance(this.position,go.transform.position);
if(distance>move){
go.transform.Translate(vector*move);
return false;
}
go.transform.Translate(vector*distance);
return true;
}
}
using UnityEngine;
using System.Collections;
//Play Sound
[AddComponentMenu("Action/PlaySound")]
public class PlaySound : Action{
public AudioSource audioSource;
public override bool Done(GameObject gameObject){
this.audioSource.Play();
return true;
}
}
To Test:
using UnityEngine;
public class Test : MonoBehaviour {
public Action[] actions;
public int index = 0;
void Start(){
if(actions == null) Destroy(this);
else if(actions.Length == 0) Destroy(this);
}
void Update(){
if(this.index < this.actions.Length){
if(actions[this.index].Done(this.gameObject)) this.index++;
}
}
}
Download link.
I find it humorous that your âFunctionâ is the one without a return. And your âActionâ is the one with a return type.
Despite the fact that function tends to be the word for something that DOES return something (think algebraic function⌠for any given input there is a unique output). And in .net Action is used for a method with no return (often referred to as a âsubroutineâ in programming).
Your solution also restricts a single method to the component. So each method to call must be written into its own component.
And lastly, it does not support parameters directly. Although I guess your component inheriting from âFunctionâ could call another method, and it have the parameters as members of itself.
Imarian97 - just letting you know that I today updated the source code to a version that supports multiple parameter methods.
The reason:
This is because, to use a base class (Function or Action) as a reference for other derived classes can only have one Main Method(Done).
That is, we can not have multiple Main Methods with different returns(void, bool, Vector3, Quaternion, âŚ) or different number/type arguments in the base class. It is a constraint.
Solutions:
-Use Main Method(Done) as Lambda function to call other methods.
-Use optional arguments in Main Method.
-Or create specific EventArgs class to pass method arguments.
-Return multiples values by ref in method arguments(you can create specific class ReturnArgs).
Thanks for helping out, everyone!
@lordofduct After few compiler errors ( I replaced âTrigger.TriggerActivationTypeâ with âcom.spacepuppy.Scenario.Trigger.TriggerActivationTypeâ) it worked. The only problem that I have is that it does not accept all the variables( ex: AudioClip) but I have to look at the code.
Thanks again for your help!
The VariantReference type that I use to house the args wasnât made to support all UnityEngine.Objects when I wrote it. I didnât really need any other at the time.
I could go in and add them. Or add a generic âObjectâ entry⌠though the âObjectâ entry would have the potential for exceptions if your method takes AudioClip and you put a PhysicsMaterial in it.
There, just updated VariantReference and VariantReferencePropertyDrawer to support a generic âObjectâ entry. If you param is a UnityEngine.Object, but not GameObject or a Component it will default to that.
NOTE - if the method is expecting an AudioClip, and you put in something else (because âObjectâ will accept ANY unity object), it will throw an error when the method is triggered.
Why not just use the UnityEvent and UnityActions in 4,6.
-
I wrote this before UnityEvent existed.
-
As OP said in their first post, UnityEvent doesnât support multiple parameters.
-
It supports fewer types (especially now since I added âUnityEngine.Objectâ to mine).
-
4.6 is only in beta at this moment, some of us may not want to go to a beta release at the moment.
-
Personally, we (my team) just prefer mine on our game because weâve been using it for a while.
@lordofduct Thanks for your quick answer. Now itâs exactly what i needed
Happy to help. Iâm just pleased someone else is getting use out of the pile of code Iâve written over the years.
@lordofduct do you mind updating your links as they do not seem to work anymore. I am trying to perform a similar task and would love to look at your code!
The main link is : https://code.google.com/p/spacepuppy-unity-framework/ . You can go to source and checkout or browse the code( âtrunkâ folder)
Yeah I did that before posting, although was unable to locate the above mentioned files.
Although, now I reread the thread and noticed I was supposed to look for the VariantReference property and drawer, and found it. Thanks for reply
Updated links for VariantReference.
I renamed the project recently because Iâm reorganizing some of the code thatâs in the not open part of the framework. May start releasing that as well soon.
The com.spacepuppy.Scenario namespace though has been temporarily removed.