Breaking Into Flash Functions
November 30, 2001
by Darrel Plant
Director 8.5's support for Flash added a number of new capabilities, including the use of the call function with Flash sprites. The call function lets you invoke the commands in a labeled frame of the Flash sprite's root Timeline. When a call is issued, the Flash movie momentarily jumps to the specified frame, executes the commands, then resumes execution of whatever it was doing before.
There's just a couple of problems:
- The call command gives you one parameter and one parameter only: the frame label. To pass parameters, you need to set a variable or two in the Flash movie with the setVariable command, then use those variables in the ActionScript in the called frame.
- The call command only works for frames on the root Timeline of the Flash movie. You can't access frames of movie clips like you can from Flash.
- You can't access standard ActionScript functions with call, only functions the Flash author has defined in frames. You can alias standard functions by embedding them in the called frame, but you have to do that for every single function.
- To top it off, the ActionScript call command was deprecated in Flash 5. Nobody who knows what they're doing uses it any more. It's much simpler to define ActionScript functions along with the rest of the scripts on objects or in the first frame of a movie -- you can't pass parameters with the ActionScript call command, either.
Given that Flash 5 has been out for well over a year now, and the vast increase in functionality that the current version of ActionScript has over Flash 4, you just can't expect to see a lot of old-style frame calls in today's Flash movies. If it's your job to incorporate those movies into a Director application, you know how tough it can be to get the Flash movie to do your bidding.
I've got a little trick that just might help.
Setting Goals
ActionScript has what I think of as procedural and return functions. A return function is a function that yields a result of some sort, like arithmetic functions. Procedural functions just carry out a series of commands. In order to keep this simple, I'm going to define the goal of this exercise as being able to invoke a procedural function and pass it some parameters. More than that, I want to call any procedural function in the movie from Lingo, no matter what movie clip it's defined by (the same function name can be defined differently for two different movie clips).
The quick response is that there's no way to do this. For some reason, direct access to ActionScript functions wasn't a part of the additions to the Flash Asset Xtra, even though it's something that's supported through the JavaScript interface in browsers.
As mentioned above, aliasing is one option. By setting a series of string variables in the Flash movie -- including the target movie clip, the function name, and parameters -- then running the function name through a series of if statements in a called frame, you can route specific functions through to a movie clip. That means you've got to spell out everything you want to do ahead-of-time, though.
There are some -- imperfect -- ways to get around this obstacle that don't require massive rewrites of the Flash movie, and which can give you some access to procedural functions in the movie. It takes advantage of Flash's object model perhaps a bit more than is seemly, but as a Director developer working in Flash, that shouldn't bother you in the least.
Starting Point
This is the movie we'll be embedding in Director.
The graphic -- a little guy I call gordon -- has a couple of simple functions defined in his onClipEvent (load) handler:
function rotate (drot) {
targetrotation += Math.round (drot);
}
function move (dx, dy) {
targetx += Math.round (dx);
targety += Math.round (dy);
}
The two text entry fields (variables par1 and par2) at the top left of the movie let you set the parameters for the functions. The rotate function requires only one parameter. Press the red or the blue button to rotate or move gordon. The ActionScript for the move button is:
on (release) {_root.gordon.move (_root.par1, _root.par2)}
The buttons explicitly invoke the move and rotate functions of the movie clip identified as gordon, deriving the parameters from the variables on the root Timeline. Simple enough, but how can we do it from Director?
Framework (with Objects)
The key is that ActionScript treats pretty much everything as an object, even functions. You can set up a variable as a pointer to a specific function, which is the basis of ActionScript's object prototyping capabilities. The cool thing is, you can even use strings as references to movie clips and function names.
For instance, I can refer to the movie clip _root.gordon as _root["gordon"]. If I have a string variable mc whose value is "gordon", the movie clip can be referenced by _root[mc]. This is important, because the only way to get data into or out of a Flash sprite in Director is as a string variable. Putting the string into brackets forces ActionScript to evaluate the string as a child object reference.
Since functions attached to a movie clip are objects themselves, they can also be invoked in the same way. The button script above could also be written as:
on (release) {_root["gordon"]["move"] (_root.par1, _root.par2)}
Now, normally, I don't think you would want to do this. I'm fairly certain it's slower than an explicit call. But hey, you're a Director developer working in Flash, you just don't care.
The small blue dot below the flame button is a movie clip named directorcaller. Its script is where the magic happens:
onClipEvent (load) {
callString = "";
}
onClipEvent (enterFrame) {
if (callString != "") {
var callArray = callString.split( "," );
var targetArray = callArray[0].split (".");
targ = _root
for (i = 0; i < targetArray.length; i++) {
targ = targ[targetArray[i]];
}
_root.targettext = (targ);
targ[callArray[1]] (callArray[2], callArray[3]);
callString = "";
}
}
Every frame, the directorcaller clip checks the callString variable. If there's something in it, it starts breaking it into pieces with the split command. Items are separated by commas. The first item in the string is the object that's going to be called, the second item is the function that will be called, and the remaining items (if any) are used as parameters.
The first item in callString is itself broken down into components separated by periods.
The split command takes each item of a string and converts it into an array, where each array element is an item from the string. Thus, a string like
"gordon.head.ear,twitch,60,-5"
becomes an array of strings:
["gordon.head.ear"], ["twitch"], ["60"], ["-5"]
After the string is broken down into the arrays callArray and targetArray, the variable targ is used to derive a reference to the targeted movie clip. It starts off as a reference to _root, but each pass through the for loop burrows into the movie another layer.
The actual invocation of the function is followed by a command that clears the callString variable. Only two parameters are passed in this version (callArray[2] and callArray[3]) but you can string any number here. References to array items that don't exist will simply pass void values, which won't matter for most purposes.
To test this, I've provided a text entry field at the bottom of the Flash movie. Enter this data:
gordon,rotate,60
then press the black emulate button. The movie clip should rotate 60 degrees, and the value of targ will appear in the text field. It should be _level0.gordon.
Try it with the move function:
gordon,move,60,-10
Finally, to test whether we can access something that's not directly on the root Timeline, you can enter in a command to access the fire movie clip that's a child of gordon:
gordon.fire,gotoAndPlay,2
This simply kicks the fire movie clip into its second frame, where it plays through to its end and stops when it returns to frame 1.
Taking it Home
After all that Flash and ActionScript, you're probably ready to jump back into Director, eh? It's almost too easy from here. You can download the sample files (Flash 5 and Director 8.5 source) in Mac or Windows archives.
If you've used Flash sprites at all at this point, you've probably already figured out what you need to do. You just need a single command to move or rotate the gordon movie clip, or to set off the fire. If your Flash sprite is in channel 1, the Lingo equivalents of the commands above are:
sprite (1).setVariable ("directorcaller.callString", "gordon,rotate,60")
sprite (1).setVariable ("directorcaller.callString", "gordon,move,60,-10")
sprite (1).setVariable ("directorcaller.callString", "gordon.fire,gotoAndPlay,2")
Setting the callString variable of the directorcaller movie clip gives you access to virtually any procedural function of the movie.
There are still some significant limitations.
Return functions aren't supported, i.e. you can't use this to set a variable in Director to the results of a function in Flash. Further work is needed to create some sort of callback where Director can be notified that the value is ready for extraction.
There is some lag time between the Lingo setVariable command and the execution of the ActionScript function. The callString variable is only evaluated once each update of the Flash movie.
Because of that, this method can only support one call per instance of the directorcaller movie clip. Setting the callString variable several times in sequence within a Lingo handler only overwrites the previous value, which never gets executed then, and you can't count on an updateStage to force execution in-between setVariables. Still, it's better than nothing.
A more complex version of this system (hey, it's only 16 lines of ActionScript counting a couple of braces!) could be used to execute multiple functions, maybe by separating each command with a bar:
gordon,rotate,60|gordon,move,60,-10|gordon.fire,gotoAndPlay,2
Another for loop, another split function, and you're there. Still have to do something about passing commas...
Copyright 1997-2024, Director Online. Article content copyright by respective authors.