Pergunta

I'm trying to replicate MonoBehaviour of the Unity 3D engine. I'm using monodevelop on Linux, and most testing will be done in Windows.

More about MonoBehavior.Start can be read here.

I want to invoke the Start method on all types which inherit from MonoBehavior.

This is what I have tried:

class MonoBehaviour
{
  static Type[] types = Assembly.GetExecutingAssembly().GetTypes();

  public static void Main ()
  {
    foreach (Type type in types)
    {
      if (type.IsSubclassOf(typeof(MonoBehaviour)))
      {
        MethodInfo minfo = type.GetMethod ("Start");
        mInfo.Invoke (type, null); // This throws
      }
    }
  }
}

class example : MonoBehaviour
{
  void Start()
  {
    // I want this to be run from MonoBehaviour
    Console.WriteLine ("HelloWorld");
  }
}

class example1 : MonoBehaviour
{
}
Foi útil?

Solução

EDIT Completely redoing this answer as now I believe I know what you are asking.

I'm going to give you code that will allow you to call Start of every object of a Type in an assembly. However, I don't think this is how MonoBehavior works. There is more to consider. For example, Awake should be called first, and there is the condition of whether scripts have been enabled. Also, I don't think this method is called on all object at once, but perhaps only when the object is create/initiated. I do not have Unity 3D experience, so I'm ignorant to these details. I'm only relying on what I've read from the link in your question.

I've added some extra binding flags to show that this could find methods of various scopes. Also, in my code I am explicitly looking for Start methods that have no parameters. This can be modified to your needs and liking.

using System;
using System.Reflection;
class Program
{
  static void Main(string[] args)
  {
    Assembly asm = Assembly.GetExecutingAssembly();
    Type[] types = asm.GetTypes();

    foreach (var type in types)
    {
      if (type.IsSubclassOf(typeof(MonoBehavior)) || type.GetInterfaces().Contains(typeof(MonoBehavior)))
      {
        MethodInfo startMethod = type.GetMethod("Start", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null);
        if (startMethod != null)
        {
          ConstructorInfo ctor = type.GetConstructor(new Type[] { });
          if (ctor != null)
          {
            object inst = ctor.Invoke(new object[] { });
            startMethod.Invoke(inst, new object[] { });
          }
        }
      }
    }
  }
}

interface MonoBehavior
{
}

class Example1 : MonoBehavior
{
  static void Start()
  {
    Console.WriteLine("Example1 Start");
  }
}

class Example2 : MonoBehavior
{
}

Now, if you don't like creating an object, you could make Start static, and a few lines in the reflection would be adjusted like this:

MethodInfo startMethod = type.GetMethod("Start", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { }, null);
if (startMethod != null)
{
  startMethod.Invoke(null, new object[] { });
}

Note that I no longer need to create an instance, and the first parameter passed to Invoke is null (this is always done for static methods).

However, I don't get the impression from reading the Unity3D doc on MonoBehaviour.Start that it is intended to run in this manner, let alone on static. I think it should be run when MonoBehaviour are created, under other various conditions. That model would not require reflection, but I would recommend building a type of factory model, where you are creating MonoBeaviour objects on requests to a factory object - which invokes the appropriate Awake and Start methods before returning the instance.

Outras dicas

A type is not its own subclass, that's why you have three types but only 2 match the condition. (If that was the question.) Besides, MonoBehaviour doesn't have a Start() method, or I'm very tired ;-).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top