|
© Andy Wilson, 1998
Tips and Tricks: Talking Shockwave pt 1
Welcome to a new section of the developers journal. The idea here is to discuss useful tips and tricks for getting your work done using Flash, Director and Shockwave. Any feedback, quibbles, requests can be sent to me at andyw@dircon.co.uk.
For a kick off I thought I'd look at how you can get Director Shockwave movies talking to one another on your web page. For example, you might want to put a game on one part of the web page and a little movie with the scores on another. The problem then would be to get the two movies talking so that the game could tell the score movie when to update itself and the score movie could, perhaps, tell the game when a new top score had been achieved.
Because there are many contexts in which you might want to use two or more director movies on the same page, a good place to start would be to write an engine that allowed you to send any command, with as many arguments as you like, to any named movie on the web page. That's what we're going to do here.
Before we go on I'll remind you that there are lots of general commands that you can call within your web page script to control a Director movie. For example, if you have a movie call 'Movie1' embedded in a web page (by using the HTML code <OBJECT NAME="Movie1"..> or <EMBED NAME="Movie1"..>) then you can call any of the preshrunk Director commands by calling them with the movie name prefixed in front. For example, you could call 'Movie1.Play()' or 'Movie1.Stop()'. Other commands are Rewind(), GetCurrentFrame(), GotoFrame(frNum), GotoMovie(movieURL) and AutoStart(), and you call them using the same syntax of name.command(). We are not talking about those commands here. Instead I just want to give you a general method to let you call any movie handler from any other movie.
Having said that, let's jump in at the deep and have a look at the Lingo, JavaScript, VBScript and HTML you will need to get your communication engine talking. The following code has been tested in Explorer 4 and Navigator 4 for Windows.
HTML / Jscript / VBScript Code
<SCRIPT LANGUAGE="JavaScript">
var InternetExplorer = ¬
navigator.appName.indexOf("Microsoft") ¬
!= -1;
function Communicate(movName, cmd){
setTimeout ("completeCommand ('" + movName + ¬
"','" + cmd + "')", 10)
}
function completeCommand (movName, cmd){
if (movName == 'Movie1') mov = ¬
InternetExplorer ? ¬
Movie1 : document.Movie1;
else mov = InternetExplorer ? ¬
Movie2 : document.Movie2;
mov.EvalScript(cmd);
}
</SCRIPT>
<SCRIPT LANGUAGE="VBScript">
Sub Movie1_ExternalEvent(paramStr)
callCommunicate paramStr
End Sub
Sub Movie2_ExternalEvent(paramStr)
callCommunicate paramStr
End Sub
Sub callCommunicate(paramStr)
Dim paramStart, paramLen, movName, script
paramStart = InStr (paramStr,"'") + 1
paramLen = InStr (paramStr, "')") ¬
- paramStart
paramStr = Mid (paramStr, ¬
paramStart, paramLen)
paramLen = InStr (paramStr,"'")
movName = Mid (paramStr, 1, paramLen - 1)
script = Mid (paramStr, paramLen + 1)
paramStart = InStr (script,"'") + 1
script = Mid (script, paramStart)
Call Communicate (movName, script)
End Sub
</SCRIPT>
<BODY>
<OBJECT NAME="Movie1"
CLASSID="clsid:166B1BCA-3F9C-11CF-8075- ¬
444553540000"
codebase="http://active.macromedia.com/¬
director6/cabs/¬
SW.CAB#version=6,0,0,165"
width="240" height="100" BGCOLOR="OOOOOO">
<param name="SRC" value="send.dcr">
<EMBED NAME="Movie1" SRC="send.dcr"
WIDTH="240" HEIGHT="100"
pluginspage="http://www.macromedia.com/¬
shockwave/download/index.cgi?¬
P1_Prod_Version=ShockwaveDirector" ¬
BGCOLOR="#000000">
</OBJECT>
<OBJECT NAME="Movie2"
CLASSID="clsid:166B1BCA-3F9C-11CF-8075-¬
444553540000"
codebase="http://active.macromedia.com/¬
director6/cabs/¬
SW.CAB#version=6,0,0,165"
width="240" height="100" BGCOLOR="OOOOOO">
<param name="SRC" value="send.dcr">
<EMBED NAME="Movie2" SRC="send.dcr"
WIDTH="240" HEIGHT="100"
pluginspage="http://www.macromedia.com/ ¬
shockwave/download/index.cgi?¬
P1_Prod_Version=ShockwaveDirector" ¬
BGCOLOR="#000000">
</OBJECT>
</BODY>
Lingo Code
on mouseup
ExternalEvent "Communicate('Movie2', ¬
'calledFunction arg1, arg2')"
end
on calledFunction arg1, arg2
put ("received msg via calledFunction" && arg1¬
&& arg2) into field "result"
end
on EvalScript cmdLine
set func = word 1 of cmdLine
set args = word 2 of cmdLine
if (args <> EMPTY) then
if (char 1 of args = "(") then
delete char 1 of args
end if
if (the last char of args = ¬
")") then
delete the last char of args
end if
set oldDelim = the itemDelimiter
set the itemDelimiter = ","
set argStr = "("
set numArgs = the number of items in args
repeat with x = 1 to numArgs
put QUOTE & item x of args ¬
& QUOTE after argStr
if (x <> numARgs) then
put "," after argStr
end if
end repeat
put ")" after argStr
set func = func && argStr
end if
do func
end
Explanation
The first step is to embed some movies into your web page. That's done with the code
<OBJECT NAME="Movie1" CLASSID ….> and <OBJECT NAME="Movie2" CLASSID
…>. The code itself is just standard HTML for embedding Director Shockwave movies into a web page – I cut and pasted it from an example on the Macromedia site. The important thing for us is that both movies are given unique names ('Movie1' and 'Movie2'). Don't forget that you have to name the movies not only in the <OBJECT> tag but in the <EMBED> tag too if you are to make sure that your movies always have the right name. You may have noticed that for the sake of example I have actually embedded the same dcr file twice ('SEND.DCR'). In the real world you will almost always be using different movies, but using the same movie works fine in this example.
The next step is to write some lingo in your movies. To issue the command I simply created a movie script with a mouseup handler that issues the command ExternalEvent "Communicate ('Movie2', 'calledFunction arg1, arg2')>". 'ExternalEvent' is a net lingo command that tells Director to pass the command string "Communicate… etc." over to the browser's script interpreter. The command string itself contains the name of the JavaScript handler we want to call ('Communicate'). Then there is the name of the movie we want to send the lingo command to ('Movie2'). Then there is a line representing the lingo we want to execute in the target movie, consisting of the name of the handler to be called ('calledFunction') and any arguments we want to pass along to it ('arg1', 'arg2'). You can pass as many arguments as you like. Note that the movie name and the lingo command line are both enclosed with single quotes – this identifies the two as separate strings in the web browser script interpreter (which accepts a single quote as a string delimiter). Notice also that when I talk about the name of the movie I mean the name you gave it when you embedded it into he web page ('Movie1' or 'Movie2', etc.) and not the actual file name or anything else.
Now you need to write the handler that is going to be called in the target movie ('calledFunction'). I simply got my function to write to a field to tell me that the command had been received as well as telling me what arguments were passed over. You also need to write your EvalScript handler as I have done above. What happens here is that because of the way the browser-Shockwave interface works, the message is actually passed from the browser to the target movie via the EvalScript handler. What I have got this to do is extract the name of the function we want to call ('calledFunction', which is the first word of the string passed to the movie as the cmdLine argument of the EvalScript method), and then extract the arguments that were passed along with it. This is done by the main body of the code: in this case, the cmdLine argument actually contains a string "calledFunction arg1, arg2", which the code then turns into the string "calledFunction ("arg1", "arg2")". The important thing is the last line of the, 'do func', which gets the Shockwave engine to playback the cmdLine string as if it were a lingo command – exactly what we are trying to achieve.
So far we have embedded the movies into the web page, written the lingo that fires off the command, and written the lingo to receive it. The next part is to write the browser scripts that pass the message from one movie to the other. I'll explain them one at a time. The main piece of code here is the Communicate() javaScript function. Having said that, you'll notice that all this seems to do is call the completeCommand() function after a 10 millisecond timeout, passing on its arguments to the new function. This is only necessary because of a bug in the implementation of ExternalEvent() in the Netscape Shockwave plugin. In some cases in Netscape not all of the javaScript function that you call with EvalScript() is executed, and the recommended work around is to call a second handler after a short interval to actually do the work. The only reason for the completeCommand() function is to implement this workaround, functionally this would work the same way if you pasted the code in completeCommand() into the Communicate() function.
At the top of the completeCommand() function is code that works out which movie to pass the command to. Obviously you will have to modify this code to reflect the names of your own movies and add lines if you are using more than two movies. Once this is done, the EvalScript() handler of the target movie is called and a string argument is passed containing the name of the lingo function and the arguments to be passed to it. Your message has now been passed on successfully to the target movie.
With these two javaScript handlers, Communicate() and completeCommand(), the bulk of the work is finished. All that's left to discuss is the VBScript subroutines. Again, these serve no great purpose in the grand scheme of things but are a workaround to get things moving in Microsoft Explorer. Explorer needs the Movie1_ExternalEvent() and Movie2_ExternalEvent() subroutines to correctly process the call to ExternalEvent(). Obviously you're going to have to write extra subroutines of the form myMovieName_ExternalEvent() for every movie that needs to use the externalEvent functionality of Shockwave. This should be easy though since, as you can see, all these subroutines do is call the further subroutine callCommunicate(), which strips out the name of the movie to be targeted as one string ('movName') and the name of the lingo handler to be called and any arguments to be passed to it as another variable, 'script'. Notice that it recognises where an argument starts and ends by looking for the single quote character – so make sure you use single quotes to mark the arguments when you issue them from Director, or if you use a different character to do this, edit the call Communicate() code appropriately.
The resulting arguments are then passed on to the Communicate() handler in the javaScript section of code and everything then goes on just as it would in Netscape. With that example as your basis, you should now be able to modify the code to do whatever it is you like. If you are going to use this script and modify it, watch out for the fact that, unlike lingo, both VBScript and JavaScript are case sensitive, and a wrong case in your code can easily send everything awry.
Further Development
In this example I have passed only strings as arguments from one movie to the other, which is all the EvalScript and ExternalEvent routines are capable of dealing with, but you could easily modify the code to pass integers, lists, etc.. You might do this simply by knowing that a particular handler always expects certain arguments to be of a certain type, so that in the code of, eg., calledFunction(), above, you could have a line that converted arg2 into an integer. Then, you simply have to make sure that whenever you call calledFunction() you remember that the second argument passed to it will always be converted to an integer or whatever. Another way to do this might be to prefix all passed values with a code to indicate their data type (eg., 'str', 'int', 'lst', and so on). Then, in EvalScript you could modify the code that strips out the arguments in order to cast each argument into the correct data type. An argument called 'int_24' would then be converted to an integer with the value 24. That way you could pass arguments of an legitimate lingo type and they would always work out correctly.
Next month I'll show you how to get Flash movies to issue commands to JavaScript and VB Script, and then how to get them to take control of a Director movie. As I said at the beginning of this piece, if you have any feedback on this column, ideas for future columns, whatever, send them to me at andyw@dircon.co.uk. Till next time, happy coding.
|