Làm thế nào tôi có thể tối ưu hóa một công cụ va chạm trong đó trật tự là đáng kể và va chạm là có điều kiện dựa trên nhóm đối tượng?


14

Nếu đây là lần đầu tiên bạn đặt câu hỏi này, tôi khuyên bạn nên đọc phần cập nhật trước bên dưới trước, sau đó là phần này. Đây là một tổng hợp của vấn đề, mặc dù:

Về cơ bản, tôi có một công cụ phát hiện và giải quyết va chạm với hệ thống phân vùng không gian lưới nơi các nhóm va chạm và va chạm có vấn đề. Một cơ thể tại một thời điểm phải di chuyển, sau đó phát hiện va chạm, sau đó giải quyết va chạm. Nếu tôi di chuyển tất cả các cơ thể cùng một lúc, sau đó tạo ra các cặp va chạm có thể xảy ra, thì rõ ràng là nhanh hơn, nhưng độ phân giải bị phá vỡ vì trật tự va chạm không được tôn trọng. Nếu tôi di chuyển một cơ thể một lần, tôi buộc phải đưa các cơ thể để kiểm tra va chạm và nó trở thành một vấn đề ^ 2. Đặt các nhóm trong hỗn hợp, và bạn có thể tưởng tượng tại sao nó trở nên rất chậm rất nhanh với rất nhiều cơ thể.


Cập nhật: Tôi đã làm việc rất chăm chỉ về vấn đề này, nhưng không thể quản lý để tối ưu hóa mọi thứ.

Tôi đã thực hiện thành công "bức tranh" được mô tả bởi Will và thay đổi các nhóm thành các bit, nhưng đó là một sự tăng tốc rất nhỏ.

Tôi cũng phát hiện ra một vấn đề lớn : động cơ của tôi phụ thuộc vào trật tự va chạm.

Tôi đã thử thực hiện một thế hệ cặp va chạm độc đáo , chắc chắn tăng tốc mọi thứ lên rất nhiều, nhưng đã phá vỡ trật tự va chạm .

Hãy để tôi giải thích:

  • trong thiết kế ban đầu của tôi (không tạo cặp), điều này xảy ra:

    1. một cơ thể di chuyển
    2. sau khi nó di chuyển, nó làm mới các tế bào của nó và khiến các cơ thể nó va chạm với nhau
    3. Nếu nó đè lên một cơ thể, nó cần phải giải quyết, giải quyết va chạm

    điều này có nghĩa là nếu một cơ thể di chuyển và va vào tường (hoặc bất kỳ cơ thể nào khác), chỉ có cơ thể đã di chuyển sẽ giải quyết va chạm của nó và cơ thể khác sẽ không bị ảnh hưởng.

    Đây là hành vi tôi mong muốn .

    Tôi hiểu rằng nó không phổ biến đối với các động cơ vật lý, nhưng nó có rất nhiều lợi thế cho các trò chơi theo phong cách retro .

  • trong thiết kế lưới thông thường (tạo các cặp duy nhất), điều này xảy ra:

    1. tất cả các cơ thể di chuyển
    2. sau khi tất cả các cơ thể đã di chuyển, làm mới tất cả các tế bào
    3. tạo các cặp va chạm độc đáo
    4. cho mỗi cặp, xử lý phát hiện va chạm và giải quyết

    trong trường hợp này, một động thái đồng thời có thể dẫn đến hai cơ thể chồng chéo lên nhau và chúng sẽ giải quyết cùng một lúc - điều này thực sự khiến các cơ thể "đẩy nhau xung quanh" và phá vỡ sự ổn định va chạm với nhiều cơ thể

    Hành vi này là phổ biến cho các động cơ vật lý, nhưng nó không được chấp nhận trong trường hợp của tôi .

Tôi cũng tìm thấy một vấn đề khác, đó là vấn đề chính (ngay cả khi nó không có khả năng xảy ra trong tình huống thực tế):

  • xem xét các cơ quan của nhóm A, B và W
  • A va chạm và giải quyết chống lại W và A
  • B va chạm và giải quyết chống lại W và B
  • A không làm gì chống lại B
  • B không làm gì chống lại A

