• Create Account

### #Actualmike3

Posted 04 March 2012 - 08:15 PM

Avoiding globals is a matter of designing (and limiting) your dependencies, not of candy wrapping some data and still try to use it from everywhere.

It's a bit like "thou shalt not goto" resulting in people not writing better code, but replacing goto with even less readable and more idiotic constructs to simulate a goto. If you ask "how to not make it global, but I still need it _everywhere_" then that's your problem right there.

So how would one limit it here, when in this case one needs it whenever a tile needs to be changed, which can happen in several places, such as destroying walls or other types of tiles, or creating new walls, any of which may happen in the course of the game. It would seem there would be no way to limit that without also removing game functionality, so am I right in classifying those as "necessary dependencies" and not worrying about them? In other words, we have to use this in multiple places, no way around it? It's just the natural way this is? Though, now that I look at this, the "global" data seems fairly well-packaged. Looking at the snippet below, it would look like nobody needs to know of the existence of TilePrototype (or almost nobody -- main() may need to call some kind of initializer that sets up the array of prototypes but that's it), just the tile IDs to change the tile to different types (or not, we could even bury that functionality in Tile, such that the first time a Tile is constructed, the prototype list is prepared, thereby removing even that little bit of visibility). All that stuff is buried inside Tile, and in implementing Tile is probably the only place we ever touch that list directly. But still, couldn't that be considered "using it from everywhere", since it's used wherever Tile is, or is that just being waaaaay too paranoid and picky? But I'm wondering, because in your snippet, right there, there's a global!!!! And you mention how "candy wrapping" doesn't solve the problem!

Another problem here is, what if one uses oooone little teeny weeny global here, but then before you know it it's joined by a dozen others, and a big, sticky, stringy, gooey dependency mess from hell with all the associated bug-inducing fun that entails?

You mean have member functions of TilePrototype that take MapCells as argument? But doesn't that set up kind of a circular dependency between the two? Isn't that bad? Or what? What did you have in mind for the structure of the actual classes/data structures/etc. involved ("outline" code like I posted would be best)?

It's not always a matter of "but text book X said this is bad", but of "what's the lesser of two evils". If you can come up with a good way to do what needs to be done and which doesn't add 10 times the complexity for the sake of avoiding a circular dependency, then of course go for it.

Without any error checking etc.

// prototype.h
class Tile;
class TilePrototype
{
static const TilePrototype& get(TileId id);
void (*onActivated)(Tile& tile);
}

// prototype.cpp
vector prototypes; ([b]<-------- GLOBAL!!!!! -- mike3[/b])

const TilePrototype& TilePrototype::get(TileId id) { return prototypes[id]; }

// tile.h
class Tile
{
TileId tileId;
TileData data;
};


If you go with virtual functions, the vector needs to store TilePrototype*, if you go with scripting, the function pointer should instead be whatever script handle your favorite method is using (might be strings or wrappers or...).

Looks pretty much like what I thought, then. But usually, we would make the elements in class Tile private, no, and provide member functions to access them, right? So then we'd also need an onActivated() function in Tile that calls the one in TilePrototype with *this as argument? And onXXXX() functions for each corresponding one in TilePrototype that we wish to invoke from outside? And what about dependency problems from that global?

And I just noticed a new problem. Consider the "onMove" event. There may be certain kinds of things that should occur on a move to very specific tiles, e.g. one particular tile on a map, not one kind of tile.

I don't know how large you intend your maps to become. Personally I'd try not to bloat my map elements with lots of functions just because maybe some day I might like to do such and such. If this particular reaction to a very specific tile is not related to the tile or tile type itself, you have found your first example of why games use triggers as "invisible" game objects. For this special stuff, you don't have the tile perform some super special reaction, but you create a single trigger and "place" it on that tile.

You really don't want to end up with every tile having members like
MySpecialOnMove, MySpecialOnItIsFriday, MySpecialOnLightLowerThan10, just because on level 24 you have this one tile that when stepped on at the wrong time in darkness will close a door.

