3

When trying to access a gameobjects position from a timer like this:

public static void Start()
{
    if(!timerStarted)
    {
        timerStarted = true;
        timer = new Timer();
        timer.Interval = 1000;

        timer.Elapsed += CheckEarnings;
        timer.AutoReset = true;
        timer.Enabled = true;
    }            
}

private static void CheckEarnings(object sender, ElapsedEventArgs e)
{
    foreach (BuildingData building in BuildingRegistry.registeredBuildings)
    {
        MonoBehaviour.print(building.attachedGameObject.transform.position);
    }
}

(In a Static Class if this makes any difference)

...The following Error is thrown:

get_transform can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function. UnityEngine.GameObject:get_transform() PlayerManagement:CheckEarnings(Object, ElapsedEventArgs) (at Assets/Scripts/PlayerManagement.cs:53) System.Timers.Timer:Callback(Object)

For me it looks like there is another thread created for the timer which can't access the main thread. How can I solve this problem?

  • Rather than fixing your problem, try to google about using timers in unity: [click](http://answers.unity3d.com/questions/351420/simple-timer-1.html), [click](http://answers.unity3d.com/questions/225213/c-countdown-timer.html), [click](http://answers.unity3d.com/questions/940197/c-simple-timer.html) ... – Sinatr Sep 11 '17 at 15:34
  • 1
    I was searching for a Solution for the System.Timers Timer but thats also a possibility. – Niklas Gromann Sep 11 '17 at 15:47
  • You can use `System.Timers` for this. See the link in the duplicate for how to call unity function from another thread. – Programmer Sep 11 '17 at 22:10

2 Answers2

1

You're correct in assuming that the issue is the timer is running on a seperate thread, that said there are many different solutions to this. I assume based on your syntax that you are using System.Timers.Timer, this in your instance means that the timer runs the elapsed event on a ThreadPool.

If you are intent on using the C# timers rather than creating your own timer using Time.deltaTime then the issue becomes how to use the UnityAPI on a seperate thread, the simple truth is that you can't. Unity is thread safe by design and thus you must create a system for running functions on the main thread by creating a list of Delegates and then on the main thread running through that list executing them all.

The simplest solution though would be to simply store a float value of total time elapsed and every frame increase it by Time.deltaTime, the float value will represent the seconds passed and you can quite easily call functions based on that value.

Matthew Loveday
  • 495
  • 4
  • 12
0

Originally there was no protection against cross threading operations. The end results were less then optimal and often non-deterministic. So the CrossThreadExceptions were added. The proper way to avoid them is to invoke. However my knowledge is about C# .NET, not Unity. And in Unity stuff might be named differntly.

Note that different 'Timer' classes can have totally different behavior. i.e., .NET Actually as no less then 4 'Timer' classes whose behaviors could not be more different. When writing this article explaining them it was only 3: https://web.archive.org/web/20150329101415/https://msdn.microsoft.com/en-us/magazine/cc164015.aspx

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • I thought Invoke exclusively for Winform applications? That's what I thought at least, hence why I did not mention it in my answer. – Matthew Loveday Sep 11 '17 at 16:06