2010-02-26 134 views
16

我正在研究一个简单的碰撞检测演示,其中只包含一堆在窗口中弹跳的对象。 (目标是看看游戏可以一次处理多少物体而不会丢帧)。C#XNA:优化碰撞检测?

有重力,所以物体要么移动要么碰撞墙。

天真溶液为O(n^2):

foreach Collidable c1: 
     foreach Collidable c2: 
      checkCollision(c1, c2); 

这是非常糟糕。所以我设置了CollisionCell对象,它保留了关于屏幕一部分的信息。这个想法是,每个Collidable只需要检查其单元中的其他对象。 60像素×60像素的电池,这样的效果几乎提高了10倍,但我想进一步推动。

一个分析器已经显示,代码花费其时间的50%在每个单元格用来获取其内容的函数中。这里是:

// all the objects in this cell 
    public ICollection<GameObject> Containing 
    { 
     get 
     { 
      ICollection<GameObject> containing = new HashSet<GameObject>(); 

      foreach (GameObject obj in engine.GameObjects) { 
       // 20% of processor time spent in this conditional 
       if (obj.Position.X >= bounds.X && 
        obj.Position.X < bounds.X + bounds.Width && 
        obj.Position.Y >= bounds.Y && 
        obj.Position.Y < bounds.Y + bounds.Height) { 

        containing.Add(obj); 
       } 
      } 

      return containing; 
     } 
    } 

该程序的20%的时间花在那个条件。

这里是上面的函数被调用:

// Get a list of lists of cell contents 
     List<List<GameObject>> cellContentsSet = cellManager.getCellContents(); 

     // foreach item, only check items in the same cell 
     foreach (List<GameObject> cellMembers in cellContentsSet) { 
      foreach (GameObject item in cellMembers) { 
       // process collisions 
      } 
     } 


//... 

    // Gets a list of list of cell contents (each sub list = 1 cell) 
    internal List<List<GameObject>> getCellContents() { 
     List<List<GameObject>> result = new List<List<GameObject>>(); 
     foreach (CollisionCell cell in cellSet) { 
      result.Add(new List<GameObject>(cell.Containing.ToArray())); 
     } 
     return result; 
    } 

现在,我必须每一个细胞迭代 - 即使是空的。也许这可以在某种程度上得到改善,但我不知道如何验证一个单元格是空的而不用以某种方式查看它。 (也许我可以实现类似睡眠对象的东西,在某些物理引擎中,如果对象将会暂时休眠并且不包括在每帧的计算中)。

