Contents
Articles
Behaviors
Books
Director News
Director Web Sites
FAQ
Games
Mailing Lists
News Groups
Project Examples
Reviews
Software
Tools
Useful Web Sites
Utilities
Xtras

Don't miss these
MediaPlayer Xtra
Replace returns with PC line breaks
Popup Text List Menu
Accept Only Alpha-Numeric Characters
Scriptzorz
GetPixelColour
DirectSMS Xtra
Easy Help
MouseWheel
Amara Flash Photo Slideshow software
 

 

 

Behavior Irregular Slider

Added on 6/10/1999

 

Compatibilities:
behavior D6_5 Mac PC

This item has not yet been rated

Author: JohnDowdell

Irregular Slider Slider behavior that can follow a curve path and do various tasks.

property spriteNum              --  The usual.
property myLocs                 --  The list of all slider positions.
property myCurrentPos           --  The current position in this list.
property myCurrentValue         --  The normalized value of the slider.
property myMin, myMax           --  The low and high slider values.
property myNotificationStyle    --  Set by BI. How often are updates broadcast?
property myNotificationTarget   --  Set by BI. Where do these messages go?
property myVideoTarget          --  Optional property, when controlling video.
property myTempoChange          --  Optional property, when controlling tempo.




--===========  PUBLIC METHODS  =========================
--
--  The following three methods are those that interact with
--  the outside world.
--
--  BroadcastNewValue is called internally and dispatches appropriate
--  messages outward.
--
--  IrregularSlider_GetCurrentValue and _SetCurrentValue will return and
--  assign the slider's current value, respectively. They use the
--  BehaviorName_MethodName convention to avoid namespace collisions with
--  other behaviors.
--
--  IrregularSlider_GetCurrentValue: Call to retrieve slider's current value.
--  IrregularSlider_SetCurrentValue: Call to assign slider's current value.
--  IrregularSlider_NewValue: Handle for custom events in movie or score script.


on BroadcastNewValue me
  --  You can automatically notify other behaviors when the slider
  --  value changes. This choice is set by the getPropDesc dialog,
  --  and is not changed by calls from the outside world. This handler
  --  does affect the outside world, however.
  --
  --  Note the "IrregularSlider_NewValue" message which can be sent
  --  out to movie scripts, frame scripts, or other behaviors. If
  --  choosing such a recipient for this message then you'd need to
  --  write a handler of this name in the movie or score script.
  
  case (myNotificationTarget) of
    #movieScript: IrregularSlider_NewValue myCurrentValue
    #allSprites:  sendAllSprites #IrregularSlider_NewValue, myCurrentValue
    #frameScript: sendSprite -1, #IrregularSlider_NewValue, myCurrentValue
    #tempo:       set myTempoChange to integer(myCurrentValue)
    #soundLevel:  set the soundLevel to integer(myCurrentValue)
    #sound1:      set the volume of sound 1 to myCurrentValue
    #sound2:      set the volume of sound 2 to myCurrentValue
    #videoSpeed:  set the movieRate of sprite myVideoTarget to myCurrentValue
    #videoTime:   set the movieTime of sprite myVideoTarget to myCurrentValue
    #null:        nothing
    otherwise:    Error me, #badNotificationTarget
  end case
end


on IrregularSlider_GetCurrentValue me, collectionList
  --     Other behaviors can query this sprite's current value
  --  through this exposed method.
  --     Note the BehaviorName_BehaviorMethod syntax to avoid
  --  namespace collisions with broadcast methods.
  --     The sendAllSprites or sendSprite or call commands work
  --  upon multiple objects and so a regular "return" value
  --  will not be available. Instead we pass in a reference to a
  --  list object, and the called behavior adds its desired value
  --  to this list for ready retrieval by the calling behavior.
  --     Example:
  --       set theList to []
  --       sendAllSprites #IrregularSlider_GetCurrentValue, theList
  --       set theNewValue to getAt(theList, 1)
  
  add collectionList, myCurrentValue
end


on IrregularSlider_SetCurrentValue me, newValue
  --     Other behaviors can set the slider's value through
  --  this exposed method. The range of newValue must be between
  --  zero and one. It automatically changes the slider's position
  --  and then changes its controlled value.
  --     Example:
  --       sendAllSprites #IrregularSlider_SetCurrentValue, 0.75
  
  if min(max(0, newValue), 1) <> newValue then Error me, #setValueOutofBounds
  set myCurrentPos to integer(newValue * count(myLocs))
  set the loc of sprite spriteNum to getAt(myLocs, myCurrentPos)
  set myCurrentValue to myMin + (myMax - myMin) * newValue
  BroadcastNewValue(me)
