Faster way to call a function from a string than sendmessage?

Hey there,

is there any faster way to call a function when all you have is a constructed name of a function as string than using sendmessage? I read somewhere, that something called refraction would be about 10 - 50x faster than sendmessage. So is it true, that sendmessage is that slow? And is there an “easy / not so complicated” way to call a function from a string variable like it is possible with sendmessage?

I think you mean “Reflection”. And you should probably post some code. SendMessage is terrible slow. Reflection is also rather slow. Using both to invoke a method is a sign of a potential flaw in your code design.

Okay, I wrote a quick script to help understanding what I want to do. I followed a tutorial for building Finite state machines, but I did not understood the 2nd and 3rd Part. So I figgured out how to have it even more easy (and slower).

Here is my Script prototype (Going to use Coroutines instead of methods later I think).

using UnityEngine;
using System.Collections;

public class StateManagerForUnits : MonoBehaviour {

#region Variables
	public enum State
	{
		Init,				// Initialize
		Warten,				// Waiting
		Laufen,				// Running
		Verfolgen,			// Following
		Angreifen,			// Attacking
		Sterben				// Dying
	}

	private State _state;
#endregion


#region Properties
	public State state
	{
		get
		{
			return _state;
		}
		set
		{
			SendMessage (_state.ToString () + "_Exit");
			_state = value;
			SendMessage (_state.ToString () + "_Start");
		}
	}
#endregion


#region State Loop
	IEnumerator Start () 
	{
		state = State.Init;

		while (true) 
		{
			switch(state)
			{
			case State.Init:
				Init_Update();
				break;
			case State.Warten:
				Warten_Update();
				break;
			case State.Laufen:
				// Do Something
				break;
			case State.Verfolgen:
				// Do Something
				break;
			case State.Angreifen:
				// Do Something
				break;
			case State.Sterben:
				// Do Something
				break;
			}

			yield return null;
		}
	}
#endregion


#region Init
	void Init_Start()
	{
		// What should happen when this State is started
	}

	void Init_Update()
	{
		// 
	}

	void Init_Exit()
	{
		
	}

	void Init_OnTriggerEnter(Collider col)
	{
		
	}

	void Init_OnTriggerExit(Collider col)
	{
		
	}
#endregion


#region Warten
	void Warten_Start()
	{
		// What should happen when this State is started
	}

	void Warten_Update()
	{
		// 
	}

	void Warten_Exit()
	{
		
	}

	void Warten_OnTriggerEnter(Collider col)
	{
		
	}

	void Warten_OnTriggerExit(Collider col)
	{
		
	}
#endregion

#region Laufen
	// Fill Methods in here
#endregion

#region Verfolgen
	// Fill Methods in here
#endregion

#region Angreifen
	// Fill Methods in here
#endregion

	
#region Collision stuff
	void OnTriggerEnter(Collider col)
	{
		SendMessage (state.ToString () + "_OnTriggerEnter", col);
	}
	void OnTriggerExit(Collider col)
	{
		SendMessage (state.ToString () + "_OnTriggerExit", col);
	}
#endregion
}

So maybe there’s a way of using some faster (and not to difficult) solution instead of sendmessage.

If I was you I’d separate all states into different classes that implement a common interface (IAIState, for example). A Dictionary or a List, or even a HashSet collection may hold one instance of each state for example, and you’d also need at least one additional field to point to the current state. Even better if the set of states is predefined and never changes just add them all as fields to your MonoBehaviour derived class. Make sure currentState field points to the instance and not its name as a string if you go with a Dictionary, so you don’t have to do the lookups on every call…

Another maybe more interesting way would be to implement states as MonoBehaviours. Then you or level designers can choose which states to add to which unit. Then your manager behaviour will only have to enable and disable them to switch the state. MonoBehaviour classes have a couple of magic methods that get called by Unity when the component is activated or deactivated, or on every update, which may simplify your logic inside the manager. However using this approach you may have a little less control on the order of events, which may make difference while programming complex AI or any other state machines, so my suggestion would be to still go with the first option, unless you’re trying something really simple.

If I could upvote this post, I would do it just for that part.

Is it possible for you to give a short example for this to make it easier for me to understand?

Here you go:

using UnityEngine;
using System.Collections;

interface IAIState
{
    void Start();
    void Update();
    void Exit();

    void OnTriggerEnter(Collider col);
    void OnTriggerExit(Collider col);
}

class AIStateInit : IAIState
{
    public override void Start() { /* Do something */ }
    public override void Update() { /* Do something */ }
    public override void Exit() { /* Do something */ }

    public override void OnTriggerEnter(Collider col) { /* Do something */ }
    public override void OnTriggerExit(Collider col) { /* Do something */ }
}

class AIStateWarten : IAIState
{
    public override void Start() { /* Do something */ }
    public override void Update() { /* Do something */ }
    public override void Exit() { /* Do something */ }

    public override void OnTriggerEnter(Collider col) { /* Do something */ }
    public override void OnTriggerExit(Collider col) { /* Do something */ }
}

class AIStateLaufen : IAIState
{
    // You'll have to implement all 5 interface methods for this class too!
}

class AIStateVerfolgen : IAIState
{
    // You'll have to implement all 5 interface methods for this class too!
}