You can of course go ahead and "unify" those concepts by removing the above function pointers/virtual functions and instead simply place certain triggers on certain tiles by default. But keep in mind that moving too much stuff from prototype to tile instance can quickly increase your memory usage.

And so that means that there needs to be some function that calls the trigger onMove, the tile onMove, etc. (all of these!) when something moves into that tile, right?

### #9mike3

Posted 04 March 2012 - 04:49 PM

Avoiding globals is a matter of designing (and limiting) your dependencies, not of candy wrapping some data and still try to use it from everywhere.

It's a bit like "thou shalt not goto" resulting in people not writing better code, but replacing goto with even less readable and more idiotic constructs to simulate a goto. If you ask "how to not make it global, but I still need it _everywhere_" then that's your problem right there.

So how would one limit it here, when in this case one needs it whenever a tile needs to be changed, which can happen in several places, such as destroying walls or other types of tiles, or creating new walls, any of which may happen in the course of the game. It would seem there would be no way to limit that without also removing game functionality, so am I right in classifying those as "necessary dependencies" and not worrying about them? In other words, we have to use this in multiple places, no way around it? It's just the natural way this is? Though, now that I look at this, the "global" data seems fairly well-packaged. Looking at the snippet below, it would look like nobody needs to know of the existence of TilePrototype (or almost nobody -- main() may need to call some kind of initializer that sets up the array of prototypes but that's it), just the tile IDs to change the tile to different types (or not, we could even bury that functionality in Tile, such that the first time a Tile is constructed, the prototype list is prepared, thereby removing even that little bit of visibility). All that stuff is buried inside Tile, and in implementing Tile is probably the only place we ever touch that list directly. But still, couldn't that be considered "using it from everywhere", since it's used wherever Tile is, or is that just being waaaaay too paranoid and picky? But I'm wondering, because in your snippet, right there, there's a global!!!! And you mention how "candy wrapping" doesn't solve the problem!

You mean have member functions of TilePrototype that take MapCells as argument? But doesn't that set up kind of a circular dependency between the two? Isn't that bad? Or what? What did you have in mind for the structure of the actual classes/data structures/etc. involved ("outline" code like I posted would be best)?

It's not always a matter of "but text book X said this is bad", but of "what's the lesser of two evils". If you can come up with a good way to do what needs to be done and which doesn't add 10 times the complexity for the sake of avoiding a circular dependency, then of course go for it.

Without any error checking etc.

// prototype.h
class Tile;
class TilePrototype
{
static const TilePrototype& get(TileId id);
void (*onActivated)(Tile& tile);
}

// prototype.cpp
vector prototypes; ([b]<-------- GLOBAL!!!!! -- mike3[/b])

const TilePrototype& TilePrototype::get(TileId id) { return prototypes[id]; }

// tile.h
class Tile
{
TileId tileId;
TileData data;
};


If you go with virtual functions, the vector needs to store TilePrototype*, if you go with scripting, the function pointer should instead be whatever script handle your favorite method is using (might be strings or wrappers or...).

Looks pretty much like what I thought, then. But usually, we would make the elements in class Tile private, no, and provide member functions to access them, right? So then we'd also need an onActivated() function in Tile that calls the one in TilePrototype with *this as argument? And onXXXX() functions for each corresponding one in TilePrototype that we wish to invoke from outside? And what about dependency problems from that global?

And I just noticed a new problem. Consider the "onMove" event. There may be certain kinds of things that should occur on a move to very specific tiles, e.g. one particular tile on a map, not one kind of tile.

I don't know how large you intend your maps to become. Personally I'd try not to bloat my map elements with lots of functions just because maybe some day I might like to do such and such. If this particular reaction to a very specific tile is not related to the tile or tile type itself, you have found your first example of why games use triggers as "invisible" game objects. For this special stuff, you don't have the tile perform some super special reaction, but you create a single trigger and "place" it on that tile.

You really don't want to end up with every tile having members like
MySpecialOnMove, MySpecialOnItIsFriday, MySpecialOnLightLowerThan10, just because on level 24 you have this one tile that when stepped on at the wrong time in darkness will close a door.

