Macromedia Director: Stepframe and Me

Previously I blogged about using Stepframe instead of EnterFrame with sprites and I discussed the why and how of making it work.

After using it a bit I have an update to explain a tick to using it.

The trick is to make sure that when adding to the Actorlist, that “me” is a behavior instance. Check this out. When a behavior is started, “me” is a behavior instance:

On Beginsprite me
    put me
end

result:
-- < offspring "behaviorName" 4 307f148>

While the numbers at the end may vary, that result tells you that “me” is an instance of the behavior called “behaviorName”. If you’re not used to working with instance variables like this, then you should read up on them because they are pretty handy.

Likewise, in a MouseUp() function you’ll get the same result:

on MouseUp me
    put me
end

result:
-- < offspring "behaviorName" 4 307f148>
However, if you call a function in a sprite’s behavior from another script, you have to look out. I often call behavior functions inside of sprites externally like so:
--  From code in sprite 1:

on mouseup me
    sprite(5).startfade()
end

-- From code in sprite 5:

on startfade me
    put me
end

result:
-- (sprite 5)

Notice that the value “me” changed. It’s not an instance (AKA offspring), it’s a sprite object. And because it’s a sprite object, it behaves differently if it’s added to The Actorlist. Specifically, if you add a sprite object to the actorlist, then every behavior attached to the sprite will get a Stepframe message every single frame. However, if the behavior’s instance is added to Actorlist, only that behavior will get it’s StepFrame handler called.

Having all the behaviors called really only becomes a problem if you’ve got two or more behaviors on one sprite that both want to use StepFrame, because if one behavior adds a sprite object to the Actorlist, other behaviors will start getting StepFrame events when they’re not expecting it. In fact, if two behaviors on one sprite both add sprite objects to the Actorlist, each behavior will get their Stepframe handler called twice per frame. Once that happens, it’s possible for really strange things to happen, which I’ll explain below. But for now, let’s take a look at how I fixed it.

How to solve the problem


I solved the problem by saving my behavior instance for use later, like so:

property MeObj

on beginsprite me
    MeObj = me
end

on startfade me
    -- Notice here I'm using "MeObj" instead of "Me"
    add the actorlist, MeObj
end

on stopfade me
     deleteone the actorlist, MeObj
end

What’s happening above is that in Beginsprite I’m storing “me” in the property MeObj. I’m doing this here because we know that when Beginsprite is first called, “me” is the behavior instance, and not a sprite object.

Later, in functions that may or may not be called externally, I use MeObj instead of Me to add to the Actorlist. This insures that the behavior instance is being added and not the sprite object. Likewise, MeObj is used when deleting from the Actorlist as well. Using this technique, the only behaviors that add themselves to the Actorlist will actually have their StepFrame handlers called.

How the problem surfaced for me


I had two different behaviors on one sprite that both used Stepframe. Each one would keep a local property called InActivated to track when it had added itself to the Actorlist so that it was not possible for the behavior to add itself multiple times. However, I was not using the MeObj technique described above, and I ran into the following problem.

  • sprite(1)’s behaviorA adds (sprite 1) to the actorlist because it is called from another sprite, and turns it’s IsActivated property on.
  • sprite(1)’s behaviorB is triggered seperately and adds < offspring "behaviorb" > to the actorlist and turns it’s own IsActivated property on.
  • sprite(1)’s behaviorA gets the Stepframe event where me = (sprite 1) and it works as expected.
  • Because (sprite 1) being in the Actorlist causes all the behaviors to get a StepFrame event, sprite(1)’s behaviorB gets the Stepframe event where me = (sprite 1).
  • BehaviorB is done, so it calls “deleteone the actorlist, me” and clears his IsActivated property. Except Me does not equal < offspring "behaviorb">, it equals (sprite 1).

The end result is that < offspring "behaviorB" > is still in the actorlist even though the behaviorB instance thinks it’s done (it’s IsActivated property is cleared), and behaviorA has stopped getting stepframe events even though it’s not done (and still has it’s IsActivated property set).

It’s very complex to be sure, and it took me a bit of debugging and carefully studying all the values involved before I cound figure out what was going on.


One Response to “Macromedia Director: Stepframe and Me”

  1. James Newton Says:

    Use sendSprite(5, #startFade) instead, and the problem will go away.

    If the sprite has no on startFade handler, sprite(5).startFade() will cause a script error, but sendSprite(5, #startFade) will fail silently.

Leave a Reply

For security, enter the word TURING below:
Comments RSS feed