0

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 Unity 3D engine editor.

more about MonoBehaviour.Update can be read here

I want to invoke the Update method on all types which inherit from MonoBehavior every 10ms.

this is how I'm doing with Start

using System;
using System.Reflection;
public class MonoBehaviour{

    public static void Main (){
        Type[] types = Assembly.GetExecutingAssembly().GetTypes();

        foreach (Type type in types) {
            if (type.IsSubclassOf(typeof(MonoBehaviour))){

                System.Reflection.MethodInfo mInfo = type.GetMethod ("Start", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null); // it is run 2 times
                if (mInfo != null) {
                    ConstructorInfo ctor = type.GetConstructor (new Type[] { });
                    if (ctor != null) {
                        object inst = ctor.Invoke (new object[] { });
                        mInfo.Invoke (inst, new object[] { });
                    }
                }
            }
        }
    }
}

class example : MonoBehaviour{
    void Start(){
        // this works perfectly
        Console.WriteLine ("HelloWorld");
    }
    void Update(){
        // I want this to be run every 10 ms
        Console.WriteLine ("HelloinUpdate");
    }
}
MilitaryG
  • 75
  • 1
  • 9

3 Answers3

2

You could do this without using reflection, it would be a bit faster (and safer):

using System;
using System.Linq;
using System.Timers;

public static class Program
{
    public static void Main()
    {
        IEnumerable<Type> assemblyTypes = typeof(MonoBehaviour).Assembly.GetTypes();

        IEnumerable<Type> behaviourTypes = assemblyTypes
            .Where(type => typeof(MonoBehaviour).IsAssignableFrom(type))
            .Where(type => !type.IsAbstract);

        List<MonoBehaviour> behaviours = behaviourTypes
            .Select(Activator.CreateInstance)
            .Cast<MonoBehaviour>()
            .ToList();

        foreach (MonoBehaviour monoBehaviour in behaviours)
        {
            monoBehaviour.Start();
        }

        var timer = new Timer(10 /* Milliseconds */);

        timer.Elapsed += (sender, eventArgs) =>
        {
            foreach (MonoBehaviour monoBehaviour in behaviours)
            {
                monoBehaviour.Update();
            }
        };

        timer.Start();

        Console.WriteLine("Press a key to stop.");
        Console.ReadKey();
    }
}

public abstract class MonoBehaviour
{
    public virtual void Start()
    {
    }

    public virtual void Update()
    {
    }

    protected static void Log(string message)
    {
        Console.WriteLine("{0} - {1}", DateTime.Now, message);
    }
}

public class Behaviour1 : MonoBehaviour
{
    public override void Start()
    {
        Log("Behaviour1 - Start");
    }

    public override void Update()
    {
        Log("Behaviour1 - Update");
    }
}

public class Behaviour2 : MonoBehaviour
{
    public override void Start()
    {
        Log("Behaviour2 - Start");
    }

    public override void Update()
    {
        Log("Behaviour2 - Update");
    }
}:

The output would be something like this (I adjusted the timer to 1000ms):

Output

Community
  • 1
  • 1
khellang
  • 17,550
  • 6
  • 64
  • 84
  • You need to add the following to your cs file: using System.Linq; – Bijington Mar 13 '14 at 12:59
  • Sorry about that. Included the using statements :) – khellang Mar 13 '14 at 13:03
  • sorry for comment before I placed override after void ok thanks, but I've found that in your code I **MUST** have both inside Start **AND** Update, while in `Unity` I don't need to, ... – MilitaryG Mar 13 '14 at 13:24
  • It's because the methods on `MonoBehaviour` is declared as `abstract`. If you change them to `virtual` and add an empty body, you should be able to override which one you want. See my updated answer. – khellang Mar 13 '14 at 13:32
  • gr8 thanks, any possibility for not need to override it? but this is already most what I needed for my testing purposes. – MilitaryG Mar 13 '14 at 13:40
  • 1
    If you don't want to add the `override` keyword (I don't see what the problem is with that), you'd have to implement an interface, but then again, that will force you to implement both methods. Another option is to declare loose functions and do as you did before, call them using reflection. But then there's no way to know if there actually is a method called "Start" or "Update"... – khellang Mar 13 '14 at 13:43
  • thank you very much for your wisdom, also can you change the vars so I'll know what they are? – MilitaryG Mar 13 '14 at 13:54
  • 1
    ProTip™: If you hold your cursor over a method call in Visual Studio, it will show the return value. I updated the answer :) – khellang Mar 13 '14 at 14:01
0

Basically, something like this should do the trick:

var timer = new Timer(10);
timer.Elapsed += new ElapsedEventHandler(MonoBehavior.Update);
timer.Start();

It creates a timer with 10ms then binds your method to the elapsed event and enables the timer to count down. When it reaches 0, the event is fired and the time resets.

Chrisi
  • 371
  • 3
  • 15
0

Use Timer:

Timer t = new System.Threading.Timer(Update, this, 10, 10);
carlpett
  • 12,203
  • 5
  • 48
  • 82