Enums in game script?
by Shawn LeBlanc · in Torque Game Builder · 03/11/2007 (9:31 pm) · 22 replies
Hello,
I was wondering if there's any way in script to create C/C++ style enumerations, like:
enum
{
STATE_IDLE,
STATE_MOVING,
STATE_SEARCHING,
STATE_DEAD
} eState;
Right now, a temporary method I'm using is to use functions, like:
function StateIdle() {return 0;}
function StateMoving() {return 1;};
function StateSearching() {return 2;};
function StateDead() {return 3;}
It's not quite perfect, but it does avoid using magic numbers everywhere in my code.
Thanks!
Shawn
I was wondering if there's any way in script to create C/C++ style enumerations, like:
enum
{
STATE_IDLE,
STATE_MOVING,
STATE_SEARCHING,
STATE_DEAD
} eState;
Right now, a temporary method I'm using is to use functions, like:
function StateIdle() {return 0;}
function StateMoving() {return 1;};
function StateSearching() {return 2;};
function StateDead() {return 3;}
It's not quite perfect, but it does avoid using magic numbers everywhere in my code.
Thanks!
Shawn
#2
Which would accomplish the same thing ... the actual use of enum's is not required ... there's always an alternative, but generically speaking, constants can take the place of Enums ... Enums are just 'object-like constants' ... such as Enum.Name where $ENUM_NAME could take it's place ...
It would be advised to use constants over function calls as well, the function calls will actually slow you down as they require more resources to execute (they have to be executed)
03/12/2007 (8:16 am)
You could use constant's as well;$STATE_IDLE = 0; $STATE_MOVING = 1;
Which would accomplish the same thing ... the actual use of enum's is not required ... there's always an alternative, but generically speaking, constants can take the place of Enums ... Enums are just 'object-like constants' ... such as Enum.Name where $ENUM_NAME could take it's place ...
It would be advised to use constants over function calls as well, the function calls will actually slow you down as they require more resources to execute (they have to be executed)
#3
03/12/2007 (9:21 am)
It should be noted that TorqueScript has no notion of a constant. You can force yourself to take some measure such as "no variable in all caps will be modified by me at any time" but it is only a handshake with yourself. You can't enforce a constant in TorqueScript. That said, I use the mentioned method of "all caps" vars for what I would typically use an enum for. I just don't want anyone reading this thread to get the idea that TS has constants.
#4
03/12/2007 (12:09 pm)
@Ben, thanks for making that note, I should have mentioned that myself -- I use the same method, any var in all caps is 'constant' and "I" should never change it's value. Sorry if it was misleading :)
#5
03/12/2007 (12:29 pm)
Or to make it more enum like (and to prevent naming overlapping)$aiState.STATE_IDLE = 0; $aiState.STATE_MOVING = 1;
#6
Then you can refer to it like this:
Doing it like this gives us a couple of extra benefits like having intellisense on it, and allowing us to define generic Enum::ToString() and Enum::Parse() methods.
03/12/2007 (5:34 pm)
I prefer doing it like so:new ScriptObject (StateEnum)
{
None = 0;
Idle = 1;
Moving = 2;
Searching = 3;
Dead = 3;
};Then you can refer to it like this:
if (%this.state == StateEnum.Moving) %this.state = StateEnum.Idle;
Doing it like this gives us a couple of extra benefits like having intellisense on it, and allowing us to define generic Enum::ToString() and Enum::Parse() methods.
#7
The generic $CAP_CONSTANT is more performance tuned, as the value should just be pushed onto a generic var stack when it's evaluated by the scripting engine, where as the ScriptObject has to be instantiated on the engine side, then the field values assigned, and to retrieve there values you also have to call the ConsoleObject's accessor methods ... more code is involved in grabbing the data from the script-object then from the $CAP_CONSTANT method.
If the $aiState.STATE_IDLE is simply interpreted by the engine as a non-object variable ... then it would also work, and Torsion supports IntelliSense on the $CAP_CONSTANT scheme, not sure about the $aiState.IDLE though (haven't tried it).
Generally speaking though, for the performance tuning nuts, the ScriptObject method is a 'last resort' ... but a good idea for making the code look clean ...
03/12/2007 (6:18 pm)
If your going for performance, for any reason, the added resources of a ScriptObject and the unnecessary accessor method calls to obtain the script objects field data makes the ScriptObject 'object-like' method non-performance friendly.The generic $CAP_CONSTANT is more performance tuned, as the value should just be pushed onto a generic var stack when it's evaluated by the scripting engine, where as the ScriptObject has to be instantiated on the engine side, then the field values assigned, and to retrieve there values you also have to call the ConsoleObject's accessor methods ... more code is involved in grabbing the data from the script-object then from the $CAP_CONSTANT method.
If the $aiState.STATE_IDLE is simply interpreted by the engine as a non-object variable ... then it would also work, and Torsion supports IntelliSense on the $CAP_CONSTANT scheme, not sure about the $aiState.IDLE though (haven't tried it).
Generally speaking though, for the performance tuning nuts, the ScriptObject method is a 'last resort' ... but a good idea for making the code look clean ...
#8
Results:
So one version takes about 8.7x10^-7s to evaluate and assign to an lvalue, and the other takes about 6.2x10^-7s. Bottom line is, if you want a little extra readability, scoping and functionality, the cost is pretty negligable. If you don't want it, then don't use it. It most likely won't matter.
03/12/2007 (9:08 pm)
I think it's a little much to be that concerned about the performance of such a trivial operation in script (where you shouldn't be doing extremely intensive operations anyway). So I went ahead and did a quick profile of the two methods just to be sure. By no means is this precise, but it's enough to get the idea. I did 5 runs. The loop is 10 million iterations, which is hopefully faaar more than you'd evaluate every enum over the entire lifetime of your game.if (!isObject (GameModeEnum))
new ScriptObject (GameModeEnum)
{
None = 0;
};
$HELLO_THERE_SIRE = 0;
%start1 = getRealTime();
for (%i = 0; %i < 10000000; %i++)
%y = GameModeEnum.None;
%end1 = getRealTime();
%start2 = getRealTime();
for (%j = 0; %j < 10000000; %j++)
%x = $HELLO_THERE_SIRE;
%end2 = getRealTime();
echo ("Time1: " @ %start1 @ " to " @ %end1 @ ": " @ (%end1 - %start1));
echo ("Time2: " @ %start2 @ " to " @ %end2 @ ": " @ (%end2 - %start2));Results:
Time1: 953856875 to 953865687: 8768 Time2: 953865687 to 953871953: 6272 Time1: 954122109 to 954130812: 8704 Time2: 954130812 to 954137015: 6208 Time1: 954142828 to 954151640: 8768 Time2: 954151640 to 954157875: 6272 Time1: 954208734 to 954217515: 8832 Time2: 954217515 to 954223734: 6208 Time1: 954228203 to 954236968: 8768 Time2: 954236968 to 954243171: 6208 Time1 average: 8768ms Time2 average: 6234ms
So one version takes about 8.7x10^-7s to evaluate and assign to an lvalue, and the other takes about 6.2x10^-7s. Bottom line is, if you want a little extra readability, scoping and functionality, the cost is pretty negligable. If you don't want it, then don't use it. It most likely won't matter.
#9
Some people are just ridiculously weird about performance ... I'm not really one of them, but I always weigh the differences ... and I figure, why go the route thats not the fastest, you never know when your code might need to be overhauled to shave a few CPU cycles off the end of a weighted process to get things to work "just right" ...
I also personally prefer the $CAP_CONSTANT method, just because it's more "C" then "C++" ... ;)
Good benchmark though ...
03/12/2007 (10:04 pm)
@Richard, thanks for doing the comparisons ... and your right, it's a bit of a nit-pick, but like I said "for the performance tuning nuts" ...Some people are just ridiculously weird about performance ... I'm not really one of them, but I always weigh the differences ... and I figure, why go the route thats not the fastest, you never know when your code might need to be overhauled to shave a few CPU cycles off the end of a weighted process to get things to work "just right" ...
I also personally prefer the $CAP_CONSTANT method, just because it's more "C" then "C++" ... ;)
Good benchmark though ...
#10
The Time1/Time2 values were as follows:
Time1: 41344 total
Time2: 30366 total
Diff : 10978 total
So, depending on what your doing ... and what your game does ... and what your target machines are ... it may make a difference ...
Could make the difference between 1-2 FPS ... could make the difference of 5-10 FPS ... depending on hardware and what your doing.
I only bring this up because some people like to use Enums for 'everything' ... now picture a 100x100 tile-layer that uses an Enum comparison on the tiles ... and it looks through each tile before drawing every other frame ... or something like that ...
Sounds weird, but I've seen games that used similar logic ... fast-paced puzzle/logic games usually ...
But yeah ... the difference is absolutely negligible unless your doing alot of 'enum' style checks between frames ...
edit when the numbers are changed to 1000 for the loops -- the difference is 0, with the exception of like every 10-15 where the Time1 is 128 and the Time2 remains 0 ... if 128ms means anything to you ...
When the numbers increased to 10,000 -- Time1 is almost always 128, but Time2 remains 0 ...
When you go up to 100,000 -- Time1 is 384 almost every time, and Time2 jumps between 256 and 384 like every 3 runs ...
1000 / 30 = 33.3333333333333
So every 33.333333333 milliseconds is a frame -- which means when you get up in the numbers, your talking about losing a fair number of frames with object.Field compared to $CAP_CONSTANT -- which means, if the code is executed in onUpdateScene you could effect frame rates with the object.Field ... though, again, you might loose 1 fps ... but you could effectively loose upwards around 5-10 depending on the hardware ... something to keep in mind if your target audience has low-end systems (again, 'performance tuning nuts beware')
03/12/2007 (10:10 pm)
Oh, I might also add, on my workstation ... which is apparently not nearly as high-end as yours?The Time1/Time2 values were as follows:
Time1: 41344 total
Time2: 30366 total
Diff : 10978 total
So, depending on what your doing ... and what your game does ... and what your target machines are ... it may make a difference ...
Could make the difference between 1-2 FPS ... could make the difference of 5-10 FPS ... depending on hardware and what your doing.
I only bring this up because some people like to use Enums for 'everything' ... now picture a 100x100 tile-layer that uses an Enum comparison on the tiles ... and it looks through each tile before drawing every other frame ... or something like that ...
Sounds weird, but I've seen games that used similar logic ... fast-paced puzzle/logic games usually ...
But yeah ... the difference is absolutely negligible unless your doing alot of 'enum' style checks between frames ...
edit when the numbers are changed to 1000 for the loops -- the difference is 0, with the exception of like every 10-15 where the Time1 is 128 and the Time2 remains 0 ... if 128ms means anything to you ...
When the numbers increased to 10,000 -- Time1 is almost always 128, but Time2 remains 0 ...
When you go up to 100,000 -- Time1 is 384 almost every time, and Time2 jumps between 256 and 384 like every 3 runs ...
1000 / 30 = 33.3333333333333
So every 33.333333333 milliseconds is a frame -- which means when you get up in the numbers, your talking about losing a fair number of frames with object.Field compared to $CAP_CONSTANT -- which means, if the code is executed in onUpdateScene you could effect frame rates with the object.Field ... though, again, you might loose 1 fps ... but you could effectively loose upwards around 5-10 depending on the hardware ... something to keep in mind if your target audience has low-end systems (again, 'performance tuning nuts beware')
#11
EDIT @ David : Thanks for your benchmark too.
Aun.
03/12/2007 (10:11 pm)
@ Richard McKinney : Thanks for the benchmark ;)EDIT @ David : Thanks for your benchmark too.
Aun.
#12
03/12/2007 (10:26 pm)
Oh, my AMD Athlon 3500+ with 1GB RAM gets 50 FPS in the scroller demo ... with an Nvidia GeForce 6600 256mb video card ... just for those curious ... with a lower end system, the scroller demo would get maybe 30-40 ... at which point, losing 5-10 FPS would make a big difference in how smooth things moved ...
#13
I would assume that it is more of a rendering problem with overdrawing (which sadly is the most common problem in 2D) than a calculation or "variable access" or calculation time problem.
This is caused especially by particle effects with larger particles or a large amount of particles which will raise this problem very fast (by larger I mean 40 and upward).
To make it worse, the overdrawing performance lose is not linear. While you might get 50 FPS on your specs, an Intel Onboard GMA 900 / 950 or 6200 TC will get 1/4 of your performance and worse, as they have far lower bandwidth than your card and those chips tend to have removed overdraw restriction functionality in the GPU itself as well.
I think this kind of optimation is much more important and impacting in a 2D environment than Torque Script most likely ever could be. Only if you try complex physics or neural networking and the like in script it will start to be a serious issue but I think thats what we assume to happen in those cases.
03/13/2007 (7:58 am)
If you are at 50 FPS with that specs, I think there are other things that should be much more important to spot and be sorted, as it sounds like something is burning a fair amount of per frame time either through calculation or through rendering.I would assume that it is more of a rendering problem with overdrawing (which sadly is the most common problem in 2D) than a calculation or "variable access" or calculation time problem.
This is caused especially by particle effects with larger particles or a large amount of particles which will raise this problem very fast (by larger I mean 40 and upward).
To make it worse, the overdrawing performance lose is not linear. While you might get 50 FPS on your specs, an Intel Onboard GMA 900 / 950 or 6200 TC will get 1/4 of your performance and worse, as they have far lower bandwidth than your card and those chips tend to have removed overdraw restriction functionality in the GPU itself as well.
I think this kind of optimation is much more important and impacting in a 2D environment than Torque Script most likely ever could be. Only if you try complex physics or neural networking and the like in script it will start to be a serious issue but I think thats what we assume to happen in those cases.
#14
03/13/2007 (8:13 am)
Going back to the original topic :) Would support for enums in-script be a good idea? Constants, too?
#15
So the question would be: How large is the execution time difference when there are actual values instead of references and how much longer would the cs -> cs.dso step take.
But I would like to know that as well. Will write up a small benchmark to compare it.
03/13/2007 (9:03 am)
It would perhaps help but it would most likely raise the compilation time of scripts as well, as constants (and enums) are replaced by their values on compile time.So the question would be: How large is the execution time difference when there are actual values instead of references and how much longer would the cs -> cs.dso step take.
But I would like to know that as well. Will write up a small benchmark to compare it.
#16
I usually have Visual Studio, SQL Server Management Studio, SQL Studio Dev Edition, Torsion, TGB, and a number of chat applications all running at once --
So my system specs, and my TGB Frame rates are not "good" benchmarks ... but I simply pointed out that on lower-end systems ... which is the usual case for casual gamers ... your frame rates are already low ...
03/13/2007 (9:22 am)
@Marc, those frame rates are on a dev box -- that usually has about 700mb of the 1gb of memory utilized for things other then TGB -- I usually have Visual Studio, SQL Server Management Studio, SQL Studio Dev Edition, Torsion, TGB, and a number of chat applications all running at once --
So my system specs, and my TGB Frame rates are not "good" benchmarks ... but I simply pointed out that on lower-end systems ... which is the usual case for casual gamers ... your frame rates are already low ...
#17
I recall reading somewhere in this forum that using a lot of global variables was not a good idea. Of course, that hasn't stopped me from using them as I always seem to need a ton of them! But I was just curious if anyone can confirm or dispell that notion. It seems to me that the hit of having a lot of global variables couldn't possble be that substantial, could it?
03/13/2007 (11:15 am)
I don't want to derail this thread too far but since it's sort of morphed into a TS performance thread. ;)I recall reading somewhere in this forum that using a lot of global variables was not a good idea. Of course, that hasn't stopped me from using them as I always seem to need a ton of them! But I was just curious if anyone can confirm or dispell that notion. It seems to me that the hit of having a lot of global variables couldn't possble be that substantial, could it?
#18
Replacing:
With:
Is effectively the same thing ... there both globally accessible ... are they stored differently? I don't believe so ... but the way the names are looked up are different, if I remember correctly.
Would storing a ton of data in a single ScriptObject that is global make up for storing tons of individual global vars? Doubtful ...
To affect performance with global vars, I think you'd have to reach a fairly large number of variables ... which, at that point, your probably doing something really wrong anyhow ...
03/13/2007 (12:24 pm)
@Dennis, I've not yet run into that anywhere, nor have I looked into it as a performance problem -- my understanding is that TorqueScript does it just like most regular scripting languages and simply creates a variable stack and puts globals at the very bottom ... I would pressume the larger the stack, the more memory your consuming ... but again, what are your alternatives to using globals for something that needs to be global?Replacing:
$player = ....
With:
new Something(Player) { };Is effectively the same thing ... there both globally accessible ... are they stored differently? I don't believe so ... but the way the names are looked up are different, if I remember correctly.
Would storing a ton of data in a single ScriptObject that is global make up for storing tons of individual global vars? Doubtful ...
To affect performance with global vars, I think you'd have to reach a fairly large number of variables ... which, at that point, your probably doing something really wrong anyhow ...
#19
and replace each instance of "global vars" with "object based enum" and you will have exactly the opposite statement of what you went to lengths to demonstrate a few posts up.
03/14/2007 (12:25 am)
Just take this statement:Quote:
To affect performance with global vars, I think you'd have to reach a fairly large number of variables ... which, at that point, your probably doing something really wrong anyhow ...
and replace each instance of "global vars" with "object based enum" and you will have exactly the opposite statement of what you went to lengths to demonstrate a few posts up.
#20
03/14/2007 (7:43 am)
@Ben, :)
Torque Owner Igor Kuryatnikov
You can expose to TS enums from C++ code, so you need to nave pro license.