Jump to content

  • Log In with Google      Sign In   
  • Create Account


Organising entities/resources


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
8 replies to this topic

#1 Ussyless   Members   -  Reputation: 142

Like
0Likes
Like

Posted 18 February 2013 - 12:12 AM

Hi, so i'm wanting to work on a small personal project, involving me building my own simplish engine, and everything related, and at the moment, i'm creating entity types similar to this (this is the header file, not cpp)

 

class e_ship : public g_entity
{
public:
	float x;
	float y;
	float xseepd;
	float yseepd;
	float dir;
	float dirspeed;
	g_texture spr;
public:

	void midstep();
	void draw();
	void init();

};

 

and entities are spawned something like this

 

    game->entities.push_back((new e_bullet));
what i'd like to have is a catalog of the entities in the game, perhaps something like this

g_entity classes[];
class classes[1] :public g_entity{};
- though this is redundant and wont work, i hope you see what i want to do, and i'd like to know how 
 
the goal of this outcome would be to allow me to loop through the list, display any tags related to an entity (derivitive of X entity, solid, non solid etc), allow easy level editing , and just make the engine more robust, and also allow me to loop through and load all needed things for the entities used
 
my thoughts were as follows:
 
i would now need 2 entity identifiers, one to identify the class, and one to identify the unique entity once spawned
the class identifier would ideally be assigned automatically, however it would possibly be not realistic, or i might delete an entity i no longer need, then everything after it moves down one ID, and all entities in map/saves are subequently changed
and i would also need to know how exactly i would store these classes, some options i thought of would be
 
create the class like class e_ship : public g_entity
 
then pass the address to the library somehow
 
or
create them directly in the library somehow
 
- though i don't know what would work, or how i'd do it, i've tried some methods on my own that havent worked
i would need some way to actually catalogue these entities, would class classes[]; actually work?
 
 
 
 
anyway, i'm hoping for some comments or links to articles about entity systems designing/architecture, libraries etc

i would also like to do similar things with my resources, materials etc, so links to articles about resource loading/management would be helpful also 

 

thanks


Edited by Ussyless, 18 February 2013 - 12:31 AM.


Sponsor:

#2 BGB   Crossbones+   -  Reputation: 1545

Like
2Likes
Like

Posted 18 February 2013 - 01:26 AM

assuming I understand what you mean here...

some games have basically called this idea the "classname", where the classname is basically a unique name assigned to each type of entity. typically, the game engine will then have some way to map these class-names to the logic needed to spawn them. (some other games use magic numbers, but personally I prefer names here).

a simple possibility here is the use of spawning functions, which are then either registered, or an array exists which maps the various classnames to their appropriate spawner functions. (there are other ways to do this, but they are more involved).


for example (nevermind style here):

Entity *Sp_EntityShip(SpawnEntity *args)
{
return new EntityShip(args);
}

...

Spawn_RegisterSpawn("entity_ship", Sp_EntityShip);

then when loading the world, if a name like "entity_ship" is seen, then the engine will lookup and then call the spawn function.

note that SpawnEntity can be a class representing whatever contents are stored in the map-file (origin, classname, angle, ...).


for storing more information, consider that internally there is an EntityInfo class (or similar), which may hold the classname, spawner-function, and any other info relevant to the type of entity.

alternatively, a person could overload the EntityInfo class, then register an instance of this class:
public class Ei_EntityShip:EntityInfo
{
public:
...
string getName(); //possible, or maybe use a raw string here
Entity *spawn(SpawnEntity *args);
...
};

Entity *Ei_EntityShip::spawn(SpawnEntity *args)
{ return new EntityShip(args); }

...
Spawn_RegisterEntityInfo(new Ei_EntityShip());
...


dunno if this helps any...

#3 Ussyless   Members   -  Reputation: 142

Like
0Likes
Like

Posted 18 February 2013 - 01:34 AM

hmm, interesting, though im not sure i understand fully, even if i were to assign classnames to identify these objects( i assume just for maploading or something, though magic numbers would seem to work better)

wouldnt i still need to catalogue these classes so i can loop through them and find which name belongs to which class, making it redundant for my purpose?

 

