Flash + Director = not quite so slow
July 25, 2000
by Gary Rosenzweig
Two weeks ago I wrote a column about how slow Flash members inside Director movies are. They are slow; no one disagreed. However, I did make a mistake (actually, a double mistake) in my method. The first mistake was to assume that the little-known Flash member property static was the same as the pausedAtStart property. It would seem to make sense that if a Flash member were paused, Director would treat it as if it were static. Not so. They are separate properties, and they give different results in the speed test.
The double mistake came when I used the loop property, rather than the pausedAtStart property, in my tests. If you check that old column, you can see that the demo movie allowed you to see the loop property. I'm not sure why I did that, except that I was thinking in terms of single-frame Flash movies. In that case, loop and pausedAtStart are not very different.
Here is an updated test movie. This provides controls for both pausedAtStart and static.
Using the "Flash over Flash" test, with high quality on and pausedAtStart and static off, I got the result of 3900 milliseconds. Changing the pausedAtStart to on, the results were the same. However, turning static on got me 1400 milliseconds. Quite an improvement!
The champion, however, is still "Bitmap over Bitmap" with 1100. This isn't quite as impressive a margin as in the previous tests, however. Also, note that I did these tests on my G3 Powerbook this time, not my G4 desktop. So the numbers from the old column and these numbers shouldn't be compared -- but their ratios should.
I did the same tests in Shockwave on an 800 MHz Pentium III with a fast video card. Both "Flash over Flash" and "Bitmap over Bitmap" came out at 300 milliseconds when the Flash static property was turned on. A tie? Well, impossible to tell, since 300 milliseconds to go about 300 frames means that the 999 fps limit was reached in both cases. The Powerbook test was a little more revealing.
Rather than just leave it at "Flash is still a little slower than bitmaps," I want to propose a solution involving Imaging Lingo. You can get the image property of a Flash member, just as you can get the image property of a bitmap. Once you have this image, you can create a new bitmap with it. This bitmap will look exactly like the Flash member, but it will be faster.
Here is a short behavior that does this. It will take the image of the Flash member, create a new bitmap, and place the image in it. Then, it will swap the Flash member for the bitmap in the sprite. To be neat, when the sprite is done it will erase the bitmap.
property pMember
on beginSprite me
-- make new member
pMember = new(#bitmap)
-- copy image to member
pMember.image = sprite(me.spriteNum).member.image
-- set the sprite to this new member
sprite(me.spriteNum).member = pMember
end
on endSprite me
-- sprite done, get rid of the evidence
erase pMember
end
The advantage of this technique over using just a bitmap to begin with, is that you don't have to create and store a bitmap in your movie. This should cut down on file size. Plus, it might make it easier for a programmer to work with a Flash artist. When the Flash artist delivers a new Flash movie, nothing has to be done other than importing it into the Director movie. The conversion happens on the fly. the other advantage, of course, is a slight speed increase. (Note: This works great in Director, but apparently it does not work at all in Shockwave! All I get, Mac and PC, is a black box instead of the Flash member's image. I tried all sorts of workarounds, like having the bitmap already on the Stage, forcing the useAlpha of the new bitmap to TRUE, etc. None of these work. If anyone finds a solution, let me know. Otherwise, the next technique will work for static images as well as animated ones).
For animated Flash movies, you can do something very similar. Instead of just converting the initial frame of the Flash member to an image, you can create a list of images from each frame in the Flash movie. A nice way to do this would be to loop through the frames of the Flash member, grab an image from each frame, store it in a list, and then replay the list of images through a single bitmap. Unfortunately, there is a problem with this. You can't tell a Flash member which frame to go to. It is always on the first frame.
But wait a minute, what about the goToFrame Flash member command? Well, that is a sprite command, not a member command. You can tell a Flash sprite to go to any frame you want, but the member will still be at frame 1.
One way around this would be to capture the image of the sprite, not the member. Unfortunately, you can't do this. But you can capture the image of the Stage. So, you can display the member on the Stage in a sprite, tell it to goToFrame for each frame, grab the image from the Stage, and build the list that way.
The major disadvantage of this is that the user will see this happening. You can find a way to mask this, either by a "rendering images..." message or by using the image capture as an intro animation.
Once the images are in a list, you can set the image of a temporary bitmap to each image in the list. To make this match the frame rate of the original Flash movie, you can use a timer to determine when the next image should be displayed. Here is the code for this behavior.
property pGotImages, pMember, pNumberOfImages
property pImages, pImageNum, pFrameRate, pNextFrame
on beginSprite me
-- can't update stage during beginSprite, so set flag to do it on first exitFrame
pGotImages = FALSE
end
on getImages me
-- save original loc, and then set sprite to middle of Stage
origLoc = sprite(me.spriteNum).loc
sprite(me.spriteNum).loc = point((the stage).rect.width/2,(the stage).rect.height/2)
-- loop through frames of Flash movie and create list of images
pImages = []
pNumberOfImages = sprite(me.spriteNum).member.frameCount
pFrameRate = sprite(me.spriteNum).member.frameRate
repeat with i = 1 to pNumberOfImages
sprite(me.spriteNum).goToFrame(i)
updateStage
image = (the stage).image
add pImages, image.crop(sprite(me.spriteNum).rect)
end repeat
-- reset location of sprite
sprite(me.spriteNum).loc = origLoc
-- create new member and set sprite to use it
pMember = new(#bitmap)
sprite(me.spriteNum).member = pMember
-- set image properties
pImageNum = 0
pNextFrame = 0
-- start showing images
showNextImage(me)
-- remember that we don't need to do this again
pGotImages = TRUE
end
on showNextImage me
-- see if it is time for next image
if the milliseconds > pNextFrame then
-- increment image, loop back if past last
pImageNum = pImageNum + 1
if pImageNum > pNumberOfImages then pImageNum = 1
-- set image
pMember.image = pImages[pImageNum]
-- set time for next image
pNextFrame = the milliseconds + 1000/pFrameRate
end if
end
on exitFrame me
if pGotImages then
-- see if time for next image
showNextImage(me)
else
-- initialize
getImages(me)
end if
end
on endSprite me
-- sprite done, get rid of the evidence
erase pMember
end
This behavior is pretty good at imitating an animated Flash movie, taking into account the frame rate and all. You can even take into account things like Flash member quality, looping, pausedAtStart, and goToFrame calls by adding additional functionality to the behavior above. I'll leave those as an exercise for the reader.
Here is a demo movie that uses this technique. It times the movement of an animated Flash member across the Stage. I used the two-frame animated biplane from the Flash library for the example.
Director 8 sample movies are available for download in Mac or PC format.
First, let's look at the results with a static image. It takes 15000 milliseconds to cross the screen, even with pausedAtStart and static properties turned on. Converting it to a bitmap first gives the result of 6000 milliseconds. Quite an improvement. This large, irregular shape shows a much bigger gap between using a bitmap and using a Flash movie than my previous red ball tests.
Now, let's try it animated. The result for the Flash movie is the same: 15000 milliseconds. That is interesting in itself. The animated Flash movie is as slow as the "static" one. So much for the static setting having much effect on a Flash movie like this. I got similar results in Shockwave on the PC.
Now, let's use the nifty behavior that converts the animation to a list of images. It takes 7000 milliseconds to cross the Stage. That's only slightly more than the non-animated version. This shows that the bitmap image swaps are taking up only a little bit of time. This is good news, as we now have a way to double the speed of this Flash animation.
Better yet, it is a nice, neat way of speeding up Flash -- no leftover members, no conversion process, just a simple behavior. We can actually do the same thing with those slow text members... but I'll save that for another week.
Gary Rosenzweig's new book "Special Edition Using Director 8" is now available. It's the most comprehensive guide to Director ever, including tons of examples and demo movies. It's suitable for novices and experts alike. More information about the book can be found at http://clevermedia.com/resources/bookstore/book5.html. It can be purchased there, or in your local bookstore.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.