FRAME EVENTS

Event handlers called every frame in a Director movie

Every frame Director calls an number of handlers. To avoid script errors the order in which these handlers in called is important.

WhereHandlerWhen
movieprepareMovie()First frame of movie
framenew() + beginSprite()First frame of sprite
sprite(1..n)new() + beginSprite()First frame of sprite
actorList[1..n]stepFrame()Every frame and updateStage()
sprite(1..n)prepareFrame()Every frame
frameprepareFrame()Every frame
After display is updated
moviestartMovie()First frame of movie
sprite(1..n)enterFrame()Every frame
frameenterFrame()Every frame
frameidle()Every frame
sprite(1..n)mouseWithin()Every frame when mouse is over sprite
sprite(1..n)exitFrame()Every frame
frameexitFrame()Every frame
moviestopMovie()Last frame of movie
frameendSprite()Last frame of sprite
sprite(1..n)endSprite()Last frame of sprite
enterFrame()

It is better to use the enterFrame() handler to modify sprites than the prepareFrame() handler. The reason is that it is about 10% faster. I suspect that code in stepFrame() handlers is also slower than the enterFrame() handler but I have not tested it.

The drawback of the enterFrame() handler is that the display is updated before this hanlder is called. Normally this is only a problem in the first frame of a sprite. The problem can be fixed by calling the enterFrame() handler from the beginSprite() handler. As illustrated here:

on beginSprite me
  me.enterFrame()
end

on enterFrame me
  -- Animate
end

There is one problem with this method. Sprites with a higher number than the current sprite may not be fully initialized when enterFrame() is called from beginSprite(). This can be fixed by moving the call to enterFrame() from beginSprite() to prepareFrame(). As illustrated here:

property pInitialized

on prepareFrame me
  if not pInitialized then
    pInitialized = TRUE
    me.enterFrame()
  end if
end

on enterFrame me
  -- Animate
end

Since prepareFrame() is called every frame this code run a little bit slower than calling enterFrame() from beginSprite(). Normally this will not be any problem at all.

mouseWithin()

This handler is called every frame as long as the mouse is over the sprite. If a sprite is only animating in this condition it is a good idea to move code from enterFrame() to mouseWithin().

stepFrame()

The stepFrame() handler was added to Director a very long time ago and it is not used a lot anymore. The prepareFrame() and enterFrame() handlers are used instead. The stepFrame() handler can be used to optimize the speed of Director movies by removing unnessary calls to prepareFrame() and enterFrame(). In this example stepFrame() is only called when the sprite is animating.

This optimazation benefits movies with many behaviors in the same frame, but is not relevant if there is only a few behaviors in the same frame.

property pChannel
property pInside

on beginSprite me
  pChannel = sprite(me.spriteNum)
end

on endSprite me
  removeStepFrame(me)
end

on stepFrame me
  if pInside then
    pChannel.blendLevel = max(pChannel.blendLevel - 10, 0)
    if pChannel.blendLevel = 0 then removeStepFrame(me)
  else
    pChannel.blendLevel = min(pChannel.blendLevel + 10, 255)
    if pChannel.blendLevel = 255 then removeStepFrame(me)
  end if
end

on mouseInside me
  pInside = TRUE
  addStepFrame(me)
end

on mouseOutside me
  pInside = FALSE
  addStepFrame(me)
end

on addStepFrame me
  -- Modifying the actorList inside a stepFrame() handler has some bad side effects. There
  -- is a work around this this, which is documented elsewhere.
  if not (the actorList).getPos(me) then (the actorList).add(me)
end

on removeStepFrame me
  -- Modifying the actorList inside a stepFrame() handler has some bad side effects. There
  -- is a work around this this, which is documented elsewhere.
  (the actorList).deleteOne(me)
end
stopMovie()

The stopMovie() handler is not the last handler called when a movie stop. First the stopMovie() handler is called and then the endSprite() handlers for behaviors is called. So you must careful not to have code in the stopMovie() handler, that will cause code in the endSprite() handlers to fail.

on stopMovie
  -- Send endSprite() events
  sendSprite(0, #endSprite)
  sendAllSprites(#endSprite)
  -- Remove behaviors from all sprites, to prevent endSprite() events later
  sprite(0).scriptInstanceList = []  
  repeat with c = 1 to the lastChannel
    sprite(c).scriptInstanceList = []  
    sprite(c).visible = TRUE
    sprite(c).puppet = FALSE
  end repeat
  -- Clear the actorList
  the actorList = []
  -- Call new stopMovie handler
  newStopMovie()
  -- Prevent code in calling handler to be executed. The may fix the bug
  -- described here www.killingmoon.com/director/bugs/quit_projector
  abort()
end

on newStopMovie
  -- Add clean up here...
end