В моем игровом движке C # я использовал обработку упорядоченного рисования путем добавления / удаления действия к объекту менеджера, который сортировал действия по приоритету, а затем выполнял их.
Вот очень упрощенный пример:
class DrawManager
{
public List<Tuple<Action, int>> DrawActions = new List<Tuple<Action, int>>();
void Draw() { foreach (var tuple in DrawActions) tuple.Item1(); }
}
class Example
{
DrawManager manager;
Example()
{
manager.DrawActions.Add(new Tuple<Action, int>(DrawBackground, 0));
manager.DrawActions.Add(new Tuple<Action, int>(DrawForeground, 100));
}
~Example()
{
manager.DrawActions.Remove(manager.DrawActions.Find(x => x.Item1 == DrawBackground));
manager.DrawActions.Remove(manager.DrawActions.Find(x => x.Item1 == DrawForeground));
}
void DrawBackground() { /* draw something */ }
void DrawForeground() { /* draw something */ }
}
Добавляя некоторые вспомогательные методы, код становится очень элегантным и простым в использовании в моем движке.
Я недавно перешел на C ++, и я не могу найти простой способ достичь того же результата.
Я попытался использовать std :: function, но для того, чтобы удалить метод уничтожения объекта, мне пришлось сохранить метод draw в указателе, принадлежащем вызывающей стороне, затем обернуть его в лямбду и передать его. Очень не элегантно и требует много времени ,
Есть ли способ получить код, аналогичный показанному в примере C #?
Ты можешь использовать std::function
на месте Action
:
typedef std::function<void()> Action;
std::vector<std::pair<Action, int> > DrawActions;
void Draw() {
for_each(DrawActions.begin(), DrawActions.end(), [](std::pair<Action, int>& a) {
a.first();
});
}
Другая идея — не использовать std :: function, а использовать std :: set, и вы создаете класс для каждого действия, которое хотите выполнить. Идея состоит в том, что классы действий определены в файле cpp (а не в заголовочных файлах, если они не являются общими) класса, который их использует, они полностью скрыты.
Сначала определите действие класс, он имеет чисто виртуальный выполнять, уровень приоритета и указатель на какого-то владельца. Идея состоит в том, чтобы не создавать другие классы, кортежи, а использовать действие класс для хранения этих значений:
class Action
{
public:
Action(const int priority, void * owner) : priority(priority), owner(owner) {}
virtual ~Action() {}
virtual void execute() = 0;
struct PrioritySorter
{
bool operator()(Action* a, Action* b)
{
return a->priority < b->priority;
}
};
bool ownerIs(void * owner) const { return this->owner == owner; }
private:
const int priority;
void * owner;
};
Затем создайте несколько действий:
class DrawBackgroundAction : public Action
{
public:
DrawBackgroundAction(const int priority, void * owner) : Action(priority, owner) {}
void execute()
{
cout << "drawing background" << endl;
}
};
class DrawForegroundAction : public Action
{
public:
DrawForegroundAction(const int priority, void * owner) : Action(priority, owner) {}
void execute()
{
cout << "drawing foreground!!!" << endl;
}
};
class DrawSomethingElseAction : public Action
{
public:
DrawSomethingElseAction(const int priority, void * owner) : Action(priority, owner) {}
void execute()
{
cout << "drawing something else" << endl;
}
};
DrawManager заботится о сохранении действий в отсортированном наборе по приоритету, и если вы хотите удалить «собственные» действия (некоторый другой класс), вы можете сделать это.
class DrawManager
{
public:
DrawManager() {}
~DrawManager()
{
for ( ActionList::iterator i = actions.begin(), e = actions.end(); i != e; i++ )
{
delete *i;
}
actions.clear();
}
void draw()
{
for ( ActionList::iterator i = actions.begin(), e = actions.end(); i != e; i++ )
{
(*i)->execute();
}
}
void addAction(Action* action)
{
actions.insert(action);
}
void removeOwnedActions(void * owner)
{
for ( ActionList::iterator i = actions.begin(), e = actions.end(); i != e; i++)
{
if ( (*i)->ownerIs(owner) )
{
delete *i;
actions.erase(i);
}
}
}
private:
typedef std::set<Action*,Action::PrioritySorter> ActionList;
ActionList actions;
};
Теперь пример класса:
class Example
{
public:
Example()
{
manager.addAction(new DrawForegroundAction(100,this));
manager.addAction(new DrawBackgroundAction(0,this));
manager.addAction(new DrawSomethingElseAction(50,this));
}
void drawAll()
{
manager.draw();
}
void removeTheActionsIfYouWant()
{
manager.removeOwnedActions(this);
}
private:
DrawManager manager;
};
И тест:
int main()
{
Example ex;
cout << "Drawing all" << endl;
ex.drawAll();
ex.removeTheActionsIfYouWant();
cout << "Drawing again" << endl;
ex.drawAll();
return 0;
}