// Add all state classes here...

public class StateManagerForUnits : MonoBehaviour
{
    // Simple solution, keep one instance of each state in dedicated fields.
    // Consider making all or some of these static eventually?
    private AIStateInit   initState   = new AIStateInit();
    private AIStateWarten wartenState = new AIStateWarten();
    private AIStateLaufen laufenState = new AIStateLaufen();
    // ... add one field for each of your states ...

    // Note here the type of _state is the common interface IAIState
    private IAIState _state;

    public IAIState state
    {
        get { return _state; }
        set
        {
            if (_state != null)
                _state.Exit();
            _state = value;
            if (_state != null)
                _state.Start();
        }
    }

    void Start()
    {
        // This is how you set the state:
        state = initState;
    }

    void Update()
    {
        if (_state != null)
            _state.Update();
    }

    void OnTriggerEnter(Collider col)
    {
        if (_state != null)
            _state.OnTriggerEnter(col);
    }

    void OnTriggerExit(Collider col)
    {
        if (_state != null)
            _state.OnTriggerExit(col);
    }
}

Note that the Start method in MonoBehaviour class is a special method and it isn’t executed as a Coroutine! Same goes for Update and a bunch of other methods, see docs: https://docs.unity3d.com/Documentation/ScriptReference/MonoBehaviour.html

Now see, if you were asking me for this example you’ll have to read a little bit about object-oriented programming and understand the basics of that concept first, otherwise this all may make very little sense to you. OOP is a must and not too difficult to learn, although it may take a little bit time until you get used to it, but trust me, it’s worth the efforts. :wink:

Hello,

so first of all I wish u all a happy new year :slight_smile:

I looked at your script and it really helped me. I got everything to work (with some fixes).
The only problem I am having now is setting the right state from the inside of another state. Here is some code:

using UnityEngine;
using System.Collections;



public interface IServerState
{
	void Start();
	void Update();
	void Exit();
	
	void OnPlayerConnected(NetworkPlayer networkPlayer);
	void OnPlayerDisconnected(NetworkPlayer networkPlayer);
}



public class StateManagerForServer : MonoBehaviour
{
	// Simple solution, keep one instance of each state in dedicated fields.
	// Consider making all or some of these static eventually?
	public ServerStateIdle stateIdle = new ServerStateIdle();
	public ServerStateInit stateInit = new ServerStateInit();
	public ServerStateLobby stateLobby = new ServerStateLobby();
	public ServerStateLoading stateLoading = new ServerStateLoading();
	public ServerStateRunning stateRunning = new ServerStateRunning();
	public ServerStateRestartingToLobby stateRestarting = new ServerStateRestartingToLobby();
	
	// Note here the type of _state is the common interface IServerState
	private IServerState _state;
	
	public IServerState state
	{
		get 
		{ 
			return _state; 
		}
		set	
		{
			if (_state != null)
				_state.Exit();
			_state = value;
			if (_state != null)
				_state.Start();	
		}	
	}
	
	
	
	void Start()
		
	{
		// Setting current state
		state = stateIdle;
	}
	
	
	
	void Update()	
	{
		if (_state != null)
			_state.Update();		
	}
	
	
	
	public void OnPlayerConnected(NetworkPlayer networkPlayer)
	{
		if (_state != null)
			_state.OnPlayerConnected (networkPlayer);
	}
	
	
	
	public void OnPlayerDisconnected(NetworkPlayer networkPlayer)
	{
		if (_state != null)
			_state.OnPlayerDisconnected (networkPlayer);
	}
}



// Idle
public class ServerStateIdle : IServerState	
{
	public void Start() 
	{ 
		Debug.Log ("Init Start");
		StateManagerForServer.state = StateManagerForServer.stateInit;
	}
	public void Update() 
	{
		Debug.Log ("Init Update");
	}
	public void Exit() 
	{
		Debug.Log ("Init Exit"); 
	}
	
	public void OnPlayerConnected(NetworkPlayer networkPlayer){}
	public void OnPlayerDisconnected(NetworkPlayer networkPlayer){}
}



// Init
class ServerStateInit : IServerState	
{
	public void Start()
	{
		Debug.Log ("Init Start");
		StateManagerForServer.state = StateManagerForServer.stateLobby;
	}
	public void Update() 
	{
		Debug.Log ("Init Update");
	}
	public void Exit() 
	{ 
		Debug.Log ("Init Exit");
	}
	
	public void OnPlayerConnected(NetworkPlayer networkPlayer){}
	public void OnPlayerDisconnected(NetworkPlayer networkPlayer){}
}

For this example I just included 2 states, actually there are more.
So for testing purpose I tried to set the state to init from inside idle. The idle class is set to public. But when typing the code it does not make any suggestions for Variables or methods which are inside the “public class StateManagerForServer : MonoBehaviour”. Its like it does not handle it like a class.

Same when trying from inside of init which is not set to public. My goal would be to make the state variable thats inside of my StateManagerForServer accessible from the inside of all the states.
Until now I just called variables inside other classes this way: class.variable = x; But seems not to work this time. I am sure I forgot something, but I am not sure what is it yet. Any Tips?

I just saw that I might have forgotten to instantiate my class StateManagerForServer -_-. But I am pretty sure in the past in other scripts it worked without that.