Introduction
QuickTime. What a cool idea for internet media delivery. Movies and Object VR are two of the best features of this great package from Apple. Great, that is, until you need to download QuickTime to view media. For the PC this means 5.8 to 11Mb for QuickTime version 6.4. And the codecs (the actual movie compression and decompression programs) can vary from PC to Mac. What about AVI format for Windows Media Player? No – same problem, and no Object VR. What is a developer to do?
Thankfully, Flash MX can cover us for movie and Object VR playback. All it takes is a little coding. And if your site visitor doesn't have Flash, version 7.0.14.0 is only 466kb to download it. That's only a minute with a 56K modem!
This tutorial will cover how to create a QuickTime VR player in Flash MX, allowing your visitors to easily rotate objects. Specifically, we will:
- Import a movie of a rotating object so that it can be played forwards and backwards without video keyframe issues.
- Build a set of Flash MX movie clips that will handle the rotation.
- Code the Object VR player to handle imported movies of any length of objects that rotate clockwise or anticlockwise.
- Provide a visual method to control the rotation characteristics.
- Have a look at the uber-useful trace ActionScript command.
We will be developing this Flash MX file: spin-me.fla (382kb). Sorry about the file size, but we have embedded some video in it so you can see what's going on. It also makes use of loader.fla (35kb).
What we will not discuss is how to create an Object VR movie with QuickTime (or any other video or 3D tool). And if you are still wondering what Object VR is, a quick search on Google for "QuickTime Object VR" should get you to Apple's site so you can see what the fuss is all about!
Before we go any further, I would like to point out that some of the screen shots are from the Western Australian Museum's "Western Australia: Land and People" exhibition. Included in that project were twelve 3D studies using the Object VR player that we are going to develop here. Their great content and our stylish Flash MX work produced an inspiring virtual exhibition.
I am assuming that you have had a glance through the Using Flash help file, and that you have completed the online Flash tutorials "Introduction to Flash MX Tutorial" and "Introduction to ActionScript Tutorial" that come with your copy of Flash MX. You should also be familiar with creating QuickTime VR movies and you may find our other tutorial "Yet Another Flash MX Loader" useful because we will be using it to provide a bit of download polish to what will be a largish file (due to the video we will embed).
What should the QTVR player do?
Here's my list of features for our player:
- Work without interaction.
We wanted the QTVR movie to rotate whenever the mouse moved over the movie. No clicking and dragging. This would provide the simplest method of interaction for all users. - Use a visual method to control the spin of the object.
Why do stuff in code when you have the best visual development tool available? We want to visually indicate how much mouse movement should cause the object to rotate once. - Be quick to add to a file.
Because I'm busy. - Be simple to add to a file.
Because I forget stuff.
You may want your player to do more. Maybe it could have a default rotation, or some momentum when using a click-and-drag interaction method. Feel free to add anything you need.
How should the player be structured?
The structure for the player is very simple:
Our main movie will have a scene containing (amongst other things) a layer (named "qtvr") with our Flash qtvr movie clip object. This is where all of our code will go.
The other movie elements that are on the stage are just plain old text, graphic, and button objects.
If you have a look at the Symbol Definitions, you can see that the qtvr Movie Clip contains the actual QTVR mov file that we import (DivingHelmet.mov in our example), and a controller Movie Clip (named "driver") that has our code to rotate the QTVR movie.
Useful reading
Our Object VR player will be a self-contained SWF file with your video file of a rotating object embedded in it. The movie will need to be imported a little later on in this tutorial, so now would probably be a good time to have a quick look at how to import video into Flash MX. You will find it in "Using Flash> Using Imported Artwork and Video> Importing video" in the contents of your online help. Be sure to read the whole section – you never know what might be useful!
We will also be mucking around with the frame rate of the Object VR player. See "Using Flash> Working in Flash> Using the Property inspector to change document attributes" in your help file for more info.
Testing the movie that we are developing will also be a strong part of this tutorial. In this case we will be focussing on the Output window. You'll find it here "Using Flash> Testing a movie> Using the Output window" in the contents of your online help, but you can read the whole "Testing a movie" section if you want.
The last thing you should check out is the trace() ActionScript command. You'll find it here "ActionScript Dictionary> T> trace" in the contents of your online help. Learn to love the trace, and use it fearlessly.
Designing the player
First, we are going build a container Movie Clip for our Object VR player.
Start Flash MX and create a new file. Save it and call it something that relates to your site. We picked "spin-me.fla".
Rename Layer 1 to qtvr.
Now, create a Movie Clip symbol, name it appropriately. We called it "qtvr". This is where we will import our QuickTime Object VR movie and place our controlling ActionScript.
The next step is to create our layers as follows:
The actions layer is for the mighty stop() command. The driver layer will hold our dummy movie (a movie that doesn't really do anything) so that we can put an enterFrame event handler on it. And the mov layer is for the QuickTime Object VR movie that we will import soon.
Go back to the "Scene 1" root. Open the Library window (F11) and drag the qtvr Movie Clip that we just created on to the stage.
Save it at this point.
Loading your QuickTime Object VR movie
The Object VR movie that you load doesn't necessarily need to be in a QuickTime format: it could be an avi, mpg, wmv or any other format that Flash MX likes. Just as long as it is a movie of an object rotating before the camera. These were the images that made up the DivingHelmet.mov file, created by the very talented Darren Mok of the Western Australian Museum:
Now to do the import of your video file. But first, we need to do a quick test import so that we can get the frame rate of the video. If you know what the frame rate of your mov (or avi, or whatever) is then you can skip this bit. If you are at all in doubt about the frame rate, this quick test import will save you a whole world of pain later.
Select frame 1 of your mov layer and hit Ctrl+R to load a resource in to your library and put it in your timeline. When the Import file selector dialog appears, go find your mov video file and open it. You will be presented with an Import Video dialog like this:
Make sure that you have selected the "Embed video in Macromedia Flash document" option. We don't want to do all this hard work and still need QuickTime!
Select OK and you will get the Import Video Settings dialog:
The important bit is where it says "Length: 36.00 secs, 1.0 frames/sec". Okay, remember that 1fps. Now cancel the dialog. We're going to change the frame rate of the Flash MX movie to match it. And make sure the "Synchronize video to Macromedia Flash document frame rate" is checked. You'll see why later.
The easiest way to change the frame rate is to double-click on the 12.0 fps box in your timeline. This will open up the Document Properties dialog where you can set the frame rate to match the frame rate of your QTVR movie (in this case, 1 fps).
Now go back and select frame 1 of the mov layer and do the import again as we did for the test import, but this time don't cancel it: OK the import. If you want to, you can fiddle with the import compression settings.
Since we will be scrubbing back and forth along the timeline, it is critical that you generate a Keyframe interval of 1. If you let Sorenson generate its own keyframes or set it to anything higher than one, when you scrub backwards through the animation it will only skip to frames that have keys. This is because when a video file is compressed, a complete image is stored only when there is a keyframe in the video. Every frame after that up to the next key only stores changes to the image – Flash (or, more accurately, the Sorenson codec in Flash) only draws what has changed, and it can only start from a keyframe.
Change your frame rate back to whatever you were using. Remember that a higher frame rate gives you smoother looking object rotation, but at a cost of a higher CPU load for the computers that your Flash MX movie plays on. Your choice.
A final note on Flash MX frame rates: you can always play around with your frame rates when importing. Experiment with the values and try importing different video files. To see how our Object VR player will perform with your imported video, click and drag the playhead back and forth along the timeline. If you don't like what you see, you can always re-import the video with different settings until you find something that works well with the media that you have.
Save it.
What have we got so far?
All we have done here is create a Flash MX movie that contains a movie clip named qtvr that has 3 layers (actions, driver, and mov), and on the mov layer we have imported a QTVR movie (or any movie file that contains a rotating object).
We changed the frame rate of the Flash MX movie so that we would make each frame in the timeline contain one frame of the QTVR movie. The plan is to scrub back and forth along the timeline as your site visitor moves their mouse over the movie. This is why we needed the "Synchronize video to Macromedia Flash document frame rate" checked.
If you preview your movie now (Ctrl+Enter), you will see a rotating object. In our case it is a spinning helmet. Not really what we want, but it's a start.
Stopping the spin
Alright, now that we have the video imported, we need to control how it will spin under the user's control.
The first step is to pad out the driver layer frames in your qtvr Movie Clip so that they run the length of the QuickTime movie that you just imported, otherwise our driver object will only be in frame 1. In our case, we had a QTVR movie of 36 frames. So, in frame 36 of the driver layer insert some frames.
Now to add a stop() ActionScript command to stop the spin. Select frame 1 in the actions layer, go to the Actions window and add the code. You should be in Expert Mode (Ctrl+Shift+E in your Actions window), and your code will look like this:
stop();
While we're here, create a new Movie Clip and name it "driver". You know: "Insert> New Symbol..." (Ctrl+F8), set Name field to "driver", and choose a Movie Clip Behavior.
Go back to your qtvr Movie Clip and select frame 1 of the driver layer. Drag the driver Movie Clip on to the stage.
Open the driver Movie Clip object and draw in a box shape starting at the 0, 0 point. Give it a width of 100 and a height of anything you like because we won't be using the height. The width of this object will define how far the mouse has to move across the screen to cause the object to complete one full rotation. The 100 width will mean 100%.
The shape doesn't need a Stroke color, but make the Fill color red for now so that we can see it when we do some testing. Later on we will set the Alpha to 0% so that we can't see it. Ultimately, we don't want to see the shape or it will obscure the movie. I know we could drag it off the stage, but I want to put it over the QTVR movie so that it is easier to compare the width of the object to the size of the QTVR movie.
You may as well name the layer "invisible box".
Previewing this should show your movie stopped at the first frame and you should have a big red box somewhere as well.
The next few sections will make extensive use of the trace() ActionScript command. Learn to love the trace and it will reward you abundantly. Check it out in your Flash MX help file at "ActionScript Dictionary> T> trace".
Save now while you have a read.
What is the mouse doing over the image?
Let's find out! Open up your qtvr Movie Clip and select the driver Movie Clip. We're going to find out what our driver Movie Clip can do with the mouse. Let's do some testing.
Enter this code on the driver Movie Clip:
onClipEvent (enterFrame) {
trace(_xmouse);
}
The trace command will write out its arguments (the bits in the parenthesis) to the Output window when you preview Flash MX movies. So: preview that (Ctrl+Enter) now. Notice that the _xmouse variable returns the x position of the mouse (left-right value) in relation to the Movie Clip that the code sits on (in this case it's the driver clip).
The reason we're doing this is that we want to somehow convert the position of the mouse to one of our QTVR movie frames (1 through to 36 for us). Our approach was to scale the driver movie. No matter how wide or narrow you make the driver clip, _xmouse will always return 0 to 100 when you move the mouse across it (because we made it with a shape that had a width of 100). Try stretching the driver clip and previewing the movie.
If we think of _xmouse as a percentage of rotation that we want the QTVR movie to go through, then the area of screen that we stretch it over will give us a visual indication of how much mouse movement we are expecting users to make to get a full rotation of the object.
Now we need to convert the 0 to 100 percentage to a fraction by dividing _xmouse by 100. If we multiply this by 36 (the number of frames we have – yours will be different) we will have the frame that we want to show. Try this new code and preview it:
onClipEvent (enterFrame) {
trace(_xmouse / 100 * 36);
}
The Output window will show the frame that we should be displaying according to where the mouse is. Unfortunately, we are not getting exact frame numbers, so let's convert the number to an integer. Here's the code:
onClipEvent (enterFrame) {
trace(int(_xmouse / 100 * 36));
}
That's better, but there's still some weirdness. The 1 to 36 is good: we have those frames. Anything out of that range is no use to us at all.
Time to roll out the modulo operator (%). When you use modulo, you get a remainder as a result. 4 % 2 gives remainder 0. 3 % 2 gives remainder 1. If we take our _xmouse calculation and do a modulo 36 we should get a remainder between 0 and 35. Add 1 and we have a good range of 1 to 36. Try it:
onClipEvent (enterFrame) {
trace(int(_xmouse / 100 * 36) % 36 + 1);
}
Here are the formulas that we have been playing with so far (the box header in the table indicates the "invisible box" shape we made, and the shaded areas show where our 36 frames are repeating):
You can try them out in a trace ActionScript statement to see how they perform as you move your mouse over the QTVR movie to the right.
If you're interested, you can read more about the modulo operator here "ActionScript Dictionary> Symbols> % (modulo)", and the int function here "ActionScript Dictionary> G-L> int" in the contents of your online help. (I know int has been deprecated since Flash 5 in favour of the Math.round method, but round doesn't do what int does so I'm going to use it anyway!)
If you want to see how this will spin the movie, try this code:
onClipEvent (enterFrame) {
_parent.gotoAndStop(1 + (int(_xmouse / 100 * 36) % 36));
}
We make use of the _parent object to do this. It refers to the parent of the Movie Clip in which the code is running. In other words, the parent Movie Clip of our driver clip is our qtvr clip. Have a look in the Movie Explorer (Alt+F3) to see how things relate to each other.
We are using _parent with the gotoAndStop method because without it, gotoAndStop would try to move the playhead of the driver Movie Clip, and that would be pointless 'cos the QTVR movie is in the qtvr Movie Clip (and that's just where we want it).
The info for gotoAndStop is here "ActionScript Dictionary> M> MovieClip.gotoAndStop" in the contents of your online help.
Moving to the left of the image
Now it's not really working when it goes below 0, so we need to adjust our formula when the mouse pointer moves to the left of our box. Our first problem is that as we are moving to the left our range is starting at -1, and we would like it to start at 0. (It actually does start at 0, but unfortunately it overlaps with the code we have already written: that's where the funkiness is creeping in.)
To correct this, we need to add 1 to our _xmouse calculation before we perform the modulo. That gets us closer to what we want, but we still have negative numbers. Since we don't have any negative frames, we finally need to correct this by adding 36.
This may be seen in the following table (the red area shows the bit that we already have working, the grey areas show where the 36 frames to the left of the box are repeating):
Once again, you can try them out in a trace ActionScript statement to see how they perform as you move your mouse left of the QTVR movie. And this is the code to spin the movie:
onClipEvent (enterFrame) {
_parent.gotoAndStop(36 + ((int(_xmouse / 100 * 36)+ 1) % 36));
}
Combining the formulas
We now have two formulas: one for zero and above, and the other for below zero. To combine these, we will make use of an if-else statement block. Also, a quick look at the formulas that we have been experimenting with shows that the int(_xmouse / 100 * 36) part gets used quite a bit (and we'll need to use it again in the if statement's condition test). Let's put that into a variable so that we don't have to calculate it over and over again. Here's the code for our driver Movie Clip:
onClipEvent (enterFrame) {framePos = int(_xmouse / 100 * 36);
if (framePos <>This is all very well if your QTVR movie is of an object rotating anticlockwise (as seen from above), but if your object rotates clockwise then you have a problem. What you end up with is an object that seems to rotate from right to left as you move your mouse from left to right. (If you have a look at the series of images that we had for the diving helmet, you can see that our QTVR movie rotated clockwise.)
The effect we are after is similar to the experience of running your hand across a prayer wheel: you expect the surface that you touch to move in the same direction.
The simple solution is to subtract our 1 to 36 value from 37. This will give us a range that runs in the other direction (36 to 1). This is what we needed for the "Western Australia: Land and People" exhibition, but your Object VR may well run in the other direction. Use what you need. Here's our code so far:
onClipEvent (enterFrame) {framePos = int(_xmouse / 100 * 36);
if (framePos <>Making the driver handle any movie length
Throughout all of our examples here, I have focussed on only one QTVR movie: the one supplied by the Western Australian Museum. It has 36 frames in it and runs at 1 fps. Your Object VR will inevitably have a different length and changing the magic numbers of 36 and 37 that we have used here to other magic numbers that relate to the number of frames in other movies will only lead to pain. So let's fix that.
The _parent object has a _totalframes property which lets us know how many frames are in the parent Movie Clip. In our case, when we use the code _parent._totalframes in our driver clip's enterFrame event handler, Flash MX looks at the qtvr clip and checks to see how many frames it has (36 for ours, yours will probably be different).
Replacing 36 with _parent._totalframes, and 37 with (_parent._totalframes + 1) gives us our new code:
onClipEvent (enterFrame) {
framePos = int(_xmouse / 100 * _parent._totalframes);
if (framePos <>This lets our driver clip handle any length QTVR movie as long as we have the same number of frames in the driver and mov layers. If the driver layer has more frames than the mov layer, the Object VR will disappear for some frames. If the mov layer has more frames than the driver layer, the Object VR will get stuck when it hits a frame where the driver doesn't exist.
Making the code simple
Those of you who can remember back to high school algebra will be able to see that these formulas can be simplified further by multiplying out and cancelling terms with opposite signs, resulting in our final clockwise code:
onClipEvent (enterFrame) { //clockwise
framePos = int(_xmouse / 100 * _parent._totalframes);
if (framePos <>And applying the same process to our anticlockwise code gives:
onClipEvent (enterFrame) { //anticlockwise
framePos = int(_xmouse / 100 * _parent._totalframes);
if (framePos <>This makes it really easy to build an Object VR player for any QTVR movie of any length by simply grabbing the code that is appropriate for the rotation of your video file and pasting it on to your driver clip.
Time to wrap things up. Let's have a quick look at how to use our player.
Using the Object VR player
Let's spin something from scratch!
Create a new file and save it (we picked "spin-me.fla").
Make two new symbols, one for the QTVR movie (named "qtvr"), and the other for the driver (named "driver").
Create and name as many layers as you require, making sure you have a qtvr layer for your Object VR, and put a stop command on the last frame of your actions layer.
Put the qtvr clip on the qtvr layer of Scene 1 and open it. Make sure you have 3 layers in the timeline of the qtvr clip (from the top: "actions", "driver", and "mov").
In frame 1 of the mov layer, import your movie resource using the process we have described. Pay particular attention to frame rates and keyframe intervals.
Stretch out the driver layer to have the same number of frames as the mov layer.
Add a stop command to frame 1 of your actions layer.
Put the driver clip on to frame 1 of your driver layer and open it.
Draw a 100 by 100 box graphic in the driver clip. Get rid of the Stroke color, and set the Fill color alpha to 0%.
Go back up to your qtvr clip and select the driver Movie Clip object. Copy the appropriate enterFrame event handler (clockwise or anticlockwise) on to the driver clip.
Scale the width of the driver clip to set how far the mouse has to move to complete one full rotation of the object.
That's it! Save and publish. Nothing more to do.
What about a loader for this?
Good question. Glad you asked.
Elsewhere on this site, you will find a tutorial called "Yet Another Flash MX Loader". That tutorial discusses the development of a loader for content such as this.
All you would have to do is open the loader.fla file in Flash and drag the loading Movie Clip on to a layer for your loader. Copy and paste some event handlers and you're done!
Go check out "Yet Another Flash MX Loader". You'll be glad you did!
Conclusion
The Western Australian Museum has some great artefacts that they wanted to make available in a way that was not possible in the physical exhibition space. By allowing the virtual exhibition site visitors to examine them interactively, we delivered an experience that was engaging and rewarding. When we came to implement the Object VR players for the 12 QTVR movies they made, we needed a method that would be quick and simple for us to use. This is it.
In our final implementation of this Object VR player in the "Western Australia: Land and People" site, we did the usual trick of loading all window, caption, and text elements as early as we could, then when we hit the Object VR player, we used our loader to indicate progress. Because we can put the loader anywhere we like, we can make the best use of the streaming capabilities of Flash MX and just when things start to stall (when it hits the Object VR Movie Clip) we whack in a loader so that the site visitor still sees something happening.
For more information about our loader, check out the "Yet Another Flash MX Loader" tutorial elsewhere on this site.
Download the files used in this tutorial. Download (1028 kb)