Как создать коллекцию игровых объектов из другой коллекции, не создавая мусора? - PullRequest
2 голосов
/ 20 августа 2010

В последнее время я много играл с XNA, а также много читал о сборке мусора в играх. Я думаю, что проделал разумную работу по сокращению количества мусора, используя пул и избегая зависимости от foreach.

В настоящий момент мои базовые игровые сущности хранятся в KeyedCollection, что позволяет мне перебирать все сущности (например, список) и ссылаться на сущность по ключу (например, словарь).

Я хотел бы иметь возможность запрашивать мою коллекцию и возвращать другую коллекцию, не создавая мусора каждый запрос. Я включил образец концепции ниже, потому что я думаю, что я лучше их кодирую, я объясняю ...

/// <summary>
/// Defines a sample entity.
/// </summary>
public class SampleEntity
{
    public uint Id;
    public Vector2 Position;
}

/// <summary>
/// Defines a collection of sample entities.
/// </summary>
public class EntityCollection : KeyedCollection<uint, SampleEntity>
{
    /// <summary>
    /// Return the key for the supplied item.
    /// </summary>
    protected override uint GetKeyForItem(SampleEntity item)
    {
        return item.Id;
    }
}

/// <summary>
/// Defines the sample game class.
/// </summary>
public class GameSample
{
    /// <summary>
    /// Create a new instance of the GameSample class.
    /// </summary>
    public GameSample()
    {
        Entities = new EntityCollection();
    }

    /// <summary>
    /// Get the collection of game entities.
    /// </summary>
    public EntityCollection Entities { get; private set; }

    /// <summary>
    /// Return the collection of entities within a radius of the supplied point.
    /// </summary>
    public List<SampleEntity> Query(Vector2 center, float radius)
    {
        List<SampleEntity> results = new List<SampleEntity>() // BAD, BAD, BAD!!!!!

        //
        // add the entities to the results collection
        //

        return results;
    }
}

В этом (чрезмерно упрощенном) примере будет много мусора, потому что он создает новый объект List при каждом вызове. Я также играл с созданием глобального списка результатов и очисткой каждого звонка, но это кажется уродливым.

    /// <summary>
    /// Return the collection of entities within a radius of the specified point.
    /// </summary>
    public List<SampleEntity> Query(Vector2 center, float radius)
    {
        _globalResults.Clear();

        //
        // add the entities to the global results collection
        //

        return _globalResults;
    }

Я просто что-то упустил? Есть ли более элегантное решение, о котором я просто не знаю?

Ответы [ 3 ]

3 голосов
/ 20 августа 2010

Я думаю, что лучшее решение в таком случае - сделать так, чтобы ваша функция выглядела примерно так:

 public void Query(Vector2 center, float radius, List<SampleEntity> result)
 {
     result.Clear();
     result.Add(/*...*/);
     // ...
 }

И возложить на вызывающего абонента ответственность за управление этим списком.

Также:При создании игр не бойтесь писать простой код.Если работает глобальный список результатов - тогда это хорошее решение.


Если вам не нужно хранить список, тогда Ответ Скурмеделя , вероятно, лучше по производительноститочка зрения.

Хотя, если бы это был я, я бы сделал действительно простую вещь и просто непосредственно перебрал бы сущности .Тот же эффект, возможно, чуть-чуть быстрее, а главное: меньше кода.

Напишите свою сложную перечислительную функцию Query после того, как напишите сложную структуру данных пространственного разделения, которую вы будете писать только после того, как вам действительно понадобится производительность, которую он предлагает.

1 голос
/ 20 августа 2010

Я думаю Эндрю Рассел имеет правильную идею, но если бы вы могли полностью отказаться от Списка, было бы еще лучше. Вы бы сделали IEnumerable (с возвращением yield или экземпляром от руки), который возвращает элементы, найденные запросом. Вам не нужен новый список. Вы также можете достичь этого с помощью LINQ.

Я представляю это как «окно» или «представление» содержимого коллекции.

Единственное, что вам нужно для проверки - это сделать IEnumerable недействительным, вы не можете изменять список, пока что-то итерирует его.

Если вы боитесь каждый раз создавать новый IEnumerable, используйте его повторно. Вы также можете повторно использовать перечислитель, но это ограничит количество вызывающих, выполняющих запрос, по одному за раз. Если два абонента начнут использовать один и тот же перечислитель одновременно, врата ада откроются.

0 голосов
/ 20 августа 2010

Я не думаю, что то, о чем вы просите, возможно - если вы хотите другую коллекцию (т.е. ту, в которой элементы этой коллекции каким-то образом отличаются от другой коллекции) , тогда вам нужно новый экземпляр некоторой коллекции , чтобы можно было хранить эти различия.

Лучшее, на что вы, вероятно, могли бы надеяться, это использовать массив вместо списка, так как объем памяти может быть меньше, однако (я не разработчик игр) это все меня поразило как оптимизацию - затраты на сбор экземпляры относительно невелики.

Не поймите меня неправильно - хорошо, что вы знаете о подобных вещах, я просто думаю, что вы не должны зацикливаться на этом уровне детализации, поскольку вы вряд ли увидите какое-либо влияние.

...