我可以做些什么来优化这个? (另外,我是C#的新手 - 是否还有其他明显的文体错误?)

当游戏开始落后时,对象倾向于打得相当紧凑,所以没有太多动作在进行。也许我可以趁这个不知何故,写一个函数,看看,给定对象的当前速度,它都不可能在下次调用之前离开其当前单元格Update()

更新1我决定维持名单在最后更新时发现在单元格中的对象,并首先检查它们是否仍在单元格中。另外,我保留了CollisionCell变量的area,当单元格填满时我可以停止查找。这是我实现这一点,它要慢得多使整个演示:

// all the objects in this cell 
    private ICollection<GameObject> prevContaining; 
    private ICollection<GameObject> containing; 
    internal ICollection<GameObject> Containing { 
     get { 
      return containing; 
     } 
    } 

    /** 
    * To ensure that `containing` and `prevContaining` are up to date, this MUST be called once per Update() loop in which it is used. 
    * What is a good way to enforce this? 
    */ 
    public void updateContaining() 
    { 
     ICollection<GameObject> result = new HashSet<GameObject>(); 
     uint area = checked((uint) bounds.Width * (uint) bounds.Height); // the area of this cell 

     // first, try to fill up this cell with objects that were in it previously 
     ICollection<GameObject>[] toSearch = new ICollection<GameObject>[] { prevContaining, engine.GameObjects }; 
     foreach (ICollection<GameObject> potentiallyContained in toSearch) { 
      if (area > 0) { // redundant, but faster? 
       foreach (GameObject obj in potentiallyContained) { 
        if (obj.Position.X >= bounds.X && 
         obj.Position.X < bounds.X + bounds.Width && 
         obj.Position.Y >= bounds.Y && 
         obj.Position.Y < bounds.Y + bounds.Height) { 

         result.Add(obj); 
         area -= checked((uint) Math.Pow(obj.Radius, 2)); // assuming objects are square 
         if (area <= 0) { 
          break; 
         } 
        } 
       } 
      } 
     } 
     prevContaining = containing; 
     containing = result; 
    } 

更新2我放弃了最后一种方法。现在,我试图保持collidables的(orphans)池,当我找到包含这些细胞从它们删除对象:

internal List<List<GameObject>> getCellContents() { 
     List<GameObject> orphans = new List<GameObject>(engine.GameObjects); 
     List<List<GameObject>> result = new List<List<GameObject>>(); 
     foreach (CollisionCell cell in cellSet) { 
      cell.updateContaining(ref orphans); // this call will alter orphans! 
      result.Add(new List<GameObject>(cell.Containing)); 
      if (orphans.Count == 0) { 
       break; 
      } 
     } 
     return result; 
    } 

    // `orphans` is a list of GameObjects that do not yet have a cell 
    public void updateContaining(ref List<GameObject> orphans) { 
     ICollection<GameObject> result = new HashSet<GameObject>(); 

     for (int i = 0; i < orphans.Count; i++) { 
      // 20% of processor time spent in this conditional 
      if (orphans[i].Position.X >= bounds.X && 
       orphans[i].Position.X < bounds.X + bounds.Width && 
       orphans[i].Position.Y >= bounds.Y && 
       orphans[i].Position.Y < bounds.Y + bounds.Height) { 

       result.Add(orphans[i]); 
       orphans.RemoveAt(i); 
      } 
     } 

     containing = result; 
    } 

这只能产生边际的改善,而不是2个或3x I”寻找。

UPDATE 3我再次弃上述方法,并决定让每个对象保持其当前小区:

private CollisionCell currCell; 
    internal CollisionCell CurrCell { 
     get { 
      return currCell; 
     } 
     set { 
      currCell = value; 
     } 
    } 

该值被更新:

// Run 1 cycle of this object 
    public virtual void Run() 
    { 
     position += velocity; 
     parent.CellManager.updateContainingCell(this); 
    } 

CellManager代码:

private IDictionary<Vector2, CollisionCell> cellCoords = new Dictionary<Vector2, CollisionCell>(); 
    internal void updateContainingCell(GameObject gameObject) { 
     CollisionCell currCell = findContainingCell(gameObject); 
     gameObject.CurrCell = currCell; 
     if (currCell != null) { 
      currCell.Containing.Add(gameObject); 
     } 
    } 

    // null if no such cell exists 
    private CollisionCell findContainingCell(GameObject gameObject) { 

     if (gameObject.Position.X > GameEngine.GameWidth 
      || gameObject.Position.X < 0 
      || gameObject.Position.Y > GameEngine.GameHeight 
      || gameObject.Position.Y < 0) { 
      return null; 
     } 

     // we'll need to be able to access these outside of the loops 
     uint minWidth = 0; 
     uint minHeight = 0; 

     for (minWidth = 0; minWidth + cellWidth < gameObject.Position.X; minWidth += cellWidth) ; 
     for (minHeight = 0; minHeight + cellHeight < gameObject.Position.Y; minHeight += cellHeight) ; 

     CollisionCell currCell = cellCoords[new Vector2(minWidth, minHeight)]; 

     // Make sure `currCell` actually contains gameObject 
     Debug.Assert(gameObject.Position.X >= currCell.Bounds.X && gameObject.Position.X <= currCell.Bounds.Width + currCell.Bounds.X, 
      String.Format("{0} should be between lower bound {1} and upper bound {2}", gameObject.Position.X, currCell.Bounds.X, currCell.Bounds.X + currCell.Bounds.Width)); 
     Debug.Assert(gameObject.Position.Y >= currCell.Bounds.Y && gameObject.Position.Y <= currCell.Bounds.Height + currCell.Bounds.Y, 
      String.Format("{0} should be between lower bound {1} and upper bound {2}", gameObject.Position.Y, currCell.Bounds.Y, currCell.Bounds.Y + currCell.Bounds.Height)); 

     return currCell; 
    } 

我认为这将使它更好 - 现在我只需要遍历collidables,而不是所有collidables *单元格。相反,现在这款游戏的速度非常慢,只用我的上述方法提供其性能的十分之一。

探查表明不同的方法是现在的主要热点,并获得邻居的某个对象的时间是平凡短。这种方法并没有从之前的变化,所以也许我打电话比我以前WAY更多...

+0

您是否尝试过使用XNA的farseer physcs引擎? http://www.codeplex.com/FarseerPhysics – jjxtra 2010-02-26 20:34:41

+0

@Rosarch:伟大的职位。很棒的更新。在下面修改我的回答,可能值得检查,我试图突出adv \ dis,更多地解释发生了什么以及为什么,并提及一些替代方案。正如杰夫约翰逊所建议的那样,也值得调查第三方图书馆。如果它有效的话,也可以为自己省去一些悲伤;) – 2010-02-27 17:36:08

