Làm cách nào để loại bỏ tốt nhất một thực thể khỏi vòng lặp trò chơi của tôi khi nó chết?


16

Ok vì vậy tôi có một danh sách lớn tất cả các thực thể của tôi mà tôi lặp qua và cập nhật. Trong AS3 tôi có thể lưu trữ điều này dưới dạng Mảng (độ dài động, chưa được gõ), Vector (được nhập) hoặc danh sách được liên kết (không phải bản địa). Hiện tại tôi đang sử dụng Array nhưng tôi dự định thay đổi thành Vector hoặc danh sách được liên kết nếu nó nhanh hơn.

Dù sao, câu hỏi của tôi, khi một Thực thể bị phá hủy, tôi nên xóa nó khỏi danh sách như thế nào? Tôi có thể vô hiệu hóa vị trí của nó, tách nó ra hoặc chỉ đặt một lá cờ trên đó để nói "bỏ qua tôi, tôi đã chết." Tôi đang tập hợp các thực thể của mình, vì vậy một Thực thể đã chết rất có thể sẽ sống lại vào một lúc nào đó. Đối với mỗi loại bộ sưu tập, chiến lược tốt nhất của tôi là gì và sự kết hợp giữa loại bộ sưu tập và phương pháp loại bỏ nào sẽ hoạt động tốt nhất?


Độ dài của Vector không cố định, nhưng nó được gõ và điều đó làm cho nó vượt trội hơn Array. Hạn chế là không có cú pháp nhanh để xác định danh sách điền sẵn, nhưng bạn không cần tôi nghĩ vậy.
Bart van Heukelom

Câu trả lời:


13

Tôi sẽ lưu trữ tất cả các lần thêm / xóa trong các danh sách riêng biệt và thực hiện các thao tác đó sau khi tôi đã lặp qua vòng lặp cập nhật.


10

Khung Flixel sử dụng cờ chết (thực tế là một số cờ xác định xem có nên vẽ, cập nhật hay không, v.v.). Tôi muốn nói rằng nếu bạn sẽ hồi sinh các thực thể và nếu hiệu suất là một vấn đề, bạn sử dụng cờ chết. Theo kinh nghiệm của tôi, khởi tạo các thực thể mới là hoạt động tốn kém nhất trong trường hợp sử dụng mà bạn mô tả và việc tách ra hoặc bỏ trống các phần tử có thể dẫn đến sự phình to bộ nhớ trong bộ sưu tập rác đôi khi của Flash.


1
+1 cho flixel. Tái chế deadthực sự giúp với hiệu suất.
Người mù tuyết

3

Mặc dù một số kỹ thuật vốn đã hiệu quả hơn các kỹ thuật khác, nhưng sẽ chỉ có vấn đề nếu bạn hết chu kỳ trên nền tảng mục tiêu của mình. Sử dụng bất cứ kỹ thuật nào cho phép bạn hoàn thành trò chơi của mình nhanh hơn. Trong thời gian đó, cố gắng không dựa vào việc triển khai cụ thể cấu trúc dữ liệu chứa của bạn và nó sẽ giúp bạn tối ưu hóa sau đó nếu bạn cần.

Chỉ để giải quyết một số kỹ thuật đã được thảo luận bởi những người khác ở đây. Nếu thứ tự của các thực thể là quan trọng, thì một cờ chết có thể cho phép bạn ghép trong vòng lặp cập nhật của bạn trên khung tiếp theo. ví dụ. mã giả rất đơn giản:

void updateGame()
{
  // updateEntities()
  Entity* pSrcEntity = &mEntities[0];
  Entity* pDstEntity = &mEntities[0];
  newNumEntities = 0;
  for (int i = 0; i < numEntities; i++)
  {
    if (!pSrcEntity->isDead)
    {
       // could be inline but whatever.
       updateEntity(pDstEntity, pSrcEntity);
       // if entity just died, don't update the pDstEntity pointer, 
       // and just let the next entity updated overwrite it.
       if (!pDstEntity->isDead)
       {
          pDstEntity++;
          newNumEntities++;
       }
    }
    pSrcEntity++;
  }
}
numEntities = newNumEntities;

