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

本文关键字:多线程 游戏 | 更新日期: 2023-09-27 18:21:15

我目前正在开发一款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#并行编程的专家,但我确信这不是一个好的解决方案。每一帧都会创建、执行和销毁一个新的线程,这会造成很大的开销。在我的情况下,一个好的解决方案会是什么样子?

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

在更新几何体时,不应阻止下一帧的渲染。您还可以同步运行计算。阻止渲染会增加每帧的总渲染时间,并对帧速率产生负面影响——这是您特别想要避免的效果!

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

净效果是,一些帧可能没有最新的几何图形,而你希望它是可用的。这在几何图形需要更长时间计算的对象上会更明显。大多数游戏的图形编程都是烟雾和镜子,所以不用担心

实施

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

    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

渲染对象的实际状态,在物理线程的副本中使其变形。按照Gusdor的建议在变形后切换对象。

我建议您使用类似ConcurrentQueue类的东西来对应该由物理线程计算的对象进行排队。这样就不需要每次都重新创建线程。只要让它们闲置,并在有东西进入队列的那一刻进行计算。