+0

我听说原来的Quake引擎通过缩小游戏世界来检查碰撞,直到玩家像素大小,并用它来检查碰撞。 – NibblyPig 2010-03-03 17:14:41

回答

12

它花费了当时50%的这种职能,因为你调用该函数很多。优化这一功能只会对性能产生渐进式改进。

或者,只需调用函数less!

您已经通过设置空间分区方案(查找Quadtrees以查看您的技术的更高级形式)开始了这条路径。

第二种方法是将您的N * N循环分解为增量形式并使用CPU预算

您可以分配一个CPU的预算为每个想在帧时间(更新中)动作的模块。碰撞是其中一个模块,AI可能是另一个模块。

比方说,你要以60fps运行游戏。这意味着您有大约1/60秒= 0.0167秒的CPU时间在帧之间刻录。不,我们可以在我们的模块之间分割这些0.0167秒。让我们给冲突 30%的预算:0.005秒

现在您的碰撞算法知道它可以只花0.005工作的;因此,如果时间不够用,它需要稍后推迟一些任务 - 您将使算法增量。

const double CollisionBudget = 0.005; 

Collision[] _allPossibleCollisions; 
int _lastCheckedCollision; 

void HandleCollisions() { 

    var startTime = HighPerformanceCounter.Now; 

    if (_allPossibleCollisions == null || 
     _lastCheckedCollision >= _allPossibleCollisions.Length) { 

     // Start a new series 
     _allPossibleCollisions = GenerateAllPossibleCollisions(); 
     _lastCheckedCollision = 0; 
    } 

    for (var i=_lastCheckedCollision; i<_allPossibleCollisions.Length; i++) { 
     // Don't go over the budget 
     if (HighPerformanceCount.Now - startTime > CollisionBudget) { 
      break; 
     } 
     _lastCheckedCollision = i; 

     if (CheckCollision(_allPossibleCollisions[i])) { 
      HandleCollision(_allPossibleCollisions[i]); 
     } 
    } 
} 

有,现在也无所谓碰撞码的速度有多快,它会以最快的速度可能不影响用户的感知性能做:作为代码实现这一目标可以很简单。

好处包括:

  • 的算法设计运行出来的时候,它只是恢复下一帧,所以你不必担心这个特殊的边缘情况。
  • 随着高级/耗时算法数量的增加,CPU预算变得越来越重要。认为AI。因此,尽早实施这样一个系统是个好主意。
  • 人体响应时间小于30 Hz,帧循环以60 Hz运行。这给算法30帧来完成它的工作,所以它没有完成它的工作是可以的。
  • 这样做给出稳定,数据无关帧率。
  • 它仍然受益于碰撞算法本身的性能优化。
  • 碰撞算法旨在追踪碰撞发生的“子帧”。那就是,你永远不会幸运地碰到碰撞只是碰巧 - 认为你这样做是对自己撒谎。
