Help: Play MovieClip at variable speed using setInterval()
I'm trying to play through a MovieClip frame by frame using setInterval. Overall I'm trying to imitate speeding up and slowing down of the MovieClip by changing the setInterval time.
Basically, when a user moves the Slider the MovieClip needs to speed up or slow down based on the Slider value.
EDIT: The code above works to a certain extent. As in the slider updates updateClip() and the MovieClip does actually play at one speed. But the MovieClip does not play at the speed variable value. If I remove the ClearInterval() the MovieClip just plays at one speed but then do开发者_运维知识库ubles when the Slider is changed. What I'm looking for is the MovieClip to play at the same rate as speed value.
Sorry, if I'm not making sense. I'm not much of a Flash guy.
EDIT: Final Version Update (Thanks for the great help everyone)
// Import classes
import fl.controls.Slider;
import fl.controls.SliderDirection;
import fl.events.SliderEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
// Set variables and types
var slow:Number=0;
var speed:Number=0;
var update:Number=0;
var frame:Number=0;
var timer:Timer=null;
// Add event listener to slider
hand_control.addEventListener(SliderEvent.CHANGE, updateClip);
function updateClip(e:SliderEvent):void {
// Set frame to slider value
frame=e.target.value;
// Update hanle movie clip
handle_motion_small_mc.gotoAndStop(frame);
handle_motion_large_mc.gotoAndStop(frame);
// Set timer if not set
if (timer==null) {
setTimer();
trace('Timer is null');
}
// Invert slider value
update=70-frame;
// Output values for debugging
trace('Actual Speed: ' + update);
trace('Chosen Speed: ' + frame);
}
// Set and start timer
function setTimer() {
timer=new Timer(update,1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, updateGrass);
timer.start();
}
// Update grass movie speed
function updateGrass( ev:TimerEvent ) {
var current:Number=grass_mc.currentFrame+1;
grass_mc.gotoAndStop(current);
setTimer();
}
It's fine as long as it works. I do have a couple of suggestions that might help:
- You might want to use a Timer instead of setInterval, as it gives you a few handy extra options.
- Try to stay away from copy and paste code, follow DRY.
Let's break this down into it's parts: - the original frame rate (frames per second ratio ) - an arbitrary movie clip (which has n frames) - a variable to update playback
The idea is simple, you have to update the current frame of your movie clip based on a variable. You can either use that variable to control the update frequency(your interval), or to control a variable which keeps track of your dynamic frame.
Here's an example on how you would control the time/interval:
var metro:Number = 1000/stage.frameRate;//'metronome' - convert fps to millis
slider.addEventListener(Event.CHANGE, sliderUpdate);//slider values must be > 0 (e.g. 0.01)
var updater:Timer = new Timer(metro);
updater.addEventListener(TimerEvent.TIMER, frameUpdate);
updater.start();
function frameUpdate(event:TimerEvent):void {
clip.gotoAndStop(updater.currentCount%clip.totalFrames);//loop at total frames (a frame variable + if(frame > totalFrame) might be faster
}
function sliderUpdate(event:Event):void{
updater.delay = metro / slider.value;
}
So, the movie clips doesn't get updated all the time, you control that with a variable updated by the slider. Notice that the frame rate is converted from the 'frame measure unit' to milliseconds.
Here is a modified version which takes into account negative and zero values for the slider:
var metro:Number = 1000/stage.frameRate;
slider.addEventListener(Event.CHANGE, sliderUpdate);
var updater:Timer = new Timer(metro);
updater.addEventListener(TimerEvent.TIMER, frameUpdate);
updater.start();
function frameUpdate(event:TimerEvent):void {//if it's negative value - go backwards/subtract from total
clip.gotoAndStop(slider.value < 0 ? (clip.totalFrames-updater.currentCount%clip.totalFrames) : updater.currentCount%clip.totalFrames);
}
function sliderUpdate(event:Event):void{//watchout for 0 value
updater.delay = metro / Math.abs(slider.value == 0 ? 0.001 : slider.value);
}
With this approach, you would update your clip independent of any other enterframe loop you might have.
Here is a rough example of how you update based on frame, rather than time:
var frame:Number = 0,roundFrame:Number;
this.addEventListener(Event.ENTER_FRAME, frameUpdate);
function frameUpdate(event:Event):void {
frame += slider.value;
roundFrame = Math.floor(Math.abs(frame))%clip.totalFrames;
if(slider.value > 0) clip.gotoAndStop(roundFrame);
else clip.gotoAndStop(clip.totalFrames-roundFrame);
}
HTH
When working with ActionScript 3, you should use the Timer class instead of setInterval.
// add imports
import flash.utils.Timer;
import flash.events.TimerEvent;
// add to variable declarations
var timer : Timer = null;
// this goes instead of clearInterval and setInterval, after you've set "speed"
if (timer == null) setTimer();
// and here is the function
function setTimer()
{
timer = new Timer (speed, 1);
timer.addListener (TimerEvent.TIMER_COMPLETE, updateGrass);
timer.start();
}
// and the updateGrass method
function updateGrass( ev:TimerEvent )
{
if (grass_mc.currentFrame>249) grass_mc.gotoAndStop(1);
else grass_mc.nextFrame();
setTimer();
}
Timer is terrible idea for time intervals less than hundreds of milliseconds. I would use ENTER_FRAME
event and getTimer
to get current time in milliseconds, then control clip manually with stop()
and nextFrame()
.
I agree with alxx -- using Timer seems classy, but I think an ENTER_FRAME approach is better, especially if you want to run your MovieClip faster than the SWF's frame rate. I wrote a blog post about how I did it: http://www.eqsim.com/blog/?p=329.
精彩评论