Event based scripting

Jun 28, 2010 at 4:27 AM
Edited Jun 28, 2010 at 4:28 AM

I just started using Ox, and I find it to be pretty well designed and extensible. However, there seems to be a lack of support for event based scripting. Event based scripting is really powerful, especially for designers. Being able to work with natural events like say, opened, closed, activated, touched, damaged, arrived, etc. is really handy. Especially if you can chain events together, like having a door opening alert a guard or trigger some other event.

Has anyone thought of adding this before, and if so, any suggestions on where to start? I was going to start digging into the component/subcomponent and scripting code and figure it out myself but I wanted to see if anyone here has done this before, or if anyone is interested in my results.

-Matt

Coordinator
Jun 28, 2010 at 4:08 PM
This is what the existing scripting system was designed to do. Just have your script class expose C# events and have the interested scripts subscribe to them. A very simple example of subscribing to gui events is in the "Loading and Scripting GUI Screens.doc" that is installed in the engine. Cheers!
Jun 28, 2010 at 5:39 PM
Edited Jun 28, 2010 at 5:40 PM

I mean a more intuitive way to wire up events for the designer. Take the generic concept of a door component or door entity (regardless of the engine). Let's say a door does two things: it opens and it closes. The script that controls a door shouldn't be responsible for subscribing to events that cause it to open. All the door cares about is taking care of the state for moving the door. Any component or entity should be able to send events to the door. If the door opens when the player uses it, it might receive an "activated" event. Thus, any component should be able to send an "activated" event to the door and the door shouldn't have to care where it came from, and certainly shouldn't have to subscribe to other script's events. This promotes reusability by shifting the burden of event subscription away from the components themselves. Otherwise (if I understand Ox correctly) you would have to write or customize the door script for every unique door in the level... a bit silly, no?

I implemented something like this an an (incomplete) engine of mine a while back. It had some really nice dynamic invocation, too, using reflection. It used dynamic methods and the magic from Reflection.Emit to make it fast. Unfortunately none of that works with the 360, but that's really an implementation detail. There are other ways to do it. But the scripts would look something like this:

public class MyDoor : Script
{
    private bool locked;

    public void Activated(Object sender, [other arguments here])
    {
        if(!locked)
        {
            // Some kind of object movement call goes here
            Engine.Move(this, someVector);
        }
        else
        {
            // Some kind of alternative behavior goes here
            Engine.PlaySound(lockedNoise);
        }
    }
}

Then any component can send an activated message to any door using this script, pass in some optional arguments (the dynamic invocation was really nifty for this) if needed. Then you could expose an event in MyDoor, call it OnActivated, that others could subscribe to.

The idea is that these events tend to follow conventions (opened, closed, hurt, damaged, activated, etc) though you could define your own as well.Once you have that in place, it makes it really easy for a designer to visually wire events together. Event A on component Foo triggers event B on component Bar, etc. Does this make sense? Does Ox support anything like this already? Ultimately what I'm getting at is making things more reactive and event-driven to promote reusability.

Coordinator
Jun 28, 2010 at 7:40 PM
Edited Jun 28, 2010 at 7:42 PM

You can likely do what you want using a couple of base script classes that expose and implement generalized message handlers. Perhaps the classes would be called MessageSender and MessageReceiver. If you have a Button script that inherits from MessageSender, the button would be programmed to sense when it's pressed and would in turn invoke MessageSender's SendMessage() method which would be implemented something like -

void SendMessage()
{
   string[] splitData = DesignTimeData.Split(' ');
 string receiverName = splitData[0]; string messageName = splitData[1]; string[] parameters = /*the rest of the split data*/; GetScript<MessageReceiver>(receiverName).HandleMessage(messageName, parameters); }

HandleMessage might do some reflection to call the appropriate method with the given message name and parameters or it could be a virtual that is overridden and does its own dispatching.

Since DesignTimeData can be edited directly in the editor, it should be easy to wire things up without writing scripts for every tiny detail and you won't need an external editor to wire things up in. Even if you ultimately need a visual editor for events, this is a good starting point.

Shouldn't take more than a few dozen lines of code to implement this in all. Send me what you come up with so I can consider it for insertion into the engine!

cheers!

Jun 28, 2010 at 8:10 PM

Yeah, something like that. In my implementation, I used reflection to build dynamic methods for every event in the script, then use a dictionary to map the names of the events to the in memory delegates. This had a nice convention over configuration scheme and was virtually as fast as static code. Since the 360 doesn't have Reflection.Emit, that's not possible. However you should be able to maintain a dictionary of event names => event delegates in order to store the events at compile time. The events can all have the same signature: void (Object sender, params Object[] arguments).

While we're on the subject, another idea I had was the concept of a "level script" or "scene script." This is essentially a script that receives events from components in the world and can dispatch messages or events to other components. If you have complicated gameplay logic (like for a complicated puzzle) it could go in one of these scripts. For example, maybe you have a major event triggered when the player opens a door. So a scene script could subscribe to a door's open event, and when the door opens, dispatch messages to sound an alarm, play dramatic music, alert guards, play a cutscene, etc etc. The motivation is that complicated puzzles or logic can't be wired together with simple events (although a visual event tool could generate these scripts perhaps). I suppose it would be implemented as another component in the level. This way, each puzzle/script can be treated as it's own component, and leave its logic out of other components. This make sense?

And yeah, I'd be happy to contribute whatever I come up with. I've worked with a few engines that are event-driven, and they're really powerful and easy to use. As I recall, UnrealScript uses an event system for most behaviors.

Coordinator
Jun 28, 2010 at 8:36 PM

Sound great! Just keep it simple and let me know what you come up with!

cheers!