+0

但是如果算法耗尽时间不会导致碰撞失败? – 2010-02-26 18:57:39

+0

@Rosarch查看编辑。特别是最后一个要点。 – 2010-02-26 19:20:35

+0

对不起,但我可以肯定地说,优化一个叫做很多的方法,甚至是递增的,都是非常有用的。我能够通过优化我的一些最常用的方法来提高我的引擎性能(*在分析器*的帮助下),并且它得到了回报。如果你在这样的功能中每次调用几百纳秒,你可以节省每个游戏更新的毫秒数。经过大量的优化,使得我的扫描和剪枝算法比我的蛮力更快,但现在是这样。 – RCIX 2010-02-26 22:34:26

5

它看起来像你循环通过所有的游戏对象正好看到包含在细胞哪些对象。似乎更好的方法是存储每个单元格的单元格中的游戏对象列表。如果你这样做,并且每个对象都知道它所在的单元格,那么在单元格之间移动对象应该很容易。这似乎会产生最大的性能增益。

这里是另一个优化技巧,用于确定对象所在的单元格: 如果您已经确定了对象所在的单元格,并且知道基于对象的速度,它将不会更改当前帧的单元格,则不需要重新运行确定对象所在单元格的逻辑。您可以通过创建一个包含该对象所在单元格的边界框来进行快速检查。然后,可以创建一个边界框,该边界框是对象的大小+当前帧的对象的速度。如果单元边界框包含对象+速度边界框,则不需要进一步检查。如果对象不移动,则更容易,只需使用对象边界框即可。

让我知道,如果这是有道理的,或谷歌/ bing搜索的“四叉树”,或者如果你不介意使用开放的源代码,看看这个真棒物理库:http://www.codeplex.com/FarseerPhysics

+1

也被称为“扫除和修剪”,http://en.wikipedia.org/wiki/Sweep_and_prune – Mez 2010-02-26 19:00:52

+0

+1对于更为先进的物理学 – RCIX 2010-02-26 22:55:32

-1

的想法可能是使用一个边界圈。基本上,当创建一个Collidable时,跟踪它的中心点并计算包含整个对象的半径/直径。然后你可以使用类似的东西来消除第一次传球;

int r = C1.BoundingRadius + C2.BoundingRadius; 

if(Math.Abs(C1.X - C2.X) > r && Math.Abs(C1.Y - C2.Y) > r) 
/// Skip further checks... 

这种下降大部分对象比较两个,但是这多少会得到你我不知道...简介!

+1

如果对象是静态的,这可能工作。然而,它们处于运动状态,这意味着对边界球的检测退化为对物体的检测。 – 2010-02-26 18:32:40

+0

这取决于游戏所代表的对象的类型......如果它是几何形状的东西,比如圆形和正方形,则可以生成一个公式来检测碰撞...但是在更广泛的术语中,边界框是最简单的游戏。 – 2010-02-26 18:33:32

0

有两件事情可以做,以加快这一进程......但据我可以看到你的检查简单的矩形碰撞的方法就好了。

但是我更换检查

if (obj.Position.X ....) 

随着

if (obj.Bounds.IntersercsWith(this.Bounds)) 

而且我也很替换线

result.Add(new List<GameObject>(cell.Containing.ToArray())); 

对于

result.Add(new List<GameObject>(cell.Containing)); 

由于包含属性返回ICollection<T>,并且继承了由List<T>构造接受IEnumerable<T>

而且方法简单ToArray()迭代到列表中返回一个数组,并创建新的列表时,这个过程再做。

+0

现在'obj'没有'Bounds'属性。它有位置和半径。你认为这是值得实施的吗? – 2010-02-26 18:56:30

+0

@Rosarch我以为你使用System.Drawing.Rectangle来定义你的对象的边界。 – 2010-02-26 19:30:37

+0

该矩形定义了“CollisionCell”的边界,但不是碰撞对象。 – 2010-02-26 19:41:59

7

