MouseOver

Date:    Thu, 16 Nov 1995 03:41:32 +0000
From:    Matthew Caldwell <sexkittn@BURN.DEMON.CO.UK>
Subject: Re: "On MouseOver": an Inspiration

>  -- In a movie script:
>  on enterFrame
>    repeat with x = 48 down to 1
>      if the scoreColor of sprite x = 5 and rollover (x) then
>        set spriteCast = the castNum of sprite x
>        if the scriptText of cast spriteCast contains "on MouseOver" then mouseOver (script (spriteCast))
>      end if
>    end repeat
>  end
Well yes, but. The trouble is this actually requires 2 checks against each of the 48 sprites rather than only 1 as is the case with a simple rollover scan. So while it provides an additional layer of filtering -- you can set visibly in the score which sprites it'll care about -- it's actually slower (though probably not massively) than the usual brute force method. (As an aside, lazy evaluation would probably be a desirable development for Lingo...)

So, what's the alternative? Here, as everywhere, lists have a part to play. For instance, if you have a setup frame followed by a loop frame (hardly a revolutionary approach, after all), the setup can compile a list of the red-coloured sprites and the loop can then check only those:

  -- compile script
  on enterFrame
    global gActiveSprites
    set gActiveSprites = []
    repeat with index = 48 down to 1
      if the scoreColor of sprite index = 5 then
        if the scriptText of cast (the castNum of sprite index)...
        contains "on mouseover" then
          add gActiveSprites, index
        end if
      end if
    end repeat
  end enterFrame
at this stage, gActiveSprites contains only a list of sprites, in front to back order, that are coloured red in the score and accept a mouseover message (though remember what JD pointed out re the scriptText check -- a no go in projectors or dxrs). The actual checking on the next frame can then be reduced to:
  -- looping rollover check
  on enterFrame
    global gActiveSprites
    repeat with spr in gActiveSprites
      if rollover(spr) then mouseOver(script (the castNum of sprite spr))
    end repeat
  end enterFrame
This is still not ideal, of course, because as you loop the sprite will get repeated mouseOver messages as long as the mouse stays over it. You may well also only want the topmost rollovered sprite to get a message, rather than all of them (its not always so, but often). What you really need are mouseEnter and mouseExit messages, so the sprite only needs to act when its state changes.

Let's assume, then, that all the active sprites coloured red in the score and containing mouseEnter and mouseExit handlers have been collected in a previous frame just as above. The modified loop frame handler might look something like this:

  on enterFrame
    global gActiveSprites, gLastWithin

    if getOne(gActiveSprites, gLastWithin) then
      if rollover(gLastWithin) then return
      mouseExit(gLastWithin)
    end if
    set gLastWithin = 0

    repeat with spr in gActiveSprites
      if rollover(spr) then
        mouseEnter(script (the castNum of sprite spr))
        set gLastWithin = spr
        return
      end if
    end repeat

  end enterFrame
Obviously, this script's longer than the previous one, but much of the time it will actually have to do less. When the mouse is over the same sprite as it was last time, the script looks no further. Only when it changes state does it bother with the full scan. (Well, ok, this is not actually true: when the mouse is over an inactive sprite, or no sprite at all, it has to be checked every loop. To avoid this, place an "active" -- but inert -- sprite in channel 1, covering the whole stage. The script will then always be able to bail out if the rollover hasn't changed.)

There's loads more, but it's nearly 3am so I guess I'll stop...