có thể xảy ra trường hợp rất nhiều cơ thể A và cơ thể B chiếm cùng một tế bào - trong trường hợp đó, có rất nhiều sự lặp lại không cần thiết giữa các cơ thể không được phản ứng với nhau (hoặc chỉ phát hiện va chạm nhưng không giải quyết được chúng) .

Đối với 100 cơ thể chiếm cùng một ô, đó là 100 ^ 100 lần lặp! Điều này xảy ra vì các cặp duy nhất không được tạo - nhưng tôi không thể tạo các cặp duy nhất , nếu không tôi sẽ có một hành vi mà tôi không mong muốn.

Có cách nào để tối ưu hóa loại động cơ va chạm này không?

Đây là những hướng dẫn phải được tôn trọng:

  • Thứ tự va chạm là vô cùng quan trọng!

    • Các cơ quan phải di chuyển từng cái một , sau đó kiểm tra va chạm từng cái một và giải quyết sau khi di chuyển từng cái một .
  • Các cơ quan phải có 3 bitcoin nhóm

    • Nhóm : nhóm cơ thể thuộc về
    • GroupsToCheck : các nhóm cơ thể phải phát hiện va chạm chống lại
    • GroupsNoResolve : nhóm cơ thể phải không được giải quyết va chạm chống lại
    • Có thể có những tình huống tôi chỉ muốn phát hiện va chạm nhưng không được giải quyết



Cập nhật trước:

Lời nói đầu : Tôi biết rằng tối ưu hóa nút cổ chai này không phải là điều cần thiết - động cơ đã rất nhanh. Tôi, tuy nhiên, vì mục đích vui vẻ và giáo dục, rất thích tìm cách làm cho động cơ nhanh hơn nữa.


Tôi đang tạo ra một công cụ phát hiện / phản ứng va chạm C ++ 2D đa năng, chú trọng vào tính linh hoạt và tốc độ.

Đây là một sơ đồ rất cơ bản về kiến ​​trúc của nó:

Kiến trúc động cơ cơ bản

Về cơ bản, lớp chính là World, sở hữu (quản lý bộ nhớ) của a ResolverBase*, a SpatialBase*và a vector<Body*>.

SpatialBase là một lớp ảo thuần túy liên quan đến phát hiện va chạm pha rộng.

ResolverBase là một lớp ảo thuần túy liên quan đến độ phân giải va chạm.

Các cơ quan giao tiếp World::SpatialBase*với SpatialInfocác đối tượng, thuộc sở hữu của chính các cơ quan.


Hiện tại có một lớp không gian : Grid : SpatialBase, là lưới 2D cố định cơ bản. Nó có lớp thông tin riêng,GridInfo : SpatialInfo .

Đây là cách kiến ​​trúc của nó trông:

Kiến trúc động cơ với không gian lưới

Các Gridlớp học sở hữu một mảng 2D của Cell*. Các Celllớp có chứa một bộ sưu tập các (không thuộc sở hữu) Body*: a vector<Body*>, trong đó có tất cả các cơ quan có trong tế bào.

GridInfo các đối tượng cũng chứa các con trỏ không sở hữu đến các tế bào mà cơ thể đang ở.


Như tôi đã nói trước đây, động cơ dựa trên các nhóm.

  • Body::getGroups()trả về một std::bitsettrong tất cả các nhóm mà cơ thể là một phần của.
  • Body::getGroupsToCheck()trả về một std::bitsettrong tất cả các nhóm mà cơ thể phải kiểm tra va chạm.

Các cơ quan có thể chiếm nhiều hơn một tế bào. GridInfo luôn lưu trữ các con trỏ không sở hữu vào các ô bị chiếm.


Sau khi một cơ thể di chuyển, phát hiện va chạm xảy ra. Tôi giả sử rằng tất cả các cơ quan là hộp giới hạn liên kết trục.

Cách phát hiện va chạm pha rộng:

Phần 1: cập nhật thông tin không gian

