2012-04-08 1 views
4

Я хочу организовать все мои объекты GameObjects в иерархическую структуру. Я бы предположил, что для этого было бы лучшим деревом. Я думал об использовании STD :: Set для обработки этого. Имеет ли это смысл? В принципе, GameObject может содержать переменное количество других GameObjects. Если я действительно обрабатываю его таким образом, тогда как лучше всего обращаться с обращением к объекту в дереве? Будет ли доступ к ним через ID достаточно быстрым? Полагаю, я мог бы также просто получить к ним доступ с помощью указателей, но передавать их вокруг звучит несколько опасно и утомительно, если вы попадете в ситуацию с большим количеством объектов.STD :: Установить, что я должен использовать для организации объектов в иерархии?

Я также буду показывать эти данные, если это имеет какое-либо влияние.

EX:

-Hierarchy 
    -GameObject 
     -GameObject 
     -Gameobject 
    -GameObject 
     -GameObject 
      -GameObject 
    -GameObject 

Я ценю любые входные ребята. Благодарю.

+3

Что заставляет вас думать, что 'std :: set' подходит для хранения деревьев? –

+0

Если вы плохо разбираетесь с указателями, даже 1 опасен. Если вы с ними добры, то 1 и 100 не имеют значения. Это предложение. Я мог бы также просто получить к ним доступ через указатели, но передавать эти звуки несколько опасно и утомительно, если вы попадаете в ситуацию с большим количеством объектов, не имеет смысла! – Shahbaz

ответ

1

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

0

Для чего это стоит, я нахожу, что доступ к ним через «путь», немного как дерево каталогов на диске, работает достаточно хорошо. Это взято из некоторых C# код, который я пишу (обратите внимание, что я пропустил бит из ниже класса), но идея перевести прямолинейно на C++:

sealed class Entity : IEntity 
{ 
    private readonly IDictionary<string,IEntity> m_children = new Dictionary<string,IEntity>(); 

    private readonly IDictionary<string,dynamic> m_properties; 

    public string Name { get { return m_properties["Name"]; } } 

    public IEntity Parent { get; set; } 

    /// <summary> 
    /// Adds a child to this entity. 
    /// </summary> 
    /// <param name="child">The child to add.</param> 
    public void AddChild(IEntity child) 
    { 
     m_children.Add(child.Name, child); 
     child.Parent = this; 
    } 

    /// <summary> 
    /// Gets the absolute path of this entity in its tree. 
    /// </summary> 
    /// <returns>The entity's absolute path.</returns> 
    public string GetAbsolutePath() 
    { 
     IEntity cur = this; 
     var path = new LinkedList<string>(); 
     while(cur.Parent != null) 
     { 
      path.AddFirst(cur.Name); 
      cur = cur.Parent; 
     } 
     path.AddFirst("."); 
     return string.Join("/", path); 
    } 

    /// <summary> 
    /// Gets another entity in this entity's tree by its absolute path (i.e. its path relative to the root entity). 
    /// </summary> 
    /// <param name="path">The absolute path to the other entity.</param> 
    /// <returns>The other entity, if found, or null otherwise.</returns> 
    public IEntity GetEntityByAbsolutePath(string path) 
    { 
     return GetRootEntity().GetEntityByRelativePath(path); 
    } 

    /// <summary> 
    /// Gets another entity in this entity's tree by its absolute path (i.e. its path relative to the root entity). 
    /// </summary> 
    /// <param name="path">The absolute path to the other entity, as a list of path components.</param> 
    /// <returns>The other entity, if found, or null otherwise.</returns> 
    public IEntity GetEntityByAbsolutePath(LinkedList<string> path) 
    { 
     return GetRootEntity().GetEntityByRelativePath(path); 
    } 

    /// <summary> 
    /// Gets another entity in this entity's tree by its path relative to this entity. 
    /// </summary> 
    /// <param name="path">The relative path to the other entity.</param> 
    /// <returns>The other entity, if found, or null otherwise.</returns> 
    public IEntity GetEntityByRelativePath(string path) 
    { 
     return GetEntityByRelativePath(new LinkedList<string>(path.Split('/'))); 
    } 

    /// <summary> 
    /// Gets another entity in this entity's tree by its path relative to this entity. 
    /// </summary> 
    /// <param name="path">The relative path to the other entity, as a list of path components.</param> 
    /// <returns>The other entity, if found, or null otherwise.</returns> 
    public IEntity GetEntityByRelativePath(LinkedList<string> path) 
    { 
     IEntity cur = this; 

     while(cur != null && path.Count != 0) 
     { 
      switch(path.First()) 
      { 
       case ".": 
        break; 
       case "..": 
        cur = cur.Parent; 
        break; 
       default: 
        cur = cur.GetChild(path.First()); 
        break; 
      } 

      path.RemoveFirst(); 
     } 

     return cur; 
    } 

    /// <summary> 
    /// Gets the root entity of this entity's tree. 
    /// </summary> 
    /// <returns>The root entity of this entity's tree.</returns> 
    private IEntity GetRootEntity() 
    { 
     IEntity cur = this; 
     while(cur.Parent != null) 
     { 
      cur = cur.Parent; 
     } 
     return cur; 
    } 
} 

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

