2010-02-26 38 views
16

Tôi đang làm việc trên một bản demo đơn giản để phát hiện va chạm, trong đó chỉ chứa một loạt các đối tượng nảy xung quanh trong cửa sổ. (Mục đích là để xem có bao nhiêu đối tượng mà trò chơi có thể xử lý cùng một lúc mà không làm rơi khung.)C# XNA: Tối ưu hóa việc phát hiện va chạm?

Có trọng lực, vì vậy vật thể đang di chuyển hoặc chạm vào tường.

Giải pháp ngây thơ là O (n^2):

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

này là khá xấu. Vì vậy, tôi thiết lập các đối tượng CollisionCell, duy trì thông tin về một phần của màn hình. Ý tưởng là mỗi Collidable chỉ cần kiểm tra các đối tượng khác trong ô của nó. Với 60 px bởi 60 px tế bào, điều này mang lại gần như một cải tiến 10x, nhưng tôi muốn đẩy nó hơn nữa.

Một trình tiết lộ đã tiết lộ rằng mã dành 50% thời gian của nó trong hàm mà mỗi ô sử dụng để lấy nội dung của nó. Dưới đây là:

// 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; 
     } 
    } 

Trong đó 20% thời gian của chương trình được chi tiêu trong điều kiện đó.

Đây là nơi mà các chức năng trên được gọi là:

// 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; 
    } 

Ngay bây giờ, tôi phải lặp qua tất cả các tế bào - ngay cả những sản phẩm nào. Có lẽ điều này có thể được cải thiện bằng cách nào đó, nhưng tôi không chắc làm thế nào để xác minh rằng một tế bào là trống rỗng mà không nhìn vào nó bằng cách nào đó. (Có lẽ tôi có thể thực hiện một cái gì đó như đối tượng ngủ, trong một số động cơ vật lý, nếu một đối tượng sẽ vẫn còn trong một thời gian nó đi ngủ và không được bao gồm trong tính toán cho mỗi khung hình.)

Tôi có thể làm gì để tối ưu hóa điều này? (Ngoài ra, tôi mới sử dụng C# - có bất kỳ lỗi phong cách rõ ràng nào khác không?)

Khi trò chơi bắt đầu tụt hậu, các đối tượng có xu hướng được đóng gói khá chặt chẽ, vì vậy không có nhiều chuyển động diễn ra. Có lẽ tôi có thể tận dụng điều này bằng cách nào đó, viết một hàm để xem, cho tốc độ hiện tại của một đối tượng, nó có thể có thể để lại di động hiện tại của nó trước khi cuộc gọi bên cạnh Update()

UPDATE 1 tôi quyết định để duy trì một danh sách các các đối tượng được tìm thấy ở trong ô ở lần cập nhật cuối cùng và kiểm tra những vật thể đầu tiên để xem liệu chúng có còn trong ô hay không. Ngoài ra, tôi duy trì một số area của biến số CollisionCell, khi nào khi ô được lấp đầy, tôi có thể ngừng tìm kiếm. Dưới đây là thực hiện của tôi về điều đó, và nó làm cho toàn bộ bản demo chậm hơn nhiều:

// 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; 
    } 

UPDATE 2 Tôi bị bỏ rơi mà tiếp cận cuối cùng. Bây giờ tôi đang cố gắng để duy trì một vũng collidables (orphans), và loại bỏ các đối tượng từ họ khi tôi tìm thấy một tế bào có chứa chúng:

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; 
    } 

này chỉ mang lại một sự cải thiện biên, không phải là 2x hoặc 3x tôi' m tìm kiếm.

CẬP NHẬT 3 Một lần nữa tôi từ bỏ cách tiếp cận trên, và quyết định để cho mỗi đối tượng duy trì tế bào hiện nay:

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

Giá trị này được cập nhật:

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

đang 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; 
    } 

