Publishers of technology books, eBooks, and videos for creative people

Home > Articles > Web Design & Development

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Working in Director

In this section, you'll learn how to work in Director. You'll learn the importance of the size of your .W3D files, how to set up the score, how to handle visibility and color issues, and finally, how to add Lingo.

File Size Considerations

In this example, the .W3D file consumes a majority of the Director movie's size. This means that the .W3D file size is what you should be concerned with, even before you import it into Director. If it's too large, your users will have an overall bad experience viewing your movie. The movie will take a long time to download, and it might not play smoothly, especially if your viewers don't have hardware-accelerated OpenGL or DirectX type video cards.

The first thing to remember is that when you "get information" (in Macintoshes) or view properties (in Windows), ensure that you are viewing the network file size, not the file size on disk. Although it's usually not a considerable difference, it's best to use accurate information for posting next to the movie, so that users know what they will have to endure in terms of download times [3.29].

Figure 3.29. The download size for this file is 890K, not 872K. Try to make every K count.

Another disadvantage of having a large file is that the majority of Internet users are not on a broadband connection; thus, a 1 megabyte file's download can take a long time. It can become an obsession to see just how small you can get a .W3D or Shockwave 3D file. I tell you about more file-saving tips later in this chapter. First, we'll set up the movie.

Score Setup

After you have imported your .W3D file and any additional assets into Director, you can lay out the score. If you prefer, you can use the house.dir file on the CD-ROM accompanying this book. This file represents a simple movie that has three necessary frames to run the entire movie [3.30].

Figure 3.30. The three frames that this movie uses are for preloading, waiting until the user is ready, and displaying the .W3D movie and interface.

I recommend that you never use the first frame of the score. This isn't a Shockwave 3D tip, but a general rule when working in Director. Director performs a lot of pre-tasks before loading the movie, such as general script initiations for sounds, windows, and video. When Director or runtime versions of your movie, such as DCRs or projectors, have assets in the first frame, you can sometimes run into inconsistencies and erratic behavior when you start the movie. I recommend that you always put sprites in frame 2, and treat frame 2 as if it is frame 1. However, if you need to run initialization scripts, putting those scripts in frame 1 is fine.

Color and Visibility

When you place your .W3D castmember into the score, you will notice a lag as Director pauses for a few seconds to process the Shockwave 3D castmember and load it into memory. The lag time occurs during any time that your playback head is moved off the Shockwave 3D castmember frame and then back on to it again. This delay can become considerably annoying at times because the program appears to hang briefly. You can also get this lag when you are trying to add behaviors to Shockwave 3D castmembers.

To avoid the lag time as you navigate the score, place your Shockwave 3D castmember in the score, and then position it on the stage where you want it to go. Now, go back to the score and turn off the visibility of the channel that contains your Shockwave 3D castmember. You don't need it to be visible to add behaviors and scripts. Now, you can scrub the playback head around the score to navigate without it pausing and hanging over the Shockwave 3D member [3.31].

Figure 3.31. Turn off the visibility of the channel that contains your Shockwave 3D sprite for better score performance.

Another tip is to color your Shockwave 3D member in the score, using the color chips in the lower-left corner of the score. This helps you quickly identify where in the score your castmember is.

Redrawing Issues

When visibility is turned on for a castmember, and you move that member to the stage, redraw issues occur. I have seen these same redraw issues occur in projectors and web browsers, where you have no control over it, because it is occurring outside of Director. This is a Shockwave 3D bug that Macromedia has to resolve in the future.

Another good reason to turn off the visibility of your Shockwave 3D member is because leaving it on can result in some "redrawing funkiness" that is present in Director after an image is redrawn in Director.

To simulate this problem, add your Shockwave 3D castmember to the stage. Then, move the Stage window to another location. The Shockwave 3D sprite does not want to update the image when the Stage window moves [3.32].

Figure 3.32. Moving your stage with a Shockwave 3D sprite can cause redraw issues.

In my opinion, there is currently no ideal work-around for this bug. You can make the redrawing problem go away by turning off the Direct to stage option in the Property Inspector window; however, when you do this, your Shockwave 3D movie is drawn in screen buffer layers, and other castmembers in the score can pass on top of the Shockwave 3D sprite. This causes such a substantial slowdown in the redrawing of the Shockwave 3D sprite that it becomes useless. It's reminiscent of the days when Director's QuickTime functionality was first added to the application. You had the ability to turn off the Direct to Stage function for the QuickTime sprite, but the number of frames per second playback would drop from 24 to approximately 4, making the QuickTime movie useless. However, today you can do this with QuickTime sprites, and the performance isn't as bad as it used to be. Hopefully, redrawing sprites will get better with future improvements.

The real issue is that these redrawing artifacts occur in projectors and browsers. This is bad, because you can't turn off the Direct to Stage option for performance reasons, and you can't let your user move the window around or scroll without causing this redrawing problem. I recommend that you simply stay away from the projector option that lets your movies be in their own window (show title bar). As for working in Director, your only options are to turn off the Shockwave 3D visibility or open and close the stage window and use other windows as "giant erasers" to wipe the screen clean.

Adding Lingo

Yes, it's Lingo time. This movie contains Lingo examples that are either simple or difficult. There is no "middle ground" with these scripts. If you open the house.dir movie from the CD, you will see in the cast that there are four scripts—members 19, 20, 21, and 23. Number 23 is the most difficult if you are new to Lingo.

Each piece of Lingo preloads the Shockwave castmember into memory, waits for the user to continue, pauses the movie in the frame that the Shockwave castmember is in, and finally, performs a form of collision detection known as ray casting.

The first script that handles the preloading is a frame script. The script should be added to one of the Score window's frame script channels. It looks like this (remember, these scripts are on the CD accompanying this book as well):

property p3dMember

on beginSprite (me)
 p3dMember = member("3D")
 p3dMember.preload()
end beginSprite

on exitFrame (me)
 if (p3dMember.state <> 4) then
  go to the frame
 else
  p3dMember.resetWorld()
  go to the frame + 1
 end if
end exitFrame

Member number 19 is a script that preloads the Shockwave 3D castmember into memory (RAM) before it starts playing. The script looks for your Shockwave 3D castmember by name, not by sprite number. Because it looks for the name, be careful. If you copy the script word-for-word, don't forget to use the name of your Shockwave 3D castmember instead of the one in the script, or change the name to 3D.

Before talking about the details of this script, I want to mention Thomas Higgins. Thomas is the author of the scripts in this movie, and he is the author of many movies located at http://www.directordev.com/. Thomas is an employee of Macromedia, and I want to extend my appreciation for his help with this demonstration and his contribution to the book.

---- Preload Shockwave 3D member into memory script ----

property p3dMember -- reference to the 3D member

on beginSprite (me)
 -- stores the 3D members name for reference
 p3dMember = member("3D")
 -- starts the preloading of the castmember
 p3dMember.preload()
end beginSprite

on exitFrame (me)
 -- check the member's state; is it loaded or not?
 if (p3dMember.state <> 4) then 
  -- if it's not loaded, then hold on current frame
  go to the frame
 else
  -- reset member's world
  p3dMember.resetWorld()
  -- if it is loaded then step to next frame
  go to the frame + 1
 end if
end exitFrame
----

The next script that is used in the movie simply pauses it until the user is ready. After the movie has been preloaded, you don't want it to instantly start playing, because the user might not be ready. This script waits until the user clicks on the screen before advancing to the next frame to play the Shockwave 3D movie you just preloaded.

-- This script holds the playback head on a given
-- frame until the user clicks anywhere on stage.
--

on exitFrame
 -- hold on current frame
 go to the frame
end exitFrame


on mouseDown 
 -- step to the next frame
 go to the frame + 1
end mouseDown

There is one additional frame script that is used to pause the Director movie on the frame that the Shockwave 3D castmember is playing on:

on exitFrame
 -- hold on current frame
 go to the frame
end exitFrame

The next Lingo script is attached to the Shockwave 3D sprite and controls the navigation and collision of your camera. It's a very intuitive use of ray casting. (This script is also on the CD.)

What the ray casting script does is project a ray (or think of it as a vector) from the camera in the direction that the camera is moving. The camera has a sphere surrounding it that is invisible. If the camera is moving forward, a ray is being projected forward to determine the distance the camera is from an object in front of it [3.33]. If the ray being projected or cast from the camera is shorter than the distance the camera is to the sphere, the camera is close to the object, if it isn't colliding with it. The script then sets the camera's position back to the path the ray was being cast. This gives the impression that your camera has collided with an object and bounced back off of it.

Figure 3.33. Ray casting is used to determine the camera's distance from an object. A result of ray casting is shown here.

Following is the ray casting Lingo and an explanation of what it is doing.

These are the properties that get initialized for the script. Properties are similar to variables or global variables depending on the programming language you are familiar with:

property pMember	-- 3D member used by this sprite
property pSprite	-- reference to this sprite
property pMouseDown	-- user is pressing the left mouse button
property pRightMouseDown	-- user is pressing the right mouse button
property pCamera	-- the sprite's camera
property pCameraSphere	-- the sphere used to surround the camera
property pCamAnimFlag	-- introduction animation is running?
property pCamAnimInfo	-- perform an introduction animation

The beginSprite handler fills the properties with their functions and any necessary data. It also creates the sphere around the camera and a light that the camera carries with it:

on beginSprite (me)
-- initialize properties
 pMember = sprite(me.spriteNum).member
 pSprite = sprite(me.spriteNum)
 pMouseDown = FALSE
 pRightMouseDown = FALSE
 pCamera = sprite(me.spriteNum).camera
 pCamAnimFlag = TRUE
 -pCamAnimInfo = [#initT: pCamera.transform. _duplicate(), \
         -#f inalT: pMember.camera _("f irst_person").transform. duplicate(), \
         #count: 0]

 -- create the camera's bounding sphere
 mr = pMember.newModelResource("camera_sphere",#sphere)
 mr.radius = 7.5
 pCameraSphere = pMember.newModel("camera_sphere",mr)
 
-- create a light to carry with the camera
 camLight = pMember.newLight("camera_light",#point)
 camLight.color = rgb(170,170,170)
 
-- make the sphere and light children of the camera
 pCamera.addChild(pCameraSphere,#preserveParent)
 pCamera.addChild(camLight,#preserveParent)
 
-- register the camera for the timeMS event in order to perform the intro animation if any
 -pCamera.registerScript(#timeMS,#animateCamera,me, 0,50,51)
 
-- register the member for regular timeMS events in order to respond to user input and resolve camera  
"collisions" -pMember.registerForEvent(#timeMS,#controlCamera, me,2500,50,0) end beginSprite

These mouse handlers switch the Boolean state for the property. If the mouse is down the pMouseDown property is TRUE, for example:

on mouseDown (me)
-- update the mouse down property 
 pMouseDown = TRUE
end mouseDown

on mouseUp (me)
-- update the mouse down property 
 pMouseDown = FALSE
end mouseUp

on rightMouseDown (me)
-- update the right mouse down property 
 pRightMouseDown = TRUE
end rightMouseDown

on rightMouseUp (me)
-- update the right mouse down property 
 pRightMouseDown = FALSE
end rightMouseUp

The animateCamera handler is used to animate the camera automatically over time:

on animateCamera (me)
-- increment the internal counter for the animation
 pCamAnimInfo.count = pCamAnimInfo.count + 1
 
-- determine the interpolation percentage
 pctg = 100.0 _ (pCamAnimInfo.count / 50.0)
 
-- if pctg is greater than 100 end animation sequence
 if (pctg > 100) then 
  pctg = 100
  pCamAnimFlag = FALSE
 end if
 
-- determine the new interpolated camera transform
 -t = pCamAnimInfo.initT.interpolate _(pCamAnimInfo.finalT,pctg)
 
-- apply that transform to the camera
 pCamera.transform = t
end animateCamera

This controlCamera handler is what controls the movement of the camera for each of the four directions. It also controls the collisions with objects:

on controlCamera (me)

-- respond only if the intro camera animation is completed
 if not(pCamAnimFlag) then
  
-- CONTROL THE LEFT/RIGHT ROTATION OF THE CAMERA
-- if the shift key is *not* down then follow the mouse -- to adjust left right looking
  if not(the shiftDown) then
   
-- check for the mouse locH wrt the sprite loc
   deltaH = the mouseH - pSprite.locH
   
-- calculate rotation value to apply
   rotn = -(deltaH/165.0) * 4.0
   
-- apply that rotation
   -pCamera.rotate(pCamera.worldPosition, vector(0,0,1),rotn,#world)
  end if
  

-- CONTROL THE FORWARD/BACKWARD MOVEMENT OF THE CAMERA
-- if the left mouse is down then move the character forward
  if pMouseDown then
   pCamera.translate(0,0,-2.5)
  end if
  
-- if the right mouse is down then move the character backward
  if pRightMouseDown then
   pCamera.translate(0,0,2.5)
  end if

The following part of the script controls the collisions of the camera with the walls or other objects. This casts rays forward or backward depending upon movement, and it will also cast rays to the left and to the right. For each ray that is cast, the script will verify that the distance to the nearest model exceeds the camera's bounding sphere radius; if the distance is less than the bounding sphere's radius, move the camera out of the collision state in a direction perpendicular to the intersected model's surface.

  --- cast one ray fwd/bckwrd depending upon which mouse button is down
  case (TRUE) of
    
-- left mouse down, cast ray forward
   (pMouseDown): 
    
-- cast ray
    -tList = pMember.modelsUnderRay _(pCamera.worldPosition,-pCamera.transform. zAxis,#detailed)
    
-- if there are models in front of the camera, check for collisions
    if (tList.count) then
     me.checkForCollision(tList[1])
    end if
-- right (control+) mouse down, cast ray backward
   (pRightMouseDown): 
    
-- cast ray
    -tList = pMember.modelsUnderRay _(pCamera.worldPosition,pCamera.transform. zAxis,#detailed)
    
-- if there are models in front of the camera, check for collisions
    if (tList.count) then
     me.checkForCollision(tList[1])
    end if
  end case
  
-- cast a ray to the left
  -tList = pMember.modelsUnderRay _(pCamera.worldPosition,-pCamera.transform. xAxis,#detailed)
  
-- if there are models in front of the camera, check  _for collisions
  if (tList.count) then
   me.checkForCollision(tList[1])
  end if
  
-- cast a ray to the right
  -tList = pMember.modelsUnderRay _(pCamera.worldPosition,pCamera.transform.xAxis, #detailed)
  
-- if there are models in front of the camera, check for collisions
  if (tList.count) then
   me.checkForCollision(tList[1])
  end if
 end if
end controlCamera


on checkForCollision (me, thisData)
-- grab the distance value
 dist = thisData.distance
 
-- if distance is smaller than bounding radius, resolve collision
 if (dist < pCameraSphere.resource.radius) then
  
-- get distance of penetration
  diff = pCameraSphere.resource.radius - dist
  
-- calculate vector perpendicular to the wall's surface to move the camera
  tVector = thisData.isectNormal * diff
  
-- move the camera in order to resolve the collision
  pCamera.translate(tVector,#world)
 end if
end checkForCollision

This script controls the navigation for the Shockwave 3D sprite. After the script is attached to your sprite and the movie is started, the cursor position relative to the Shockwave 3D sprite determines the rotation of the camera, even if your cursor isn't rolled over the Shockwave 3D sprite. So, for example, if your cursor is off the screen to the right, your camera will be turning to the right inside your Shockwave movie. Be aware of the functionality, because at some point, your movie will need to explain to your users how to navigate.

With the script controls in order, it's time to test what you have completed.

  • + Share This
  • 🔖 Save To Your Account