Unity3d 访问主线程的计时器

3

当尝试像这样从定时器中访问gameobject的位置:

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);
    }
}

(如果这是一个静态类会有所不同)
…会抛出以下错误:
get_transform只能从主线程调用。构造函数和字段初始化器将在加载场景时从加载线程执行。请勿在构造函数或字段初始化器中使用此函数,而是将初始化代码移动到Awake或Start函数中。UnityEngine.GameObject:get_transform() PlayerManagement:CheckEarnings(Object, ElapsedEventArgs)(位于Assets/Scripts/PlayerManagement.cs:53) System.Timers.Timer:Callback(Object)
在我看来,似乎为计时器创建了另一个线程,该线程无法访问主线程。如何解决这个问题?

不要只是解决问题,尝试在谷歌上搜索有关在Unity中使用计时器的信息:点击点击点击 ... - Sinatr
1
我正在寻找 System.Timers.Timer 的解决方案,但也有可能是其他的选项。 - Niklas Gromann
你可以使用 System.Timers 来实现。请参考重复链接中的内容,了解如何从另一个线程调用 Unity 函数。 - Programmer
2个回答

1

你的判断是正确的,问题在于计时器在一个单独的线程上运行,但是有很多不同的解决方案。根据你的语法,我假设你正在使用System.Timers.Timer,这意味着在你的实例中,计时器在ThreadPool上运行。

如果你想使用C#计时器而不是使用Time.deltaTime创建自己的计时器,那么问题就变成了如何在单独的线程上使用UnityAPI,简单的事实是你不能这样做。Unity是通过设计来保证线程安全的,因此你必须创建一个运行函数的系统,在主线程上创建一个Delegates列表,然后遍历该列表并执行其中的所有函数。

最简单的解决方案是仅存储总时间流逝的浮点值,并且每帧增加 Time.deltaTime,该浮点值将表示经过的秒数,您可以根据该值轻松调用函数。


0

最初没有保护措施来防止跨线程操作。结果不够理想,常常是非确定性的。因此,添加了CrossThreadExceptions。避免它们的正确方法是invoke。然而,我的知识是关于C# .NET,而不是Unity。在Unity中,事物可能被命名为不同的名称。

请注意,不同的“计时器”类可以具有完全不同的行为。即,.NET实际上至少有4个“计时器”类,它们的行为可能大相径庭。在编写本文解释它们时,只有3个: https://web.archive.org/web/20150329101415/https://msdn.microsoft.com/en-us/magazine/cc164015.aspx


我认为Invoke只适用于Winform应用程序?至少我是这么想的,所以我在我的答案中没有提到它。 - Matthew Loveday

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接