我可以在这里帮忙;我写了自己的碰撞检测作为一个实验。我想我现在可以告诉你,如果不更改算法,你将无法获得所需的性能。当然,天真的做法很好,但只适用于折叠之前的很多项目。你需要的是Sweep and prune。其基本思路是这样的(从我的碰撞检测库项目):

using System.Collections.Generic; 
using AtomPhysics.Interfaces; 

namespace AtomPhysics.Collisions 
{ 
    public class SweepAndPruneBroadPhase : IBroadPhaseCollider 
    { 
     private INarrowPhaseCollider _narrowPhase; 
     private AtomPhysicsSim _sim; 
     private List<Extent> _xAxisExtents = new List<Extent>(); 
     private List<Extent> _yAxisExtents = new List<Extent>(); 
     private Extent e1; 

     public SweepAndPruneBroadPhase(INarrowPhaseCollider narrowPhase) 
     { 
      _narrowPhase = narrowPhase; 
     } 

     public AtomPhysicsSim Sim 
     { 
      get { return _sim; } 
      set { _sim = null; } 
     } 
     public INarrowPhaseCollider NarrowPhase 
     { 
      get { return _narrowPhase; } 
      set { _narrowPhase = value; } 
     } 
     public bool NeedsNotification { get { return true; } } 


     public void Add(Nucleus nucleus) 
     { 
      Extent xStartExtent = new Extent(nucleus, ExtentType.Start); 
      Extent xEndExtent = new Extent(nucleus, ExtentType.End); 
      _xAxisExtents.Add(xStartExtent); 
      _xAxisExtents.Add(xEndExtent); 
      Extent yStartExtent = new Extent(nucleus, ExtentType.Start); 
      Extent yEndExtent = new Extent(nucleus, ExtentType.End); 
      _yAxisExtents.Add(yStartExtent); 
      _yAxisExtents.Add(yEndExtent); 
     } 
     public void Remove(Nucleus nucleus) 
     { 
      foreach (Extent e in _xAxisExtents) 
      { 
       if (e.Nucleus == nucleus) 
       { 
        _xAxisExtents.Remove(e); 
       } 
      } 
      foreach (Extent e in _yAxisExtents) 
      { 
       if (e.Nucleus == nucleus) 
       { 
        _yAxisExtents.Remove(e); 
       } 
      } 
     } 

     public void Update() 
     { 
      _xAxisExtents.InsertionSort(comparisonMethodX); 
      _yAxisExtents.InsertionSort(comparisonMethodY); 
      for (int i = 0; i < _xAxisExtents.Count; i++) 
      { 
       e1 = _xAxisExtents[i]; 
       if (e1.Type == ExtentType.Start) 
       { 
        HashSet<Extent> potentialCollisionsX = new HashSet<Extent>(); 
        for (int j = i + 1; j < _xAxisExtents.Count && _xAxisExtents[j].Nucleus.ID != e1.Nucleus.ID; j++) 
        { 
         potentialCollisionsX.Add(_xAxisExtents[j]); 
        } 
        HashSet<Extent> potentialCollisionsY = new HashSet<Extent>(); 
        for (int j = i + 1; j < _yAxisExtents.Count && _yAxisExtents[j].Nucleus.ID != e1.Nucleus.ID; j++) 
        { 
         potentialCollisionsY.Add(_yAxisExtents[j]); 
        } 

        List<Extent> probableCollisions = new List<Extent>(); 
        foreach (Extent e in potentialCollisionsX) 
        { 
         if (potentialCollisionsY.Contains(e) && !probableCollisions.Contains(e) && e.Nucleus.ID != e1.Nucleus.ID) 
         { 
          probableCollisions.Add(e); 
         } 
        } 
        foreach (Extent e2 in probableCollisions) 
        { 
         if (e1.Nucleus.DNCList.Contains(e2.Nucleus) || e2.Nucleus.DNCList.Contains(e1.Nucleus)) 
          continue; 
         NarrowPhase.DoCollision(e1.Nucleus, e2.Nucleus); 
        } 
       } 
      } 
     } 

