Shockwave VR with a grip
December 6, 1999
by Jean-François Cloutier
As Quicktime VR and Java applets reproducing VR are more and more popular on the Internet, Shockwave developers need to look no further than Director and Lingo to reproduce an equivalent or even better version without using any Xtras or browser plugins. This article will show the basic principles to achieve such a Shockwave VR. And while we're at it, why not add a little something different like grabbing the image to move it around and simulating residual momentum to enhance our project.
The use of this technique can go much beyond the simple viewing of a large image. It could be used for some kind of never ending scrollable menus or simulate the roller motion of a slot machine. Interactivity could be taken much further by adding some clickable hot spots or even use Flash cast members with animated buttons, all of which I will not demonstrate here. The code used in this article is dot syntaxed, so it only works in Director 7.
Let's get started
We need a seamless graphic preferably very wide. A 360 degrees picture is a dream come true for this task but any left and right end seamless graphic can do the trick, you could make your own using Photoshop. For a quick start, you can use a color spectrum gradient. The first thing to do is to take note of your image's width and decide the stage size you want, it should be much less wider than your image. I suggest you pick a stage height smaller than your image in order to be able to allow vertical image movements also. Then modify your image in the following way : at one end of your image (say left end), copy a portion of your image exactly the width of your stage and paste it at the other end (see picture below).
It was seamless wasn't it? Now your new image's width should be that of the original image + the stage width. For memory issues, since the image is now very wide, I suggest you reduce the image colordepth as much as possible.
The infinite loop
What really happens when you move the image horizontally and one end of the image reaches the stage, is that it instantly jumps back or forward a precise number of pixels to bring the image exactly where it was at the other end. Since both ends are identical, the illusion is perfect. This precise number of pixels is, as you may have guessed, the width of the stage. See the little animation below, a slowed down simulation of what's happening. Well ok... not exactly what's really happening, but sort of.
The above animation was done using film loops, however, in the real thing, we will use Lingo in the form of a behavior to do it much more efficiently. We need to understand one more thing before moving on.
Momentum
The idea is to simulate the continuity of the movement we gave to the image after the mouse is released. Momentum is used as a metaphor here since no mass is involved, no vectorial velocity and no vectorial quantity called momentum is really calculated or used here. Now, we don't want the image to move in a direction forever when we let go, so we will introduce a friction coefficient to reduce the horizontal displacement over time.
The horizontal displacement will be measured each exitframe while we drag the image. Then, as soon as we stop dragging the image, if the horizontal displacement value is not equal to zero then the image should keep moving as long as there is some displacement value left. To keep things simple, vertical momentum will be omitted.
Let's define the behavior properties :
Property | Comment |
pMySprite | Reference for sprite number |
pMyMember | Reference for member |
pDragging | Boolean, TRUE if the user is dragging |
pClickOffset | Point, difference between clickloc and sprite regpoint |
pDisplacementH | Integer, current horizontal displacement of image |
pLastMouseLoc | Point, remembers the last mouse location |
pFriction | Integer, friction coefficient to slow down image H movement |
pHandCursor | Boolean, for cursor management (optional) |
pRegPointH | Integer, image regpoint.locH |
pRegPointV | Integer, image regpoint.locV |
pOriginalWidth | Integer, width of the image before modification |
pBottomLimit | Integer, image lower locV limit |
Some of the properties get their value from a dialog box when the GetPropertyDescriptionList handler is called and some others in a beginSprite handlers, both not shown in this article (so download the example movie). To let our image object know if it is being dragged, we will use a Boolean flag called pDragging toggled on mouse events :
on mouseDown me pDragging = TRUE pClickOffset = the clickLoc - pMySprite.loc pLastMouseLoc = the clickLoc end on mouseUp me pDragging = FALSE end on mouseUpOutSide me pDragging = FALSE end
When spinning the image, mouseUp events happen sometimes off the stage, thus the need to have a mouseupOutside handler. To avoid the repositioning of the regpoint of the image with the mouse, pClickOffset is used to take the offset with respect to the regpoint into account while we drag.
You really moved me!
To know the distance travelled, we first need to know the dragging start point, pLastMouseLoc will do just that and it will be refreshed each exitframe as the mouse is dragging the image around. Hence the horizontal displacement will be recalculated each exitframe. The exitframe handler is shown below :
on exitFrame me --------- the first part --------- if pDragging then -- Position image to the mouse while dragging pDisplacementH = mouseH() - pLastMouseLoc.locH DisplacementV = mouseV() - pLastMouseLoc.locV pLastMouseLoc.locH = mouseH() else if pDisplacementH <> 0 then -- Not dragging but image still has some -- pDisplacementH value to use DisplacementV = 0 -- No vertical momentum, DisplacementV = 0 if pDisplacementH > 0 then pDisplacementH = pDisplacementH - pFriction if pDisplacementH < 0 then pDisplacementH = 0 else pDisplacementH = pDisplacementH + pFriction if pDisplacementH > 0 then pDisplacementH = 0 end if end if --------- the second part --------- -- Check for boundaries and reposition image if pDisplacementH <> 0 then newlocH = pMySprite.locH + pDisplacementH if (newlocH < pRegPointH - pOriginalWidth ) then ¬ newlocH = newlocH + pOriginalWidth if newlocH > pRegPointH then newlocH = newlocH ¬ - pOriginalWidth pMySprite.locH = newlocH end if if DisplacementV <> 0 then newlocV = mouseV() - pClickOffset.locV if newlocV > pRegPointV then newlocV = pRegPointV if newlocV < pBottomLimit then newlocV = pBottomLimit pMySprite.locV = newlocV end if end exitframe
The first part of the exitFrame handler takes care of displacement variations and pseudo momentum. The second part takes care of the repositionning and the infinite loop effect by keeping track of the image boundaries, always ready to make the image jump if ever a right or left end is about to be displayed on the stage. DisplacementV is a local variable, since there is no vertical momentum, there is no need to have it remembered between exitFrames.
Take a look at this Shockwave VR example, go ahead and give it a good spin !
A sample movie is available for download in Mac (580K) or PC (504K) format. This is a Director 7 movie.
In this example the original image dimensions were 2100 x 310 pixels, while the stage is 400 x 240 pixels. So, the final image width is 2500 pixels (2100 + the stage width). To increase fluidity of movement, the frame rate is kept rather high, in this case 30 fps. The friction coefficient is set to 6 . To have a feel of grabbing something I like to use cursor 260 (open hand) and cursor 290 (closed hand) commands. The behavior parameter dialog box in the sample movie finds the original image width by means of simple calculations. It allows you to modify the friction coefficient and lets you decide if you want to use the hand cursors.
Conclusion
The basic principles behind the creation of a VR behavior were demonstrated in this article. A little image editing was necessary to achieve our goal. Some improvements and variations could be made and will be left for you to experiment. Please, let me know of any suggestions you might have while experimenting with this technique.
References
The creation of the behavior in this article was inspired by the some of the work of Jim Collins. The technote section of his website really got me started on this.
I first learned how to use dot syntax by reading Zac's "Director[7].syntax" article.
The nice Prague Castle Garden picture used in this article is from this Czechsite Travel Guide website. The 360 degrees pictures can be used for educational purposes. The author of the pictures is Albert Jindra.
I suggest reading Raman Pfaff's articles called "Bodies, Rest, and Motion" and "The physics of an elastic collision: Part 1" for his excellent description and lingo translation of physics quantities like velocity and momentum.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.