Sign in to follow this  
nullsquared

Game States, Level Loading and Entity Management

Recommended Posts

Game programming is definitely a lot harder than engine programming. At least for me. [grin] Anyways, I'm having various doubts as to how level loading fits in along with game states and entity management. 1) Is there some general path to follow here? 2) Do I simply create a list of entities to keep track of within the game state? 3) If so, then how would level loading work? 4) Would the level loader need a game state pointer, and somehow "inject" entities? 5) Does the level loader simply inject info and have the game state create the entity, or does the level loader know enough about the entity to create it and then simply "inject" it into the game state? 6) Who really ends up with ownership of these entities? 7) And is a list (list/vector/name-object map/etc.) really the way to own these entities, or should they own themselves via static members? I already dived head-first into this without any design, and failed miserably in horrible, ugly code. Now I'm planning first [wink]. Thanks in advance!

Share this post


Link to post
Share on other sites
Have a World class.

+ GameState
+ World
+ SceneGraph*
- []Entities
- []Props
- []Terrain

*All world objects use/modify the SceneGraph as they contain the SceneNodes representing them in the scene. When an entity gets destroyed, the world deletes the scene node it controls.

+ LevelLoader
->GameState->World->CreateEntity()

Share this post


Link to post
Share on other sites
Quote:
Original post by thre3dee
Have a World class.

+ GameState
+ World
+ SceneGraph*
- []Entities
- []Props
- []Terrain

*All world objects use/modify the SceneGraph as they contain the SceneNodes representing them in the scene. When an entity gets destroyed, the world deletes the scene node it controls.

+ LevelLoader
->GameState->World->CreateEntity()


Hm. A separate world object sounds useful. Previously I had the game state have direct ownership of the entities. If there are any other methods of doing this, I'd love to hear them [smile].

As for the level loader... questioning design here, does the level loader know about every type of entity to load it into the world? So:

void game::levelLoader::load(game::world &world)
{
// do loading
world.addEntity(new someTypeOfEntity());
}

Does the level loader construct the entity, or does the level loader pass data to the entity to construct itself? Or does the level loader avoid the whole entity business and pass data to the world to create the entity? What happens when there's lots of different types of entities, such as playerSpawn, endLevel, enemySpawn, etc.?

Share this post


Link to post
Share on other sites
I have a class Game, and a class Level. A Game object is responsible for creating Level objects, and calling their play() method, checking how things went, and either replaying it, going to the next, or skipping based on user input.

The Level class loads it's data from file based on arguments to its constructor. When it is play()ed, it just updates all its data and draws to the space on the screen allotted to it.

int main(), then, contains the main menu loop, which calls game.play(), which starts playing levels. This way, game state is determined by which function is currently executing.


class Level{
public:
Level(int levelnum);//loads appropriate level from file
int play();//starts the level, returns success//failure code
};

class Game{
Game();
int play(){
//in loop,
//create a level
//play it
//check if it needs to be replayed
//if user wants to terminate game
//then return to main
}
};

int main(){
//init game

bool done = false;
Game game;

while(!done){
//draw main menu stuff
//check input
if(/*player has clicked control to start game*/){
game.play();//may not need to check return
}
}
}



In my current design, I'm actually traversing states using exceptions, but apparently this is bad, so I'm using returns here. If you think my design is stupid or weird, I wouldn't blame you, but it works for me. It may give you some ideas.

Getting to your other questions: In class Level, I have a list of creatures in the level, and an array of tiles (it's tile-based). I just iterate through them on each cycle, updating them, and culling sprites that have requested deletion (a flag member variable, bool deleteMe). Most of your questions, there are no strict answers to, particularly about ownership. It's just whatever works with the rest of your design. I believe that whoever is updating them should own them.

Quote:
As for the level loader... questioning design here, does the level loader know about every type of entity to load it into the world?


[headshake] In my design, yes, unfortunately! But there are design patterns that will let you get around that. It really shouldn't have to.

Share this post


Link to post
Share on other sites
Thanks [smile]. About the level loader knowing about every entity out there, it's just that lately I've been trying to write well-designed code and flows out not only programmatically but logically as well. Having it one sided (flows out programmatically but not logically, or vice-versa) in the past used to only create trouble for me down the line [grin].

So, I see there is no one-fits-all answer here. I've love to see more people's designs regarding these issues, just to see how different people do it differently. Here is a quick prototype of what I might end up using:

class entity;
typedef boost::shared_ptr<entity> entityPtr;
typedef std::map<engine::string, entityPtr> entityList;
// for the incoming Zalhman and friends, engine::string is a typedef for std::string [wink]

class world
{
private:
entityList _ents;

public:
world();
~world();

void addEntity(entityPtr ent);
entityPtr getEntity(const engine::string &name) const;

void tick(engine::real dt) const;
};

Basically, the world has ownership of the entities via shared pointers, and anything that has access to the world has the ability to retrieve entities as well as add entities. I guess the master question here is:

// do this?
void levelLoader::load(game::world &world)
{
// load information & create entities
world.addEntity(loadedEntity);
}

// or do this?
void levelLoader::load(game::world &world)
{
// load information only
world.createEntity(information);
}

But, in case 2, what would information look like?

Share this post


Link to post
Share on other sites
'information' is whatever you load from your level file. It's probably a type, and arguments that determine the entity's initial state. I think that the best approach would be something like

void levelLoader::load(Game::World& world){
world.addEntity(CreateEntity(/*info from file, or wherever*/));
}


I'd be happy to be corrected by any of the resident OOP experts here [grin]. Exactly where the entity is constructed is fairly trivial. But you don't want either levelLoader or world to have to deal with the subclasses of Entity. So you encapsulate it into a function like CreateEntity. But this is something I need to work on myself.

Share this post


Link to post
Share on other sites
You could have a entity or object factory. Say you use XML to define your entities like so:

<level>
<entities>
<entity type="monster" position="10, 10, 10" scale="1.0,1.0,1.0" weapon1="axe" weapon2="knife">
</entities>
</level>


Then when your level loader gets to the entity definition it does something like this (note this is hypothetical engine code):

std::string type = "monster";
vector3 pos (10, 10, 10);
vector3 scale (1, 1, 1);
world->getObjectFactory()->createEntity (world, type, pos, scale);

// createEntity code
entity* object_factory::createEntity (const World* world, const std::string& type, const vector3& pos, const vector3& scale)
{
// create an entity using the correct model file for our "monster" type
entity* e = world->createEntity (m_entModels[type].modelName());
e->setPosition (pos);
e->setScale (scale);
e->setSceneNode (world->getSceneGraph ()->getRootNode ()->createNode ());
e->applyToSceneNode ();
return e;

}

Share this post


Link to post
Share on other sites
Thanks for the advice. Here is the interface I came up with for now:

entityFactoryPtr genericPropFactory(new genericPropFactory(parentNode, physWorld));
// parentNode and physWorld are some parameters that entities of type "genericProp" require

entityFactory factory;
// register a custom factory for types of "genericProp"
factory += genericPropFactory;

// create an entity of type "genericProp" named "ent"
entityPtr ent(factory("ent", "genericProp"));

entityPtr anotherEnt(factory("anotherEnt", "someOtherType"));
// the above would print
// could not create entity "anotherEnt":
// entity factory for type "someOtherType" not registered



Share this post


Link to post
Share on other sites
Quote:
Original post by agi_shi
Thanks for the advice. Here is the interface I came up with for now:
*** Source Snippet Removed ***


Hmm I like the use of operators on your game classes!

I've never though of using the function () operator on something like a factory. Makes sense when you think of it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this