Jump to content
  • Advertisement
Sign in to follow this  
Telastyn

Metadata/properties/effects design

This topic is 4800 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Sorry for the poor title. In my current project, I find myself often in a scenario which seems as though it should be straight forward, but isn't. In the game, objects can be tagged with effects. The effect itself isn't much more than a label, with the ability later for me to add other parameters to it [such as "does this stack with similar effects"]. The actual effect will be handled throughout the game in response to the tag. There are other portions of the game with a similar sort of information pattern. In the past, I would just make 1 object and then stick pointers everywhere. Or worse yet, just use an enum or similar label and hard-code the properties around it. For this project, I generally make a 'definition' class which holds the majority of the information, as well as the definition's label and/or ID. Then they're stuffed into a 'table' sort of class, which uses hash_maps, maps, or simple vectors for the label and/or ID to get access to the actual data. The object that references the stuff can then store the label and/or ID and/or pointer.... A big mess of slightly different classes for slightly different requirements. Actually, it's not a big mess, because I see it coming, and refuse to do it. So, anyone have any good suggestions or links to nicely abstract out this "many references to different elements in a mostly static table of properties"? A [quite simple] real-game example, so I don't sound completely vague: Tile based strategy game. Tiles have different terrain types. Different terrain types have different properties such as food production, wood production, movement for units, movement for flying units... All that doesn't need stored with every single tile, just a reference to the terrain definition.

Share this post


Link to post
Share on other sites
Advertisement
Meh. I know, I know bumping is poor form.

Not really a bump, but a description of the current solution; just in case anyone was interested, and happens to find the above via search.

Anyways, I just implimented what I described above. It doesn't solve the 'slightly different requirements' problem. I'll still have to impliment something almost exactly similar for other things. Quite annoying, and I'm sure some more elegant solution will come to me after I do a bunch of work. Damn thee Murphy.

Anyways, the code in question is to be used for my current project [a turn based strategy game] to allow for game object effects. Effects are... 'states' or 'properties' that an object [and by extension all carried objects] are... affected by. An example effect would be say "immunity to poison". A unit might have immunity to poison by virtue of their race. A building might bestow immunity to poison to all units in that tile.

Either way, I need code to signal that the game object is under a given effect. I don't want to copy the entirety of the effect object with each game object. I do want to allow for some sort of reference counting so that if "immunity from poison" is bestowed by two sources, the effect doesn't go away when one of the sources is destroyed.

[an aside: That little reference counting tidbit, and similar 'behavior' is the sort of thing that tends to vary in the various situations where this sort of pattern arises. I am unsure of how to abstract that well.]

So. The setup involved writing 5 classes, two of which probably already exist in boost or the STL, but I did myself because I am stubborn and ignorant.

First, a simple [heh simple!] templated smart pointer for reference counting. Takes data, and wraps a number and a functor with it. When the reference count hits 0, the functor is triggered. Other implimentations are much nicer from what little I've seen. A good learning experience for a hobbyist project though, and it works well enough.

Second, a simple [hah!] 'group' class for the templated reference counter. It wraps over a vector of the first class, and provides add(T *), remove(T *), has(T *),and contents(). The general idea here is to provide a unique set of whatever is being stuffed into the reference count.

Those two are pretty reusable as a simple group of reference counted objects. I can see perhaps using those later with the standard resource loading code. Next is the effect specific classes.

First, the effect itself. It contains a string label [because human readable labels are very useful in debugging, and code readability], and nothing else really, since I've not decided fully what else really needs to be in there yet. The constructor and destructor are private, while effect_table is befriended.

Speaking of effect_table... it is the second effect class. It wraps around a hash table [sgi's hash_map] which stores the game's effect definitions [the effect class above]. It also defines a global, since I expect there to only ever be one effect list. There might be more though, so it's not a singleton. It provides an interface for effect lookup by label, and an interface to add effects [but not remove them]. The add effect interface also does label checking to make sure there's no duplicate entries that will overwrite an old effect, and that the label provided is kosher.

The final class, and is the one that will ultimately be used in each game object is the effect_group. The effect group is almost exactly the reference counted group described above. It has a private reference counted group, and provides the same functions as it, only using the effect's label as the parameter taken. The effect group does the label->effect lookup and then queries the private group.

The net result is a table of effects, and a class which contains an arbitrary number of reference counted pointers to various entries in the table.



effect ----- *friend* ----- effect_table
|
^ |
lookup | effect *
| v
|
ref_count |
| |
ref_group [vector<ref_count> + utils] |
| |
\ |
------ effect_group [ref_group<effect> + lookup]


Unit test code: [effect_map is the global instance of effect_table]

#include "effects.h"
#include <string>
#include <vector>
#include <iostream>
#include "vine.h"


using namespace std;


int main(){

effect_group foo;
effect_group bar;
vine *v;

register_types();

effect_map.add_effect("moo.");
effect_map.add_effect("arf.");
effect_map.add_effect("moo.");

v=effect_map.stringify();
v->sendout(&v);
cout << "---\n";

cout << "bad label\n\n";
foo.add("quack.");
v=foo.stringify();
v->sendout(&v);
cout << "---\n";

cout << "good label\n\n";
foo.add("moo.");
v=foo.stringify();
v->sendout(&v);
cout << "---\n";

cout << "double entry\n\n";
foo.add("moo.");
v=foo.stringify();
v->sendout(&v);
cout << "---\n";

cout << "another entry\n\n";
foo.add("arf.");
v=foo.stringify();
v->sendout(&v);
cout << "---\n";

cout << "remove 1 from each\n\n";
foo.remove("moo.");
foo.remove("arf.");
v=foo.stringify();
v->sendout(&v);
cout << "---\n";

cout << "empty\n\n";
foo.remove("moo.");
v=foo.stringify();
v->sendout(&v);
cout << "---\n";

}



test output:

effect_table
effect moo.
effect arf.
end.
---
bad label

effect_group
end.
---
good label

effect_group
effect moo.
end.
---
double entry

effect_group
effect moo.
end.
---
another entry

effect_group
effect moo.
effect arf.
end.
---
remove 1 from each

effect_group
effect moo.
end.
---
empty

effect_group
end.
---

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!