As the introductory paragraph for this module states, behaviors are object-oriented code themselves. This means you’ve been working within an object-oriented framework since your first project in Director. You probably realized (or at least suspected) this was the case once you were introduced to parent scripts, which use the me keyword, just as sprite behaviors do. Thus, you’ve had a pretty good chance to see how straightforward object-oriented code can be, and you might even wonder why it’s worth discussing at any length. Well, that’s probably because you weren’t programming before there was such a thing as OOP, so you don’t really have the perspective necessary to appreciate what a great leap forward it is.
That’s as may be. Now that you know the true nature of sprite behaviors, we’re going to start using that knowledge to real advantage.
You saw an example in the last module of how five different sprites can each have the same behavior attached to them, and that those behaviors are encapsulated at the sprite level. The pepperoni behavior did not get confused, for instance, and think it was handling data for the mushroom sprite.
Well, just as behaviors can retain information specific to
the sprite they happen to be associated with, they can also retain
discrete properties (including databases). In fact, pretty much
everything you can do in a parent script can also be done in a sprite
behavior?plus a little more. You can prove to yourself that behaviors
are discrete, encapsulated critters by making a really simple one:
on beginSprite me
put meEND beginSprite
Attach it to a few sprites in the same frame and then click
Play. You’ll see that the two identical behaviors, in fact, hold two
quite different memory addresses. Although they are the same code, they
are not the same instances.
Behavior Instantiation, Destruction, and Messaging Hierarchy
That’s a scary sounding header, but you’re well versed enough by now to know you can handle anything I might throw at you.
Behavior instantiation, as you’ve realized by now, does not use the same kind of syntax as parent object instantiation. Gone is the new ( script "blaBlaBla" ) syntax; instead, it is handled automatically for you, and in fact, it’s not even a piece of code you need to enter.
You might think instantiation takes place in beginSprite, and that’s a reasonable assumption to make, but it’s not correct. After all, we’ve seen sprites that accept sendSprite calls even when they don’t have a beginSprite handler attached; in fact, there’s no return me you have to code in either. Thus, you can infer (and you’d be correct) that Director handles the instantiation for you where behaviors are concerned. It associates the behaviors?or collection of them?automatically with their sprites, so you don’t have to worry about keeping track of them on your own.
Similarly, Director does the behavior destruction for you as well. This is also convenient, because you don’t have to explicitly zero any of the behaviors on your own.
However, you can also get into a little bit of trouble here because of the hierarchy Director uses to instantiate and destruct the sprite behaviors that might exist in any given frame. This is actually pertinent to a larger discussion regarding messaging hierarchy as it occurs in the Lingo engine, so we’re going to discuss it here.
Let’s begin with a definite example. Create two new sprites
in the same frame on the Stage, and to the first sprite, in channel 1,
attach this behavior:
on beginSprite me
bAreYouLoaded = sendSprite ( 2, #ReturnLoaded )if bAreYouLoaded = TRUE then
put "Sprite 2 is loaded."
else
put "Sprite 2 is NOT loaded"
end if
END beginSprite
Pretty simple, right? Now?without adding any behavior to
sprite 2?click Play. What happens? You get the "not loaded" message,
and you get it every time. This occurs because bAreYouLoaded returns <Void>, which is treated in this context as meaning the same thing as false.
on ReturnLoaded me
return TRUEEND ReturnLoaded
This is, of course, the behavior that’s supposed to send an
answer to the question asked by sprite 1. Now that you have these
behaviors, go ahead and click Play again.
Did the results surprise you? You still get the message saying sprite 2 isn’t loaded, and it won’t matter how often you click Play, because the message will not change. This is because sprite 1’s behavior is actually instantiated before that of sprite 2. Thus, sprite 1’s code is asking its question before sprite 2’s code can even get itself loaded into memory.
Unfortunately, there is no easy way to get around this; if you are relying on beginSprite to be able to create some kind of internal list of sprites that are active in a given frame, and are expecting these sprites to intercommunicate with all the others, you might want to consider a bottom-up approach.
For example, take sprite 1 as it exists now and put it in channel 3. Then click Play. The results change, of course, because now sprite 2 has in fact been loaded; sprite 3 is asking its question after the instantiation of 2.
It’s easy to forget that the topmost sprites in the Score are going to have their behaviors instantiated before the lower ones, because Director does a fine job of displaying all of them at once when they appear in a given frame. That is, we don’t see them draw themselves one atop the other when Director enters any given frame, so it can sometimes be hard to remember that, as far as the system is concerned, that’s actually precisely what is happening. Sprite 1 comes into existence immediately before 2, which comes into existence immediately before 3, and so on.
The same is true, by the way, for endSprite events. Sprite 1 is cleared from memory before 2 is.
This also means we should talk about messaging hierarchy, or the order in which Director handles events that occur. The program does not treat every event equally; that is, a mouse click or keypress is not noticed at the same time on all levels of code.
For instance, suppose you wanted to check the keys being
pressed by the user as she types text into a field, and make sure she
didn’t use the "\" character for some reason. You might think to attach
this behavior to an editable #field or #text member:
on keyDown me
if the key = "\" thenEND keyDown
BEEP
end if
But if you ran your movie, none of your keystrokes would get
through to the editable #field, because they’re all being intercepted
by Lingo, and not explicitly passed down the event hierarchy. You’d
need to revise your code a little:
on keyDown me
if the key = "\" then
BEEP
else
pass
end ifEND keyDown
Here we still beep and trap the forbidden key, but we also pass everything else, since everything else is permissible.
Director in general behaves like this. Another example is with mouse click interactions. If a sprite in channel 20 has a mouseUp script attached to it, and it’s on top of a sprite in channel 5, which also has a mouseUp script, only the one for sprite 20 will execute. Channel 5 will behave as though it never was clicked. This is true, by the way, even if you explicitly pass the mouse event. Mouse clicks do not pass like key events do, at least not on the sprite level.
There are also hierarchies that are followed for event scripts. For instance, we saw in Module 9 that beginSprite calls in frame 1 took place before startMovie, and in fact, the Director documentation does speak of hierarchies in general. For the Director 8 and 8.5 printed material, the relevant page is 190 of the Using Director book. Here’s the basic flow of messages that occurs when a movie first runs:
Frame events follow this flow:
As for mouse clicks and keypresses in general, they are not handled by sprite behaviors first necessarily. If you have activated a key or mouse script for the movie, it’ll be detected by that before it reaches anything on the Stage; that is, mouse and key scripts for movies are handled before sprite mouse and key scripts.