Đối với mỗi Body body:

    • Các ô chiếm dụng trên cùng bên trái và các ô chiếm dụng dưới cùng bên phải được tính toán.
    • Nếu chúng khác với các ô trước đó, body.gridInfo.cellssẽ bị xóa và chứa đầy tất cả các ô mà cơ thể chiếm giữ (vòng lặp 2D cho từ ô trên cùng bên trái đến ô dưới cùng bên phải).
  1. body bây giờ được đảm bảo để biết những gì nó chiếm tế bào.

Phần 2: kiểm tra va chạm thực tế

Đối với mỗi Body body:

  • body.gridInfo.handleCollisions được gọi là:

void GridInfo::handleCollisions(float mFrameTime)
{
    static int paint{-1};
    ++paint;

    for(const auto& c : cells)
        for(const auto& b : c->getBodies())
        {
            if(b->paint == paint) continue;
            base.handleCollision(mFrameTime, b);
            b->paint = paint;
        }
}

void Body::handleCollision(float mFrameTime, Body* mBody)
    {
        if(mBody == this || !mustCheck(*mBody) || !shape.isOverlapping(mBody->getShape())) return;

        auto intersection(getMinIntersection(shape, mBody->getShape()));

        onDetection({*mBody, mFrameTime, mBody->getUserData(), intersection});
        mBody->onDetection({*this, mFrameTime, userData, -intersection});

        if(!resolve || mustIgnoreResolution(*mBody)) return;
        bodiesToResolve.push_back(mBody);
    }

  • Va chạm sau đó được giải quyết cho mọi cơ thể trong bodiesToResolve.

  • Đó là nó.


Vì vậy, bây giờ tôi đã cố gắng tối ưu hóa phát hiện va chạm ở pha rộng này. Mỗi lần tôi thử một cái gì đó khác với kiến ​​trúc / thiết lập hiện tại, một cái gì đó không đi theo kế hoạch hoặc tôi đưa ra giả định về mô phỏng mà sau này được chứng minh là sai.

Câu hỏi của tôi là: làm thế nào tôi có thể tối ưu hóa pha rộng của động cơ va chạm ?

Có một số loại tối ưu hóa C ++ ma thuật có thể được áp dụng ở đây?

Kiến trúc có thể được thiết kế lại để cho phép hiệu suất cao hơn?


Đầu ra Callgrind cho phiên bản mới nhất: http://txtup.co/rLJgz


Hồ sơ và xác định tắc nghẽn. Hãy cho chúng tôi biết họ đang ở đâu, sau đó chúng tôi có một cái gì đó để làm việc.
Maik

@MaikSemder: Tôi đã làm điều đó, và đã viết nó trong bài viết. Đó là đoạn mã duy nhất là nút cổ chai. Xin lỗi nếu nó dài và chi tiết, nhưng đó là một phần của câu hỏi bởi vì tôi chắc chắn rằng nút cổ chai này chỉ có thể được giải quyết bằng cách thay đổi một cái gì đó trong thiết kế của động cơ.
Vittorio Romeo

Xin lỗi, rất khó tìm. Bạn có thể cho chúng tôi một số số? Thời gian chức năng và số lượng đối tượng được xử lý trong chức năng đó?
Maik

@MaikSemder: thử nghiệm với callgrind, trên một nhị phân được biên soạn với Clang 3.4 SVN-O3: 10000 cơ quan năng động - chức năng getBodiesToCheck()được gọi là 5.462.334 lần, và mất 35,1% của toàn bộ thời gian profiling (Hướng dẫn đọc thời gian truy cập)
Vittorio Romeo

2
@Quonux: không có hành vi phạm tội. Tôi chỉ thích "phát minh lại bánh xe". Tôi có thể lấy Bullet hoặc Box2D và tạo một trò chơi với các thư viện đó, nhưng đó không thực sự là mục tiêu của tôi. Tôi cảm thấy thỏa mãn hơn và học hỏi được nhiều hơn bằng cách tạo ra mọi thứ từ đầu và cố gắng vượt qua những trở ngại xuất hiện - ngay cả khi điều đó có nghĩa là bị thất vọng và yêu cầu giúp đỡ. Khác với niềm tin của tôi rằng mã hóa từ đầu là vô giá cho mục đích học tập, tôi cũng thấy nó rất nhiều niềm vui và một niềm vui lớn để dành thời gian rảnh của mình.
Vittorio Romeo

Câu trả lời:


14

getBodiesToCheck()

Có thể có hai vấn đề với getBodiesToCheck()chức năng; Đầu tiên:

if(!contains(bodiesToCheck, b)) bodiesToCheck.push_back(b);

Phần này là O (n 2 ) phải không?

Thay vì kiểm tra xem cơ thể đã có trong danh sách hay chưa, hãy sử dụng tranh thay thế.

loop_count++;
if(!loop_count) { // if loop_count can wrap,
    // you just need to iterate all bodies to reset it here
}
bodiesToCheck.clear();
for(const auto& q : queries)
    for(const auto& b : *q)
        if(b->paint != loop_count) {
            bodiesToCheck.push_back(b);
            b->paint = loop_count;
        }
return bodiesToCheck;

Bạn đang hủy bỏ con trỏ trong giai đoạn thu thập, nhưng dù sao bạn cũng sẽ hủy bỏ nó trong giai đoạn thử nghiệm vì vậy nếu bạn có đủ L1 thì không có vấn đề gì lớn. Bạn cũng có thể cải thiện hiệu suất bằng cách thêm các gợi ý tìm nạp trước vào trình biên dịch __builtin_prefetch, mặc dù điều đó dễ dàng hơn với for(int i=q->length; i-->0; )các vòng lặp cổ điển và như vậy.

Đó là một điều chỉnh đơn giản, nhưng suy nghĩ thứ hai của tôi là có thể có một cách nhanh hơn để tổ chức việc này:

Tuy nhiên, bạn có thể chuyển sang sử dụng ảnh bitmap và tránh toàn bộbodiesToCheck vectơ. Đây là một cách tiếp cận:

Bạn đang sử dụng các khóa số nguyên cho các cơ quan, nhưng sau đó tìm kiếm chúng trong bản đồ và mọi thứ và giữ xung quanh danh sách của chúng. Bạn có thể di chuyển đến một bộ cấp phát khe, về cơ bản chỉ là một mảng hoặc vectơ. Ví dụ:

class TBodyImpl {
   public:
       virtual ~TBodyImpl() {}
       virtual void onHit(int other) {}
       virtual ....
       const int slot;
   protected:
      TBodyImpl(int slot): slot(slot_) {}
};

struct TBodyBase {
    enum ... type;
    ...
    rect_t rect;
    TQuadTreeNode *quadTreeNode; // see below
    TBodyImpl* imp; // often null
};

std::vector<TBodyBase> bodies; // not pointers to them

Điều này có nghĩa là tất cả những thứ cần thiết để thực hiện các va chạm thực tế là trong bộ nhớ thân thiện với bộ đệm tuyến tính và bạn chỉ đi ra bit cụ thể thực hiện và gắn nó vào một trong các khe này nếu cần.

Để theo dõi sự phân bổ trong vectơ cơ thể này, bạn có thể sử dụng một mảng các số nguyên dưới dạng bitmap và sử dụng phép xoay bit hoặc __builtin_ffsv.v ... Điều này siêu hiệu quả để di chuyển đến các vị trí hiện đang bị chiếm dụng hoặc tìm một vị trí không có người trong mảng. Thậm chí đôi khi bạn có thể thu gọn mảng nếu nó phát triển lớn một cách vô lý và sau đó rất nhiều dấu được xóa, bằng cách di chuyển các mảng ở cuối để điền vào các khoảng trống.

chỉ kiểm tra mỗi lần va chạm

Nếu bạn đã kiểm tra nếu một va chạm với b , bạn không cần phải kiểm tra nếu b va chạm với một quá.

Nó xuất phát từ việc sử dụng id số nguyên mà bạn tránh các kiểm tra này bằng câu lệnh if đơn giản. Nếu id của một xung đột tiềm năng nhỏ hơn hoặc bằng với id hiện tại đang được kiểm tra, nó có thể bị bỏ qua! Bằng cách này, bạn sẽ chỉ kiểm tra từng cặp có thể một lần; sẽ hơn một nửa số lần kiểm tra va chạm.

unsigned * bitmap;
int bitmap_len;
...

