Еще один вопрос, связанный с дизайном C ++ - PullRequest
0 голосов
/ 22 апреля 2010

Привет!

Я пытаюсь найти оптимальные решения в шаблонах кодирования C ++, и это один из моих игровых движков , связанных с вопросами.

Взгляните на объявление игрового объекта (я удалил почти все, что не имеет отношения к вопросу) .

// Abstract representation of a game object
class Object : 
public Entity, 
       IRenderable, ISerializable {

   // Object parameters
   // Other not really important stuff

public:
   // @note Rendering template will never change while
   // the object 'lives'
   Object(RenderTemplate& render_template, /* params */) : /*...*/ { }

private:
   // Object rendering template
   RenderTemplate render_template;

public:
   /**
    * Default object render method
    * Draws rendering template data at (X, Y) with (Width, Height) dimensions
    *
    * @note If no appropriate rendering method overload is specified 
    * for any derived class, this method is called
    *
    * @param  Backend & b  
    * @return void
    * @see         
    */
   virtual void Render(Backend& backend) const {
      // Render sprite from object's
      // rendering template structure
      backend.RenderFromTemplate(
         render_template, 
         x, y, width, height
         );
   }
};

Здесь также IRenderable объявление интерфейса:

// Objects that can be rendered
interface IRenderable {
   /**
    * Abstract method to render current object
    *
    * @param  Backend & b  
    * @return void
    * @see
    */
   virtual void Render(Backend& b) const = 0;
}

и образец реального объекта, полученного из Object (с серьезными упрощениями:)

// Ball object
class Ball : public Object {
   // Ball params
public:
   virtual void Render(Backend& b) const {
      b.RenderEllipse(/*params*/);
   }
};

То, что я хотел получить, этовозможность иметь какую-то стандартную функцию, которая будет рисовать спрайт для объекта (это Object::Render), если нет соответствующей перегрузки.

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

Я думаю, этот способ работы довольно хорош, но что я не могу понять - Есть ли способ отделить реализацию "нормальных" методов объектов (например, Resize(...) или Rotate(...)) от их реализации рендеринга?

Потому что, если все сделано способом, описанным ранее, общийФайл .cpp, который реализует любой тип объекта, обычно смешивает реализацию методов Resize(...) и т. д. и этот метод virtual Render(...), и это кажется беспорядком.Я на самом деле хочу иметь процедуры рендеринга для объектов в одном месте и их «логической реализации» - в другом.

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

Ответы [ 2 ]

2 голосов
/ 23 апреля 2010

В этом конкретном примере я не уверен, что желательно отделить Resize от Render, поскольку Resize оказывает прямое влияние на визуализированное изображение.

То, как я это делаюэто сделать Entity с содержать Renderable.Например, Entity будет иметь переменную-член, которая будет отображать спрайт.Когда сущность становится видимой, она передает спрайт в систему рендеринга и в основном говорит: «Хорошо, нарисуйте этот кадр каждый раз, пока я не скажу вам остановиться», хотя механизм рендеринга отбирает объекты вне экрана и другое безумие.Возможно, очевидно, что механизм рендеринга затем поддерживает список визуализируемых объектов (или, может быть, несколько списков, если вы хотите разделить спрайты из линий или частиц для эффективности рендеринга), а затем он просто перетаскивает этот гораздо менее сложный список элементов, когда приходит времясделать ничью.

Таким образом, сущность может контролировать визуализируемый объект, она может масштабировать его или вращать (т.е. манипулировать своей матрицей рендеринга), но ей не нужно решать, когдауместно нарисовать его вне зависимости от того, "жив" он или нет.Механизм рендеринга, тем не менее, знает только, как нарисовать элемент и не нуждается в знании графа вашей сущности.

2 голосов
/ 22 апреля 2010

Оттяните IRenderable от Entity и наследуйте от этого интерфейса, только когда захотите.

Тогда используйте посетителя:



struct rendering_visitor : visitor<Entity>, visitor<IRenderable>
{
  void visit(Entity & e) { ... do default stuff ... }
  void visit(IRenderable & renderable) { renderable.Render(); }
};

См. "Modern C ++ Design" для хорошей реализации посетителя. Ваш Renderable должен быть доступен для посещения, также как и ваши сущности. Происходит какая-то высокая иерархия, о которой в рекомендуемой книге не говорится, но ее не так сложно понять.

Есть и другие способы реализации посетителей. Выбирай.

...