Tôi nghĩ điều này sẽ làm cho nó tốt hơn - bây giờ tôi chỉ phải lặp qua các collidables, không phải tất cả các ô * collidables. Thay vào đó, trò chơi hiện đang rất chậm, chỉ phân phối 1/10 hiệu suất của nó với các phương pháp trên.

Trình hồ sơ chỉ ra rằng một phương pháp khác bây giờ là điểm nóng chính và thời gian để có được hàng xóm cho một đối tượng là không đáng kể. Phương pháp đó đã không thay đổi so với trước đây, vì vậy có lẽ tôi đang gọi nó nhiều hơn so với trước đây ...

+0

Bạn đã thử sử dụng công cụ vật lý farseer cho XNA chưa? http://www.codeplex.com/FarseerPhysics – jjxtra

+0

@Rosarch: bài đăng tuyệt vời. cập nhật tuyệt vời. sửa đổi phản ứng của tôi dưới đây, có thể đáng để kiểm tra, tôi đã cố gắng để làm nổi bật adv \ dis, giải thích thêm về những gì đã xảy ra và tại sao, và đề cập đến một số lựa chọn thay thế. cũng đáng để điều tra các thư viện của bên thứ ba, như Jeff Johnson đề xuất. –

+0

Tôi nghe nói rằng động cơ Quake nguyên bản đã kiểm tra va chạm bằng cách thu hẹp gameworld cho đến khi người chơi có kích thước pixel, và sử dụng nó để kiểm tra va chạm. – NibblyPig

Trả lời

12

Nó dành 50% thời gian của nó trong hàm đó vì bạn gọi hàm đó rất nhiều. Tối ưu hóa một chức năng sẽ chỉ mang lại những cải tiến gia tăng cho hiệu suất.

Cách khác, chỉ cần gọi hàm ít hơn!

Bạn đã bắt đầu xuống đường dẫn đó bằng cách thiết lập lược đồ phân vùng không gian (tra cứu Quadtrees để xem hình thức kỹ thuật nâng cao hơn).

Cách tiếp cận thứ hai là phá vỡ vòng lặp N * N của bạn thành dạng gia tăng và sử dụng ngân sách CPU.

Bạn có thể phân bổ ngân sách CPU cho từng mô-đun muốn hành động trong thời gian khung (trong khi cập nhật). Va chạm là một trong những mô-đun này, AI có thể là một mô-đun khác.

Giả sử bạn muốn chạy trò chơi của mình ở tốc độ 60 khung hình/giây. Điều này có nghĩa là bạn có khoảng 1/60 s = 0,0167 giây của thời gian CPU để ghi giữa các khung hình. Không, chúng tôi có thể chia các 0,01767 giữa các mô-đun của chúng tôi. Hãy cung cấp cho vụ va chạm 30% ngân sách: 0,005 s.

Bây giờ thuật toán va chạm của bạn biết rằng thuật toán chỉ có thể chi tiêu 0,005 s làm việc. Vì vậy, nếu nó hết thời gian, nó sẽ cần phải trì hoãn một số nhiệm vụ cho sau này - bạn sẽ làm cho thuật toán tăng dần. Mã để đạt được điều này có thể đơn giản như:

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]); 
     } 
    } 
} 

Ở đó, bây giờ nó không quan trọng nhanh như thế nào mã va chạm là, nó sẽ được thực hiện càng nhanh càng tốt mà không ảnh hưởng hiệu suất nhận thức của người dùng.

Lợi ích bao gồm:

  • Các thuật toán được thiết kế để chạy ra khỏi thời gian, nó chỉ sẽ tiếp tục vào khung tiếp theo, vì vậy bạn không cần phải lo lắng về trường hợp cạnh đặc biệt này.
  • Ngân sách CPU ngày càng trở nên quan trọng vì số lượng thuật toán nâng cao/tốn thời gian tăng lên. Hãy suy nghĩ AI. Do đó, nên sớm triển khai hệ thống như vậy.
  • Thời gian phản hồi của con người nhỏ hơn 30 Hz, vòng lặp khung của bạn đang chạy ở 60 Hz.Điều đó mang lại cho thuật toán 30 khung hình để hoàn thành công việc của nó, vì vậy nó là OK rằng nó không hoàn thành công việc của mình.
  • Làm theo cách này cung cấp cho ổn định, dữ liệu độc lập tốc độ khung hình.
  • Nó vẫn được hưởng lợi từ tối ưu hóa hiệu suất cho chính thuật toán va chạm.
  • Thuật toán va chạm được thiết kế để theo dõi "khung phụ" trong đó xảy ra xung đột. Đó là, bạn sẽ không bao giờ may mắn như vậy để bắt được va chạm chỉ khi điều đó xảy ra - suy nghĩ bạn đang làm như vậy là nói dối với chính mình.
+0

Nhưng điều này sẽ không dẫn đến các xung đột không xảy ra nếu thuật toán hết thời gian? –

+0

@Rosarch Xem chỉnh sửa. Đặc biệt là điểm đạn cuối cùng. –

+0

Xin lỗi, nhưng tôi chắc chắn có thể nói rằng tối ưu hóa một phương pháp được gọi là rất nhiều, thậm chí từng bước, là ồ ạt hữu ích. Tôi đã có thể tăng hiệu suất động cơ của tôi (* với sự giúp đỡ của một profiler *) bằng cách tối ưu hóa một số phương pháp được sử dụng nhiều nhất của tôi, và nó đã được đền đáp. Nếu bạn cạo thậm chí vài trăm nano giây cho mỗi cuộc gọi trong một chức năng như vậy, bạn sẽ tiết kiệm được mili giây cho mỗi lần cập nhật trò chơi. Nó đã mất rất nhiều tối ưu hóa để làm cho thuật toán quét và tỉa của tôi nhanh hơn so với lực lượng vũ phu của tôi, nhưng bây giờ. – RCIX

5

Có vẻ như bạn đang lặp qua tất cả các đối tượng trò chơi chỉ để xem những đối tượng nào được chứa trong một ô . Dường như cách tiếp cận tốt hơn là lưu trữ danh sách các đối tượng trò chơi trong một ô cho mỗi ô. Nếu bạn làm điều đó và mỗi đối tượng biết những tế bào đó là gì, thì việc di chuyển các đối tượng giữa các ô sẽ dễ dàng. Điều này có vẻ như nó sẽ mang lại lợi ích hiệu suất lớn nhất. Dưới đây là một mẹo tối ưu khác để xác định ô nào một đối tượng đang ở: Nếu bạn đã xác định ô nào đang ở trong và biết rằng dựa trên vận tốc đối tượng, nó sẽ không thay đổi ô cho khung hiện tại. , không cần phải chạy lại logic để xác định ô nào mà đối tượng đang ở. Bạn có thể thực hiện kiểm tra nhanh bằng cách tạo một hộp giới hạn chứa tất cả các ô mà đối tượng đang ở. Bạn có thể tạo một hộp giới hạn là kích thước của đối tượng + vận tốc của đối tượng cho khung hiện tại. Nếu ô giới hạn ô chứa đối tượng + hộp giới hạn vận tốc, không cần kiểm tra thêm nữa. Nếu đối tượng không di chuyển, nó thậm chí còn dễ dàng hơn và bạn chỉ có thể sử dụng hộp giới hạn đối tượng.

Hãy cho tôi biết nếu điều đó có ý nghĩa, hoặc google/bing tìm kiếm cho "Quad Tree", hoặc nếu bạn không nhớ sử dụng mã nguồn mở, kiểm tra thư viện này vật lý tuyệt vời: http://www.codeplex.com/FarseerPhysics

+1

Được gọi là "quét và tỉa" là tốt, http://en.wikipedia.org/wiki/Sweep_and_prune – Mez

+0

+1 cho vật lý farseer – RCIX

-1

