In my the last article I solved a technical issue related to my action graphs. Now I want to make it easier to write powerful scripts for my actors. Let’s recall a fragment from the previous example:
Actor* dog=[[Actor alloc]initWithX:0 y:0 z:0 tag:@"dog"]; OnApproach* inner=[[OnApproach alloc]initWithSource:dog target:@"actor" range:30.0f]; XAction* chase=[[MoveToActor alloc]initMovingActor:dog to:inner.target]; ReflexMap* reflexes=[[ReflexMap alloc]init]; [reflexes add:chase on:inner];
This isn’t practical, for several reasons:
- The script requires defining an actual actor. Often, we’d like to specify ‘class behavior’ instead – maybe we want to describe general behaviors associated with dogs.
- All this [[Actor alloc]init… stuff is about OK for framework level stuff. For behavioral scripts, it is too heavy.
What we’d really want to write is something like (check the previous article for reference)
class Dog{
- bark at actors within 60 metres
- chase actors within 30 metres
- bite actors within 1 metre
}
I don’t want to get into the writing/binding of a scripting language and interpreter, but we can surely clean things up and get it closer to the above.
A Script class
I’ve specified a Script class with convenience methods. With these methods added, the ‘guarding dog’ script now looks like:
Script* s=[[Script alloc]init]; // [s type:DOG]; [s let:BITE when:HUMAN within:10]; [s approach: HUMAN whenWithin:30]; [s let:BARK when:HUMAN within:60]; [s xType];
type: and xType: tell the script to start/end a class declaration. The other functions are meant to be translated into an action graph. The next step is to decide how this is done. In the meantime, this is worth a couple of comments:
- We no longer need defining a dog actor. The DOG tag allows us to define class behavior. This doesn’t prevent us from defining ad-hoc behaviors for a given actor (use the tag only once). I hope that tags will prove very useful helpers in writing concise, yet powerful scripts.
- Script methods are somewhat ad-hoc. However, this is probably OK. I’m trying to simplify 90% of scripting. Having said that, the current script spec still leaves out a lot of the (structural/modal) complexity of the action graph.
Implementing the script class
So far my script class is just semantics. We want to do something with our semantics:
- We want to store the script in some form.
- We want to be able to make a script current on a given ‘stage’. In other words, when we attach the script to a scene or virtual worlds, we want our actors to behave according to that script.
The classes provided by my framework so far are not meant to describe a script. They are normally instantiated as runtime nodes in an action graph. At best, I can use them as prototypes, and this is exactly what I tried first. Here is a sample function implementation:
-(void)observe:(tag)kind{
NearestActor* condition=[[NearestActor alloc]initWithAgent:proto target:kind];
XAction* response=[[LookAt alloc]initWithAgent:proto lookingAt:cond.target];
[map add:response on:condition];
}
This can potentially get very untidy. Say we had an actor with a given tag. we’d have to go back and check for all rules including an instance of the same-tag prototype and duplicate the rule so it becomes applicable to that actor. The reason why this process can become messy is that neither conditions nor actions enforce the object paradigm. The Script class, on the other hand, ‘starts with typing’. it assumes an actor as the context of any script statement.
As a result, it is probably wiser to define separate constructs used to store behavior definitions associated with a tagged actor. This is simply because doing ensures we track typing information at least until we actually bind a runtime actor to its behavior. Given an on-stage actor, we can then collect definitions for each of the actor’s tags and generate reflex maps accordingly. At this point, none of the reflex maps will be associated to the actors per se, which may cause other problems. This, however, goes down to a little of book-keeping.
Script structure – draft
I used the following classes to store script nodes:
- Type - A type is associated with a tag name and (for now), a top level selector.
- ScriptDef – ScriptDef is the base type for script elements, in this case ‘selector’ and ‘statement’
- Selector – A selector specifies a ReflexMap (should be explained in my previous article)
- Statement - For now, my statements are structured, conditional statements. Probably I need to break this down later, but for now this form should cover most of the interactions I wish to describe using the scripts.
Interpreter
I then defined an Interpreter class responsible for setting up a script given a Stage and Transition. This reads the script; for each class, I retrieve the actors bearing the matching tag name and build an action graph based on the class definition. I muddled a little more with my existing Condition and Action subclasses, but somehow managed to tie everything together.
Next time I’ll do some testing to check how everything works, and try to summarize things a little.


Comments