Callback Messages
With Callback Messages you can make an event, for example an animation or a sound, wait on another event to finish.
There are several types of callback messages: oneshot callbacks for avatar animations, timer callbacks and event callbacks. Oneshot callbacks are discussed elsewhere but we must take them into account as well when we are going to set up the "wait to command" (waittocmd) table.
Note 1: This tutorial is far from complete. It just contains what has been figured out so far. If you have any additional information feel free to add it.
Note 2: This is an advanced tutorial which assumes that you are already familiar with setting up responders. In short, a responder consists of one or more command messages to be executed. Without callbacks all of these commands are executed at the same time.
Contents
General Principles
- waittocmd keys are stored into a waittocmd table within the responder.
- A waittocmd key is assigned to the command which should be waited on.
- The waiton value of the next command (the command that should wait) is set to the value of the waittocmd key. A waiton value of -1 always means "do not wait".
- waittocmd keys must be numbered in order of appearance starting with 0.
- The number of callbacks can be defined for each responder (this may be optional).
Timer Callback Message
This is a simple type of callback. It makes the next command message wait for any given time. Here is an example AlcScript:
logic: actions: - type: responder name: <responder name> responder: states: - cmds: - type: timercallbackmsg params: receivers: - respondermod:<responder name> id: 0 time: 3.14 waiton: -1 - type: <next message> params: <any params> waiton: 0 nextstate: 0 ncallbacks: 1 waittocmd: - key: 0 msg: 0
- waittocmd key / msg: Ok, so we have a waittocmd key 0 but how do we set that key in the timer callback message? This has been a source of confusion for some time. At first glance you may think the key is set by the id property. However, this is not the case. You do not need to set the key in the message itself because it is already defined in the waittocmd table's msg property. "msg: 0" tells Plasma to look for key 0 in the first command it finds within the responder which happens to be our timer message (remember to count like a programmer starting from 0).
- id: Should we set the id in the timer script to match our key? For simplicity's sake I would say yes, although this should not be necessary as long as the id is unique for this responder.
- time: Setting the actual timer is straightforward. This is done by the time property (3.14 seconds in the example).
- waiton: As you can see I have placed a generic "next message" into the script. All that matters here is its waiton value which should match our key: 0.
- ncallbacks: And finally, since we have a single callback, we will set the number of callbacks to 1.
Event Callback Message
Event Callback Messages can make any command message wait on a previous animation- or sound command message. Let's add an animation with callback to our timer example:
logic: actions: - type: responder name: <responder name> responder: states: - cmds: # msg 0, callback key 0 - type: timercallbackmsg params: receivers: - respondermod:<responder name> id: 0 time: 0.1 waiton: -1 # msg 1, callback key 1 - type: animcmdmsg params: receivers: - 006D:<animated object> animname: <animation name> callbacks: - type: eventcallbackmsg params: receivers: - respondermod:<responder name> user: 1 event: 1 cmds: - addcallbacks - continue waiton: 0 #waits on msg 0, key 0 # msg 2 - type: <next message> params: <any params> waiton: 1 #waits on msg 1, key 1 nextstate: 0 ncallbacks: 2 waittocmd: - key: 0 msg: 0 - key: 1 msg: 1
- waittocmd key / msg: The key / msg for our timer remains the same, but now we add a second key / msg combo to the waittocmd table. Keys must be numbered in order of appearance so this becomes key 1. "msg: 1" tells Plasma to look for key 1 in the second command in this responder, our animation command message.
- user: This number must match the command key, which in the above example is 1.
- event: Use the same number for all event callbacks within your responder! If you use a different number for subsequent event callbacks the sequence will come to a grinding halt somewhere halfway.
- waiton: The animation command waits on the timer with key 0 and therefore its waiton value must be 0. The generic "next command" waits on the animation with key 1 and therefore its value must be 1.
- ncallbacks We now have 2 callbacks so we will set this to 2.
At this point you may wonder: Why did I put a useless timer message at the beginning of the responder? Was this only done as an example? Unfortunately, no. Somehow you cannot have an event callback as your first callback. It will fail. The first callback must be either a oneshot or a timer message. This is most likely a PyPRP bug. So for now if you are not starting the responder with a oneshot callback you have to start with a very short timer callback.
Advanced Example
In the previous examples the waittocmd key and msg values were the same. However, within complex responders this may be different. In such cases you will have to number your command messages carefully (counting up from 0) and then combine them with the proper key within the waittocmd table.
In the following RL example I had a very long elevator ride with 3 different sounds. A start sound, a running sound loop and a stop sound. Obviously these sounds should not all start at once. And I also needed to stop the sound loop at the end of the ride. So here we have a perfect example for callbacks. This is the AlcScript I used:
<animated object>: animations: - name: <animation name> autostart: 0 loop: 0 logic: actions: - type: responder name: <responder name> responder: states: - cmds: # msg 0 - type: soundmsg params: receivers: - 0011:<start sound> cmds: - play - setvolume volume: 1 waiton: -1 # msg 1, callback key 0 - type: timercallbackmsg params: receivers: - respondermod:<responder name> id: 0 time: 0.5 waiton: -1 # msg 2 - type: soundmsg params: receivers: - 0011:<running sound loop> cmds: - play - setvolume volume: 0.2 waiton: 0 #waits on msg 1, key 0 # msg 3, callback key 1 - type: animcmdmsg params: receivers: - 006D:<animated object> animname: <animation name> callbacks: - type: eventcallbackmsg params: receivers: - respondermod:<responder name> user: 1 event: 1 cmds: - addcallbacks - continue waiton: -1 # msg 4 - type: soundmsg params: receivers: - 0011:<stop sound> cmds: - play - setvolume volume: 1 waiton: 1 #waits on msg 3, key 1 # msg 5, callback key 2 - type: timercallbackmsg params: receivers: - respondermod:<responder name> id: 2 time: 1.0 waiton: -1 # msg 6 - type: soundmsg params: receivers: - 0011:<running sound loop> cmds: - stop waiton: 2 #waits on msg 5, key 2 nextstate: 0 ncallbacks: 3 waittocmd: - key: 0 msg: 1 - key: 1 msg: 3 - key: 2 msg: 5 curstate: 0 flags: - detecttrigger
First take a look at the waittocmd table. Hmm, that looks weird... The key and msg values do not match up at all here. Then take a good look at my comments in the script where I counted the command messages and their keys. At this point there should be a lightbulb appearing over your head. ;) If it does not then I am sorry. I'm afraid I cannot make it any clearer than this.
A few notes about the timers: The first timer had to be added because of the bug I mentioned earlier which prevents having an animation (or sound) as the first callback. And since I had that timer anyway I made it fire up the running sound loop somewhere halfway through the start sound. The running sound loop starts and stops rather abruptly and now the start sound can mask that. In a similar way the second timer was added to make the sound transitions at the end more smooth. The stop sound has a fade in and a fade out so the timer is used to end the running sound loop right in the middle of it.
Dominoes Example (with Blender file)
Here is another example which demonstrates a common use for callbacks: the domino principle. This small age has three domino tiles. You push the tile one. Tile two does not start falling until it is "hit" by tile one and tile three waits until tile two touches it.
The animations for tiles 1 and 2 are split in half so that we can make the next tile fall halfway through the animation of the previous one.
Domino02: animations: - name: Fall02Start autostart: 0 loop: 0 loopstart: 0 loopend: 1 - name: Fall02Continue autostart: 0 loop: 0 loopstart: 1 loopend: 2
The animations still need some tweaking but for a demo they should be good enough.
This is the AlcScript for the responder. As before I have carefully counted the command messages and keys in order to set up the waittocmd table (see comments in script).
actions: - type: responder name: DominoesFall responder: states: - cmds: # msg 0, callback key 0 - type: oneshotmsg params: receivers: - oneshotmod:PushOneShot callbacks: - marker: DoorButtonTouch receiver: respondermod:DominoesFall user: 0 waiton: -1 # msg 1, callback key 1 - type: animcmdmsg params: receivers: - 006D:Domino01 animname: Fall01Start callbacks: - type: eventcallbackmsg params: receivers: - respondermod:DominoesFall user: 1 event: 1 cmds: - addcallbacks - continue waiton: 0 #waits on msg 0, key 0 # msg 2 - type: animcmdmsg params: receivers: - 006D:Domino01 animname: Fall01Continue cmds: - continue waiton: 1 #waits on msg 1, key 1 # msg 3, callback key 2 - type: animcmdmsg params: receivers: - 006D:Domino02 animname: Fall02Start callbacks: - type: eventcallbackmsg params: receivers: - respondermod:DominoesFall user: 2 event: 1 cmds: - addcallbacks - continue waiton: -1 # msg 4 - type: animcmdmsg params: receivers: - 006D:Domino02 animname: Fall02Continue cmds: - continue waiton: 2 #waits on msg 3, key 2 # msg 5 - type: animcmdmsg params: receivers: - 006D:Domino03 animname: Fall03 cmds: - continue waiton: -1 nextstate: 0 ncallbacks: 3 waittocmd: - key: 0 msg: 0 - key: 1 msg: 1 - key: 2 msg: 3 curstate: 0 flags: - detecttrigger
The rest of the script can be found in this Blender file. This is a fully exportable age.
There is now also an improved (though more complex) version of Dominoes.blend which adds sound to the callback train.