     private bool comparisonMethodX(Extent e1, Extent e2) 
     { 
      float e1PositionX = e1.Nucleus.NonLinearSpace != null ? e1.Nucleus.NonLinearPosition.X : e1.Nucleus.Position.X; 
      float e2PositionX = e2.Nucleus.NonLinearSpace != null ? e2.Nucleus.NonLinearPosition.X : e2.Nucleus.Position.X; 
      e1PositionX += (e1.Type == ExtentType.Start) ? -e1.Nucleus.Radius : e1.Nucleus.Radius; 
      e2PositionX += (e2.Type == ExtentType.Start) ? -e2.Nucleus.Radius : e2.Nucleus.Radius; 
      return e1PositionX < e2PositionX; 
     } 
     private bool comparisonMethodY(Extent e1, Extent e2) 
     { 
      float e1PositionY = e1.Nucleus.NonLinearSpace != null ? e1.Nucleus.NonLinearPosition.Y : e1.Nucleus.Position.Y; 
      float e2PositionY = e2.Nucleus.NonLinearSpace != null ? e2.Nucleus.NonLinearPosition.Y : e2.Nucleus.Position.Y; 
      e1PositionY += (e1.Type == ExtentType.Start) ? -e1.Nucleus.Radius : e1.Nucleus.Radius; 
      e2PositionY += (e2.Type == ExtentType.Start) ? -e2.Nucleus.Radius : e2.Nucleus.Radius; 
      return e1PositionY < e2PositionY; 
     } 
     private enum ExtentType { Start, End } 
     private sealed class Extent 
     { 
      private ExtentType _type; 
      public ExtentType Type 
      { 
       get 
       { 
        return _type; 
       } 
       set 
       { 
        _type = value; 
        _hashcode = 23; 
        _hashcode *= 17 + Nucleus.GetHashCode(); 
       } 
      } 
      private Nucleus _nucleus; 
      public Nucleus Nucleus 
      { 
       get 
       { 
        return _nucleus; 
       } 
       set 
       { 
        _nucleus = value; 
        _hashcode = 23; 
        _hashcode *= 17 + Nucleus.GetHashCode(); 
       } 
      } 

      private int _hashcode; 

      public Extent(Nucleus nucleus, ExtentType type) 
      { 
       Nucleus = nucleus; 
       Type = type; 
       _hashcode = 23; 
       _hashcode *= 17 + Nucleus.GetHashCode(); 
      } 

      public override bool Equals(object obj) 
      { 
       return Equals(obj as Extent); 
      } 
      public bool Equals(Extent extent) 
      { 
       if (this.Nucleus == extent.Nucleus) 
       { 
        return true; 
       } 
       return false; 
      } 
      public override int GetHashCode() 
      { 
       return _hashcode; 
      } 
     } 
    } 
} 

和这里的,做插入排序(更多或更少的直接的伪here的翻译)代码:

/// <summary> 
/// Performs an insertion sort on the list. 
/// </summary> 
/// <typeparam name="T">The type of the list supplied.</typeparam> 
/// <param name="list">the list to sort.</param> 
/// <param name="comparison">the method for comparison of two elements.</param> 
/// <returns></returns> 
public static void InsertionSort<T>(this IList<T> list, Func<T, T, bool> comparison) 
{ 
    for (int i = 2; i < list.Count; i++) 
    { 
     for (int j = i; j > 1 && comparison(list[j], list[j - 1]); j--) 
     { 
      T tempItem = list[j]; 
      list.RemoveAt(j); 
      list.Insert(j - 1, tempItem); 
     } 
    } 
} 

IIRC,我能够得到一个非常大的性能提高,特别是在处理大量碰撞物体时。您需要根据代码进行调整,但这是扫除和修剪的基本前提。

我想提醒你的另一件事是,你应该使用一个探查器,就像Red Gate所做的那样。有一个免费的试用版可以让你持续足够长的时间。

+0

是的,我一直在使用红门探查器。这很漂亮。 – 2010-02-26 23:47:53

1