i'll read through it better when i get home, maybe you explained that and i missed it



#4 BGB   Crossbones+   -  Reputation: 1545

Like
0Likes
Like

Posted 18 February 2013 - 11:41 AM

hmm, interesting, though im not sure i understand fully, even if i were to assign classnames to identify these objects( i assume just for maploading or something, though magic numbers would seem to work better)
wouldnt i still need to catalogue these classes so i can loop through them and find which name belongs to which class, making it redundant for my purpose?
 
i'll read through it better when i get home, maybe you explained that and i missed it

classnames are more about "niceness", but are not strictly necessary.

the main drawback of a magic number is that upon seeing it, it may not always be immediately obvious what it is, and typically some sort of central assignment of magic numbers is needed (as well as still needing a way to map them to the actual entities, ...).

IME, they don't really tend to save all that much effort overall.


as for the catalog, this would be partly the purpose of EntityInfo (or similar). it would be a class mostly serving the role of describing other entity classes. you would create and register them, mostly so that you could walk the list of known (registered) entity types.


nicer options don't really exist apart from metaclasses or run-time reflection or similar, neither of which are really available in C++. (your posts seemed to be hinting at the possibility of metaclasses, but sadly, these don't exist).

now, in a scenario where a person could have metaclasses or reflection or similar (or is using spawner functions), the classname could more directly indicate what to spawn. this is more typically the case if the high-level game logic is written in a scripting language or similar.

#5 Ussyless   Members   -  Reputation: 142

Like
0Likes
Like

Posted 19 February 2013 - 01:56 AM

so what im understanding, i would do something like

 

class myclass : public baseclass

{

string Cname ="c_myclass"

}

register(myclass) (or a pointer to it?)

 

registering it would add it to a list of all the other classes using its Cname, i would then be able to spawn shit by say

 

SpawnEntity(findCLassByName("c_myclass"),x,y)- and i could also register it with a "Magic number" also to facilitate smaller savefiles or, uh faster processing times (strings vs ints)

 

 

so, question time, would  this register be er, pointers to the classes?, i'm still a little new to this, but i assume that if i did something like function(aclass) it would pass aclass's position in memory, and i could do something like x=aclass->x  or .x or whatever.

 

i also found this

https://developer.valvesoftware.com/wiki/Authoring_a_Logical_Entity

 

though it doesnt seem to have any description of how the classes are held so it's not so helpful


Edited by Ussyless, 19 February 2013 - 02:51 AM.


#6 BGB   Crossbones+   -  Reputation: 1545

Like
0Likes
Like

Posted 19 February 2013 - 11:43 AM

so what im understanding, i would do something like

 

class myclass : public baseclass

{

string Cname ="c_myclass"

}

register(myclass) (or a pointer to it?)

 

registering it would add it to a list of all the other classes using its Cname, i would then be able to spawn shit by say

 

SpawnEntity(findCLassByName("c_myclass"),x,y)- and i could also register it with a "Magic number" also to facilitate smaller savefiles or, uh faster processing times (strings vs ints)

 

you would register an instance of the class.

you can't really have a pointer to the class itself, only to an instance of one.

 

typically, you would spawn something by passing the name, rather than something like a class-pointer (again, classes aren't first-class in C++, so you can't do this).

 

 

hence the whole point of having a class to describe the entity-class, so you can create an instance of it, and register this instance.

these particular classes though would be "one-off".

 

 

also, the added performance costs of using strings are not likely to really make a significant difference in this case (things like world spawning are "rarely significant"). (unless it is done very badly, it is unlikely to take long enough for anyone to be able to notice).

 

granted, it is possible to optimize a lot of string-handling code (if needed) via the use of hash-tables and similar (and probably store these files with any binary save-games). however, doing all this up-front would likely be a case of premature optimization.

 

for example, Quake 1 got along well enough dumping its savegames as text-files, and internally parsed all its entities from a textual form during map loading.

 

 

 

so, question time, would  this register be er, pointers to the classes?, i'm still a little new to this, but i assume that if i did something like function(aclass) it would pass aclass's position in memory, and i could do something like x=aclass->x  or .x or whatever.

 

i also found this

https://developer.valvesoftware.com/wiki/Authoring_a_Logical_Entity

 

though it doesnt seem to have any description of how the classes are held so it's not so helpful

 

this also works, but does depend on some valve-specific macros and tools.

 

so, yeah, the goal is to understand how to do it directly.

 

 

in my case... I sort of-errm, cheat some.

namely, tools exist which parse the source-code (headers in this case), and basically mine out most of this stuff as "metadata", which is used both to allow loading/serialization and also to allow script code to make use of the entities, ... but this is all very non-trivial to implement (and many of my tools are also C only).

 

more likely, it will require code to load and store the contents of entities manually.

 

in my case, my storage is more in terms of (named) key/value pairs.

the "spawn entity" is basically just a collection of key/value pairs, which is generally the form loaded from maps or from savegames or world-regions.

when saved, the contents are converted back into a collection of key/value pairs, which are then written out.


Edited by cr88192, 19 February 2013 - 11:49 AM.


#7 EWClay   Members   -  Reputation: 659

Like
0Likes
Like

Posted 19 February 2013 - 02:25 PM

+1 for string identifiers, meaningful, easy to make unique, and possible to optimise later in the unlikely event that they become a performance issue.
 
You can create a handler object for each type, that knows how to create, serialise or do anything else you need. I create these from a template to save writing every one from scratch.
 
Then store the handlers in a map using the unique identifiers as a key.
 
When you want to create an object and you have an identifier, look up the handler in the map and call the create function.

 

Here's an example:

class ObjectBase
{
public:
	virtual ~ObjectBase() {}
};

class BaseHandler
{
public:
	virtual ObjectBase* Create() const = 0;
	virtual const char* Type() const = 0;
};

template<typename T>
class Handler : public BaseHandler
{
public:
	ObjectBase* Create() const
	{
		return new T();
	}

	const char* Type() const
	{
		return T::Type();
	}
};

class Factory 
{
public:
	void Register(BaseHandler* pHandler)
	{
		m_handlerMap.emplace(pHandler->Type(), std::unique_ptr<BaseHandler>(pHandler));
	}

	ObjectBase* Create(const std::string& type) const
	{
		auto it = m_handlerMap.find(type);

		if (it != m_handlerMap.end())
			return it->second->Create();
		else
			return 0;
	}

private:
	std::map<std::string, std::unique_ptr<BaseHandler>> m_handlerMap;
};

class Bullet : public ObjectBase
{
public:
	static const char* Type() 
	{
		return "Bullet";
	}
};

class Ship : public ObjectBase
{
public:
	static const char* Type() 
	{
		return "Ship";
	}
};

void RegisterObjects(Factory *factory)
{
	factory->Register(new Handler<Bullet>());
	factory->Register(new Handler<Ship>());
}
 
int main()
{
	Factory factory;

	RegisterObjects(&factory);

	Bullet* pBullet = (Bullet*) factory.Create("Bullet");
	Ship* pShip = (Ship*) factory.Create("Ship");

	delete pBullet;
	delete pShip;
}

 



#8 Ussyless   Members   -  Reputation: 142

Like
0Likes
Like

Posted 19 February 2013 - 07:18 PM

if i understand correctly, the idea is to just link a create function to each class, which then returns the address of the instance created, which you can then use to get the data from, rather than reading directly from the class (which i didnt think was possible anyway) ?

 

so for example i could do 

Attributes attrib=factory.create("Bullet")->Attribs();

 

or something along those lines, rather than say, having the address to the class itself?

and so i could also have just a list of these instances to see these attributes themselves spawned with above method, and just keep them handy for when i need it

 

correct me if i'm wrong though, i think i get it though



#9 EWClay   Members   -  Reputation: 659

Like
0Likes
Like

Posted 20 February 2013 - 03:00 AM

The factory create function returns a new instance of the class, that you can store in the entities list. You have to delete it later.

If you want to store attributes that are unique to the class, you can store those in a map too. Though I don't quite understand what you want to do with that.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS