Loại bỏ các vật thể chết trong một trò chơi có hiệu quả?


10

Tôi đang sử dụng vòng lặp for hoặc vòng lặp foreach (không quan trọng) để lặp qua tất cả các đối tượng của tôi khi chúng cần được cập nhật hoặc vẽ. Tuy nhiên, khi một đối tượng bị giết, tôi muốn xóa nó khỏi bộ sưu tập một lần nữa.

Tôi làm điều này bằng cách thêm đối tượng vào danh sách đối tượng chết và sau đó, khi mọi thứ đã được rút ra và cập nhật, tôi cũng duyệt qua mọi thứ trong danh sách này, loại bỏ các đối tượng trong danh sách khỏi nguồn ban đầu.

Có cách nào tốt hơn để làm điều này?



2
Điều này thực sự không liên quan gì đến bộ sưu tập rác, mặc dù việc sử dụng từ "rác" trong tiêu đề và các câu trả lời cho đến nay đã đề cập đến Trình thu gom rác .NET.
Andrew Russell

Tôi đã tự do thực sự thay đổi từ "rác" thành "chết", để tránh nhầm lẫn với trình thu gom rác .NET.
Không bao giờ bắt đầu

Oh tôi biết điều đó. Bài viết thu gom rác vẫn có liên quan vì nó có thể phát vào những gì bạn làm với những vật thể chết đó. Ví dụ như mảnh vỡ chết? Hủy liên kết nó khỏi mọi thứ, đặt lại về mặc định, đặt lại vào thùng rác.
doppelgreener

Câu trả lời:


11

Trước hết: thuật ngữ. Nếu bạn nói "danh sách rác", mọi người sẽ nghĩ bạn đang nói về người thu gom rác. Hãy gọi nó là "danh sách chết".

Như bạn có thể đã phát hiện ra, do đó, câu hỏi của bạn, bạn không thể xóa các mục khỏi bộ sưu tập trong khi bạn đang lặp qua nó. Nếu bộ sưu tập của bạn là một List<>cách đơn giản nhất để xóa các mục khỏi nó là:

for(int i = list.Count-1; i >= 0; --i)
{
    if(list[i].IsDead)
        list.RemoveAt(i);
}

Lưu ý rằng bạn lặp đi lặp lại . Bạn có thể loại bỏ các mục trong khi lặp về phía trước, nhưng nó lộn xộn hơn và yêu cầu a goto. Bạn không thể làm điều đó với foreach.

Bạn có thể thực hiện loại bỏ riêng biệt từ vòng lặp cập nhật của bạn. Hoặc bạn có thể hợp nhất nó vào vòng cập nhật của bạn. Luôn luôn thực hiện RemoveAtở cuối vòng lặp - sau điểm đó trong vòng lặp, list[i]không thể được coi là hợp lệ (nó sẽ trở lại hợp lệ trong lần lặp tiếp theo).

Ngoài ra, cũng lưu ý rằng mỗi đối tượng có IsDeadcờ riêng (nếu bạn đang sử dụng mẫu xử lý, bạn có thể tạo ra nó IsDisposed) - bạn thực sự không cần phải duy trì một "danh sách chết".

Sử dụng cờ trên mỗi mục thích hợp hơn cho hiệu suất, vì bạn tránh phải tìm kiếm trong danh sách để xóa. Và nó cũng thích hợp hơn cho thiết kế - điều đó có nghĩa là mỗi đối tượng có thể dễ dàng kiểm tra xem nó đã chết chưa - vì vậy bạn không vô tình gọi bất kỳ phương thức nào trên đó (nếu các đối tượng này đang thực hiện mô hình dùng một lần, họ có thể ném ObjectDisposedExceptiontrong trường hợp này).

Nếu bạn có một bộ sưu tập khác ngoài a List<>, thì việc xóa các mục chết khỏi bộ sưu tập đó vẫn có thể liên quan đến việc tạo danh sách chết (vì việc xóa trong quá trình lặp có thể không thực hiện được). Theo tôi, nó đẹp hơn, thiết kế khôn ngoan hơn, để tạo danh sách chết ngay trước khi nó được sử dụng, bằng cách lặp qua bộ sưu tập tìm kiếm IsDeadcờ, thay vì cố gắng duy trì danh sách khi các đối tượng bị giết.

Một khi bạn đã hoàn thành với một danh sách chết, bạn nên giữ Clear()nó, và sau đó giữ nó xung quanh để sử dụng lại sau này.


Vậy mô hình dùng một lần được coi là 'hiệu quả' hơn?
Jonathan Connell

@Jonathan: Tôi không hiểu câu hỏi của bạn. Các IsDeadmô hình là chức năng giống hệt với IsDisposed. Về mặt ngữ nghĩa, việc sử dụng IsDeadngụ ý rằng bạn đang duy trì danh sách Vẽ / Cập nhật các đối tượng này sẽ xóa các mục chết sau đó. Các mô hình xử lý không ngụ ý rằng. Hơn nữa, mẫu xử lý không ngụ ý rằng bạn có thể có các tài nguyên không được quản lý bởi đối tượng đó (thường không phù hợp với các đối tượng chơi trò chơi).
Andrew Russell

Bạn có thể làm điều đó với foreach với việc sử dụng ngược lại.
shinzou

0

99,9% thời gian, người thu gom rác (GC) sẽ lo mọi thứ mà không gặp rắc rối. Chỉ cần để nó thực hiện công việc của nó sau khi bạn đã xóa / hủy các đối tượng đó. Có một độ trễ trong việc dọn dẹp phụ thuộc vào một số yếu tố (ví dụ có bao nhiêu khối bộ nhớ đã được chỉ định), nhưng bạn không cần phải thường xuyên biết về điều đó, đừng lo lắng về điều đó. Nó được thiết kế để chỉ hoạt động. Đó là một lý do bạn đang sử dụng C # chứ không phải C.

Nói chung, hiệu suất của GC nói chung, đừng quá lo lắng trừ khi bạn biết (thông qua hồ sơ) rằng nó đang tạo ra một nút cổ chai - nói chung, điều này chỉ có trong các trò chơi khá đòi hỏi khắt khe. Nếu đây là trường hợp, hãy nhìn vào GC.Collect () và các cạm bẫy khác nhau của nó, để bạn biết khi nào thì phù hợp để sử dụng nó. Tôi đề nghị googling "lực gc xna", có rất nhiều tài liệu ngoài đó.


Vì vậy, nếu tôi có một danh sách mà tôi đang sử dụng trong cuộc gọi Update () của mình mỗi lần, tất cả các phần tử trong đó phần tử là "null" sẽ tự động bị xóa, VÀ bị xóa?
Mathias Lykkegaard Lorenzen

@Mathias Không. Xem nhận xét của tôi về câu hỏi của bạn. Trình thu gom rác sẽ không sửa đổi bất cứ điều gì bạn vẫn có thể truy cập - bao gồm cả bộ sưu tập của bạn và nội dung của nó.
Andrew Russell

1
Câu hỏi không liên quan đến GC, ngoại trừ sự lựa chọn không may của từ "rác".
Không bao giờ bắt đầu
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.