Best way to design an object list

Started by
7 comments, last by arkane7 11 years, 10 months ago
For my current project I have an Entity class that all game objects are inherited from.
I also have an EntityManager class that keeps a list of pointers to all the game objects created. The idea being that this list will be used for updating and drawing all game objects to the screen.

I find myself struggling to find the best way to design all of this.
How do I make sure all game objects are added to the EntityManager list?
How do I make sure that when an object is deleted, all other game objects that might have a pointer to said object are not now referencing deallocated memory?
During cleanup I delete all the Entity objects in EntityManager, and therefore they all need to be heap objects created with the new keyword. How do I make sure none of the pointers point to a stack object?

Right now I am doing this project alone, and ultimately the easy way out to most of these questions is for me to just remember things and don't make mistakes.

"Just dont forget that all entity objects need to be made using new."
"Just dont forget to add all Entities you make to the EntityManager list."

But that is bad programming right? What if I was on a development team...that wouldnt fly! The other devs would not know all the catch clauses of what not to do and what may cause a bug with my code. Thats the whole point of classes and privacy and encapsulation ect...to make code that is hard to break and easy to organize.

I'm trying to improve my skill as a designer and programmer...am I overcomplicating this? Somebody help me.

Advertisement
How do I make sure all game objects are added to the EntityManager list?

Do you need to? Consider that an entity might create another entity, but may withdraw adding it into the entity manager until the appropriate time.

How do I make sure that when an object is deleted, all other game objects that might have a pointer to said object are not now referencing deallocated memory?[/quote]
By using smart pointers. The entity manager will contain strong references to the entities, while an entity can contain a weak refercence to other entities. In this way, when the entity manager removes an entity, anything attempting to reference that entity through a weak reference will return null.

During cleanup I delete all the Entity objects in EntityManager, and therefore they all need to be heap objects created with the new keyword. How do I make sure none of the pointers point to a stack object?[/quote]
Smart pointers resolve this issue, as they automatically destroy the object when there are no more strong references referring to the object. You can also specify a custom deletion function for the smart pointer, so you can create a smart pointer to a stack allocated object with no-op deleter function.

For my current project I have an Entity class that all game objects are inherited from.


Heavy use of inheritance is a rather brittle way of structuring your entities. These days, it's all about object composition instead, and structuring your data to be cache-friendly and batchable, rather than as an indirection hell of virtual pointers and jumps.


I also have an EntityManager class that keeps a list of pointers to all the game objects created. The idea being that this list will be used for updating and drawing all game objects to the screen.
[/quote]

This also might not be optimal, as these two tasks (updating and drawing) are fundamentally different, thus requiring fundamentally different data structures behind the scenes to operate efficiently.



I find myself struggling to find the best way to design all of this.
How do I make sure all game objects are added to the EntityManager list?

[/quote]

Many people will use object factories for this. The factory is responsible for building the entity.



How do I make sure that when an object is deleted, all other game objects that might have a pointer to said object are not now referencing deallocated memory?

[/quote]

Google for shared_ptr and weak_ptr. In essence, you would wrap an allocated pointer into a shared_ptr, and anywhere you wanted a "strong" reference to the object (ie, a reference that would prevent the object from being deleted if all other references were cleared) you would use a copy of the shared_ptr. In places where you need a "weak" reference to the object (ie, a reference that will be cleared if all strong references are cleared) you would use a weak_ptr.



During cleanup I delete all the Entity objects in EntityManager, and therefore they all need to be heap objects created with the new keyword. How do I make sure none of the pointers point to a stack object?

[/quote]

Your pointers aren't going to magically point to stack objects unless you explicitly point them at a stack object. So just, you know, make sure you don't point any of them at a stack object.



Right now I am doing this project alone, and ultimately the easy way out to most of these questions is for me to just remember things and don't make mistakes.

"Just dont forget that all entity objects need to be made using new."
"Just dont forget to add all Entities you make to the EntityManager list."

But that is bad programming right? What if I was on a development team...that wouldnt fly! The other devs would not know all the catch clauses of what not to do and what may cause a bug with my code. Thats the whole point of classes and privacy and encapsulation ect...to make code that is hard to break and easy to organize.

I'm trying to improve my skill as a designer and programmer...am I overcomplicating this? Somebody help me.
[/quote]

This is why you document your code. Most of your code should be self-documenting (ie, broken up and structured in such a way that it is easy to read, methods and members named appropriately, etc...) and where necessary commented. Things like "

Just dont forget that all entity objects need to be made using new" are no-brainers, because there will probably only be one place that objects are constructed, and that's in your factory. Or, at any rate, you probably shouldn't be randomly new-ing entities all over the place. Leads to spaghetti.

Might want to read a book or two about basic design patterns, before you start making assumptions on what is good programming and what is bad. Also, don't let what the "professionals" do be your only guide; even pros can turn out some shockingly bad code in the name of Ship It!

Another thing you can do to ensure an entity is added to the EntityMaanager is to use the factory pattern:

EntityManager& mgr = ...;
EntityFacotry& factory = ...;
SpecificEntityType* e = factory.createEntity<SpecificEntityType>( mgr, SpecificEntityTypeArgs( ... ) );

template<typename Type, typename Args> // or varadic template parameters
Type* EntityFactory::createEntity( EntityManager& mgr, const Args& params ) {
Type* ent = new Type( params );
mgr.addEntity( (Entity*)ent );
return ent;
}



EDIT:
Interleaving Ninjas'd
Also, the responsibility of creating entities should be separated out into a new class.

Interleaving Ninjas'd
[/quote]
inorite? :D
You guys are awesome. FleBlanc I want to have your babies.

Thank you!

Edit: Any reccomendations on design pattern books?
I'm going to second the recommendation of using an entity factory. Request all entities from the factory instead of creating them manually, and your factory can handle any additional setup such as registering the entity with an entity manager. As has been mentioned already, consider keeping specialized collections of entities--triggers don't need to be drawn for instance.

The recommendation of smart pointers is a good call, but also consider adding an is_alive member to entities. It's often advantageous to kill an entity (set its is_alive member to false) and add it to a dead entities vector rather than to delete it outright. This lets you reuse entities if needed rather than to continually create and delete them, and lets you delete entities at a more convenient more time.
Edit: I was able to resolve it by moving #include "Bullet.h" and #include "EnemyGuy.h" into the EntityFactory.cpp.
I also moved #include "EntityFactory.h" into EnemyGuy.cpp


Ok so I went and read up about the factory design pattern and I am trying to implement it and I have hit a roadblock.

[source lang="cpp"]// in EnemyGuy.h
#include "Entity.h"
#include "EntityFactory.h"

class EnemyGuy: public Entity {
public:
void update();
}
[/source][source lang="cpp"]// in EnemyGuy.cpp
#include "EnemyGuy.h"

void EnemyGuy::update(){
if (you see player)
Entity* shotFired = EntityFactory::create("bullet", params);
}
[/source][source lang="cpp"]// in EntityFactory.h
class Entity
#include "Bullet.h"
#include "EnemyGuy.h"

class EntityFactory{
public:
static Entity* create(const std::string passed, params);
}
[/source][source lang="cpp"]// in EntityFactory.cpp
#include "EntityFactory.h"
static Entity* EntityFactory::create(const std::string passed, params){
if (passed == "bullet")
return new Bullet(params);
if (passed == "enemyguy")
return new EnemyGuy(params);
}
[/source]
I get a cyclic dependancy error because the factory needs to include EnemyGuy so it can create instances of it and then EnemyGuy needs to include the factory so it can call the create() method.

Normally you would break a cyclic dependancy with a forward declare but in this case a forward declare won't do it.
How can I resolve this issue?
First of all, don't include any header files inside another header file unless you actually use a real object inside that file.

Second, you should include those headers in the cpp files. Since all your real implementation code is in the cpp files the linker will go to those; if you #include h1 in h2 and #include h2 in h1 you have a cyclic pattern.
Avoid this by only including inside the cpp files where there is actual implementation code. Basically move all your includes out of the headers and into the cpp files


Edit: sorry didn't see that you resolved it. Makes my comment redundant
Always improve, never quit.

This topic is closed to new replies.

Advertisement