cityA.GetEntityByRelativePath("../city:B/building:1"); 
+0

По этой логике в 3D-шутере вам нужно будет предоставить уникальное имя для каждого врага и даже для каждого снаряда. Звучит как случай «ЯГНИ» для меня. – SigTerm

+0

@SigTerm: Это для игры в городском строительстве, похоже, хорошо работает в контексте. Я согласен, что для FPS с гораздо большим количеством объектов это может быть немного глупо :) Кстати, предоставление уникальных имен не очень сложно, когда вы можете использовать GUID. –

+0

@SigTerm: «кажется, работает разумно». Это будет работать разумно до тех пор, пока ваша игра не включает атаки godzilla и аналогичные катаклизмы, разрушающие город. Если части города будут уничтожены и регенерированы часто, или если город часто изменяется, (ИМО), вам, скорее всего, придется иметь дело с возможными столкновениями имен, которые могут быть беспорядочными. С другой стороны, вы получите уличный адрес для всего, что может быть полезно для вашей игры. – SigTerm

1

Я думал об использовании STD :: Set справиться с этим. Имеет ли это смысл?

Номер станд :: Комплект сделан для хранения коллекции уникальных объектов, где объекты упорядочены с использованием либо operator< или lessthan компаратора предоставленного пользователем. Нет смысла использовать std :: set для списка объектов.

Доступ к ним через ID будет достаточно быстрым?

Вот улов. Нет ID s в std::set. Есть объекты. Поэтому, если вы используете std :: set, вы не сможете получить доступ к объекту по ID без повторения всего набора (если только объекты не упорядочены по ID, но это еще одна история). Чтобы сопоставить что-то еще, вы используете std::map. std::set - для коллекций.

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

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

Нет никакого «лучшего» способа представления деревьев.

  1. Вы можете хранить корневые объекты иерархии в списке И хранить дочерние объекты в родительских объектах.
  2. Вы можете хранить все объекты в списке (как для детей, так и для родителей), для дочерних объектов хранить ссылку на родителя, а затем убедиться, что функция «update()» учитывает это.
  3. Вы можете сохранить два списка: один для корневых объектов и другой для всех объектов. (например, вы вызываете обновление в первом списке и используете второй список для запросов на обнаружение конфликтов и т. д.)

Все эти подходы имеют смысл и могут быть полезны для определенных сценариев.

но прохождение окружающих звуки несколько опасно

Да, это верный путь к краху игры, когда кто-то, кто выпустил ракету было вдребезги и ракета имеет указатель на владелец для дружественного огня проверить. Однако, поскольку ваш вопрос отмечен «boost», вы можете использовать boost::weak_ptr для решения этой проблемы. Также объекты могут быть сохранены как список shared_ptr в std :: list, std :: deque или even std :: vector - в зависимости от того, как ваша игра будет их использовать.

+0

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

+0

@StuartGolodetz: Да, есть несколько способов сделать это. Однако я думаю, что лучше всего использовать решение, которое имеет дело с ситуацией автоматически, и не требует, чтобы вы что-либо писали. Если механизм обмена сообщениями не предоставлен существующей библиотекой (и «собирается быть уничтожен», уведомление выполняется автоматически), я думаю **, лучше будет использовать что-то вроде weak_ptr. Примером таких уведомлений является [QObject :: destroy()] (http://qt-project.org/doc/qt-4.8/qobject.html#destroyed) сигнал, но он зависит от приложения - все, что QObject будет безусловно избыток для многих приложений. – SigTerm

3

Я предполагаю, что вы хотите сделать что-то вроде этого:

class GameObject { 
    some_container<GameObject> nested_objects_; 
    .... 
} 

Если вы на самом деле не нужно O(log(n)) поиска и удаления в вашей коллекции объектов, я предложил бы использовать std::vector вместо этого, так что вы» ll избежать накладных расходов памяти, и (очень вероятно) некоторые издержки процессора тоже. Поэтому, если вам нужно хранить только несколько вложенных объектов на каждый объект, тогда обязательно выберите std::vector

Но если вам нужно быстро найти или удалить объекты или сохранить уникальность, то std::set может быть хорошим выбором. Чтобы использовать std::set, вам необходимо предоставить функцию сравнения, которая действует как оператор < (строго говоря, ваша функция сравнения должна быть strict weak ordering.) Вы можете просто определить operator < для вас GameObject класс. От этой функции зависит эффективность std::set, поэтому будьте осторожны.

Обратите внимание, что вы либо траверсы, весь набор или find индивидуальный GameObjects путем предоставления самого GameObject (строго говоря, вы можете предоставить его эквивалент - смотреть в функции сравнения). Поэтому, если вам нужно получить объекты по его идентификатору, лучше используйте std::map, он гораздо более подходит для такого типа работы. std::set reference. std::map reference

Я также предлагаю взглянуть на повышение :: unordered_set & повышающего :: unordered_map (или станд :: unordered_set & станд :: unordered_map, если вы можете использовать C++ 11).