Game Development Community

Eval() Troubles

by Richard_H · in Torque Game Engine · 11/11/2006 (5:44 pm) · 18 replies

Hi,

for my game I am using triggers to control the path my AIPlayers take and one function which gets the distance to the end of the path refuses to work due to some faulty eval code.
function pathTrigger::distToNext(%this, %path)
{
   %destPos = eval("%this." @ %path @ ".getPosition()");
   %ourPos = %this.getPosition();
   %dist = VectorDist(%destPos + %ourPos);
   %otherDist = eval("%this." @ %path @ ".distToNext(" @ %path @ ")");
   %totalDist = %dist + %otherDist;
   return %totalDist;
}
%path represents the name of a variable. What this function should do is get the position of the next node in the current path (represented by a varibale in the pathTrigger with the name of the path) and then find the distance to it, then repeat this for that node until it reachs the destination, then all the distances get added together to be processed by another function. Unfortunetly the console returns a parse error.
Can anyone help?

#1
11/11/2006 (7:27 pm)
Try adding a semicolon to the end of the evals.
#2
11/11/2006 (7:37 pm)
Thanks!
Now my one problem is with the way my objects are stored in varaibles. How do I store the ID of the object?
Say the object is named test, would I just type:
var = test.getID();
Or something like that?
#3
11/11/2006 (8:46 pm)
@Richard,

1. Local:
%var = test.getID();

2. Global:
$var = test.getID();

Note, be careful how you create your eval scripts. Your script will work, but by encapsulating %this in quotes, you're telling the scripting engine to 'late evaluate' it.

Because you are evaluating it right away, you don't notice a problem and %this is interpreted properly, but consider the following example carefully:
function evalBuilder( %aVar )
{
   %evalStr = "echo( \"%aVar == \" SPC %aVar );";
   echo( "evalBuilder() => ", %evalStr );

   echo( "Evaluated locally we get: " );
   eval("echo( \"%aVar == \" SPC %aVar );");
   return %evalStr;
}

function evalBuilder2( %aVar )
{
   %evalStr = "echo( \"%aVar == \" SPC " @ %aVar @ " );";
   echo( "\nevalBuilder2() => ", %evalStr );
   echo( "Evaluated locally we get: " );
   eval("echo( \"%aVar == \" SPC " @ %aVar @ " );");
   return %evalStr;
}

function testIt( )
{
   // test 1
   %aVar = 30;
   %evalStr = evalBuilder( 10 );
   echo( "\ntestIt() => ", %evalStr );
   echo( "Evaluated remotely we get: " );
   eval( %evalStr );

   // test 2   
   %evalStr = evalBuilder2( 20 );   
   echo( "testIt() => ", %evalStr );   
   echo( "Evaluated remotely we get: " );
   eval( %evalStr );
   

}

If you put this code into a file, exec() it, and then run: testit();

You will see that you get this output:
==>testIt();
evalBuilder() => echo( "%aVar == " SPC %aVar );
Evaluated locally we get: 
%aVar ==  10

testIt() => echo( "%aVar == " SPC %aVar );
Evaluated remotely we get: 
%aVar ==  30
evalBuilder2() => echo( "%aVar == " SPC 20 );
Evaluated locally we get: 
%aVar ==  20
testIt() => echo( "%aVar == " SPC 20 );
Evaluated remotely we get: 
%aVar ==  20


As you can see, in the first example evalBuilder(), the value of %aVar is interpreted in the current scope, giving a different value for a local evaluation versus a remote (or late) evaluation of same script.

I hope you find this interesting. I wanted to save you some heartache in the future, as well as introduce you to a useful TorqueScript feature.

Cheers,

www.hallofworlds.com/how.ico Hall Of Worlds - For Gamers
EdM|GPGT


Made changes to improve legibility.
#4
11/12/2006 (11:24 am)
Now I have yet another question, though hopefuly it will be my last. In my code I get the position of an object whose ID is stored in a variable. Right now I am unable to get the value out of the variable and I'm not sure why.
Here is the important sections of my code.
// pathTrigger.cs - this trigger was made to allow dynamic pathing in my game Silent Killer
// made by Richard Hofer
datablock TriggerData (pathTrigger){
tickPeriodMS = 150;
};
.........
function pathTrigger::distToNext(%this, %path)
{
   %destPos = eval("return %this." @ %path @ ";").getPosition();
   %ourPos = %this.getPosition();
   %dist = VectorDist(%destPos + %ourPos);
   %otherDist = eval("%this." @ %path @ ".distToNext(" @ %path @ ");");
   %totalDist = %dist + %otherDist;
   return %totalDist;
}
and from the bottom of my mission file
new Trigger(p1) {
      canSaveDynamicFields = "1";
      position = "365.342 306.89 219.486";
      rotation = "1 0 0 0";
      scale = "5 5 5";
      dataBlock = "pathTrigger";
      polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
   };
   new Trigger(p2) {
      canSaveDynamicFields = "1";
      position = "343.949 262.803 204.756";
      rotation = "1 0 0 0";
      scale = "5 5 5";
      dataBlock = "pathTrigger";
      polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
   };
};
//--- OBJECT WRITE END ---

p1.getDatablock().w1 = p2.getID();
p2.getDatablock().w1 = 1.getID();   //1 is the name of an object
It seems to be a problem with storing the information in the variables.
Does anyone know how to solve this?
(If coding is required I can post in the engine section.)
#5
11/13/2006 (2:49 pm)
@Richard,

Hi. Datablocks should be considered static. That is, you should not modify them and should not expect modifications made to them (after their creation) to have any effect.

The problem, or part of it, is that datablocks live on both the server and the client. The initial copy exists on the server and a copy is sent to each attached client (local client in single player and listen server configurations).

It is possible through some standard Torque Scripting techniques (and a little bit of trickery) to get and modify datablock values, but unless you know exactly what you are doing (i.e. you've been reading the code and understand how the current version of the engine is 'consuming' that datablock), you are better off not attemping to modify ANY datablock.

Finally, even if you go through the trouble to modify the server-side copy of the datablock, your clients won't see those changes. In short, using a datablock as a way to share databetween clients (in other than a static form) is difficult at best. (See note below)

It would be interesting to hear what your end goal is here. i.e. If you could lay out what you want to achieve, I (or others) may be able to give you an alternate solution.


www.hallofworlds.com/how.ico Hall Of Worlds - For Gamers
EdM|GPGT



Visibility Note: When I said that datablock changes would not be visible I wasn't being entirely truthful. In single player mode, it is in fact possible to modify a datablock and see those changes. Additionally, when you're playing in single player mode, many datablocks can be modified and those changes can be seen to take effect immediately, or after a setDatablock() call, BUT this is still not a good practice and I don't encourage it.

-EFM
#6
11/13/2006 (3:01 pm)
I would like to implement my own pathing system. The part I showed you is setting up the path by assigning values, and then attempting calculate distance to current goal (mainly a pond or other water source). When the AIPlayer is thinking about how to choose the best path he calls the distToNext function which is given the path he is considering "w1" for example. Then the function finds the distance to the trigger who's ID is in the "w1" value and calls it's distToNext function which keeps going until it reachs a waterTrigger which just returns 0 in it's distToNext function so eventualy all the distances get added up and returned to the AIPlayer who compares them and goes for the shortest route. Is it possible to have all the variables set before the datablocks are ghosted?
#7
11/14/2006 (8:52 pm)
@Richard,

Hi.

1. Dynamic Fields (those created in from scripts in objects) are not ghosted.

2. My suggestion is that you place this information in the trigger (or waypoint marker) objects themselves. All of your scripts will be running on the server-side and will have access to these values.

For example:

new Trigger(p1) {    

 // ..
};

new Trigger(p2) {    

 // ..
};

p1.w1 = p2.getID();
p2.w1 = AnObject.getID();   //AnObject is the name of an object


3. In your code above, you had this comment: //1 is the name of an object
You didn't literally mean that 1 was the name of an object did you? I'm guessing you didn't, but just in case, you can't use numeric values for object names as this will conflict with IDs.

I hope I haven't missed your point/question.

www.hallofworlds.com/how.ico Hall Of Worlds - For Gamers
EdM|GPGT
#8
11/19/2006 (7:06 am)
You haven't at all. This means I should modify my datablck code to use the trigger like this
function pathTrigger::distToNext(%this, %trigger, %path)
{
   %destPos = eval("return %trigger." @ %path @ ";").getPosition();
   %ourPos = %trigger.getPosition();
   %dist = VectorDist(%destPos + %ourPos);
   %otherDist = eval("%trigger." @ %path @ ".distToNext(" @ %path @ ");");
   %totalDist = %dist + %otherDist;
   return %totalDist;
}
right?
P.S. : Sorry for the late replay, I've been in bed with a bronchial infection.
#9
11/21/2006 (2:06 pm)
Still getting errors, I think I have the wrong variable. If anyone can help, it would be greatly apreciated.
#10
11/21/2006 (4:02 pm)
Quote:
%destPos = eval("return %trigger." @ %path @ ";").getPosition();

This almost certainly won't work--eval doesn't work as an in-place substitution. You also cannot magically create methods on a namespace using eval (which isn't what it's intended for anyway).
Try something like this:

%evalString = "%trigger." @ %path % ".getPosition();";
%destPos = eval(%evalString);

However, I'm not sure how you are storing the %trigger.path dynamic field--are you storing an ObjectID there? That's the only way this would work.
#11
11/22/2006 (5:15 pm)
I'm still getting problems. I altered my code to this
function pathTrigger::distToNext(%this, %trigger, %path)
{
   %evalString = %trigger @ "." @ %path @ ".getID();";
   %theObject = eval(%evalString);
   echo(%evalString);
   echo("Outputs...");
   echo(%theObject);
   %evalString = %theObject @ ".getPosition();";
   echo(%evalString);
   %destPos = eval(%evalString);
   %ourPos = %trigger.getPosition();
   %dist = VectorDist(%destPos + %ourPos);
   %otherDist = eval("%trigger." @ %path @ ".distToNext(" @ %path @ ");");
   %totalDist = %dist + %otherDist;
   return %totalDist;
}
(%Trigger is passed in as the ID of the trigger object )
But I'm getting a string of errors:
<input> (0): Unable to find object: '' attempting to call function 'getId'
160.w1.getID();
Outputs...
160.w1.getID();
160.w1.getID();.getPosition();
parse error
<input> (0): Unable to find object: '' attempting to call function 'getId'
silentkiller.proto/server/scripts/pathTrigger.cs (31): Unknown command GetPosition.
    Object pathTrigger(160) pathTrigger -> TriggerData -> GameBaseData -> SimDataBlock -> SimObject
silentkiller.proto/server/scripts/pathTrigger.cs (32): ::VectorDist - wrong number of arguments.
silentkiller.proto/server/scripts/pathTrigger.cs (32): usage: (Vector3F a, Vector3F b) Calculate the distance between a and b.
<input> (0): Unable to find object: '' attempting to call function 'distToNext'
parse error
silentkiller.proto/server/scripts/aiPlayer.cs (276): Unable to find object: '' attempting to call function GetTransform
If anyone can help it would be greatly apreciated.
#12
11/25/2006 (9:04 am)
@Richard,

It looks like nothing is stored in 'w1'.

Try giving the triggers easy to remember names and then dumping them to see if your variable is being set.

i.e.

Step #1 - Create Triggers:
new Trigger(p1) {    

 // ..
};

new Trigger(p2) {    

 // ..
};

p1.w1 = p2.getID();
p2.w1 = AnObject.getID();   //AnObject is the name of an object

// extra debug code:

error("######## p1.getID() == " , p1.getID() );
error("######## p2.getID() == " , p2.getID() );
error("########      p1.w1 == " , p1.w1 );
error("########      p2.w1 == " , p2.w1 );

Step #2 - Run your game and load the mission that creates these triggers.

Step #3 -

p1.dump();

Step #4 - Look for w1 as a 'Tagged Field'.


www.hallofworlds.com/how.ico Hall Of Worlds - For Gamers
EdM|GPGT
#13
11/26/2006 (9:56 am)
The problem seems to be with this section
160.w1.getID();
Outputs...
160.w1.getID();
after some tinkering I got it to be this
1679.w1.getID();
Outputs...
1679.w1.getID();
Which should work, yet the output seems to say that eval does nothing with it. When I entered the commands into the console it was fine, but when I put a string around it and put it into eval it returns a blank line. This is all very confusing, but it seems like it's so close to working.

Edit: BTW w1 was a Tagged Field.
#14
11/26/2006 (10:56 am)
@Richard,

Better take another look there. It seems you pasted the same lines twice, not the outputs?

www.hallofworlds.com/how.ico Hall Of Worlds - For Gamers
EdM|GPGT
#15
11/26/2006 (3:57 pm)
I just realized I was checking it wrong, it turns out eval is returning a blank line. Yet if I run the command I send it, it returns the ID. Am I doing it right? I send eval a variable holding this string
"1679.w1.getID();"
and I assign it to a variable like this
%theObject = eval(%evalString);
This all looks right to me, but when I echo it, I get this
Can you see something wrong here?		
#16
11/26/2006 (8:55 pm)
The eval statement isn't going to return a result that is usable in the manner you wish.

You need to have the "%theObject =" as part of the eval string for it to be evaluated.

PS: I realize after scrolling back that I mislead you when I first stepped into the thread--I apologize. I caught the issue you were having with the statement itself, but not how you wanted to use the result, and my last line of the "try this" code will not work.
#17
11/27/2006 (11:17 am)
@Richard,

Yes, Stephen is right. This is a common mistake. I make it occasionally too. :)

Try adding this code to a file and load it with exec():

function forRichard()
{
   // CREATING LOCAL WITH EVAL
   echo( "0. %x == " , %x );

   // Use eval to create a local variable
   eval( "%x = 10;" );
   
   echo( "1. %x == " , %x );
   
   // CREATING GLOBAL WITH EVAL
   forRichard2( 0 );

   // Use eval to create a global variable
   eval( "$x = 10;" );

   forRichard2( 1 );
}

function forRichard2( %num )
{
   echo( %num , ". $x == " , $x );
}

Running this will print the following:

==>forRichard();
0. %x ==
1. %x == 10
0. $x ==
1. $x == 10

www.hallofworlds.com/how.ico Hall Of Worlds - For Gamers
EdM|GPGT
#18
11/27/2006 (11:31 am)
I fixed this by re-adding the return to the start of each statement. I'm still getting problem, but I think I can solve them. I do have one question though, is there a variable name I can use like %this that automaticly fills in the ID of the object that contains the datablock?