Một ý tưởng có thể sử dụng một vòng tròn bao quanh. Về cơ bản, khi một Collidable được tạo ra, hãy theo dõi điểm trung tâm của nó và tính bán kính/đường kính chứa toàn bộ đối tượng. Sau đó bạn có thể thực hiện loại bỏ đầu tiên bằng cách sử dụng một cái gì đó như;

int r = C1.BoundingRadius + C2.BoundingRadius; 

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

Điều này làm giảm so sánh hai đối với hầu hết các đối tượng, nhưng mức độ này sẽ giúp bạn không chắc chắn ... tiểu sử!

+1

nếu các đối tượng là tĩnh, điều này có thể làm việc.tuy nhiên, chúng đang chuyển động, có nghĩa là phát hiện cho các quả cầu bị biến dạng thoái hóa để phát hiện đối tượng. –

+0

Điều đó phụ thuộc vào loại đối tượng mà trò chơi đại diện ... Nếu đó là thứ hình học, như hình tròn và hình vuông, công thức có thể được tạo ra để phát hiện va chạm ... Nhưng trong một cụm từ rộng hơn, hộp giới hạn là quy tắc trò chơi đơn giản nhất. –

0

Có một vài điều có thể được thực hiện để tăng tốc quá trình ... nhưng theo như tôi có thể thấy phương pháp kiểm tra va chạm hình chữ nhật đơn giản của bạn là tốt.

Nhưng tôi muốn thay thế các séc

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

Với

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

Và tôi cũng muốn thay thế dòng

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

Đối

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

Khi thuộc tính Chứa trả về một ICollection<T> và thừa hưởng số IEnumerable<T> được chấp nhận bởi hàm tạo List<T>.

Và phương pháp ToArray() chỉ cần lặp lại danh sách trả về một mảng và quá trình này được thực hiện lại khi tạo danh sách mới.

+0

Ngay bây giờ 'obj' không có thuộc tính' Bounds'. Nó có vị trí và bán kính. Bạn có nghĩ rằng nó có giá trị nó để thực hiện điều đó? –

+0

@ Rosarch Tôi nghĩ rằng bạn đang sử dụng System.Drawing.Rectangle để xác định giới hạn của đối tượng của bạn. –

+0

hình chữ nhật xác định các giới hạn của 'CollisionCell', nhưng không phải là đối tượng va chạm. –

7

Tôi có thể trợ giúp tại đây; tôi đã viết phát hiện va chạm của riêng tôi làm thử nghiệm. Tôi nghĩ rằng tôi có thể nói với bạn ngay bây giờ rằng bạn sẽ không nhận được hiệu suất mà bạn cần mà không cần thay đổi thuật toán. Chắc chắn, cách ngây thơ là tốt đẹp, nhưng chỉ hoạt động cho rất nhiều mục trước khi sụp đổ. Những gì bạn cần là Sweep and prune. Ý tưởng cơ bản đi như thế này (từ dự án thư viện phát hiện va chạm của tôi):

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; 
      } 
     } 
    } 
} 

và đây là đoạn code mà không được loại chèn (hoặc ít hơn nhiều hơn một bản dịch trực tiếp của giả 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, tôi đã có thể tăng hiệu suất cực lớn với điều đó, đặc biệt là khi xử lý các số lượng lớn các vật thể va chạm. Bạn sẽ cần phải thích nghi nó cho mã của bạn, nhưng đó là tiền đề cơ bản đằng sau quét và tỉa.

Điều khác tôi muốn nhắc nhở bạn là bạn nên sử dụng một hồ sơ, giống như được tạo bởi Red Gate. Có bản dùng thử miễn phí sẽ kéo dài đủ lâu.

+0

Vâng, tôi đã sử dụng hồ sơ Red Gate. Nó khá ngọt ngào. –

1

Tôi đang ở cùng một thuyền giống như bạn. Tôi đang cố gắng tạo ra một game bắn súng trên cao và cần phải đẩy hiệu quả lên mức tối đa để tôi có thể tấn đạn và kẻ thù trên màn hình cùng một lúc.

Tôi muốn nhận tất cả các đối tượng có thể ghim của tôi trong một mảng có chỉ mục được đánh số. Điều này tạo cơ hội tận dụng lợi thế của một quan sát: nếu bạn lặp lại toàn bộ danh sách cho mỗi mục bạn sẽ nhân đôi nỗ lực. Đó là (và lưu ý, tôi đang chiếm biến tên chỉ để làm cho nó dễ dàng hơn để nhổ ra một số pseudo-code)

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

tương đương với:

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

Vì vậy, nếu bạn sử dụng một đánh số chỉ mục bạn có thể tiết kiệm thời gian bằng cách không nhân đôi nỗ lực. Thực hiện việc này thay thế:

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 
    } 
} 