for(int i=0; i<bitmap_len; i++) {
  unsigned mask = bitmap[i];
  while(mask) {
      const int j = __builtin_ffs(mask);
      const int slot = i*sizeof(unsigned)*8+j;
      for(int neighbour: get_neighbours(slot))
          if(neighbour > slot)
              check_for_collision(slot,neighbour);
      mask >>= j;
  }

tôn trọng trật tự va chạm

Thay vì đánh giá một vụ va chạm ngay khi tìm thấy một cặp, hãy tính khoảng cách để đánh và lưu trữ nó trong một đống nhị phân . Các đống này là cách bạn thường thực hiện các hàng đợi ưu tiên trong tìm đường, vì vậy mã tiện ích rất hữu ích.

Đánh dấu mỗi nút bằng một số thứ tự, vì vậy bạn có thể nói:

  • A 10 đánh B 12 lúc 6
  • Một 10 hits C 12 tại 3

Rõ ràng sau khi bạn tập hợp tất cả các va chạm, bạn bắt đầu bật chúng từ hàng đợi ưu tiên, sớm nhất là trước. Vì vậy, lần đầu tiên bạn nhận được là A 10 lần truy cập C 12 tại 3. Bạn tăng số thứ tự của từng đối tượng ( 10 bit), đánh giá va chạm và tính toán các đường dẫn mới của chúng và lưu trữ các va chạm mới của chúng trong cùng một hàng đợi. Va chạm mới là A 11 đánh B 12 lúc 7. Hàng đợi hiện có:

  • A 10 đánh B 12 lúc 6
  • A 11 đánh B 12 lúc 7

Sau đó, bạn bật khỏi hàng đợi ưu tiên và A của nó 10 hits B 12 tại 6. Nhưng bạn thấy rằng A 10 ; A hiện đang ở mức 11. Vì vậy, bạn có thể loại bỏ sự va chạm này.

Điều quan trọng của nó là không bận tâm đến việc cố gắng xóa tất cả các va chạm cũ từ cây; loại bỏ từ một đống là tốn kém. Đơn giản chỉ cần loại bỏ chúng khi bạn bật chúng.

Cái lưới sắt

Bạn nên xem xét sử dụng một quadtree thay thế. Đó là một cấu trúc dữ liệu rất đơn giản để thực hiện. Thường thì bạn thấy các triển khai lưu trữ các điểm nhưng tôi thích lưu trữ các phần tử và lưu trữ phần tử trong nút chứa nó. Điều này có nghĩa là để kiểm tra các va chạm, bạn chỉ phải lặp đi lặp lại trên tất cả các cơ thể và, đối với mỗi cơ quan, hãy kiểm tra nó với các cơ quan đó trong cùng một nút bốn cây (sử dụng thủ thuật sắp xếp được nêu ở trên) và tất cả các cơ quan trong các nút của cây bốn nhánh. Cây tứ thân tự nó là danh sách va chạm có thể.

Đây là một Quadtree đơn giản:

struct Object {
    Rect bounds;
    Point pos;
    Object * prev, * next;
    QuadTreeNode * parent;
};

struct QuadTreeNode {
    Rect bounds;
    Point centre;
    Object * staticObjects;
    Object * movableObjects;
    QuadTreeNode * parent; // null if root
    QuadTreeNode * children[4]; // null for unallocated children
};

Chúng tôi lưu trữ các đối tượng có thể di chuyển một cách riêng biệt vì chúng tôi không phải kiểm tra xem các đối tượng tĩnh có va chạm với bất kỳ thứ gì không.

Chúng tôi đang mô hình hóa tất cả các đối tượng dưới dạng các hộp giới hạn theo trục (AABB) và chúng tôi đặt chúng vào QuadTreeNode nhỏ nhất có chứa chúng. Khi QuadTreeNode có rất nhiều trẻ em, bạn có thể chia nhỏ nó hơn nữa (nếu các đối tượng đó tự phân phối vào trẻ em một cách độc đáo).

Mỗi lần đánh dấu trò chơi, bạn cần lặp lại vào góc phần tư và tính toán di chuyển - và va chạm - của từng đối tượng có thể di chuyển. Nó phải được kiểm tra va chạm với:

  • mọi đối tượng tĩnh trong nút của nó
  • mọi đối tượng có thể di chuyển trong nút của nó nằm trước nó (hoặc sau nó; chỉ cần chọn một hướng) trong danh sách MovableObjects
  • mọi đối tượng di chuyển và tĩnh trong tất cả các nút cha

Điều này sẽ tạo ra tất cả các va chạm có thể, không có thứ tự. Sau đó bạn thực hiện các động tác. Bạn phải ưu tiên các di chuyển này theo khoảng cách và 'ai di chuyển trước' (đó là yêu cầu đặc biệt của bạn) và thực hiện chúng theo thứ tự đó. Sử dụng một đống cho việc này.

Bạn có thể tối ưu hóa mẫu tứ giác này; bạn không cần phải thực sự lưu trữ giới hạn và điểm trung tâm; Điều đó hoàn toàn có thể tạo ra khi bạn đi trên cây. Bạn không cần kiểm tra xem một mô hình có nằm trong giới hạn hay không, chỉ kiểm tra xem bên nào thuộc điểm trung tâm (kiểm tra "trục phân tách").

Để mô hình hóa những thứ bay nhanh như đạn, thay vì di chuyển chúng từng bước hoặc có một danh sách 'viên đạn' riêng mà bạn luôn kiểm tra, chỉ cần đặt chúng vào góc phần tư với phần chính của chuyến bay của chúng trong một số bước của trò chơi. Điều này có nghĩa là chúng di chuyển trong nhóm mười bốn hiếm khi hơn, nhưng bạn không kiểm tra đạn chống lại các bức tường ở xa, vì vậy đó là một sự đánh đổi tốt.

Các đối tượng tĩnh lớn nên được chia thành các phần thành phần; một khối lập phương lớn nên có mỗi mặt được lưu trữ riêng biệt, ví dụ.


"Vẽ tranh" nghe có vẻ hay, tôi sẽ thử và báo cáo kết quả sớm nhất có thể. Tôi không hiểu phần thứ hai trong câu trả lời của bạn - Tôi sẽ cố gắng đọc một cái gì đó về tìm nạp trước.
Vittorio Romeo

Tôi sẽ không đề xuất QuadTree, nó phức tạp hơn việc tạo lưới và nếu không được thực hiện đúng cách, nó sẽ không hoạt động chính xác và sẽ tạo / xóa các nút quá thường xuyên.
ClickerMonkey

Về đống của bạn: thứ tự của phong trào được tôn trọng? Xem xét cơ thể Một và cơ thể B . A di chuyển sang phải về phía B và B di chuyển sang phải về phía A. Bây giờ - khi chúng va chạm đồng thời, người di chuyển trước sẽ được giải quyết trước , và người còn lại sẽ không bị ảnh hưởng.
Vittorio Romeo

@VittorioRomeo nếu A di chuyển về phía B và B di chuyển về phía A trong cùng một tích tắc, và ở cùng một tốc độ, họ có gặp nhau ở giữa không? Hay là A, di chuyển trước, gặp B nơi B bắt đầu?
Sẽ


3

Tôi cá là bạn chỉ có một tấn bộ nhớ cache bị bỏ lỡ khi lặp đi lặp lại trên cơ thể. Bạn đang gộp tất cả các cơ thể của bạn lại với nhau bằng cách sử dụng một số sơ đồ thiết kế theo định hướng dữ liệu? Với một broadphase N ^ 2, tôi có thể mô phỏng hàng trăm và hàng trăm , trong khi ghi lại bằng các yếu tố, cơ thể mà không có bất kỳ khung hình nào rơi vào các vùng liên kết (dưới 60), và tất cả đều không có bộ cấp phát tùy chỉnh. Chỉ cần tưởng tượng những gì có thể được thực hiện với việc sử dụng bộ nhớ cache thích hợp.

Manh mối ở đây:

const std::vector<Body *>

Điều này ngay lập tức giương cao một lá cờ đỏ khổng lồ. Bạn đang phân bổ các cơ quan này với các cuộc gọi mới thô? Có một phân bổ tùy chỉnh trong sử dụng? Điều quan trọng nhất là bạn có tất cả các cơ thể của mình trong một mảng lớn mà bạn đi qua tuyến tính . Nếu đi ngang qua bộ nhớ một cách tuyến tính không phải là thứ bạn cảm thấy, bạn có thể thực hiện xem xét sử dụng danh sách liên kết xâm nhập thay thế.

Ngoài ra, bạn dường như đang sử dụng std :: map. Bạn có biết bộ nhớ trong std :: map được phân bổ như thế nào không? Bạn sẽ có độ phức tạp O (lg (N)) cho mỗi truy vấn bản đồ và điều này có thể được tăng lên thành O (1) với bảng băm. Trên hết, bộ nhớ được phân bổ bởi std :: map cũng sẽ phá hủy bộ nhớ cache của bạn một cách khủng khiếp.

Giải pháp của tôi là sử dụng bảng băm xâm nhập thay cho std :: map. Một ví dụ điển hình về cả danh sách liên kết xâm nhập và bảng băm xâm nhập nằm trong cơ sở của Patrick Wyatt trong dự án coho của anh ấy: https://github.com/webcoyote/coho

Vì vậy, trong ngắn hạn, bạn có thể cần phải tạo một số công cụ tùy chỉnh cho mình, cụ thể là bộ cấp phát và một số thùng chứa xâm nhập. Đây là điều tốt nhất tôi có thể làm mà không cần lược tả mã cho chính mình.


"Bạn đang phân bổ các cơ quan này với các cuộc gọi mới thô?" Tôi không gọi một cách rõ ràng newkhi đẩy cơ thể vào getBodiesToCheckvector - bạn có nghĩa là nó đang xảy ra trong nội bộ? Có cách nào để ngăn chặn điều đó trong khi vẫn có một bộ sưu tập cơ thể có kích thước động?
Vittorio Romeo

std::mapkhông phải là một nút cổ chai - tôi cũng nhớ đã cố gắng dense_hash_setvà không đạt được bất kỳ loại hiệu suất nào.
Vittorio Romeo

@Vittorio, vậy phần nào của getBodyToCheck là nút cổ chai? Chúng tôi cần thông tin để giúp đỡ.
Maik

@MaikSemder: trình hồ sơ không đi sâu hơn chính chức năng. Toàn bộ chức năng là nút cổ chai, bởi vì nó được gọi một lần trên mỗi khung cho mỗi cơ thể. 10000 cơ thể = 10000 getBodiesToCheckcuộc gọi trên mỗi khung. Tôi nghi ngờ việc làm sạch / đẩy liên tục trong vector là nút cổ chai của chính chức năng. Các containsphương pháp cũng là một phần của cuộc suy thoái, nhưng vì bodiesToCheckkhông bao giờ có hơn 8-10 cơ quan trong nó, nó phải được rằng chậm
Vittorio Romeo

@Vittorio Sẽ tốt hơn nếu bạn đưa thông tin này vào các câu hỏi, đó là một thay đổi cuộc chơi;) Particularily Ý tôi là phần mà getBodiesToCheck được gọi là cho tất cả các cơ quan, vì vậy 10000 lần mỗi frame. Tôi tự hỏi, bạn nói rằng họ ở trong các nhóm, vậy tại sao lại đưa họ vào mảng bodyToCheck, nếu bạn đã có thông tin nhóm. Bạn có thể giải thích về phần đó, có vẻ như là một ứng cử viên tối ưu hóa rất tốt đối với tôi.
Maik