end








--==================  STANDARD EVENTS  ===================
--
--  beginSprite, exitFrame, mouseDown, stepFrame

on beginSprite me
  
  InitProperties me
  
  --  Get list of stored slider positions from the castmember
  --  script of the slider sprite:
  if the scriptText of the member of sprite spriteNum = "" then Error(me, #noData)
  set myLocs to GetInitialData(script (the member of sprite spriteNum))
  if myLocs = void then Error(me, #noData)
  
  --  This handling assumes that the slider starts at its minimum value.
  --  Scripters may wish to change the following three lines:
  set myCurrentPos to 1
  set the loc of sprite spriteNum to getAt(myLocs, 1)
  set myCurrentValue to myMin
  
end


on exitFrame me
  --  This is an odd handler. The only time it's needed is if
  --  setting the tempo. From what I've seen we cannot issue a
  --  puppetTempo from within a stepFrame handler. Instead, the
  --  stepFrame sets the 'myTempoChange' property, and then this
  --  exitFrame handler will later act upon this instruction.
  
  if voidP(myTempoChange) then exit
  if the frameTempo <> myTempoChange then puppetTempo myTempoChange
  set myTempoChange to VOID
end


on mouseDown me
  --  This is a common way to handle asynchronous operations...
  --  once the mouse is clicked then this behavior starts to receive
  --  regular stepFrame events until the mouse is released. This
  --  approach lets other animations continue in the background, etc.
  
  add the actorList, me
end


on stepFrame me
  
  if the mouseUp then
    deleteOne(the actorList, me)
    if myNotificationStyle = #notifyonMouseUp then BroadcastNewValue(me)
  end if
  
  set mouseLoc to point(the mouseH, the mouseV)
  set currentDistance to GetDistance(mouseLoc, getAt(myLocs, myCurrentPos))
  set tempPos to myCurrentPos
  
  --  Find the nearby slider position which is closest to the mouse:
  if myCurrentPos > 1 then set leftPos to myCurrentPos - 1
  else set leftPos to myCurrentPos
  if myCurrentPos < count(myLocs) then set rightPos to myCurrentPos + 1
  else set rightPos to myCurrentPos
  
  if GetDistance(mouseLoc, getAt(myLocs, leftPos)) < currentDistance then
    set myCurrentPos to leftPos
  else if GetDistance(mouseLoc, getAt(myLocs, rightPos)) < currentDistance then
    set myCurrentPos to rightPos
  end if
  
  set the loc of sprite spriteNum to getAt(myLocs, myCurrentPos)
  
  set currentRatio to (float(myCurrentPos - 1) / (count(myLocs) - 1))
  set myCurrentValue to myMin + (myMax - myMin) * currentRatio
  if myNotificationStyle = #notifyDuringDrag then BroadcastNewValue(me)
end




--================  SUBROUTINES  ==================
--
--  GetDistance, InitProperties, RecordSpriteLocs, FindFirstVideoChannel, GetInitialData.


on GetDistance ptA, ptB
  --  Utility routine. Given two points, returns the square
  --  of the distance between them. (sqrt calcs are avoided
  --  for speed reasons... gives relative magnitude only.)
  --  Called by stepFrame method.
  
  set deltaH to the locH of ptA - the locH of ptB
  set deltaV to the locV of ptA - the locV of ptB
  return deltaH * deltaH + deltaV * deltaV
end


on InitProperties me
  --  Routine to turn string-based configurable properties to
  --  faster-evaluating symbols. Some believe that input values should
  --  never be changed, but this sure beats having duplicate sets of
  --  properties. Called by beginSprite method.
  
  case (myNotificationStyle) of
    "Notify during drag":      set myNotificationStyle to #notifyDuringDrag
    "Notify on mouseUp":       set myNotificationStyle to #notifyonMouseUp
    "Don't notify; will call": set myNotificationStyle to #null    
  end case  
  
  case (myNotificationTarget) of
    "Notify movie script":     set myNotificationTarget to #movieScript
    "Notify all sprites":      set myNotificationTarget to #allSprites
    "Notify frame script":     set myNotificationTarget to #frameScript
    "Change Tempo":            set myNotificationTarget to #tempo  
    "Change the sound level":  set myNotificationTarget to #soundLevel
    "Adjust sound channel 1":  set myNotificationTarget to #sound1
    "Adjust sound channel 2":  set myNotificationTarget to #sound2
    "No target; will call":    set myNotificationTarget to #null
    "Control video's speed":   set myNotificationTarget to #videoSpeed
    "Control video's time":    set myNotificationTarget to #videoTime
    otherwise: put "bad notification target: " & myNotificationTarget
  end case
  
  --  Finally set special-case min/max values and other initializations:
  case (myNotificationTarget) of
    #soundLevel:
      set myMin to 0
      set myMax to 7
    #sound1, #sound2:
      set myMin to 0
      set myMax to 255
    #videoSpeed:
      set myVideoTarget to FindFirstVideoChannel()
    #videoTime:
      set myVideoTarget to FindFirstVideoChannel()
      set myMin to 0
      set myMax to the duration of member (the member of sprite myVideoTarget)
  end case
end


on RecordSpriteLocs me
  --  Routine to record all locations of the current sprite over its
  --  lifetime, and then to store this data in a function in the
  --  castmember script for ready retrieval at runtime.
  --  Called by getPropertyDescriptionList method.
  
  if count(the scoreSelection) < 1 then exit
  if count(the scoreSelection) > 1 then Error me, #multipleSprites
  set theSelection to getAt(the scoreSelection, 1)
  set spriteNum to getAt(theSelection, 1)
  set firstFrame to getAt(theSelection, 3)
  set lastFrame to getAt(theSelection, 4)
  set origFrame to the frame
  
  --  March through the Score and record slider positions:
  go firstFrame
  set myFirstMember to the member of sprite spriteNum
  set theLocs to []
  repeat while the frame < lastFrame
    add theLocs, the loc of sprite spriteNum
    go the frame + 1
  end repeat
  
  --  Write these sprite positions to the castmember script for storage.
  --  First check that there is actually a script to call.
  --  Note that scripts that cannot handle a "GetInitialData" message will
  --  pass it back to this script's own void-returning handler, below.
  if the scriptText of myFirstMember = "" then set the scriptText of myFirstMember to " "
  set oldData to GetInitialData(script myFirstMember)
  set theScript to "on GetInitialData" & RETURN
  put "return " & theLocs & RETURN after theScript
  put "end" & RETURN & RETURN after theScript
  
  --  The following stores the previous position data as a comment within
  --  the member script. You could instead set this routine up so that it
  --  adds "return newData" at line 2 of the scriptText, giving you a
  --  (potentially very long) history list of editing changes.
  put "--  Previous data: " & oldData & RETURN & RETURN after theScript
  set the scriptText of myFirstMember to theScript
  
  go origFrame
end


on FindFirstVideoChannel
  --  Find if there are any videos in the current frame.
  --  Return the channel if so, or FALSE if not.
  --  Called by getPropDescList and InitProperties.
  --
  --  (I believe that in later versions of Director you may be able to
  --   replace the "string(member)" test with "objectP(member)", although
  --   I've not tested this phrase across all current versions of
  --   Director yet, myself.)
  
  repeat with i = 1 to 120
    if string(the member of sprite i) <> "(member 0 of castLib 0)" then
      if the type of the member of sprite i = #digitalVideo then return i
    end if
  end repeat
  return FALSE
end


on GetInitialData me
  --  The beginSprite method calls a GetInitialData function in
  --  the castmember script. If for some reason that script has
  --  been altered since it was created then an error can result.
  --  By having a GetInitialData handler in this calling script
  --  here, we'll fall back to this routine if the castmember
  --  script cannot handle this message. The beginSprite method
  --  will test for whether it receives the following VOID result.
  --  Tricky, but it eliminates one way to achieve an error.
  
  return VOID
end







--===================   STANDARD BEHAVIOR FUNCTIONS  ================
--
--  getPropDescList, getBehaviorDescription, Error


on getPropertyDescriptionList me
  if the currentSpriteNum > 0 then
    RecordSpriteLocs me
    set videoChan to FindFirstVideoChannel()
    
    set theProps to [:]
    
    set c to "What to do when slider changes?"
    set f to #string
    set r to ["Notify movie script", "Notify all sprites", "Notify frame script", "Change Tempo", "Change the sound level", "Adjust sound channel 1", "Adjust sound channel 2", "No target; will call"]
    if videoChan <> 0 then
      add r, "Control video's time"
      add r, "Control video's speed"
    end if
    set d to "Notify all sprites"
    addProp theProps, #myNotificationTarget, [#comment:c, #format:f, #range:r, #default:d]
    
    set c to "When to notify?"
    set f to #string
    set r to ["Notify during drag", "Notify on mouseUp", "Don't notify; will call"]
    set d to "Notify during drag"
    addProp theProps, #myNotificationStyle, [#comment:c, #format:f, #range:r, #default:d]
    
    set c to "What's the minimum value for this slider?"
    set f to #float
    set d to 0.0
    addProp theProps, #myMin, [#comment: c, #format:f, #default:d]
    
    set c to "What's the maximum value for this slider?"
    set f to #float
    set d to 1.0
    addProp theProps, #myMax, [#comment: c, #format:f, #default:d]
    
    return theProps
    
  end if
end


on getBehaviorDescription me
  set line1 to "   This is a generic slider routine. It is self-contained and does not rely on other sprites. "
  set line2 to "You can use it for slider paths which are not straight lines... works well for curved slider paths, circles, etc." & RETURN
  set line3 to "   You can have this behavior send messages to other sprites, or to the frame script, or to the movie script when the slider is dragged or released. "
  set line4 to "You can also use it to control the overall sound level, the volumes of the first two channels, or the video speed or time of another video in the Score. "
  set line5 to "Or you could have the behavior not send any messages at all, but instead have other behaviors call into this slider to request its current value." & RETURN & RETURN
  set line6 to "TO USE:" & RETURN
  set line7 to "   Animate your slider in the Score along the path you wish to drag it. The more frames you animate over, the smoother-yet-slower the drag. The animation path will be stored and will determine the path of the drag." & RETURN
  set line8 to "   Apply the behavior. You'll see the sprite animate along its path. This data will then be stored in the sprite's castmember script. (Open up the castmember script to see how the slider positions are stored.)" & RETURN
  set line9 to "   At this point you can choose your options from the Parameter dialog. The top popup describes what happens, the second describes when it happens, and the two lower fields are optional for those uses where you need to set a minimum and maximum value for the slider. (Won't need it for video scrubbing or soundlevel, for instance.)" & RETURN & RETURN
  set line10 to "Tip: The initial number of frames of the slider's animation will determine how smoothly the slider moves... the more steps, the slower. Each time you click the Parameters Dialog in the Behavior Inspector then this list of sprite positions will be recalculated. "
  set line11 to "The previous values will still be stored in the script too... if you open the slider's castmember script you'll see them commented out beneath the main handler. You can replace these back into the function if you wish. " & RETURN
  set line12 to "   Once you've recorded the slider positions you can drag the slider out over an arbitrary period of time... the rhythm is to determine the number of steps, configure the behavior, and then drag out the slider sprite so it's on Stage as long as you wish." & RETURN & RETURN
  set line13 to "(Note that the options to control video will only appear if there is a video sprite in the frame in which you assign and configure this behavior. Also note that each sliding castmember can be used for only one path, because path data is stored in the member script.)" & RETURN
  
  return line1 & line2 & line3 & line4 & line5 & line6 & line7 & line8 & line9 & line10 & line11 & line12 & line13
  
end


on Error me, whatError
  --  This is just a regular error-handling routine. By calling it
  --  you can keep all the alert messages in one place, and reduce
  --  clutter within the scripts themselves.
  
  case (whatError) of
    #noData:    
      alert "The sprite in channel " & spriteNum & " seems to have changed since it was initialized... you may wish to reset its slider behavior."
    #multipleSprites:
      alert "This behavior can be applied to only one sprite at a time."
    #badNotificationTarget:
      alert "Scripting error -- I don't know how to handle an event sent to " & myNotificationTarget
    #setValueOutofBounds:
      alert "Error when trying to set slider value from outside handler."
    otherwise:
      alert "Unknown error: " & whatError
  end case
  halt  
end  

 


Contact

MMI
36 South Court Sq
Suite 300
Newnan, GA 30263
USA

Send e-mail