Ngoài ra, tôi muốn mỗi ô có đếm hoặc cờ để xác định xem bạn có cần kiểm tra xung đột hay không. Nếu một cờ nhất định được đặt hoặc nếu số lượng nhỏ hơn 2, không cần kiểm tra các xung đột.

1

Chỉ cần cảnh báo: Một số người đề xuất người thuê; đó là một thư viện vật lý 2D tuyệt vời để sử dụng với XNA. Nếu bạn đang ở trong thị trường cho một công cụ vật lý 3D cho XNA, tôi đã sử dụng bulletx (một cổng C# bullet) trong các dự án XNA để có hiệu quả tuyệt vời.

Lưu ý: Tôi không có liên kết với dự án bullet hoặc bulletx.

0

Tôi biết chủ đề này là cũ nhưng tôi sẽ nói rằng answar đánh dấu là hoàn toàn sai ...

mã của ông chứa một lỗi nghiêm trọng và đừng cho hiệu suất improvent's nó sẽ mất performence!

Lúc đầu, một chút notic ...

mã của ông được tạo ra để bạn phải gọi mã này trong tole Draw của bạn nhưng điều này là sai chỗ cho va chạm phát hiện. Trong methode vẽ của bạn, bạn chỉ nên vẽ không có gì khác!

Nhưng bạn không thể gọi HandleCollisions() trong Cập nhật, vì Cập nhật nhận được nhiều cuộc gọi hơn Draw nhiều.

Nếu bạn muốn gọi HandleCollisions() mã của bạn phải giống như thế này ... Mã này sẽ ngăn chặn phát hiện va chạm của bạn chạy nhiều hơn một lần cho mỗi khung hình.

private bool check = false; 

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

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

Bây giờ chúng ta hãy xem có gì sai với HandleCollisions().

Ví dụ: Chúng tôi có 500 đối tượng và chúng tôi sẽ thực hiện kiểm tra mọi Va chạm có thể có mà không tối ưu hóa việc phát hiện của chúng tôi.

Với 500 đối tượng chúng ta nên có 249.500 kiểm tra va chạm (499X500 vì chúng ta đừng muốn kiểm tra xem một đối tượng va chạm với tự Nó rất)

Nhưng với mã Frank's dưới đây chúng ta sẽ mất 99,998% của collosions của bạn (chỉ 500 va chạm-kiểm tra sẽ được thực hiện). < < NÀY S INC TĂNG CÁC BIỂU TƯỢNG!

Tại sao? Bởi vì _lastCheckedCollision sẽ không bao giờ được như vậy hoặc lớn hơn sau đó allPossibleCollisions.Length ... và vì lý do đó bạn sẽ chỉ kiểm tra chỉ số cuối cùng 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 

bạn phải thay thế này

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

với

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

để toàn bộ mã của bạn có thể được thay thế bằng cách này.

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; 
} 

... này nên có rất nhiều nhanh hơn so với mã của bạn ... và nó hoạt động: D ...

RCIX câu trả lời nên đánh dấu là đúng vì Frank's answar là sai.

Các vấn đề liên quan