C# 在游戏中使用正确的多线程

C# 在游戏中使用正确的多线程,c#,multithreading,C#,Multithreading,我目前正在开发一个3D游戏,它应该模拟可变形的3D对象。使用可变形,我的意思是3D对象的几何体在帧之间变化。游戏中有一个渲染循环,应该尽可能频繁地执行(至少30次/秒),以获得下降帧速率。但在这个渲染循环中,我还做了另外两件事:计算新几何体和计算轴对齐的边界框层次(AABVH)。我需要AABVH进行碰撞检测。在渲染三维对象之前完成几何体和AABVH计算非常重要。计算新的几何体和AABVH是一项耗时的任务,因此我的帧速率会迅速下降。因此,我的想法是在一个单独的线程中计算AABVH。 这看起来像这样

我目前正在开发一个3D游戏,它应该模拟可变形的3D对象。使用可变形,我的意思是3D对象的几何体在帧之间变化。游戏中有一个渲染循环,应该尽可能频繁地执行(至少30次/秒),以获得下降帧速率。但在这个渲染循环中,我还做了另外两件事:计算新几何体和计算轴对齐的边界框层次(AABVH)。我需要AABVH进行碰撞检测。在渲染三维对象之前完成几何体和AABVH计算非常重要。计算新的几何体和AABVH是一项耗时的任务,因此我的帧速率会迅速下降。因此,我的想法是在一个单独的线程中计算AABVH。 这看起来像这样:

Thread t;
public void Render(Object3D o) // renders 3D object
{
  if (t != null) // wait until the new geometry got calculated
  {
     t.Join();
  }
  o.RenderGeometry();
  t = new Thread(() => o.CalcAABVH());
  t.Start();
}

我不是C#并行编程方面的专家,但我确信这不是一个好的解决方案。每一帧,都会创建、执行和销毁一个新线程,这会造成很大的开销。在我的例子中,一个好的解决方案会是什么样子?

在几何体更新时,不应该阻止下一帧的渲染。您也可以同步运行计算。阻止渲染将增加每个帧的总渲染时间,并对帧速率产生负面影响-这是您特别试图避免的效果

相反,使用标志指示新几何体的计算是否正在进行,如果正在进行,则渲染旧几何体。现在,您需要两个几何体集-就像使用帧缓冲区一样,在新几何体完成后交换它们

最终的效果是,某些帧可能没有您希望的最新几何体。这在几何体计算时间较长的对象上更为明显。大多数游戏的图形编程都是烟雾和镜子,所以不用担心

实施 请为这些更新操作使用线程池线程,因为创建成本较低。理想情况下,使用任务并行库。作为奖励,
任务
对象为您跟踪其运行状态

    using System.Threading.Tasks;

    ...

    Task t;
    public void Render(Object3D o) // renders 3D object
    {
        if(t != null && t.Status == System.Threading.Tasks.TaskStatus.Running)
        {
            //render old geometry
        }
        else
        {
            t = Task.Factory.StartNew(o.CalcNewGeometry())
                    .ContinueWith(p => o.UpdateGeometry); //swap the new geometry in
        }
    }

您需要在此处进行一些同步,以确保在渲染旧几何体时不会交换新几何体。我将由您决定。

游戏中的多线程主要是这样使用的(取决于类型、功能等):

  • 1线程渲染

  • 1..x线程-物理(变形所属的位置)

  • x线程-AI
渲染对象的实际状态,在physic线程的副本中对其进行变形。 按照Gusdor的建议在变形后切换对象


我建议您使用类似于类的东西来对应该由physic线程计算的对象进行排队。这样,您就不需要每次都重新创建线程。让他们闲着,在有什么东西进入队列时计算。

这是在哪个平台上?OpenGL?DirectX 11?你能在GPU上的着色器中修改几何体吗?您是否可以简化变形网格以使其更合理,并与GPU合作,例如使用蒙皮网格?多线程是非常复杂的,你真的想避免它,直到它是绝对必要的。现代的DX使这变得更容易,因为您可以从多个线程等访问DX资源,但这仍然相当困难。如果您确实需要多线程,Gusdor的建议是避免大多数复杂性的好方法;多线程不一定会提高应用程序的性能。只有当您有多个CPU内核并确保在不同的内核上调度线程时,才能获得显著的性能提升。显然,硬件图形加速器是实现这一点的一个很好的方法。:-)@Luaan:它是Windows上的OpenGL。你说得对,把它做好既昂贵又复杂。问题是,我不仅要计算新几何体,还要计算新几何体的轴对齐边界框层次。我不能用几何体着色器来做这个。@Mikeofst:绝对正确!硬件图形加速器?从没听说过。这是什么?抱歉我不够精确。我实际上在计算两件事:新几何体和边界体积层次。我已经编辑了我的问题。如果OP想要固定步长的模拟,例如弹跳球,则渲染旧几何体可能不是一个选项。尽管如此,这至少允许他使用线程池来安排工作,并在需要时甚至在最后一帧开始处理之前实际安排工作,这可能会大大有助于延迟。@Luaan,如果你想要固定步长的模拟,您可以同步执行以下步骤:
计算物理->计算几何体->渲染
。准确度比帧率更重要。对我来说,这看起来像是基于实体的游戏编程,我坚持这个主题。是的,这是通常的方式。但是,如果在
Update
步骤中更早地开始处理,则可以轻松地减少一两毫秒的延迟,这对FPS有很大帮助。毕竟,OPs解决方案已经落后于任何输入一帧——他在渲染最后一个几何体后计算下一个几何体。但是我确信我会得到一个NullReferenceException,因为当第一次到达if子句时,t是null。所以我必须事先创建一个任务,而不是在else子句中,对吗?谢谢你的努力。我就是这样实现的。效果很好。