🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Script scope change

Started by
11 comments, last by Yuri Ivatchkovitch 17 years, 3 months ago
Hello, First of all, let me congratulate you for the clean interface that your lib have. It is much more easier to understand it than other script engines that I have experienced. It appears to be an excellent script engine. I am completely newbie with AngelScript, so I'm sorry if my doubt is something trivial. Lets put it in a practical way: we have a class Foo, and this class haves a Update() method and two attributes, and a kind of script holder.

class Foo 
{
public:
    Foo(){};
    Update() {g_ASEngine->ExecuteString("whatever", mScript.c_str())};

    float mVarA;
    float mVarB;

    string mScript;
};
Imagine that we have a lot of class instances and I want that classes running different scripts in the class scope. For instance:

Foo aInstance, anotherInstance;
aInstance.mScript = "mVarA = mVarA + mVarB";
anotherInstance.mScript = "mVarA = mVarA + mVarB";
When updating, obviously it wont work. I gave a look into Script Contexts and haven't fount anything that could change the scope of execution to the object 'this'. Any ideas? Thanks a lot.
--http://www.mytos.com.br
Advertisement
It's not possible to change the scope of the script execution in the way you're describing it. Though I may consider this for a future enhancement of AngelScript.

ExecuteString() is executed as if it were a global function in the script module "whatever". Thus the variables it doesn't declare itself has to be either global variables in the script or global properties registered by the application.

Since you want to access the members of an object, you'll have to tell the script which object to work on. You could register a global property handle, e.g. "Foo @self", which the script in ExecuteString() could use to access the members, e.g. "self.mVarA = self.mVarA + self.mVarB;"; The application could then set the address the self variable points to before the call to ExecuteString.

Or, you can compile script functions and pass the Foo pointer as a parameter. If you do this, then you'll have to use asIScriptContext instead of ExecuteString() as you need to set the function parameters.

asIScriptContext *ctx = engine->CreateContext();ctx->Prepare(engine->GetFunctionIDByDecl("whatever", "void function(Foo @)"));ctx->SetArgObject(fooObject);ctx->Execute();ctx->Release();


Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Thanks for your fast reply!

I was thinking on the second alternative to solve this problem. I believe that it can run faster and it probably can have the same syntax than the first solution.

And just another question, a off-topic one: Are you really Brazilian?

Thanks again.
Best regards,
Yuri I.
--http://www.mytos.com.br
And a performance question came to my mind.
How the AngelScript engine deals with hundreds of contexts running at the same time, being prepared and executed 100 times per second?

Sorry for bothering you.
Regards,
Yuri Ivatchkovitch
--http://www.mytos.com.br
No, I'm swedish, but I live in Brazil since 2001. Your name is not brazilian either, where are you from?

AngelScript has very good performance, but with thousands of calls per second you are likely to see a big performance hit. Make sure you reuse as much resources as possible between calls, e.g. try to use the same context with the same script function each time. I'm constantly improving the performance of AngelScript, so it is sure to get faster in the future.

Though, I think that you may want to rethink your design. Do you really need thousands of calls per second? I'm assuming you're writing a game with hundreds of entities controlled by script. You probably want to write the code so that you don't have to call a script for each entity every frame. Maybe it is enough to call the script just once in a while, when the entity really has something to do? For example, upon an event, or upon a timer?

The above is true for any script language, not just AngelScript.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I'm Brazilian living in São Paulo ... just my surname is from Yugoslavia. lol

You have a good point when you say to call scripts on isolated events. I have exaggerated on the description. The game currently being developed will have around 30 entities being updated constantly (but I'm working on a personal project that could fall in the "hundred entities" case). Some entities can have their script designed to be executed just some times, but some cases like the camera control or some sort of FX behavior will have a much more complex script that probably would be executed every frame.

In most of the cases the script is just an expression to compute damages or change projectile speed over the time or based on other projectile attributes.

Anyway, I'm using this approach because most part of the game is controlled by the engine/framework itself, and the script should be used as slave just on some classes to allow the game designers to change sightly the entities behavior.

I've coded a test like you suggested, and the code is creating a context on the entity construction and releasing it on entity destruction, the Function ID is being get when the "behavior" is assigned (once in the entity lifetime), so the update code is doing something like this:

cxt->Prepare(mFnID);cxt->SetArgObject(0, this);cxt->Execute();


Is this the best way to do it?
--http://www.mytos.com.br
I think you'll be quite pleased with the performance of AngelScript. I'm told it is faster than Lua and Squirrel in execution, though it has a slightly higher overhead on the Prepare call. Let me know if you need me to crank up the performance even more. If so, I'll up the priority for that effort.

By using the same context/function pair each execution you can minimize the overhead of Prepare(). I'll try to decrease the overhead even when the function is not always the same.

cxt->Prepare(mFnID);cxt->SetArgObject(0, this);cxt->Execute();


This is as small as you can get with calling a script function that takes one parameter.

You may want to think about pooling the contexts though, instead of keeping one for each entity. Even though you'll take a performance hit by using pooled contexts (since you won't be able to always use the same context/function pairs) you will have a much smaller memory usage. Each context holds its own script stack, so the memory usage for the context is rather large.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I (was) actually creating a module for each 'entity' in my game. This allowed each entity to have it's own copy of the script's globals, which made writing the scripts a hell of a lot easier. I had a stress test case with 2000 entities running busy loops, and found that rendering took far more time than the scripts.

So I wouldn't worry about Angelscript's speed. 'Prepare' really is the only bottleneck, and it's not much of one. Still, it helps to be sane. My engine can only realistically handle ~50 entities running scripts at once at interactive framerates.
I believe that the memory is less critical than the processing in that case.
The concept that Deyja was using is very near of the idea that I had. Creating a context and a module per scripted entity and storing some specific function IDs probably will solve my problem.

And here goes another idea that could enhance a little bit the usability of AngelScript: registering a module property.

r = engine->RegisterModuleProperty("Script1", "int g_nVar", &mVar);


That would make the g_nVar available just for that module/namespace.

Those ideas that I had came from the experience that I had with Shockwave Director, that each sprite had it own behavior and properties, based on events, but the scripts where added as local scripts. For instance all sprite had their own "on EnterFrame" function that could be defined.

Thanks a lot!
Yuri I.
--http://www.mytos.com.br
Currently, using one module for each entity like this is quite expensive on the memory, because the bytecode will be duplicated for each entity as well. But if you can handle the memory, then it's quite easy to use.

I'll see what I can do in the future to improve the resource sharing for designs like this. The bytecode for example could be shared between the modules, and only the global variables would be local to each entity.

The idea of module properties is also a good one. I'll take note of it. Thanks.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement