passing variable to classes automatically

Started by
11 comments, last by Storyyeller 14 years, 6 months ago
In my game, some entities can spawn other entities, so they get passed a pointer to a vector of entities where they can put the new entities so that they can be added to the list of all entities which is used by the game loop to update and draw. This method seemed fine when I only had one or two classes that did this, but now that I have many entity subclasses which all need this vector, it is getting problematic. It's not very good for code reuse, and it also clutters up the constructor arguments and initializer list. What can I do? Is there a better design? Is there anyway to make the class receive this pointer without explicitly passing it through the constructor?
I trust exceptions about as far as I can throw them.
Advertisement
Where is the actual instance of the vector stored?

Steven Yau
[Blog] [Portfolio]

as a global variable in the main module
I trust exceptions about as far as I can throw them.
There is always a better way : )

However, whether it's better for your (limited explanation), you could set up a static member variable in the base class of your entities. Set it once CEntityBase::m_sEntityList = g_EntityList or some such, and any child can access it upon creation/whenever.

Another option is to use a 'global', or base class function (CEntityBase* pEntity = CreateEntity()) that creates the entitities, so that they are only ever created in one place, and the global list isn't accessed by code all over the project.

-Alamar
I thought about the first one but I assumed that would be a bad idea because it would be exposing the list to all subclasses, even ones that don't need it.

I'm confused by what you mean in the section option. No matter what you do, there has to be someway for the entities to get back to the main list. Whether it's a straight pointer, or a function callback, it seems like you always need some sort of member variable.

Would it be a good idea to create a separate class with the list pointer as a static member, and then use multiple inheritance to make all the entities inherit from it which need to do so?
I trust exceptions about as far as I can throw them.
You shouldn't give each entity a pointer to a vector for a number of reasons:

1. If you ever change the data structure that you're using to store entities, you have to go back and update all of your existing entities
2. It's not very clean to have each entity store direct pointers to global containers like that

Instead of giving each entity the information it needs to add other entities at creation time, you could pass it at update time. For instance:

class Entity;class EntityContext{public:    virtual void AddEntity (Entity *entity) = 0;    virtual void RemoveEntity (Entity *entity) = 0;};class GameEntityContext : public EntityContext{private:    typedef std::vector<Entity *> EntityVector;    EntityVector *entities_;public:    explicit GameEntityContext (EntityVector *entities) : entities_(entities)        {assert(entities_);}    void AddEntity (Entity *entity) {entities_.push_back(entity);}    void RemoveEntity (Entity *entity) { ... }};class Entity{public:    virtual void Update (EntityContext *entity_context) = 0;};...// in the main game's update function, you do this:// NOTE: this nees to be much more sophisticated since// any Entity could potential add to or remove from the vectorGameEntityContext::EntityVector current_entities(entities_);GameEntityContext entity_context(&entities_);for (GameEntityContext::iterator it= current_entities_.begin();    it != current_entities_.end(); ++it){    it->Update(&entity_context);}// then in a particular Entity's Update function:entity_context->AddEntity(new_entity);


Code not tested and not guaranteed to compile. However, it illustrates the point.

Each Entity does not store a persistent pointer to either a vector or an EntityContext: instead, this is passed in as a parameter to the Update function. All an Entity needs to do to add another is call AddEntity on that EntityContext. This gives you a lot of flexibility and makes you resilient to changes later on.
Wouldn't that just change the problem of passing too many arguments in the constructor, to passing too many arguments in the update function?

I probably will encapsulate the vectors though
I trust exceptions about as far as I can throw them.
Would something like this work?

#ifndef GLOBALLISTS_H#define GLOBALLISTS_H#include <vector>#include <algorithm>#include "BOOST/shared_ptr.hpp"#include <cassert>#include "entity.h"class EntityAdder{    //This should prevent the Static Initialization Order Fiasco    static std::vector<entity_sptr>& MyEntityList()    {        static std::vector<entity_sptr>* mylist = new std::vector<entity_sptr>();        return *mylist;    }    public:    static void AddEntity(entity_sptr newentity)    {        assert(newentity);        MyEntityList().push_back(newentity);    }    static void AddEntityPtr(Entity* newentityptr)    {        assert(newentityptr);        entity_sptr newentity(newentityptr);        MyEntityList().push_back(newentity);    }    static void DumpListInto(std::vector<entity_sptr>& targetentitylist)    {        targetentitylist.insert (targetentitylist.end(),MyEntityList().begin(),MyEntityList().end());        MyEntityList().clear();    }};#endif


Edit: It seems to be working perfectly. I think I'll do this for my some of my other global objects too

[Edited by - Storyyeller on October 18, 2009 3:07:53 PM]
I trust exceptions about as far as I can throw them.
I would encapsulate that vector into some class and pass a reference to it around to whatever entity that needs it. There is a dependency here, so why not make it obvious that it is there? If that means you're passing too much around in your constructors, then group them together into classes or cut out the useless arguments, not the ones that you do need.

What you're doing right now is nothing but a global list hidden under some static functions. Convenient for small programs but not very scalable or maintainable for large ones. Besides, if you do intend to stick with this, just put those functions in a namespace, not a static class, and keep the vector in the implementation file as a normal variable.
Create-ivity - a game development blog Mouseover for more information.
Quote:Original post by Storyyeller
Wouldn't that just change the problem of passing too many arguments in the constructor, to passing too many arguments in the update function?

I probably will encapsulate the vectors though


No, the idea is you pass a single interface that the entity uses to interact with the outside world. This interface hides the implementation details.

If you have too many arguments, then you bundle them up into some object as a single argument.

When you can, don't pass data (i.e. a concrete container type, such as a vector); instead, pass an interface.

When you can, don't require your entities to hold direct pointers to global state; instead, pass an interface.

This topic is closed to new replies.

Advertisement