Update

posted in A Keyboard and the Truth for project 96 Mill
Published April 28, 2005
Advertisement
So the last three days have been...interesting.

In my pursuit to get a save-game mechanisim working, I found that somthing I fixed a while ago (which was to actually making saving games easier) didn't quite work well.


The Scripting Engine:

our scripting engine allows for multiple scripts to be running at once (synched though). When one runs a script, that script is loaded from file, compiled, and cached,(if the script was cached it wouldn't do this process over), then a new ScriptProcess object is created, and the Script object is 'plugged' into it; The ScriptProccess, contains a local variable store, as well as the Execution Head. The ScriptProccess is then added to the list of proccesses, and then Execute() is called on it.

Now for 'normal' code in the script file, this code will execute(and block) until it is finished, just like real C++ code. However, ScriptProcesses have a handly little feature, any function that the script executes, can tell the script to 'LOCK'. When a script is locked, it is locked with a certain condition, more specificaly, an event, the script abandons it's execution, until a specific event is raised on a specific object, then that event will open the lock on the script and resume the execution.

This functionality allows you to do things in script code that you cant in normal C++, for instance.

Animate(sprite,"yay");
Move(sprite,10,10);
Animate(sprite,"foo");
Move(sprite,20,20);

wherein, Move takes more than a single frame of time to complete. In the case of the script code, the Move function's underlying implementation would tell the script to lock itself, with the condition of EventSpriteMoveDone, on the object, sprite.

It's really very nice =D and is the basis for most of the game logic.

The Scripting Problems:

The scripting engine can use two scopes of variables and two types of variables:

Global/Local
Number/String

Global variables look like this: $myglobal
and can be shared between all scripts.

Local variables look like this: mylocal
and are unique only within a single script file.

variables are dynamically typed:

$global="wow!";
local="wow!";

$global=10000;
local=10000;

$global=10.2;
local=10.2;

Now this is all well and good, but issues began to crop up when we needed to represent object references in script code.

Since the language wasn't built with an object type (dumbass.), some kind of hack improvisation was required.

The answer was simple: just use the Number type to store an object pointer =)

and it worked, so then we had things like:

$morning=CreateSprite("morning","Sprite","MorningDelegate","data/morning.sprite");

and the CreateSprite implementation would just return the real object's pointer as a number, no sweat.

However =D
Then we came to realize that, if we needed to save the state of a script (for a save-game), saving a bunch of pointers wasn't all that helpful; sure it was easy to save, but when you loaded up the game those saved pointers wouldn't match the new pointers of the deserialized objects, so it would make it impossible to re-bind the variables. PLUS! when doing straight deserialization, how would we know what variables we want to re-bind, since all we know is that they are numbers, not neccisarily meant as object pointers, oh the woes of type ambiguity =D

The Really Really DUMB solution:

So, in an effort to stave off this doom, I came up with a solution, that eventualy proved to be more harm than help.

Since each entity object in the game can have a string name, we decided:

"Hey, why not use the name of the object to reference it"

At the time it sounded brilliant, however it came with problems. The main problem was that, objects found thier associated scripts based on thier name, so a sprite named "raymond" would try to use "raymond.script" this was perhaps a bad move, but we were married to it now, a better solution would have been to have a "scriptname" variable. But the problem, was now, certain objects should use the same script, and to do that it means the objects need to be named the same thing, and here comes the gotcha! if you are referencing an object by some identifier(a name) and there is more than one object with that same identifier(which we wanted to be possible to allow shared script use) then the reference becomes ambiguous =/

We got around this by making sure that no two objects could have the same name (if they wanted to be referenced), and it stunk of kludge =/

The Better Solution:

So now that the save game feature is absolutely neccisary, I have revisited this issue, and amazingly enough I have found an elegant solution, and here is the basis of how it works:

Each object has a previousPointer variable.

When an object is created (not loaded from file), previousPointer is set to NULL.

When an object is saved, the first value saved is the current pointer(this).

When an object is loaded, previousPointer is set to the value that was saved(see above^)

With this system, when we load an object we will always know what the LAST pointer of an object was, this is important, because it provides a unique pool of referencing, which is readily availible when the game is saved.

Now, for the script integration. It was neccisary to modify the scripting engine a bit for this to work. I had to implement a system of 'Re-Bind', that is, when a script is deserialized, all of it's variables that are objects, use thier values (which are the pointers that the objects WERE when they were saved) and it searches through the object store(which contains all game objects)looking at the object's previousPointer variable, trying to match it with theirs. If a match is found, it means that this object WAS the object that this script variable WAS referencing, so now, the script variable can take on the new object's pointer, and the reference will be restored.

The change that was neccisary, was to add an Object type to the script engine, so that i could identify what variables needed to be re-bound, this was a 'fairly' simple change, and really, it simply used a modification of the Number type, and can only be passed directly, for instance:

obj1=FunctionThatReturnsAnObjectExplicitly();
obj2=obj1;
FunctionThatTakesAnObjectExplicitly(obj2);

you cannot express a specific address though:

obj=10238740;
//above would be of type Number not Object

and numerical manipulation will always result in a number type

obj3=obj1+obj2;
//obj3 will be a number even if obj1 and obj2 are objects

these limitations arn't that big of a deal, since most of them (barring PERHAPS direct address assignment) you would never want to do; All logical operations are intact and work just like numbers.

//numerical comparison
if(obj1!=0)
{
//woot!
}

//numerical comparison
if(obj1==obj2)
{
//woot!
}

In short, a much more elegant solution, and small development time (3 days * 6 hours = 18hours)

Hopefully somone finds this informaative =D
Previous Entry *yawn*
0 likes 3 comments

Comments

Rob Loach
Sounds pretty cool. It'll be interesting to write the AI using that system.
April 28, 2005 12:12 PM
Mushu
Once I get something rendered I should write a scripting system. Or download Lua. Or something.
April 28, 2005 04:38 PM
EDI
Correction

Once I get something rendered I should have ALL my teeth pulled out with pliers write a scripting system. Or make things easy on myself download Lua. Or something.

unless you are making somthing as simple as:

sequential function execution(as in no branching or logic)

then you are gonna be tearing your hair out, from the parsing alone =D

imagine...

write some code to parse this into somthing that you could execute:

if(a==b&&d==c||c!=a)
{
while(a<50)
{
ret=func1(10.0,"w00t",b);
}
switch(d)
{
case 10:
func2("omg!");
break;
case "n0es!":
func3("argh!!");
break;
}
}

Think you can do that? huh? HUH!?

Well my scripting engine does, and know what, it was a bitch to make =/ I was hyped up on caffine for like 2 weeks =/

=D
April 29, 2005 01:41 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement