在游戏中使用正确的多线程
本文关键字:多线程 游戏 | 更新日期: 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类的东西来对应该由物理线程计算的对象进行排队。这样就不需要每次都重新创建线程。只要让它们闲置,并在有东西进入队列的那一刻进行计算。