You can of course go ahead and "unify" those concepts by removing the above function pointers/virtual functions and instead simply place certain triggers on certain tiles by default. But keep in mind that moving too much stuff from prototype to tile instance can quickly increase your memory usage.

And so that means that there needs to be some function that calls the trigger onMove, the tile onMove, etc. (all of these!) when something moves into that tile, right?

### #8mike3

Posted 04 March 2012 - 04:30 PM

Avoiding globals is a matter of designing (and limiting) your dependencies, not of candy wrapping some data and still try to use it from everywhere.

It's a bit like "thou shalt not goto" resulting in people not writing better code, but replacing goto with even less readable and more idiotic constructs to simulate a goto. If you ask "how to not make it global, but I still need it _everywhere_" then that's your problem right there.

So how would one limit it here, when in this case one needs it whenever a tile needs to be changed, which can happen in several places, such as destroying walls or other types of tiles, or creating new walls, any of which may happen in the course of the game. It would seem there would be no way to limit that without also removing game functionality, so am I right in classifying those as "necessary dependencies" and not worrying about them? In other words, we have to use this in multiple places, no way around it? It's just the natural way this is? Though, now that I look at this, the "global" data seems fairly well-packaged. Looking at the snippet below, it would look like nobody needs to know of the existence of TilePrototype (or almost nobody -- main() may need to call some kind of initializer that sets up the array of prototypes but that's it), just the tile IDs to change the tile to different types (or not, we could even bury that functionality in Tile, such that the first time a Tile is constructed, the prototype list is prepared, thereby removing even that little bit of visibility). All that stuff is buried inside Tile, and in implementing Tile is probably the only place we ever touch that list directly. But still, couldn't that be considered "using it from everywhere", since it's used wherever Tile is, or is that just being waaaaay too paranoid and picky? But I'm wondering, because in your snippet, right there, there's a global!!!!

You mean have member functions of TilePrototype that take MapCells as argument? But doesn't that set up kind of a circular dependency between the two? Isn't that bad? Or what? What did you have in mind for the structure of the actual classes/data structures/etc. involved ("outline" code like I posted would be best)?

It's not always a matter of "but text book X said this is bad", but of "what's the lesser of two evils". If you can come up with a good way to do what needs to be done and which doesn't add 10 times the complexity for the sake of avoiding a circular dependency, then of course go for it.

Without any error checking etc.

// prototype.h
class Tile;
class TilePrototype
{
static const TilePrototype& get(TileId id);
void (*onActivated)(Tile& tile);
}

// prototype.cpp
vector prototypes; ([b]<-------- GLOBAL!!!!! -- mike3[/b])

const TilePrototype& TilePrototype::get(TileId id) { return prototypes[id]; }

// tile.h
class Tile
{
TileId tileId;
TileData data;
};


If you go with virtual functions, the vector needs to store TilePrototype*, if you go with scripting, the function pointer should instead be whatever script handle your favorite method is using (might be strings or wrappers or...).

Looks pretty much like what I thought, then. But usually, we would make the elements in class Tile private, no, and provide member functions to access them, right? So then we'd also need an onActivated() function in Tile that calls the one in TilePrototype with *this as argument? And onXXXX() functions for each corresponding one in TilePrototype that we wish to invoke from outside? And what about dependency problems from that global?

And I just noticed a new problem. Consider the "onMove" event. There may be certain kinds of things that should occur on a move to very specific tiles, e.g. one particular tile on a map, not one kind of tile.

I don't know how large you intend your maps to become. Personally I'd try not to bloat my map elements with lots of functions just because maybe some day I might like to do such and such. If this particular reaction to a very specific tile is not related to the tile or tile type itself, you have found your first example of why games use triggers as "invisible" game objects. For this special stuff, you don't have the tile perform some super special reaction, but you create a single trigger and "place" it on that tile.

You really don't want to end up with every tile having members like
MySpecialOnMove, MySpecialOnItIsFriday, MySpecialOnLightLowerThan10, just because on level 24 you have this one tile that when stepped on at the wrong time in darkness will close a door.