我和你一模一样。我正在尝试创造一个头顶射击游戏,并且需要将效率提高到最大,以便我可以立即在屏幕上拥有大量子弹和敌人。

我会在数组中使用一个带编号的索引来获取所有可碰撞对象。这提供了利用观察的机会:如果您完全遍历每个项目的列表,您将会重复努力。这是(和记,我做了变量名,只是为了更容易地吐出一些伪代码)

if (objs[49].Intersects(objs[51])) 

相当于:如果你使用一个编号

if (objs[51].Intersects(objs[49])) 

所以通过不重复的努力,您可以节省一些时间。而是执行此操作:

for (int i1 = 0; i1 < collidables.Count; i1++) 
{ 
    //By setting i2 = i1 + 1 you ensure an obj isn't checking collision with itself, and that objects already checked against i1 aren't checked again. For instance, collidables[4] doesn't need to check against collidables[0] again since this was checked earlier. 
    for (int i2 = i1 + 1; i2 < collidables.Count; i2++) 
    { 
     //Check collisions here 
    } 
} 

而且,我有每个单元要么计数或标志来确定是否你甚至需要检查碰撞。如果设置了某个标志,或者计数小于2,则不需要检查碰撞。

1

只是站起来:有人建议farseer;这是一个很好的用于XNA的2D物理库。如果您在XNA的3D物理引擎市场上,我已经在XNA项目中使用bulletx(c#端口号bullet),效果很好。

注意:我没有从属于子弹或bulletx项目。

0

我知道这个线程是旧的,但我要说的是标记answar是完全地错误的...

他的代码包含一个致命的错误,鸵鸟政策给性能improvent's将需要性能自动!

起初有点诺蒂奇...

创建他的代码,因此,你必须把这个代码在你绘制梅索德但这是碰撞检测错了地方。在你的绘制方法中,你只能画任何东西!

但是你不能在更新中调用HandleCollisions(),因为Update会比Draw更多的调用。

如果您想调用HandleCollisions(),您的代码必须如下所示...此代码将防止您的碰撞检测每帧运行多次。

private bool check = false; 

protected override Update(GameTime gameTime) 
{ 
    if(!check) 
    {  
     check = true; 
     HandleCollisions(); 
    } 
} 

protected override Draw(GameTime gameTime) 
{ 
    check = false; 
} 

现在让我们来看看HandleCollisions()有什么问题。

示例:我们有500个对象,我们将检查每个可能的碰撞,而不优化我们的检测。

随着500的对象,我们应该有249500个碰撞检测(499X500因为我们不想将检查对象与it's自我冲突)

但随着Frank's代码下面我们就失去了99.998%你的爆炸(只有500次碰撞检查会完成)。 < <这将增加性能!

为什么?因为_lastCheckedCollision将永远是相同或大于allPossibleCollisions.Length ......正因为如此,你只检查上次指数499

for (var i=_lastCheckedCollision; i<_allPossibleCollisions.Length; i++) 
    _lastCheckedCollision = i; 
//<< This could not be the same as _allPossibleCollisions.Length, 
//because i have to be lower as _allPossibleCollisions.Length 

你有这个

更换这

if (_allPossibleCollisions == null || 
    _lastCheckedCollision >= _allPossibleCollisions.Length) 

if (_allPossibleCollisions == null || 
    _lastCheckedCollision >= _allPossibleCollisions.Length - 1) { 

所以你的整个代码可以被这个替换。

private bool check = false; 

protected override Update(GameTime gameTime) 
{ 
    if(!check) 
    {  
     check = true; 
     _allPossibleCollisions = GenerateAllPossibleCollisions(); 
     for(int i=0; i < _allPossibleCollisions.Length; i++) 
     { 
      if (CheckCollision(_allPossibleCollisions[i])) 
      { 
       //Collision! 
      } 
     } 
    } 
} 

protected override Draw(GameTime gameTime) 
{ 
    check = false; 
} 

...这应该是很多的比你的代码快...和它的作品:d ...

RCIX答案应该标记为正确的,因为Frank's answar是错误的。