views:

318

answers:

6

In a game, many entities should be updated every frame. Im toying with different design patterns to achieve this. Up until now, Ive had a singleton manager class to which every Logic instance is added. But Im considering the following, a static list in the Logic class itself. This is nice since it would remove a class from the project. "Engine" in this example would be the master class calling the update_all.

class Logic
{
public:
    Logic() { all.push_back(this); }
    virtual ~Logic() { all.erase(this); }
    virtual void update(float deltatime) = 0;

private:
    friend Engine;
    static std::list<Logic*> all;
    static void update_all(float deltatime)
    {
        for (std::list::iterator i = all.begin(); i!=all.end(); ++i)
            (*i)->update(deltatime);
    }
};
  • Does this pattern have a name?
  • Do you consider this a nicer approach than a singleton manager class?
  • Any other comments or caveats?
A: 

One caveat would be that this pattern (currently) doesnt allow Logic instances to be deleted during the call to update_all, since it would invalidate the iterator pointer.

A solution could be maybe to make the destructor private and letting update return a flag stating if the instance should be deleted or not?

mizipzor
+1  A: 

I think its still a singleton : "there can be only one"

The singleton is a pattern, a concept : you can implement it different ways...

A static class member or a global instance are two possible implementations of the same idea.

The question is : why do you want to change it ?

siukurnin
+1  A: 

You can also use observer pattern for this

Naveen
+2  A: 

First, you need to use remove() instead of erase() (the latter would need an iterator as argument)

If you use a slightly different loop like

std::list<Logic*>::iterator it = all.begin();
while (it != all.end()) {
  Logic* current = *it;
  ++it;
  current->update(deltatime);
}

you can even overcome the problem siukurnin mentioned (deletion of a Logic object during update()). list::remove() doesn't invalidate iterators except of the ones pointing to the removed element.

Apart from this, I also vote for this being a variation of the singleton pattern. And I would suggest for keeping the original solution with a separate management class, just in case you want to have to have two loops with different delta times or explicit multithread support (different Logic objects on different threads) or whatever in the future.

In my opinion this is a general advantage of the singleton class over static methods (which you could always use): You can easily multiply your functionality if you want to do so in the future ...

MartinStettner
+1  A: 

Imho, it's an Observer pattern (cfr the update call to every subscriber), in which the Subject happens to be a Singleton.

The 'caveat' of unregistering while updating the observers is a hard one. I found myself struggling with it many times.

An elegant solution to that problem was hinted in this answer on my question about it: for every observer, add an intermediate 'proxy' containing a pointer to the 'real' observer. Unregistering is then equivalent to swapping (atomically) the proxy's pointer. After updating, all proxies with null pointers can be removed safely.

xtofl
+1  A: 

Generally, you want to go through every entities in your game each update call, thus you could go ahead and use a Composite pattern where you'd have a root node. From that, you'd go recursively through the node and call each entity' update() method. From what I can see from your code, you already have a list, but using the composite pattern, you'd be able to make groups of entities instead, which might simplify your task.

From what I understand, your Engine simply need to call the Update() method of the root node (if you use a composite pattern). From that, the root node will call subsequent node using their update(). At some point through the composite tree, you'll reach leafs who will know how to update themselves correctly.

You will only need to have a pointer to your root node in your engine which will have a function UpdateAll() (or something else) which will then call rootNode->Update(); which in turn, will do what I described in the previous paragraph.

tomzx