1

Giảm số lượng cơ thể để kiểm tra từng khung:

Chỉ kiểm tra cơ thể thực sự có thể di chuyển. Các đối tượng tĩnh chỉ cần được gán cho các ô va chạm của bạn một lần sau khi được tạo. Bây giờ chỉ kiểm tra xung đột cho các nhóm có chứa ít nhất một đối tượng động. Điều này sẽ làm giảm số lượng kiểm tra từng khung.

Sử dụng một tứ giác. Xem câu trả lời chi tiết của tôi ở đây

Xóa tất cả các phân bổ từ mã vật lý của bạn. Bạn có thể muốn sử dụng một hồ sơ cho việc này. Nhưng tôi chỉ phân tích cấp phát bộ nhớ trong C #, vì vậy tôi không thể giúp với C ++.

Chúc may mắn!


0

Tôi thấy hai ứng cử viên có vấn đề trong chức năng thắt cổ chai của bạn:

Đầu tiên là phần "chứa" - đây có lẽ là lý do chính của nút cổ chai. Nó lặp đi lặp lại qua các cơ thể đã được tìm thấy cho mọi cơ thể. Có lẽ bạn nên sử dụng một số loại hash_table / hash_map thay vì vector. Sau đó chèn phải nhanh hơn (với tìm kiếm trùng lặp). Nhưng tôi không biết bất kỳ con số cụ thể nào - tôi không biết có bao nhiêu cơ thể được lặp lại ở đây.