Đây là những đặc điểm của sơ đồ này:

  • độ nén tự nhiên của các thực thể (mặc dù có thể có độ trễ 1 khung hình trước khi có thể lấy lại một khe thực thể).
  • không có vấn đề đặt hàng lại ngẫu nhiên.
  • trong khi các danh sách được liên kết đôi có chèn / xóa O (1), nhưng rất khó để tìm nạp trước để ẩn độ trễ bộ đệm tối ưu. Giữ chúng trong một mảng nhỏ gọn cho phép chặn các kỹ thuật tìm nạp trước hoạt động độc đáo.
  • Trong trường hợp phá hủy đa đối tượng, bạn không phải thực hiện các bản sao ca dự phòng để duy trì trật tự và sự gọn nhẹ (tất cả được thực hiện một lần trong suốt quá trình cập nhật)
  • Bạn tận dụng việc chạm vào dữ liệu sẽ cần trong bộ đệm trong quá trình cập nhật.
  • Nó hoạt động tốt nếu các poitners thực thể nguồn và đích của bạn là để tách các mảng. Sau đó, bạn có thể đệm đôi các mảng thực thể của mình để tận dụng lợi thế của đa lõi / vd. một luồng cập nhật / ghi các thực thể cho khung N, trong khi một luồng khác đang hiển thị các thực thể của khung trước đó cho khung N-1.
  • Tính gọn nhẹ có nghĩa là DMA dễ dàng hơn rất nhiều đối với bộ xử lý không đồng nhất để giảm tải cho CPU. SPU hoặc GPU.

+1. Tôi thích điều này. Mặc dù tôi hầu như không cần cập nhật theo thứ tự trong một hồ bơi, tôi sẽ thêm nó vào túi những điều cần nhớ nếu tôi gặp phải tình huống này: o)
Kaj

2

Nói về kinh nghiệm lập trình chung của tôi, nối thường là một hoạt động chậm, liên quan đến việc chuyển tất cả các yếu tố hiện có lên một. Tôi nghĩ rằng đặt nó thành null sẽ là giải pháp tốt nhất ở đây ; một cờ chết sẽ hoạt động nhưng bạn cần cẩn thận để không làm cho mã của bạn bị lộn xộn.

Chúng tôi thực sự chỉ nói về việc tổng hợp tài nguyên trong phòng chat, thực sự. Đó là một thực hành rất tốt, và thật tốt khi nghe bạn đang làm điều đó. :)


1
Nếu thứ tự cập nhật không quan trọng thì việc nối sẽ đơn giản như việc di chuyển thực thể cuối cùng sang chỉ mục hiện tại và giảm số lượng nhóm và chỉ số lặp của bạn.
Kaj

Wow, điểm rất tốt Kaj! :)
Ricket

2

Cá nhân, tôi sẽ sử dụng một danh sách liên kết. Lặp lại danh sách thích là nhanh, cũng như thêm và xóa các mục. Sử dụng Mảng hoặc Vector sẽ là một lựa chọn tốt nếu bạn cần truy cập trực tiếp vào các mục trong cấu trúc (ví dụ: quyền truy cập vào một chỉ mục), nhưng có vẻ như bạn không cần điều đó.

Bất cứ khi nào bạn xóa một mục khỏi danh sách được liên kết, bạn có thể thêm nó vào nhóm đối tượng mà sau đó có thể được tái chế để tiết kiệm phân bổ bộ nhớ.

Tôi đã sử dụng các cơ sở dữ liệu đa giác trong một số dự án và đã rất hài lòng với chúng.

Chỉnh sửa: Xin lỗi, tôi nghĩ rằng câu trả lời không rõ ràng về mặt chiến lược loại bỏ: Tôi khuyên bạn nên xóa mục này khỏi danh sách, ngay khi nó chết và thêm trực tiếp vào cấu trúc gộp (tái chế). Vì việc xóa một mục khỏi danh sách được liên kết là rất hiệu quả, tôi không thấy có vấn đề gì khi làm như vậy.


1
Tôi giả sử rằng bạn đề xuất một danh sách liên kết đôi ở đây? (Lùi về sau)? Ngoài ra: Bạn có đề xuất một số loại nhóm trên các thành phần liên kết hay bạn đang tự động phân bổ từng người giữ con trỏ trong danh sách được liên kết?
Simon

Vâng, nó sẽ phải là một danh sách liên kết đôi phù hợp nhất cho nhiệm vụ đó. Cảm ơn đã chỉ ra rằng! Về việc tái sử dụng các vật phẩm: Tôi đã suy nghĩ về một lớp cơ sở hạ tầng / cơ sở hạ tầng chuyên biệt, một lớp tạo ra các đối tượng mới theo yêu cầu hoặc sử dụng các thể hiện hiện có nếu có một số trong nhóm. Do đó, sẽ tốt hơn nếu thực sự loại bỏ các mục "chết" khỏi danh sách và thêm chúng vào nhóm để sử dụng sau.
bummzack

Một danh sách liên kết đơn sẽ làm tốt. Danh sách liên kết đôi chỉ cung cấp lợi thế của việc lặp theo cả hai hướng. Để lặp qua danh sách liên kết đơn với tùy chọn xóa mục hiện tại, bạn sẽ cần theo dõi mục trước đó.
deft_code

@caspin có chính xác. Nếu bạn đang sử dụng một danh sách liên kết đơn, thì bạn cần theo dõi các nút trước đó và liên kết nextcon trỏ của chúng với nút sau khi xóa một nút. Nếu bạn không muốn làm phiền việc này, một danh sách liên kết đôi sẽ là lựa chọn cơ sở dữ liệu.
bummzack

1

"chỉ cần đặt một lá cờ trên đó để nói" bỏ qua tôi, tôi đã chết. "Tôi đang tập hợp các thực thể của mình, vì vậy một Thực thể đã chết rất có thể sẽ sống lại vào một lúc nào đó"

Tôi nghĩ rằng bạn đã trả lời câu hỏi của riêng bạn liên quan đến ứng dụng cụ thể này. Tôi sẽ tránh xa các mảng nếu bạn có kế hoạch thực hiện chúng ngoài việc đẩy và bật. Danh sách được liên kết sẽ là một cách thông minh hơn nếu bạn có kế hoạch thực hiện các hoạt động nặng. Với tất cả những gì đã nói, nếu bạn có kế hoạch tích hợp lại cùng một thực thể vào trò chơi thì sẽ rất hợp lý khi chỉ đặt một biến boolean và kiểm tra nó trong các vòng lặp vận hành trò chơi.


0

Một giải pháp chung và sạch mà tôi tìm thấy trên một lib tôi đã sử dụng là sử dụng bản đồ có thể khóa.

bạn có 2 thao tác lock()unlock(), trong khi bạn lặp lại trên bản đồ lock(), bây giờ, từ thời điểm này, mọi thao tác thay đổi bản đồ sẽ không có hiệu lực, nó chỉ được đẩy vào một CommandQueuehoạt động sẽ chạy khi bạn gọi unlock().

Vì vậy, loại bỏ một thực thể sẽ có mã giả sau đây:

void lockableMap::remove(std::string id) {
   if(isLocked) {
       commandQueue.add(new RemoveCommand(id));
   } else {
       //remove element from map
   }

và khi bạn unlock()

isLocked = false
commandQueue.execute(this);

Điều duy nhất bạn phải xem xét là bạn sẽ chỉ xóa thực thể sau vòng lặp.

EDIT: đây là giải pháp được đề xuất bởi Simon.



0

Tôi có hai phương pháp.

Khi bạn gọi một đối tượng sẽ bị xóa, nó thực sự đặt hai cờ:

1.Một để nói với container rằng một đối tượng đã bị xóa

2.Một để nói với container những đối tượng đã được yêu cầu xóa

void object::deleteObject()
{
    container->objectHasBeenDeleted = true;
    isToDelete = true;
}

Một Sử dụng một vectơ của các đối tượng

std::vector<object*> objects;

Sau đó, trong chức năng cập nhật, kiểm tra xem liệu một đối tượng đã bị xóa chưa và nếu vậy lặp đi lặp lại qua tất cả các đối tượng và xóa những đối tượng có cờ xóa

void container::update()
{
    if (objectHasBeenDeleted)
    {
        std::vector<object*>::iterator ListIterator;
        for(ListIterator=objects.begin(); ListIterator!=objects.end();)
        {
            if( (*ListIterator)->isToDelete )
            {
                ListIterator = objects.erase(ListIterator);
                delete *ListIterator;
            }
            else {
                ++ListIterator;
            }
        }
    objectHasBeenDeleted = false;
    }
}

Hai Sử dụng một vectơ (con trỏ đến a) của các đối tượng.

std::vector<object*> *objects;

Trong chức năng cập nhật, nếu một đối tượng sẽ bị xóa, hãy lặp qua các đối tượng và thêm những đối tượng không bị xóa vào một vectơ mới. xóa vectơ đối tượng và đặt con trỏ đến vectơ mới

void container::update()
{
    if (objectHasBeenDeleted)
    {
        std::vector<object*> *newVector;
        unsigned long i;
        for (i = 0; i < objects->size(); i++)
        {
            if (!objects->at(i)->isToDelete)
            {
                newVector->push_back(objects->at(i));
            }
        }
        delete objects;
        objects = newVector;
        objectHasBeenDeleted = false;
    }
}
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.