You’ve Been Object Oriented All Along

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 me
END 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.

Tip: If you really wanted to test the void status, you would use voidP, as we did in the last module. Now go ahead and attach this behavior to sprite 2:
 
on ReturnLoaded me
 
 
return TRUE
END 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 = "\" then
 
BEEP


end if

END keyDown


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 if

END 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:

So, as you can see here, startMovie has a rather unfortunate name!

Frame events follow this flow:

Finally, you get these two events when a movie stops playing: Why this matters is simply that you can use this knowledge to do some nice things with code, particularly with prepareFrame. You can, for instance, cause a sprite to resize before it becomes visible on the Stage, so there’s no visual jump effect when it first appears. But it’s also important to know this stuff for reasons of variable handling and general movie interaction (again, as we saw in Module 9).

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.