Sign in to follow this  
Sijmen

Asynchronious scripting

Recommended Posts

Sijmen    231
In an utopian world, game logic is written like this:
Enemy *myEnemy;
while (!dead)
{
    myEnemy = FindNearestEnemy();
    MoveWithinRange(myEnemy->location, 10);
    FireAt(myEnemy);
    if (dead)
    {
        Say("Did j00 killed teh mighty AI!");
        Respawn(locations[rand()%10]);
    }
}


As you probably all know, in reality it doesn't work like this. The problem is that the game state is evaluated every frame, so you'd have to make a state machine to make this kind of behaviour. edit: this counts especially for the FindNearestEnemy() and FireAt() commands, which take at least several frames to execute. It seems like scripting is a viable option, but what kind of scripting languages would supply this behaviour? (Ie, a command that takes multiple frames). And how is this implemented? Thanks for any help, and apologies if my description is a bit unclear, ask me and I'll try to elaborate.

Share this post


Link to post
Share on other sites
_the_phantom_    11250
'co-routines' are the general solution; kind of like threads in that they maintain their own state, but you have to explicately yield from them.

In this case, for example, the 'MoveWithinRange' function would do a step, then yield control back, then when this script is called again it picks up from where it left off in the MoveWithinRange' function, which does the next step, and then repeat.

Share this post


Link to post
Share on other sites
Rand    193
I understand exactly your problem. We did use Lua in our last project and it certainly didn't offer such a mechanism. We ended up writing our own scripting language which had delays in and the special kind of game control your talking of. I'm not aware of a scripting language that has async delays in that your after.

Share this post


Link to post
Share on other sites
Hatori    143
I have used angelscript as my scripting language to do this. HOWEVER, it does not directly support what you wish to do. But there are examples that come with writing coroutines. So what I did is use their scripting language and wrote the additional code to support the execution of the script over several frames...

There are examples with angelscript on how this is done...

I hope this helps you.

Share this post


Link to post
Share on other sites
DrEvil    1148
Game Monkey Script is built entirely on asynchronous support and I use it extensively in my project. You wouldn't be able to use the exact syntax in your example, but it has a built in functionality that allows you to block a script thread on an event, so for example your script might look like this.


Enemy *myEnemy;
while (!dead)
{
block(EVENT.ENEMYSEEN); // native code would signal the scripting system with an event whenever new enemies are seen
myEnemy = FindNearestEnemy();
MoveWithinRange(myEnemy->location, 10);
FireAt(myEnemy);
block(EVENT.WEAPON_FIRED); // same here
if (dead)
{
Say("Did j00 killed teh mighty AI!");
Respawn(locations[rand()%10]);
}
}





In GM, you can hide the above calls to block by doing the block inside your FireAt function, MoveWithinRange, FindNearestEnemy function.

Game Monkey is ideal for such scripting. Every script run with GM is run in a script thread. They are not real threads. They are similar to Microthreads or Fibers, so there are no issues common to real threads, and no need for protection mechanisms like mutex and critical sections and such.

Stackless Python works similarly, and with some work yourself you could get Lua coroutines to work similarly. Quake4/Doom3 engine implements a custom scripting language that runs the same sort of 'threads'. Particularly for AI, it makes scripting game entities much much easier than using polling or other methods.

Share this post


Link to post
Share on other sites
ToohrVyk    1596
Any language with lambdas should be able to handle this more or less elegantly, when combined with a scheduler.


class enemy (pos) = object(self)

inherit entity

var position = pos
mutable var activity = Action.idle

method spawn =
position # set_random;
self # act

method act =
let enemy = World.findNearestEnemy (position) in
let shooting =
new shootAt
~actor:self
~target:enemy
in
activity <-
new doWithinRange
~actor:self
~target:enemy
~distance:10
~action:shooting

method die =
activity # cancel;
self # say ("Omgnoes");
self # spawn
end



Internally, the shootAt and doWithinRange actions schedule actions and triggers, which are then executed by a scheduler. All of them work as coroutines, which execute a small action (start moving, schedule a trigger for when the object reaches the destination to execute the intended action, and a trigger for when the object leaves the distance to start moving again).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this