You can of course go ahead and "unify" those concepts by removing the above function pointers/virtual functions and instead simply place certain triggers on certain tiles by default. But keep in mind that moving too much stuff from prototype to tile instance can quickly increase your memory usage.

And so that means that there needs to be some function that calls the trigger onMove, the tile onMove, etc. (all of these!) when something moves into that tile, right?

### #7mike3

Posted 04 March 2012 - 04:29 PM

Avoiding globals is a matter of designing (and limiting) your dependencies, not of candy wrapping some data and still try to use it from everywhere.

It's a bit like "thou shalt not goto" resulting in people not writing better code, but replacing goto with even less readable and more idiotic constructs to simulate a goto. If you ask "how to not make it global, but I still need it _everywhere_" then that's your problem right there.

So how would one limit it here, when in this case one needs it whenever a tile needs to be changed, which can happen in several places, such as destroying walls or other types of tiles, or creating new walls, any of which may happen in the course of the game. It would seem there would be no way to limit that without also removing game functionality, so am I right in classifying those as "necessary dependencies" and not worrying about them? In other words, we have to use this in multiple places, no way around it? It's just the natural way this is? Though, now that I look at this, the "global" data seems fairly well-packaged. Looking at the snippet below, it would look like nobody needs to know of the existence of TilePrototype (or almost nobody -- main() may need to call some kind of initializer that sets up the array of prototypes but that's it), just the tile IDs to change the tile to different types (or not, we could even bury that functionality in Tile, such that the first time a Tile is constructed, the prototype list is prepared, thereby removing even that little bit of visibility). All that stuff is buried inside Tile, and in implementing Tile is probably the only place we ever touch that list directly. But still, couldn't that be considered "using it from everywhere", since it's used wherever Tile is, or is that just being waaaaay too paranoid and picky? But I'm wondering, because in your snippet, right there, there's a global!!!!

You mean have member functions of TilePrototype that take MapCells as argument? But doesn't that set up kind of a circular dependency between the two? Isn't that bad? Or what? What did you have in mind for the structure of the actual classes/data structures/etc. involved ("outline" code like I posted would be best)?

It's not always a matter of "but text book X said this is bad", but of "what's the lesser of two evils". If you can come up with a good way to do what needs to be done and which doesn't add 10 times the complexity for the sake of avoiding a circular dependency, then of course go for it.

Without any error checking etc.

// prototype.h
class Tile;
class TilePrototype
{
static const TilePrototype& get(TileId id);
void (*onActivated)(Tile& tile);
}

// prototype.cpp
vector prototypes;

const TilePrototype& TilePrototype::get(TileId id) { return prototypes[id]; }

// tile.h
class Tile
{
TileId tileId;
TileData data;
};


If you go with virtual functions, the vector needs to store TilePrototype*, if you go with scripting, the function pointer should instead be whatever script handle your favorite method is using (might be strings or wrappers or...).

Looks pretty much like what I thought, then. But usually, we would make the elements in class Tile private, no, and provide member functions to access them, right? So then we'd also need an onActivated() function in Tile that calls the one in TilePrototype with *this as argument? And onXXXX() functions for each corresponding one in TilePrototype that we wish to invoke from outside?

And I just noticed a new problem. Consider the "onMove" event. There may be certain kinds of things that should occur on a move to very specific tiles, e.g. one particular tile on a map, not one kind of tile.

I don't know how large you intend your maps to become. Personally I'd try not to bloat my map elements with lots of functions just because maybe some day I might like to do such and such. If this particular reaction to a very specific tile is not related to the tile or tile type itself, you have found your first example of why games use triggers as "invisible" game objects. For this special stuff, you don't have the tile perform some super special reaction, but you create a single trigger and "place" it on that tile.

You really don't want to end up with every tile having members like
MySpecialOnMove, MySpecialOnItIsFriday, MySpecialOnLightLowerThan10, just because on level 24 you have this one tile that when stepped on at the wrong time in darkness will close a door.

You can of course go ahead and "unify" those concepts by removing the above function pointers/virtual functions and instead simply place certain triggers on certain tiles by default. But keep in mind that moving too much stuff from prototype to tile instance can quickly increase your memory usage.

