尝试从这样的计时器访问gameobjects位置时:
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:回调(对象)
对我来说,看起来还有另一个为计时器创建的线程无法访问主线程。 我该如何解决这个问题?
答案 0 :(得分:0)
最初没有针对交叉线程操作的保护。最终结果不是最优的,而且通常是非确定性的。因此添加了CrossThreadExceptions。避免它们的正确方法是invoke。但是我的知识是关于C#.NET,而不是Unity。在Unity中,可能会有不同的名字命名。
请注意,不同的“Timer”类可能会有完全不同的行为。也就是说,.NET实际上不少于4个'Timer'类,其行为不会更加不同。写这篇文章时解释它们只有3: https://web.archive.org/web/20150329101415/https://msdn.microsoft.com/en-us/magazine/cc164015.aspx
答案 1 :(得分:0)
你认为问题是计时器在一个单独的线程上运行是正确的,它说有很多不同的解决方案。我假设您根据语法使用System.Timers.Timer
,这在您的实例中意味着计时器在ThreadPool
上运行已过去的事件。
如果您打算使用C#定时器而不是使用Time.deltaTime
创建自己的计时器,那么问题就变成了如何在单独的线程上使用UnityAPI,简单的事实是您不能。 Unity在设计时是线程安全的,因此您必须创建一个系统,通过创建Delegates列表来运行主线程上的函数,然后在运行该列表的主线程上执行它们。
最简单的解决方案是简单地存储经过的总时间的浮点值,每帧增加Time.deltaTime
,浮点值将代表传递的秒数,您可以很容易地根据该值调用函数