|
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Director Best Behavior
Best Behavior
By: Martin Kloss
Mar. 2, 2004 12:00 AM
One of the great things about working with Director is that there are so many tools and functionalities, but only when you start combining these do you start to realize the real power of Director. In this article we will combine three cool tools and use them to create another tool that we can use like any other in Director. Not only will it take a mere half hour to create, but it will save you hours of development time in the future (see Image I) This probably sounds confusing so let me be more specific: I'm talking about creating a behavior. Behaviors property pName Properties are variables that are accessible in every handler throughout the entire behavior script, but they belong to that specific behavior so they are not global. That means you can have properties with the same name in different behaviors and they will not overwrite each other, as globals would do. A behavior really consists only of properties and handlers. As with the properties, the handlers are not global but belong to the behavior script, so you cannot call a handler in a behavior like a movie script. Let's add a handler to the behavior we just created: on mouseDown (me) Now the behavior can react to a "mouseDown" event. But how does the behavior get this event? This is where sprites come into play. Create a button cast member (or bitmap, text, or any other type of sprite you like) and drag it onto the stage. Now drag your behavior script onto the sprite on the stage. Run the movie, click on the sprite, and watch the message window: -- "My name is: " The sprite gets the "mouseDown" event from Director because you clicked on it. Because it has a behavior with a handler that reacts to this event, the handler is executed and you see the output. The only problem is that the property pName does not have any value yet. That's where the cool part of behaviors needs to be explained - the getPropertyDescriptionList()handler. This handler is called when you drag the behavior onto the sprite or when you click the behavior "parameters" button in the property inspector. It's used to assign default property values to the behavior. In this case we want to give the property pName a value. In order to do that the handler needs to return a property list in a specific format that contains all of the needed information to generate a behavior dialog for the user to input the property values to be used by the behavior instance. Take a look at the handler for our simple example behavior: on getPropertyDescriptionList () The property pName has a description (#comment), a format (#string), and a default value in case the user does not type anything into the behavior dialog. To see the dialog simply drag the behavior onto the sprite and you will be prompted to input a value for the name property. Now type a name, hit enter, and run the movie again. When you click on the sprite again, the message window will output the value you just entered: -- "My name is: Woody" But the real beauty of behaviors will become obvious in a second. Create a second sprite on the stage and drag the same behavior onto the sprite. Now type another name into the behavior dialog field, run the movie again, and click on the first and then the second sprite: -- "My name is: Woody" The secret behind this is that you just created two totally independent instances of the same behavior from one behavior script, doing nothing but drag and drop. The behavior script is like a blueprint from which behavior instances are created when you drag the script onto a sprite, much like sprites are instances of cast members you drag onto the stage. The behavior instances don't know about each other, and they don't share any values or handlers since they only belong to the sprite they are assigned to and only receive messages from that sprite. But what if you wanted to call a function of the behavior from somewhere in your movie, say from a movie script or from the message window? There are two commands in Lingo that let you do exactly that: sendSprite(1, #reportName) These commands send the message to either a single sprite or all sprites in the current frame. They don't call the handler of the sprite, they just send a message to the sprite. If the sprite has a handler with that name, it will automatically call that handler. If the sprite does not have a handler with that name or does not have a behavior at all, nothing will happen - the sprite will just ignore the message. So if you are not sure which channel your sprite is in or you want to send a message to all sprites, you can use sendAllSprites. Let's add another handler to the example behavior to test this functionality: on reportName (me) As in the mouseDown handler before, the "me" Keyword after the handler name is a reference to the behavior instance and is always required in behavior scripts. (See the "me" entry in your Lingo dictionary for further explanations.) Now you can call the handler by sending a message to all sprites from the message window: sendAllSprites(#reportName) Now you know almost everything there is to know about creating and working with behaviors. But if you're still not really sure what behaviors are, look at the behaviors that come with Director and check out some of the demo movies. In this article, we're going to create a behavior that uses two other great functionalities in Director. The first one is called "Imaging Lingo". While this sounds a little scary at first, it's really nothing more than a set of relatively simple Lingo commands that give you access to Director's internal sprite-rendering engine. That means you can create new or manipulate existing bitmap images in Lingo. Image Objects put member("my picture").image What you see is the reference to the image object in Director's memory. It looks complicated, but trust me, it really isn't. You can manipulate that image, copy it, or create a new image from scratch using the "image()" function and assign the new image to the member's image property. Try the following in the message window: myMember = new(#bitmap) Director created a new bitmap cast member for you in the current cast library. Open the new cast member in the paint window by double clicking it and type the following two commands in the message window: myMember.image = image(100, 100, 16) The paint window will now show a red square. The same thing that happens when you use the tool in the paint window to draw a square box just happened because of these two lines of Lingo code. You told Director to create a new 100x100 pixel, 16-bit image object, and assigned the new member's image property to it. In the next line you filled the whole rect of the image (0,0,100,100) with the color red (rgb(255, 0, 0)). See how easy it is to work with image objects? Imaging Lingo really opens up a whole new world for the Director developer. If you haven't learned about this exciting functionality I recommend reading a good Director book or a few of the various online articles on the topic. Timeout Objects I know this sounds a little confusing, but once you get to know timeout objects, you'll realize that there's really nothing complicated about them. It's really easy to create one: t = timeout("My Timeout").new(1000, #helloWorld) This creates a new timeout object with the name "My Timeout", stored in the variable t. The first parameter sets the interval and the second parameter defines the name of the function to be called. After the timeout object has been created it starts to do what it's told to do automatically. In this case it calls the function "helloWorld" every second (1000 milliseconds). You can easily test it for yourself; just create a new movie script and write a simple "helloWorld" function: on helloWorld Start the movie and see how the timeout object calls your function by watching its output in the message window. That's pretty much all there is to know about working with timeout objects. Some of the functions and properties of timeout objects will be covered later. The Goal These time displays make a nice addition to many applications. They're much nicer than simply displaying numbers and they can be used in a broad variety of colors or shapes. But as a requirement, since we're talking about displaying something as constant as time, they need to indicate the passing of time independent of the movie's frame tempo. While scientists could well argue that time indeed isn't constant at all, we'll just assume it is because we're going to create a multimedia application, not the navigation system for the next Mars mission. Timeout objects send their messages based on the interval they were given when they were created. The frame tempo of your movie does not influence their behavior at all, so you can have your movie running at 1 or 30 frames per second and the timeout object will send a message every second if you tell it to do so. One important thing to know about timeout objects is that if you specify very short intervals, such as 1 or 10 milliseconds, Director in some cases might not be able to send out a message at every interval because other tasks, like rendering the stage, might take too much time, so there's not enough processing time left to send out the message. In that case the message will be dropped and, hopefully, at the next interval a message will be sent. So if you know there are many things to be animated on the stage, set the timeout interval to something Director can manage. In most cases you will not need such very short intervals anyway, so don't worry about it. The Behavior We'll begin by defining the behavior's properties. Take a look at the comments describing the properties; most of it should be pretty self-explanatory, and the rest will be made clear in the process. Properties
Now that all of the properties are defined and we know how most of them are set, let's start with the internal events and how our sprite reacts to them (see Image III). The beginSprite handler is always a great place to do all or most of the initializations of your behaviors since it is called automatically when the playback enters the frame where the sprite is started. The same goes for the endSprite handler, since this event is also automatically called when the playback head leaves the sprite. The sprite initialization here is really simple. In order to save some typing work, we store a reference to the sprite object and its member in properties. In order to be able to restore the image of the sprite's member, we copy the current image using the duplicate() function. (Note: Using member(n).image always returns a reference to that image, so any manipulation of that image will be reflected in the original cast member.) The timer bar will be constructed by two images, an "on" and an "off" digit. Each digit will have four properties: color, width, height, image. These properties are stored in a property list to make it easier to access them. The color, width, and height of each digit will be set through the property description dialog (see above); the image will be assigned in a minute, so for now we'll just set it to 0. After the properties are set, the handler calls two custom initialization handlers. The first one, initDigits(), creates the images of the "on" and "off" digits; the second, initTimer() handler, creates the timer bar image and starts the display. Let's look at these two handlers: -- init the digits This handler calls another function to create each digit (see Image IV). The function expects the property list with the information about color, width, and height of the timer digit; it creates a new image in the given dimensions, fills it with the desired color, and stores it in the "image" property of the list: -- create a digit image The initTimer () handler is very similar. It calls a function to create the timer bar image, resets the pCurrentTime property, and if needed, starts the display of the timer (see Code II). The creation of the timer is based on three things: the orientation of the timer (horizontal, vertical), the direction of the movement of the timer animation (up, down), and the images of the digits we just created (see Code III). The timer orientation determines the total width and height of the display, based on the width and height of the digits and the amount of time set in the properties. Depending on the direction in which the animation moves, the "on" or "off" digits will then be copied into the timer image. After the image is created we just need to update the member's image so the timer will be displayed in the sprite on the stage: -- update the member image Depending on how you want the timer to be aligned with other elements on the stage, you can set the regpoint to point(0,0) or center it. It's really a personal choice and something that can be changed by uncommenting a single line, so don't worry about it at this point. Hard to believe, but that's almost it. We really have only one final handler left to build to manipulate the display of the timer (see Code IV). This handler draws the image of a single digit ("on" or "off") into the timer image, so it's basically our animation handler. The direction property defines which way the timer animates; the elapsed time property helps us find the location of the current digit in the timer image. After the image is altered, we just need to call the updateImage () handler again and the changes will be reflected on the stage. All that is left now is to write a couple of handlers to control the timer from the outside. That's where the Timeout object we mentioned earlier comes into play (see Code V). Before we create a new timeout object, we check whether there already is a timeout object so we wouldn't have to create a new one. The timeout object we create will call the mStepTimer () handler; using the "me" keyword we tell the timeout to look for the handler in this behavior instance. The calls interval was set through the behavior description dialog. If there already is a timeout object, we activate it by setting its "period" property manually. Everything that has a start also has an end, so we not only want to be able to start the timer, but also to pause or to stop it: -- pause the timer This handler simply sets the timeout object's "period" property to 0 and keeps it from calling the handler it's told to call. This is a neat trick to pause a timeout object without having to destroy the object altogether. This is done in the mStopTimer handler. The timeout object's internal "forget()" method is called to deconstruct the timeout object and the property is set to VOID to ensure that no reference is left. -- stop the timer In some cases you might want not only to start, pause, and stop the timer, but also to reset it, so a running timer is stopped and restarted. That can be done by combining two already existing functions: -- reset the timer See how nice it is to create reusable code? Not only does it save you a huge amount of work, it also enables you to build new functionalities with already existing code. Okay, we're almost done now. If you're still with me, you probably already know what's missing. I'm talking about the mStepTimer handler we used in the creation of the timeout object to call the animation functions of our behavior (see Code VI). The handler increments the internal counter pCurrentTime and calls the drawTimerStep () handler to draw the next digit into the timer image. If the timer has finished with its animation, the mStopTimer () handler is called to stop the timer and destroy the timeout object. If there is a Lingo function you want to call after the timer has done its job, it will be called using the "do" command. Now you can control the behavior from anywhere in your movie by simply calling these handlers using sendAllSprites or sendSprite if you have more than one timer display on the stage: sendAllSprites(#mStartTimer) Conclusion To give you a small idea of what you can do to extend the functionality of this behavior, here's a handler to set the image of the "on" or "off" digits of the behavior from the outside: -- set digit image You can use existing images of your application or game for the timer display or even change the digits while the animation is running. The number of possible extensions is really up to your imagination. You can view and download a demo movie, including the source files, at www.sys-con.com/mx/sourcec.cfm. Reader Feedback: Page 1 of 1
Latest Cloud Developer Stories
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week
Breaking Cloud Computing News
|
|||||||||||||||||||||||||||||||||||||||||||||||||