Позбавлення від мертвих предметів у грі ефективно?


10

Я використовую цикл for цикл або foreach (не важливо), щоб проглянути всі мої об'єкти, коли їх потрібно оновити або намалювати. Однак, коли об’єкт вбивається, я хочу його знову вилучити з колекції.

Я роблю це, додаючи об'єкт до списку мертвих об’єктів, а потім, коли все було намальовано та оновлено, я проходжу все, що знаходиться в цьому списку, видаляючи також об’єкти зі списку з початкового джерела.

Чи є кращий спосіб зробити це?



2
Це насправді не має нічого спільного зі збиранням сміття, незважаючи на вживання слова "сміття" в заголовку та відповіді до цих пір, які згадували .NET Garbage Collector.
Ендрю Рассел

Я взяв на себе сміливість фактично змінити слово "сміття" на "мертвий", щоб запобігти плутанині зі збирачем сміття .NET.
Nevermind

О, я це знаю. Стаття щодо вивезення сміття залишається актуальною, оскільки може зіграти те, що ви робите з тими мертвими предметами. Наприклад мертве сміття? Від’єднайте його від усього, скиньте його за замовчуванням, поставте назад у відро для сміття.
doppelgreener

Відповіді:


11

Насамперед: термінологія. Якщо ви скажете "список сміття", люди подумають, що ви говорите про сміттєзбірник. Назвемо це "списком мертвих".

Як ви, напевно, виявили, звідси і ваше запитання, ви не можете видалити елементи з колекції, поки ви повторюєте її. Якщо ваша колекція - це List<>найпростіший спосіб видалення з неї:

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

Зауважте, що ви повторюєте назад . Ви можете видаляти елементи під час ітерації вперед, але це швидше і потрібно goto. Ви не можете це зробити foreach.

Ви можете зробити видалення окремо від циклу оновлення. Або ви можете об'єднати його у циклі оновлення. Завжди робити це RemoveAtв кінці циклу - після цього точка в циклі list[i]не може вважатися дійсною (вона знову стає дійсною при наступній ітерації).

Зауважте також, що у кожного об’єкта є власний IsDeadпрапор (якщо ви використовуєте шаблон розпорядження, ви могли це зробити IsDisposed) - насправді взагалі не потрібно підтримувати "список мертвих".

Використання прапора на кожному елементі є кращим для продуктивності, оскільки вам не доведеться шукати список, щоб зробити його видаленням. І це також краще для дизайну - це означає, що кожен об'єкт може легко перевірити, чи він мертвий, - тому ви випадково не викликаєте жодних методів на ньому (якщо ці об'єкти реалізують одноразовий зразок, вони можуть кинутись ObjectDisposedExceptionу цьому випадку).

Якщо у вас є інша колекція, ніж List<>видалення мертвих предметів із цієї колекції все ще може спричинити створення списку мертвих (оскільки видалення під час ітерації може бути неможливим). На мою думку, приємніше створити список мертвих безпосередньо перед його використанням шляхом повторення колекції, шукаючи IsDeadпрапори, а не намагаючись підтримувати список, коли об’єкти вбиваються.

Після того як ви закінчите зі списком мертвих, ви повинні Clear()його, а потім зберегти його, щоб потім його повторно використовувати.


Тож одноразова модель вважається більш ефективною?
Джонатан Коннелл

@Jonathan: Я не розумію вашого запитання. IsDeadКартина функціонально ідентичні IsDisposed. Семантичне використання IsDeadозначає, що ви зберігаєте список малюнків / оновлень цих об'єктів, які згодом будуть видаляти з нього мертві елементи. Модель утилізації цього не означає. Крім того, схема утилізації дійсно означає , що ви можете мати некеровані ресурси , що належать цей об'єкт (який, як правило , не підходить для об'єктів ігрового процесу).
Ендрю Рассел

Ви можете зробити це за допомогою foreach за допомогою зворотного ходу.
shinzou

0

У 99,9% часу сміттєзбірник (GC) піклується про все без клопотів. Просто дозвольте виконувати свою роботу, як тільки ви видалили / обнулили ці об’єкти. Існує затримка в очищенні, яка залежить від ряду факторів (скільки блоків пам'яті призначено, наприклад), але вам не потрібно зазвичай навіть про це знати, не кажучи вже про це. Він призначений просто працювати. З яких причин ви використовуєте C #, а не C.

Що стосується продуктивності GC в цілому, не переживайте себе надмірно, якщо ви не знаєте (через профілювання), що це створює вузьке місце - це взагалі лише в досить складних іграх. Якщо це так, загляньте в GC.Collect () та його різні підводні камені, щоб ви знали, коли це доцільно використовувати. Я пропоную гуглити "force gc xna", матеріалу там багато.


Отже, якщо у мене є список, який я використовую в своєму виклику Update () щоразу, всі елементи в ньому, де елемент "недійсний", автоматично очищаються, І видаляються?
Mathias Lykkegaard Lorenzen

@Mathias Ні. Дивіться мій коментар до вашого питання. Збирач сміття не змінюватиме нічого, до чого ви все ще можете отримати доступ, включаючи вашу колекцію та її вміст.
Ендрю Рассел

1
Питання не має нічого спільного з GC, крім нещасного вибору слова "сміття".
Nevermind
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.