Vấn đề thứ hai có thể là vector :: Clear và push_back. Rõ ràng có thể hoặc không thể gợi lên sự phân bổ lại. Nhưng bạn có thể muốn tránh nó. Giải pháp có thể là một số cờ cờ. Nhưng bạn có thể có rất nhiều đối tượng, vì vậy bộ nhớ không hiệu quả để có danh sách tất cả các đối tượng cho mọi đối tượng. Một số cách tiếp cận khác có thể tốt, nhưng tôi không biết cách tiếp cận nào: /


Về vấn đề đầu tiên: Tôi đã thử sử dụng dense_hash_set thay vì vectơ + chứa và nó chậm hơn. Tôi đã thử điền vào vector và sau đó loại bỏ tất cả các bản sao, và nó chậm hơn.
Vittorio Romeo

0

Lưu ý: Tôi không biết gì về C ++, chỉ có Java, nhưng bạn sẽ có thể tìm ra mã. Vật lý là ngôn ngữ phổ quát phải không? Tôi cũng nhận ra đây là một bài viết cũ, nhưng tôi chỉ muốn chia sẻ điều này với mọi người.

Tôi có một mẫu quan sát mà về cơ bản, sau khi thực thể di chuyển, nó trả về đối tượng mà nó đã va chạm, bao gồm cả một đối tượng NULL. Chỉ cần đặt:

( Tôi đang làm lại minecraft )

public Block collided(){
   return World.getBlock(getLocation());
}

Vì vậy, nói rằng bạn đang lang thang trong thế giới của bạn. Bất cứ khi nào bạn gọi cho move(1)bạn sau đó gọi collided(). nếu bạn có được khối bạn muốn, thì có lẽ các hạt bay lên và bạn có thể di chuyển sang trái và quay lại nhưng không chuyển tiếp.

Sử dụng điều này một cách khái quát hơn là chỉ minecraft làm ví dụ:

public Object collided(){
   return threeDarray[3][2][3];
}

Đơn giản, có một mảng để chỉ ra các tọa độ, theo nghĩa đen là cách Java thực hiện, sử dụng các con trỏ.

Sử dụng phương pháp này vẫn đòi hỏi một cái gì đó khác hơn là một tiên nghiệm phương pháp phát hiện va chạm . Bạn có thể lặp lại điều này, nhưng điều đó đánh bại mục đích. Bạn có thể áp dụng điều này cho các kỹ thuật Va chạm rộng, giữa và hẹp, nhưng một mình, nó là một quái thú, đặc biệt là khi nó hoạt động tốt cho các trò chơi 3D và 2D.

Bây giờ nhìn thêm một lần nữa, điều này có nghĩa là, theo phương pháp minecraft collide () của tôi, tôi sẽ kết thúc bên trong khối, vì vậy tôi sẽ phải di chuyển người chơi ra ngoài nó. Thay vì kiểm tra trình phát, tôi cần thêm một hộp giới hạn để kiểm tra khối nào đang chạm vào mỗi bên của hộp. Vấn đề cố định.

đoạn văn trên có thể không dễ dàng với đa giác nếu bạn muốn độ chính xác. Để chính xác, tôi sẽ đề nghị xác định một hộp giới hạn đa giác không phải là hình vuông, nhưng không được tàu. nếu không, thì một hình chữ nhật là tốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.