|
Lingo
Traditionally, Lingo is the hardest part for anyone to get used to with Director, since most folks who are working with Director have had very little experience writing programs of anything like real complexity. General Tips & Techniques: Programming is only as hard as you wish to make it. We write our own programs daily, and work within them fluidly. Consider the following: Alarm goes off Shut off alarm
Mate?
Shower
Don underwear
Time for breakfast?
Go to work The above is a fairly-simple set of steps anyone might undergo on any given day. It is also an algorithm -- a set of processes which might be implemented in the order given, or in a variant of some or all of the order given. It entails the idea of modularity, variability of parameters, and response based on parameter variation. In other words, it's a doggone program, and that's just how hard it is to work with one. It's not even programming which is difficult; it's thinking in terms of a program that might get tricky. So if you're stuck on a Lingo question and don't know where to start your coding, try to think of what you want to do in terms of plain language, then outline it as a series of clear steps. Refine the series to make it more precise. After several iterations along these lines, you will probably have an "aha!" moment wherein you understand just what to do in Lingo, and how to do it. Another way to approach this is with the "outline" format most of us are familiar with from various composition classes. You know: I First point II Second point 1 Subtext to II
a Subtext to 1
2 Subtext to II III Third point ...And so on. A third way to think of it is in terms of drawing a picture on paper. You outline the image first and then fill in the details. If you're working in watercolors you sometimes have to think backwards, but the approach is the same: Know your medium and do the work in the right order. (For instance it would be a huge mistake to apply the dark colors first in a watercolor, unless you knew precisely what you were doing.) Ideally you have a rough pencil sketch or two to begin building from. Comment, comment, comment. Comment everything. Assume your code will have to be read someday by someone who has no idea what sort of coding conventions you've followed. In addition, commenting your own lines and clarifying your logic can help you condense your program -- that is, make it run faster, simpler and with fewer weird loops. Many times I've been able to delete entire chunks of code because I stopped to think about the necessity of given active code lines as I was commenting them. Try to think around corners. Many things you might wish to do with Lingo can in fact be done. Part of the procedure involves a willingness to experiment with your development engine. (A while ago someone asked how to sort things by date, even if the user was not employing a US-standard date format. One possible workaround for this would be to use the long date function in Lingo rather than just the date.) Don't put long scripts into button or Score scripts. Put the scripts into the Movie Script(s) and call them from the Score item or button in question. If you use several Score scripts which are functionally identical and discover you need to change them all, this simple step can save you a lot of headaches. Make your variable and cast member names descriptive. Even to you personally, a variable name such as "xTF" might not mean beans to you in six months' time. Consider a better name, such as "sExportFromTextField" or something similar. Defining variables. Consistently tricky and consistently inconsistent. Much Director documentation has examples such as this: on HandlerDefinition GLOBAL globalVariable blaBlaBla END HandlerDefintion In Movie scripts, a better way to do things is this: GLOBAL globalVariable on HandlerDefinition blaBlaBla END HandlerDefinition GLOBALs, if they exist, must be declared at the top of all Movie scripts to work across scripts. With button or Score scripts that use GLOBALs, the syntax is a little different: on mouseUp GLOBAL globalVariable HandlerCall() doSomeThingWith globalVariable end or on exitFrame GLOBAL globalVariable HandlerCall() doSomeThingWith globalVariable end A nice syntax notation is as follows:
l -- letter ell -- list
Hence bLoaded is a boolean (true/false) variable which indicates if a given thing is loaded or not. nCurrentInteger would be an integer that has just been used and/or created. lFeedbackList might be a series of items in a list. For GLOBALS, just add the letter "g" before the variable defintion. Hence gnInputInteger is a GLOBAL integer obtained from some thing or another, often user input or internal processing. gsUserName might be a global string which contains the user's name everywhere. FAQ: Can I make a screensaver-like event in my Director file? What about odd Stage dimensions -- Stage sizes not equal to the 16-pixel multiple? I want to make a clickable hot-spot in my text like a Web page. How can I do it? What is me? What does it mean? Please help me a little with FileIO. I am going insane. I'd like to convert time in seconds to h/m/s. How can I do that?
Here's a recipe. This might need some adjusting; for instance, it doesn't actually support animation on the screensaver part of your file. However with a little creative work incrementing a global counter, you can set this saver to -- at least -- place randomly-selected text onscreen. You can also puppet a sprite or two and animate them.
-- top of movie script GLOBAL gnLastFrame, glLastMouseLoc -- add these lines to your StartMovie() handler set the timeoutLength to 60 * 60 * 5 -- that is, 60 ticks times 60 = 1 minute, times 5 = 5 minutes
when timeOut then ScreenSaver()
-- create the following handler in your movie script
on ScreenSaver
put the frame into gnLastFrame
END ScreenSaver
-- what "loopFrame" does depends on whether you want some sort of animation there or just some randomly-selected text
on exitFrame CheckUserEvent()
end
-- create this handler now in your movie script
on CheckUserEvent when keyDown then ExitSaver() if the mouseLoc <> glLastMouseLoc then ExitSaver() go the frame
END CheckUserEvent
-- create this handler now in your movie script
on ExitSaver
go gnLastFrame
END ExitSaver
Well, there are two possible ways. One is to simply check the FULL SCREEN option when compiling your Projector. This has the advantage of being extrenely easy to do, but the distinct disadvantage of not centering your presentation's window onscreen. Furthermore things which might appear to be "off screen" in your development file could well end up appearing in the compiled release, if they are "off screen" to either the right or the bottom. Bleah. The other possibility is to call the external file from a stub projector in Movie In A Window (MIAW). This requires some programming on your part and so is more difficult; however the results will also be more satisfactory. Here is a general recipe. Start with a new Director document and set its Stage color to whatever color you would like the background "splash" screen to be. Then, in Frame 1 of the Score, define the following script: on exitFrame pause end After that, enter the following in your Movie script:
GLOBAL goFileWindow, gsWindowName, gaFileWindowRect, gsWindowPath, gsSizeOfWindow
on StartMovie SetUpWindowSpecs() LaunchMIAW()
END StartMovie
on StopMovie PurgeMIAW() unload
END StopMovie
on SetUpWindowSpecs
-- inits dimensions and origins for MIAW object over splash screen
-- this can really be any name you want; it's an internal reference
-- this is the actual MIAW file you want opened by the stub
put ( the stageRight / 2 ) into nHorizontalCenterOfMonitor
-- set dims for location of window relative to screen size
set gaFileWindowRect = rect ( nHorizOrigin, nVertOrigin, nWindowWidth, nWindowHeight )
END SetUpWindowSpecs
on LaunchMIAW
if objectP ( goFileWindow ) then forget goFileWindow
-- windowType 5 is draggable on Macs; might need tweaking on PCs. Experiment.
open goFileWindow
END LaunchMIAW
on PurgeMIAW if objectP ( goFileWindow ) then forget goFileWindow
END PurgeMIAW
Once you've done that, compile the file (make sure to set the FULL SCREEN option) and distribute it with the document it is meant to open as an external resource file.
Don't despair. You can easily mask your windows, cutting off things you don't want shown on the bottom or right edges. Many movie houses do something similar. What you see projected on the theater screen is probably not the full bulk of image being displayed in the projector; likely there are bits of cardboard or matte paper cutting off the edges in the projection booth to make the screen image fit the rectangle you see. You, in the theater, are never aware of this, because you never see the extra trailing images to the bottom or the sides. You can do something much like this with Director. Just do things as normally in development with the 640 pixel width, but open the file in a smaller window, set to 631 pixels wide instead of 640 (the extra 9 pixels will be cut off). Remember, though, you will have to move things to the left in the development window to make them all fit in the smaller "masked" window in your release file. However if you have a scrolling text field and want to hide the scroll bar, this might be a fine way in which to do it -- you could, for instance, make your own custom scroll controls and position them to the *left* of the field to be scrolled. This is just one example of how you can make masks with Movie In A Window. This is a ducumented and known feature of Director 4 on up, but is little known and not even close to obvious. Many thanks to Terry Schussler for this oblique tip. Find out more Terry by clicking here.
Here is a very basic recipe which should get you going in the right direction: 1. Create a brand-new Director document. 2. In the first Score channel, enter the following Score script: on enterFame pause end 3. Immediately below that, in sprite channel 1, create a field cast member (not an RTF member!) and enter the following three words: Wow this works 4. Name that member in the Cast "myTestField". In the Cast window, assign the following script to the text member you just made: on mouseUp DisplayTextClicked( the mouseWord ) end 5. In your Movie script, create the following handler: on DisplayTextClicked nWordClicked if nWordClicked > 0 then ALERT "This is the word you clicked:" && word nWordClicked of member "myTestField" else ALERT "You did not click on anything I can recognize." END DisplayTextClicked Something similar will work for mouseLine. Note that mouseLine is delimited by RETURNs you enter, not by apparent line breaks onscreen. That is, while
moby
might look like
moby [RETURN]
onscreen, they are not the same. In the first sample, moby dick is read as one line, while in the second moby and dick are read as two different lines.
"me" is an internal reference to an object script, as in "on new me" and so on. It's a description of the object's instance in memory and, within the object itself, specifies that object. Let me try to make that even less clear... When a code object is instantiated into memory it is given, by the system, a memory address -- a hexadecimal number referring to the location of that object in memory. "me" is a keyword which refers to the object and its location in memory. Think of the computer as a sort of large apartment building with lots of vacant flats. When an object is created, essentially it "moves in" to the apartment building and is assigned a flat number -- 2-B or 13-F or whatever -- which is where the object "lives". The assignment of numbers is controlled by the "landlord", in this case the computer's operating system, and depends in part on where vacant flats may lie. That is, the address is purely arbitrary. You can't say to your object, "come into existence at this memory address." Not via Lingo, at any rate. What that means is that, before you make the object exist, there's no way for you to determine where it will be. You must, therefore, have a way to get a handle on the item's location. That's where the "me" comes in. Whenever the "me" is used it references that object's flat (the object itself is aware of where it is currently residing, just as you are aware of your home address). Thus the "me" is a reference to the location where the object may be found, and is much shorter to write than "the object located in flat 13 of floor 6". ANYway, you use "me" inside any script which causes an object to be instantiated. For instance a frame script would probably not contain the "me", as in
on exitFrame me
...however something which is a persistent behavior would become an object, because you'd want to refer to it, probably, more than once, and for more than a single line of code, as in a sprite behavior script; creating an object and keeping it in memory is thus a faster and more stable way to deal with recurrent or complex operations:
on mouseEnter me
on mouseLeave me
on mouseUp me
on rightMouseDown me
...the above four scripts would all be attached to the same sprite as a behavior; the behavior itself is attached, therefore, to the sprite which interacts in the ways described. Thus in this case the "me" could be seen as a reference to the sprite itself, which is not entirely accurate; the "me" is instead a reference to the behavior which is instantiated as a result of the sprite appearing onscreen. Naturally object scripts can be created which do not have direct attachment to anything onscreen. I've used such items, for instance, to create interactive alert/information dialog boxes, passing parameters to the object to indicate both what it should display, and what kind of information to pass back to the main program once the user has responded to the dialog box.
Yes, I certainly understand that. FileIO has been around since at least Director 3 (1992!) and is still as clearly documented as ever. Put another way, it is nice to know that, no matter how Director changes, some things will never change, such as the quality of the FileIO documentation. What continues to surprise me personally is that the functionality in FIO has not been built into Director yet. You still need to use it as an Xtra. Unfortunately Xtras for Director are characteristically not documented by Macromedia -- including, to some extent, the FIO Xtra they distribute with Director! Sure, there are docs available -- but damn few sample files. Here is a simple (minimal! But functional) little script to get FileIO working on D5+. You can download the D6 version by clicking here. Or make this script work by copying everything, pasting it into a movie script and typing "trynsave" into the message window. See my comments below on how to cusomize it a little. NOTE that I have tried these scripts in both D6 and D7, and they work, and I know 5 well enough to know the 6 script should work there -- and it will probably work for 4 too, if I recall it clearly enough. So if the scripts do not work for you, odds are pretty good you did something wrong. Download one of the sample files and try it on its own before you email me with complaints about my code. 'Kay? on SaveFile sFile, sContents
set bSuccess = 0
-- give the file a name if it's lacking one. if voidP ( sFile ) then set sFile = "untitled.txt" -- give the file contents if it's lacking them. if voidP ( sContents ) then set sContents = "Hello, world!" -- create the save file object in ram. set oSaveObject = new ( xtra "fileio" ) -- kick up a save as... dialog in FIO and get the user response all at once. set sSavePath = displaySave ( oSaveObject, "Save file as...", sFile ) -- this kicks out of the handler if the user has clicked CANCEL. if voidP ( sSavePath ) or sSavePath = "" then exit -- make the actual zerobyte file. createFile ( oSaveObject, sSavePath )
-- clear the initial file object from ram prefatory to write.
closeFile ( oSaveObject ) -- create a new write object once the initial file exists. set oWriteObject = new ( xtra "fileio" ) -- open the zerobyte file made above. openFile ( oWriteObject, sSavePath, 2 ) -- write string content to the file. writeString ( oWriteObject, sContents ) -- close the file when we're done.
closeFile ( oWriteObject )
-- bSuccess indicates only that the saveFile function has executed successfully.
END SaveFile
on TryNSave set bSuccess = SaveFile() if bSuccess = FALSE then ALERT "File was not saved!" END TryNSave NOW the generic trynsave handler call will save a file called "untitled.txt" to any place you specify, more or less, with the contents "Hello, world!". This is rather constrained, no? Yes. Therefore you modify the SaveFile() call in one of two ways. Easy way first. In the TryNSave handler, substitute the line set bSuccess = SaveFile() with *anything* you want in slot 1 and slot 2 of the SaveFile parameters. Such as: set bSuccess = SaveFile( "myfile.txt", "Isn't it great that I can pass parameters?" ) Or: on TryNSave
sFile = "myfile.txt"
END TryNSave ==== Hard way: -- modify TryNSave thus: on TryNSave sFile, sContent set bSuccess = SaveFile( sFile, sContent ) if bSuccess = FALSE then ALERT "File was not saved!"
END TryNSave
-- message window entry TryNSave ( "howdyMan.txt", "Howdy, man!" ) Well -- try it! Presto! Welcome to the basics of FileIO! After this it's all pretty easy -- just testing for errors, mostly. ==== Here is the same basic thing for D7 less the explication above. Click here to get the source file itself. The only real change is that the "set" keywords are missing pursuant to Dir7 syntax. ==== on SaveFile sFile, sContents bSuccess = 0 if voidP ( sFile ) then sFile = "untitled.txt" if voidP ( sContents ) then sContents = "Hello, world!"
oSaveObject = new ( xtra "fileio" )
if voidP ( sSavePath ) or sSavePath = "" then exit
createFile ( oSaveObject, sSavePath )
END SaveFile
on TryNSave bSuccess = SaveFile() if bSuccess = FALSE then ALERT "File was not saved!" END TryNSave
This is a pretty easy one, fortunately. Essentially you do some calculations using the modulus (mod) function. The modulus is the remainder of a given number after that number has been divided by another one. All right then, a sample. Suppose you divide seven by 3. What you have left over is whatever did not go evenly into the division, in this case 1: 7/3 = 2 1/3 -- the modulus is 1. Now suppose you divide six by 3. In this case your modulus would be 0, since this division operation left no remainder. Well, you use the modulus function to do the conversion above, along with a little division magic. Here's the basic script, which is called with a single parameter -- the value in seconds you want to convert. Click here to download the Director 6 source file. Call it by typing ConvertSeconds( timeInSeconds ) in the Message window:
on ConvertSeconds nSeconds
-- get the value in minutes for the nSeconds parameter.
if voidP ( nSeconds ) then
ALERT "There was no seconds parameter for me to process. Exiting function."
end if set nMinutes = nSeconds / 60 -- get the value for the trailing seconds after the minutes. set nSecondsExtra = nSeconds mod 60 -- get the value in hours for the minutes. set nHours = nMinutes / 60 -- get the trailing minutes. set nMinutesExtra = nMinutes mod 60
put nSeconds && "seconds equals" && nHours && "hour(s)" && nMinutesExtra && "minute(s)" && nSecondsExtra && "second(s)"
END ConvertSeconds It's shorter for 7, of course. Click here to get that source file.
Don't even get me started on what a bastard this one can be. Can you say "perpetual anal angina"? The entire basis of getNetText, downloadNetThing and their variants is that this stuff happens in the background, which means that traditional repeat loops will not repeat not work for them. You have to use the benighted and ill documented "on Idle" function instead. The basic premise of this kind of call -- I swear I am not making this up -- is this:
1. The user decides to download something from the net.
Note that if your URL is [whatever]:[someInteger], as in 255.255.255.255:14 it is NOT going to work with Director's built-in Xtras. Believe me. I know. This is a rare FAQ in that I have simply sent up the file rather than go into a long explication here on how to do it. Here is the 7 version of the code; note that you have to include the "set" stuff here and there to make it workable for 6: -- by Warren "The Howdy Man" Ockrassa, warren@nightwares.com
-- this script gets files off the net.
-- note that this basic script can be used to copy files from one drive or folder to another. -- a basic call to this might look like: -- GetFile( "http://www.beckerinc.com/", #data, 1, "localIndexCopy.htm" )
-- sFile can be pretty much any file at all.
-- yType indicates the kind of file being retrieved:
-- bUserSetPath is a boolean:
-- sName is the name that you want a given item saved under.
GLOBAL gsFile, gnNetId, gsSavePath
on GetFile sFile, yType, bUserSetPath, sName
-- Change the destination by setting the gsSavePath variable: gsSavePath = the moviePath if voidP ( sFile ) then
ALERT "There was no file specified for retrieval." & RETURN & RETURN & "Exiting function."
end if gsFile = sFile case ( yType ) of #data: if voidP ( sName ) then
ALERT "There was no name specified for the file to be saved under." & RETURN & RETURN & "Exiting function."
else if bUserSetPath then
oSavePath = new ( xtra "fileio" )
if voidP ( gsSavePath ) or gsSavePath = "" then
ALERT "Download cancelled by user." & RETURN & RETURN & "Exiting function."
else gnNetId = downloadNetThing ( gsFile, ( gsSavePath ) ) else gnNetId = downloadNetThing ( gsFile, ( gsSavePath & sName ) ) #text: gnNetId = getNetText ( gsFile ) otherwise
ALERT "Unknown symbol type:" & RETURN & RETURN & yType & RETURN & RETURN & "Exiting function."
end case
END GetFile
on Idle
if voidP ( gsFile ) or gsFile = 0 then exit sError = netError ( gnNetId ) if sError = "" then exit if sError <> "OK" then case ( sError ) of
4, 5: sError = "Required Xtra(s) missing or damaged."
otherwise sError = "Unknown error." end case
ALERT "An error has occurred:" & RETURN & RETURN & sError & RETURN & RETURN & "Exiting function."
end if sResult = netTextResult ( gnNetId )
-- this is the text that has been downloaded.
put sResult
gsFile = 0
END Idle
It makes a hell of a lot more sense once you have actually cranked through the scripts. The Director 7 version is available here. The version for 6 can be had here. Like the site? Buy the book! Director 8.5 Shockwave Studio: A Beginner's Guide by Warren Ockrassa, published by Osborne-McGraw/Hill; preview it now! |