And so that means that there needs to be some function that calls the trigger onMove, the tile onMove, etc. (all of these!) when something moves into that tile, right?

### #6mike3

Posted 04 March 2012 - 04:29 PM

Avoiding globals is a matter of designing (and limiting) your dependencies, not of candy wrapping some data and still try to use it from everywhere.

It's a bit like "thou shalt not goto" resulting in people not writing better code, but replacing goto with even less readable and more idiotic constructs to simulate a goto. If you ask "how to not make it global, but I still need it _everywhere_" then that's your problem right there.

So how would one limit it here, when in this case one needs it whenever a tile needs to be changed, which can happen in several places, such as destroying walls or other types of tiles, or creating new walls, any of which may happen in the course of the game. It would seem there would be no way to limit that without also removing game functionality, so am I right in classifying those as "necessary dependencies" and not worrying about them? In other words, we have to use this in multiple places, no way around it? It's just the natural way this is? Though, now that I look at this, the "global" data seems fairly well-packaged. Looking at the snippet below, it would look like nobody needs to know of the existence of TilePrototype (or almost nobody -- main() may need to call some kind of initializer that sets up the array of prototypes but that's it), just the tile IDs to change the tile to different types (or not, we could even bury that functionality in Tile, such that the first time a Tile is constructed, the prototype list is prepared, thereby removing even that little bit of visibility). All that stuff is buried inside Tile, and in implementing Tile is probably the only place we ever touch that list directly. But still, couldn't that be considered "using it from everywhere", since it's used wherever Tile is, or is that just being waaaaay too paranoid and picky? But I'm wondering, because in your snippet, right there, there's a global...!!!!

You mean have member functions of TilePrototype that take MapCells as argument? But doesn't that set up kind of a circular dependency between the two? Isn't that bad? Or what? What did you have in mind for the structure of the actual classes/data structures/etc. involved ("outline" code like I posted would be best)?

It's not always a matter of "but text book X said this is bad", but of "what's the lesser of two evils". If you can come up with a good way to do what needs to be done and which doesn't add 10 times the complexity for the sake of avoiding a circular dependency, then of course go for it.

Without any error checking etc.

// prototype.h
class Tile;
class TilePrototype
{
static const TilePrototype& get(TileId id);
void (*onActivated)(Tile& tile);
}

// prototype.cpp
vector prototypes;

const TilePrototype& TilePrototype::get(TileId id) { return prototypes[id]; }

// tile.h
class Tile
{
TileId tileId;
TileData data;
};


If you go with virtual functions, the vector needs to store TilePrototype*, if you go with scripting, the function pointer should instead be whatever script handle your favorite method is using (might be strings or wrappers or...).

Looks pretty much like what I thought, then. But usually, we would make the elements in class Tile private, no, and provide member functions to access them, right? So then we'd also need an onActivated() function in Tile that calls the one in TilePrototype with *this as argument? And onXXXX() functions for each corresponding one in TilePrototype that we wish to invoke from outside?

And I just noticed a new problem. Consider the "onMove" event. There may be certain kinds of things that should occur on a move to very specific tiles, e.g. one particular tile on a map, not one kind of tile.

I don't know how large you intend your maps to become. Personally I'd try not to bloat my map elements with lots of functions just because maybe some day I might like to do such and such. If this particular reaction to a very specific tile is not related to the tile or tile type itself, you have found your first example of why games use triggers as "invisible" game objects. For this special stuff, you don't have the tile perform some super special reaction, but you create a single trigger and "place" it on that tile.

You really don't want to end up with every tile having members like
MySpecialOnMove, MySpecialOnItIsFriday, MySpecialOnLightLowerThan10, just because on level 24 you have this one tile that when stepped on at the wrong time in darkness will close a door.

You can of course go ahead and "unify" those concepts by removing the above function pointers/virtual functions and instead simply place certain triggers on certain tiles by default. But keep in mind that moving too much stuff from prototype to tile instance can quickly increase your memory usage.

And so that means that there needs to be some function that calls the trigger onMove, the tile onMove, etc. (all of these!) when something moves into that tile, right?

### #5mike3

Posted 04 March 2012 - 04:28 PM

Avoiding globals is a matter of designing (and limiting) your dependencies, not of candy wrapping some data and still try to use it from everywhere.

It's a bit like "thou shalt not goto" resulting in people not writing better code, but replacing goto with even less readable and more idiotic constructs to simulate a goto. If you ask "how to not make it global, but I still need it _everywhere_" then that's your problem right there.

So how would one limit it here, when in this case one needs it whenever a tile needs to be changed, which can happen in several places, such as destroying walls or other types of tiles, or creating new walls, any of which may happen in the course of the game. It would seem there would be no way to limit that without also removing game functionality, so am I right in classifying those as "necessary dependencies" and not worrying about them? In other words, we have to use this in multiple places, no way around it? It's just the natural way this is? Though, now that I look at this, the "global" data seems fairly well-packaged. Looking at the snippet below, it would look like nobody needs to know of the existence of TilePrototype (or almost nobody -- main() may need to call some kind of initializer that sets up the array of prototypes but that's it), just the tile IDs to change the tile to different types (or not, we could even bury that functionality in Tile, such that the first time a Tile is constructed, the prototype list is prepared, thereby removing even that little bit of visibility). All that stuff is buried inside Tile, and in implementing Tile is probably the only place we ever touch that list directly. But still, couldn't that be considered "using it from everywhere", since it's used wherever Tile is, or is that just being waaaaay too paranoid and picky? But I'm wondering, because in your snippet, right there, there's a global...!!!![i]

You mean have member functions of TilePrototype that take MapCells as argument? But doesn't that set up kind of a circular dependency between the two? Isn't that bad? Or what? What did you have in mind for the structure of the actual classes/data structures/etc. involved ("outline" code like I posted would be best)?

It's not always a matter of "but text book X said this is bad", but of "what's the lesser of two evils". If you can come up with a good way to do what needs to be done and which doesn't add 10 times the complexity for the sake of avoiding a circular dependency, then of course go for it.

Without any error checking etc.

// prototype.h
class Tile;
class TilePrototype
{
static const TilePrototype& get(TileId id);
void (*onActivated)(Tile& tile);
}

// prototype.cpp
vector prototypes;

const TilePrototype& TilePrototype::get(TileId id) { return prototypes[id]; }

// tile.h
class Tile
{
TileId tileId;
TileData data;
};


If you go with virtual functions, the vector needs to store TilePrototype*, if you go with scripting, the function pointer should instead be whatever script handle your favorite method is using (might be strings or wrappers or...).

Looks pretty much like what I thought, then. But usually, we would make the elements in class Tile private, no, and provide member functions to access them, right? So then we'd also need an onActivated() function in Tile that calls the one in TilePrototype with *this as argument? And onXXXX() functions for each corresponding one in TilePrototype that we wish to invoke from outside?

And I just noticed a new problem. Consider the "onMove" event. There may be certain kinds of things that should occur on a move to [i]very specific
tiles, e.g. one particular tile on a map, not one kind of tile.

I don't know how large you intend your maps to become. Personally I'd try not to bloat my map elements with lots of functions just because maybe some day I might like to do such and such. If this particular reaction to a very specific tile is not related to the tile or tile type itself, you have found your first example of why games use triggers as "invisible" game objects. For this special stuff, you don't have the tile perform some super special reaction, but you create a single trigger and "place" it on that tile.

You really don't want to end up with every tile having members like
MySpecialOnMove, MySpecialOnItIsFriday, MySpecialOnLightLowerThan10, just because on level 24 you have this one tile that when stepped on at the wrong time in darkness will close a door.

You can of course go ahead and "unify" those concepts by removing the above function pointers/virtual functions and instead simply place certain triggers on certain tiles by default. But keep in mind that moving too much stuff from prototype to tile instance can quickly increase your memory usage.

And so that means that there needs to be some function that calls the trigger onMove, the tile onMove, etc. (all of these!) when something moves into that tile, right?

PARTNERS