Guide to Using TrackThemColors Xtra

(using video frame analysis to detect presence, motion, etc)

Professor Stephen Wilson, Conceptual Information Arts Program, Art Department, San Francisco State University


There is growing interest in using image analysis to extract information from video frames.  This techniques is used for purposes such as identification of objects, identification of persons, motion detection, gesture recognition, pattern detection, etc.  DARPA (the military's research wing) has put major money into video analysis research.  Interface researchers are especially interested using the technique to allow more freedom in the way persons can interact with their computers.  Artists are interested in using the technique to explore cultural commentary, embodiment, games, etc.

TrackThemColors is an xtra for Macromedia Director that adds some rudimentary video scene analysis capabilities.  (xtras are plug-ins that add functionality to Director.) It allows the creation of Director movies that incorporate motion, color, and pattern tracking.  It also adds the ability to incorporate a live video feed as one of cast members.  This is a step-by-step guide to using the xtra.  Note that this is an attempt to present selected features of the TTC xtra. Some features are not illustrated. There is an annotated demo director movie available.  Here are some links to add additional information

How does it work?

For computers all graphics are pixels on the screen.  The pixels visually present values stored in memory.  With graphics drawing programs such as Illustrator, Photoshop, or Painter, these values are put in memory (and on the screen) by a user's drawing actions.  Pick the color red and move the mouse across the screen and the program takes care of representing the line and storing values in memory.  When you scan or attach a camera to a computer, special hardware and software takes what the camera 'sees' and converts it into pixel values on the screen to represent the colors and intensities of what the camera sees.  Each pixel is assigned a value to match as closely as possible what the camera saw.  It as though someone has drawn each pixel to match what is perceived in the scene.  With a video feed the computer changes the pixel values 30 times a second to represent the changing scene seen by the video camera.  By reading changes in those pixel values, software can derive information about the scene.  Here are some of the characteristics that can be detected.

Using the TrackThemColorsPro Xtra

**Note - this is a quick summary.  Read the TrackThemColors readme documentation for full details.

There are several stages in preparing an installation based on motion detection that uses the TrackThemColors Xtra for Director.

  1. I Physical arrangement of space and camera
  2. II Placement of Xtra in Director and Initialization
  3. III Basic setup of xtra subroutine to read characteristic of scene and definition of hot spots
  4. IV Set up of Idle routine to continually check for changes; set flags to enable work without TTC; see if hot spots entered
  5. V Set up of alternative mouse routine to use for testing
  6. VI Set up of routines to respond to motion
  7. VII Problems/Variations/Adjustments

I  Physical arrangement of space and camera

Connect the Video:  Add a video camera to your computer.  Most contemporary Macintoshes will accept firewire and USB cameras.  USB cameras will often require special 'driver' software.  Usually it comes on cd with the camera.  This adds an extension to the operating system that allows it to use the camera.  Different cameras require different drives.  With the addition of special interface box or PCI card, Macintoshes can accept regular analog video.  A special driver is also required for these hardware interfaces.  Track them colors will not work if it does not detect a video signal.

Checking the video signal:  Before you run any software you authored, you want to check whether there is a video signal.  TrackThemColors will not run without a video signal.  It is pretty good on connecting to video signals using the built in operating system functions.  Run the trackthemcolors demo program first or any other program that reads the video signal (such as Premiere or Final Cut). If everything is working it will show a live feed of the current video signal.  Then you are ready to go.  (**Note if you use the TrackThemColors demo you must place the xtra as described below.)

Arrange the space:  Postion the camera and lighting to minimize shadows and transient changes in lighting.  Usually constant diffuse artificial lighting is the easiest to deal with.   View the space through the video signal to determine what area is in view of the camera.  Determine where the motion you are interested in shows up in the scene.

II Placement of Xtra in Director and Initialization and Cleanup

Place the Xtra:  Add the xtra to the xtra folders in the main Director folder.  When Director starts up, it loads all the xtras it can find in the special xtras folder.   There is also a way to explicitly tell Director where to find the Xtra.

Initialize the Xtra and start the video flow:  Add these routine to initialize the xtra and start the video flow.  Add them as  scripts in the movie level.   You should probably put it in the on startmovie handler so it is automatically activated when Director starts.
--The initialization routines are put in a startMovie handler
-- so they will be  activated on startup
-- the initialization routine is put in subroutine called setupTTC

-- StartMovie
-- TrackObj is the variable that will contain the reference to the xtra
-- All subsequent TrackThemColor functions will use it.
-- Flag = variable that indicates whether video tracking should be active (= 1)
-- setupTTC = routine that will intialize the TTC xtra
-- startVideo = ttc routine that starts taking in video
-- takeRef = routine that takes snapshot of video to use (see section III below)
-- initvalues = routine to set values for variables used later
-- the flag variable is explained down in section IV
-- it avoids calling the routines if ttc is not active

on StartMovie
  global flag
  if flag = 1 then
  end if

end startMovie

-- initvalues 
-- Sets global variables curevent and vidrect
-- curevent is used later to tell where there is motion and what event is active
-- vidrect is a rectangle telling how big the video image is
-- you might set other values here for your own movies
-- it is used in later routines

on initvalues
  global  curevent, vidrect
  curevent = 0
  vidrect = rect(0,0,320,240)
end initvalues

-- setupTTC
-- The "if not TrackObj" checks whether you have already initialized the xtra
-- The variable TrackObj will not have a value 
-- the if statement will end up being false 
-- and the inside of the if
-- statement then executes with the 'new' command 
-- the reference to  Xtra "TTCPro" must match the name of the file
-- the variable TrackObjthen gets set to an address where the xtra lives
-- that variable will be used in all the xtra routines
-- the name TrackObj  is arbitrary -  it can have any name you want
-- as long as you are consistent in later references

on setupTTC
  Global TrackObj 
  if not  TrackObj then
    set temp = Xtra "TTCPro"

    set TrackObj = new(temp)
  end if 
end startTTC

-- startVideo
-- This next routine sets the xtra to start using the video signal
--  It is put in a subroutine called startVideo that is called on startup
-- the xtra offers a command with parameters in the form
-- InitVideo(TrackObj, Source, SourceRect,VideoSource)
-- The TrackObj is the variable set above that references the xtra
-- Source indicates what kind of signal
-- 0 means track a quicktime or non live video;
-- 1 means usb or analog video
-- 2 means firewire or dv camera
-- SourceRect defines the resolution of the video signal by upper right
-- and lower left point coordinates
-- it should start with 0,0 for live video - eg (0,0,320,240)
-- To make it less confusing,
-- I generally make a variable to represent the video dimensions
-- using director's rect command (vidrect =rect(0,0,320,240)
-- the vidrect was set in the initialization routines
-- it must not be bigger than the video camera feed or it will get garbage
-- NOTE - your vidrect may be bigger than this example eg (0,0,640,480)
-- VideoSource indicates which camera you are using
-- if you just have one camera  it would be 1
-- if you had 2 cameras you could track both of them by using 1 and 2
-- in various different section.  I suggest to start with just one.
-- the example below shows the command in action
-- you would need to change the parameters as needed

on startVideo
  --form for InitVideo(TrackObj, Source, SourceRect,VideoSource)
  global trackObj, vidrect
  --vidrect = rect(0,0,320,240)
  InitVideo(TrackObj, 1, vidrect,1) 
end startVideo

Cleanup - Closing the Xtra and stopping the video flow:  TrackThemColors wants you to release the xtra and video flow before you call those functions again.  It can cause memory problems and even malfunctions if this is not done.  The easiest way is to put them in an on stopMovie handler.  That way they will be automatically closed when the movie is stopped and reactivated when it is started again.
-- stopMovie
-- this routine closes the xtra by setting the global variable reference = 0
-- it also stops the video tracking by calling the cleanup() function
-- the cleanup() function needs the variable trackObj reference so it should
-- be called before the xtra is zeroed out
-- if you call the new again before cleaning up you can crash the xtra or director
-- the cleanup is only called when flag = 1 -- meaning video tracking has been inited

on StopMovie
  global flag
  if flag = 1 then
    global TrackObj 
    set TrackObj = 0
  end if 


III Basic setup of xtra subroutine to read characteristics of scene and see if there are changes

Pick the function you want to use - for example, motion, color, brightness detection.  Each one has a slightly different setup although they are similar.  This will illustrate the subroutine you could use to detect motion.  TrackThemColor's routines return rectangles that indicate where it has found change (or the target color, etc.)   You must decide what to do with that information.  For example, you might see if the rectangles intersect known places in the scene (for example, location of particular props) or see if the rectangles are moving to the right or the left relative to where they were at the last reading.  The details of what you are interested in will be determined by your artistic agenda.  Director provides some built in functions to determine if rectangles intersect, etc.

These routines assume that you have used the setup routines described above in II to initialize the xtra and to start the video flow.  The reference to the xtra is put into a variable called trackobj.    One routine takes a snapshot of the reference frame (getsnap).  The other routine asks the xtra to report on any changes (checknew).  Your master routine desribed below (V) will call these routines and your other routines (VI) will respond
-- takeRef
-- this TakeRef routine sets up the reference frame
-- it sets up a search rectangle to define the active video area
-- it could be the whole rectangle of the video or some part
-- for example, if you knew all the important action was on the right
-- side of the screen, you would tell the xtra to ignore the left side
-- this makes it slightly faster and cuts out sources of possible noise
-- even if you do the whole frame, TTC documentation suggests
-- taking off 10 pixels around the edge because video often has noise there
-- it uses the vidrect global variable set in initvalues routine at start
-- the remember() function is provided by the xtra to create the ref frame
-- it takes 2 parameters - TrackObj which is the varuabke with the xtra
-- set up in the initialization routine and 
-- a number that tells it where to get the video scence - 0 means live feed
-- ohter numbers refer to cast members to use as refs
-- NOTE:  depending on the nature of your installation, it might be called 
-- once or several times.  If the reference frame never changes (for example
-- a video of the installation with no one in it, then you would call it once at
-- the beginning)
-- if there is something in the scene that is changing besides the motion
-- of the people, then you would repeatedly call it to get a snapshot
-- to compare against - for example a moving object or projected video or light change

on takeRef
  global TrackObj,vidrect 
  videoWidth = vidrect[3]
  videoHeight = vidrect[4]
  set searchRect = rect(10,10,VideoWidth-10,VideoHeight-10) 

-- checkNew
-- this checkNew routine sees if there has been change
-- and reports where the change is in the form of a list of rectangles
-- it cannot be called until a reference frame has been created (takeRef see above)
-- typically you set your script so Director continually calls
-- the routine via an idle function (see IV below)
-- GrabOneFrame (TrackObj) is the function the xtra gives to get a video
-- Another function GrabSomeFrames(TrackObj, Method, Frames) 
-- grabs several frames and does some averaging.  See docs for more info
-- TrackColorBlobs(TrackObj, BlobList) is the function that
-- analyzes and identifies where the changes are
-- the parameter TrackObj is the xtra ref used in all functions
-- BlobList is a director list of areas to track and sensitiviies
-- in the example below just one area is tracked - the whole screen
-- thus the area to track is listed as the whole screen --
-- the variable searchRect just like what was used in GrabOneFrame()
-- you could list several different areas and similarity settings
-- for example, you might just be interested in left and right borders
-- see the docs for technical info
-- Similarity  can be any number from 1 to 100; in the example below =30
-- Higher numbers indicate higher threshold, less tolerance for difference
-- Notice that the parameters must be formatted in director's list format 
-- indicated with the enclosing square brackets  [ ]
-- the xtra returns a list of rectangles where it found change 
-- in a function called the result[1]
-- the routine below puts the result into a variable called foundList 
-- the xtra will return a list of rectangles in all places it finds change
-- some of them will be noise; you will have to sort that out
-- this sample routine shows how to take the first 2 rectangles returned
-- you might want to adjust for more although often 2 is enough
-- this routine first checks if the foundlist has anything in it
-- the xtra will return empty list [ ] if there is nothing
-- the variables to store the rects (frect1 and frect2) are reset to 0
-- note that they are set as global variables so other routines can use them
-- if foundlist  is not empty, it uses  director function getat(list,1) to get the
-- first rectangle and puts it in frect1
-- if then checks if the count of lists in foundlist  is greater than 1
-- if it is, it gets the next one using getat (foundList ,2)
-- and puts it into frect2
-- these are the variables your other routines will use to run the event

on checkNew
  global TrackObj ,frect1, frect2, vidrect
  videoWidth = getat(vidrect,3)
  videoHeight = getat(vidrect,4)
  set searchRect = rect(10,10,VideoWidth-10,VideoHeight-10)

  BlobList = [40,searchRect]
  trackchangeblobs(trackobj,[BlobList] )

  -- get results
  frect1 = 0
  frect2 = 0
  set foundList = the result[1]
  if foundList <> [] then  -- is something there
    frect1 = getat (foundList 1) 
    -- check more than one
    if foundList.count > 1 then
      frect2 = getat (foundList ,2)
    end if
  end if

end checkNew


IV Set up of Idle routine to continually check for changes; set flags to enable work without TTC; see if hot spots entered

The heart of the event is a master routine that continually checks for changes and then branches to respond -- for example, playing a certain movie when people get near a particular prop and stoping when they leave.  This routine makes use of Director's ability to constantly send messages via the idle handler.  Director automatically sends an idle message to itself on a regular basis.  Your strategy is to write an on idle handler which will call the routines to see if there is any change (secition III).  That routine sends back the definition of the rectangles where there were changes.  You then need to write routines to respond to that information.  This section also shows how to set up flags to go to an alternative mouse based routine so you can work on the event even without a camera or live action video.

Notice that the flag used in the idle is also used in the startMovie handler.  If the flag is not 1 then none of the initialization routines will be called.  This is useful if you are developing the parts of the movie that don't need active tracking. (see section II above)

The materVidalt routine shows how to do something different.  Instead of seeing if people have moved into target areas it just tracks motion by putting a sprite at the place of their motion.  A showVid routine displays the current video feed on the stage when the showflag = 1.
-- idle
-- Director constantly sends idle events
-- this routine checks what interface it should use
-- if flag = 1 then it goes to the video tracking using TTC
-- that subroutine is arbitraily called 'mastervid'
-- if flag = 2 then it goes to mouse based tracking
-- that subroutine is arbitraily called 'mastermouse'
-- if flag is any other value, then idle will do nothing
-- this choice allows you to turn off video tracking while you are
-- developing the program -- using the mouse to simulate movement
-- you will need to type in "flag = 1or2or0" before you start to 
-- tell it what you want.  Use the message box
-- the variable showflag controls whether the video feed will be displayed
-- if it is set to 1 it will call the showVid routine described below

on Idle
  global flag,showflag

  if flag = 1 then

    if showflag = 1 then
    end if
  end if

  if flag = 2 then
  end if

end idle

-- materVid
-- This is the routine that sees if there is change within hot spot
-- you defined.  It checks where the xtra is reporting motion.
-- It returns a rectangle pixel description.  The routine above 
-- called checkNew put those rectangles in variables frect1 and frect2
-- you need to set up hot spots.  You can do this by turning on the video
-- then drag  open box sprites to surround the areas of interest
-- the rectangle of those sprites will then be the areas to watch for motion
-- in the example below, the boxes have been put in sprite channel 5 & 6
-- if for some reason you didn't want actual sprites marking the areas,
-- you could use the boxes to get the coordinates and then create 
-- artificial rectangle variables and get rid of the spritres
-- For example, you could substitute this line for "tarrect1 = sprite(5).rect"
--  tarrect1 = rect(10,30,100,120)
-- newevent is a variable to check where motion is
-- it is initialized to a value of 0 to indicate no motion in hot area
-- checkNew is the actual routine to call ttc to see if motion
-- the if routines below check to see if the xtra motion-detection rectangle
-- intersects one of the hot spot rectangles

-- they use Director's intersect(rec1,rec2) function
-- this routine compares 2 rectangles and reports back rectangle coords
-- that enclose both rectangles.  If they don't touch it gives back
-- rect(0,0,0,0)
-- the routine below deducts that if the intersect is not equal to (< >)
-- to the 0000 rect then there is an intersection and 
-- motion has been detected near that area.  Newevent is set equal to 1 or 2
-- if there have been no intersections then newevent is still 0
-- Note this design would need to be changed if you were trying to detect 
-- several motions in different hotspots at the same time
-- or just trying to track motion whereever it goes (see vidMasteralt below)
-- the series of 3 if statements branch Director to different places
-- based on what ttc has detected
-- if there is motion near hotspot 1 it goes to a series of frames
-- that we arbitrailly named "event1", "event2", or "event0"
-- this is where the action happens - see section VI below
-- before testing for the intersection the routines make sure
-- that frect1 and frect2 are not equal (< >) 0
-- if they are equal to 0 director would give error because they were
-- not the rectangles that the intersect function needs.
-- Notice the check to see if newevent <> curevent
-- curevent is a variable that tells what is the current active event
-- if people were near hot spot 1 before and the xtra picks up new
-- motion there, we don't want to start event 1 over again
-- thus we use the comparison of the newevent and the current event
-- if they are the same we don't have to go anywhere because
-- we are already in the midst of that event
-- if they are not the same (< >) then we branch to the new place
-- and set the variable curevent to the new value

on masterVid
  global flag, frect1, frect2, tarrect1, tarrect2
  global curevent, newevent
  tarrect1 = sprite(5).rect
  tarrect2 = sprite(6).rect
  newevent = 0
  -- see if in place #1 -- go to appropriate frame
  if frect1 <> 0 then
    if intersect (frect1,tarrect1) <> rect(0,0,0,0) then
      newevent = 1 
    end if
  end if

  -- see if in place #2-- go to appropriate frame
  if frect2 <> 0 then
    if intersect (frect2,tarrect2) <> rect(0,0,0,0) then
      newevent = 2 
    end if
  end if

  -- branch to right section of movie
  if curevent <> newevent then
    if newevent = 1 then
      go frame "event1"
      set curevent = 1
    end if 

    if newevent = 2 then
      go frame "event2"
      set curevent = 2
    end if 

    if newevent = 0 then
      go frame "event0"
      set curevent = 0
    end if 

    --put frect1&&frect2 --&& if you wanted to see what motion was detected
    -- you would take out the comments and it would print rectangles ttc found
    -- in the message box

  end if

-- masterVidalt
-- tracks the motion and places sprite 15 at the location on screen
-- it uses the top right corner of the found rectangle as the place to put the sprite
-- frect[1] gets x coordinate and frect[2] gets y coordinate 
-- it then makes a point out of the two coordinates and sets the location of sprite
-- to that location
-- the routine can be used to develop the movie without ttc and videotracking active
-- for example, you could develop what media events happen 

on masterVidalt
  global flag, frect1
  if frect1 <> 0 then
    x1 = frect1[1]
    y2  = frect1[2]
    z = point(x1,y2)

    sprite(15).loc = z
  end if

end masterVidalt

-- showVid
-- this routine will show the video feed from the source rect of the video
-- in the targetrect of the stage (in this example the same as sourceRect)
-- it is useful for checking if video is live and where things are
-- it may not be useful in the actual event so it should
-- be set off and on with a flag (showflag in this example)

on showVid
  global TrackObj
  sourceRect = rect(0,0,320,240)
  targetRect = sourcerect
end showVid


V Set up of alternative mouse routine to use for testing

In the idle routine the variable flag is tested.  If flag = 1 then the routine branaches to the MasterVid rotuine wihich uses TTC to track motion in the video scene.  If  flag = 2 it branches to a mouse based routine.  The purpose of this is to allow you to work on your Director movie even when video tracking is not active - for example while you are developing the routines of what will happen when people move into certain areas.  It simulates tracking movement with the mouse.

Here is the strategy.  When the flag = 2 then the mouse interface is active.  2 sprites become visible that represent the hot spots.  Moving the mouse pointer into those places is equivalent to someone walking into a hot spot in the room.
  -- masterMouse
  --this routine reads mouse movement as though it was person movement
  -- everything else in your movie will work the same if you set it up right
  -- the only thing different is the substitution for the intersect function
  -- this routine checks if the mouseloc is inside the hot rectangles
  -- Beware that the simulation is not perfect
  -- for example, ttc makes more errors than the mouse movement detection

on masterMouse
  global flag, tarrect1, tarrect2
  global curevent, newevent
  tarrect1 = sprite(5).rect
  tarrect2 = sprite(6).rect
  newevent = 0

  -- see if in place #1 -- go to appropriate frame
  if inside(the mouseloc,tarrect1) then
    newevent = 1 
  end if

  -- see if in place #2-- go to appropriate frame
  if inside(the mouseloc,tarrect2) then
    newevent = 2 
  end if

  if curevent <> newevent then
    if newevent = 1 then
      go frame "event1"
      set curevent = 1
    end if 

    if newevent = 2 then
      go frame "event2"
      set curevent = 2
    end if 

    if newevent = 0 then
      go frame "event0"
      set curevent = 0
    end if 

  end if

end masterMouse


VI Set up of routines to respond to motion

So what happens when your program branches to the sections of the movie devoted to event 1 or event 2 or event 0.  This is the art of this work.  What is your metaphor.  What did visitors physically approach?  What image, sound, text, digital video events make sense?  Perhaps it is different sounds or movies that play when they go one place or another?  Perhaps it is some conceptual play on approach/avoidance - for example, warnings that they are getting "closer".  Perhaps it is some image that blurs or gets clearer based on their actions?  Also, what is the event like when they are not in particular hot spots.  This part is limited only by your imagination (and lingo/director skill).

This example assumes that there are 3 sections in the score.  There are markers called event1,event2,event0.  Each of these markers is the entry frame to a sequence that runs the events.  At the end of these sequences there is an on exit handler that sends it back to the marker at the beginning.  Each is a closed loop that can be exited only through movement of people or mouse equivalent.  This example assumes that event0 is at frame 20; event1 is at frame40; and event2 is at frame60.  It assumes that each sequence runs for 10 frames.  These assumptions are completely arbitrary and would need to be customized with your artistic approach.  It assumes that event0 is the default for when people have not moved to one of the hot spots.

You would need to set up the director movie by putting scripts in right places and putting images,sounds,movies whereever.  Just to show you many options, this example makes an image become visible, starts a sound playing, and jumps to a particular part of a digitial movie.  There are lots of ways in director to accomplish this or related media events.
-- this script is put at beginning of event0 in frame script of frame 20
 -- it assumes that sprite 10 has the appropriate image loaded for event0
 -- it assumes that sprite 11 has event 1 image and sprite 12 have event 2
 -- assumes that event0.wav is the appropriate sound for event 0
 -- and that its link has been loaded into a cast member called sound0
 -- it arbitraily plays it in sound channel 2
 -- assumes that there is a link to a movie loaded into a cast member and t
 -- that movie is placed in sprite channel 13

 -- place in script of frame 20
 -- turn off other images (channels 21 and 22); turn on event 0 (channel 20)
 -- start playing sound0
 --   jump to position 100 of the movie in sprite channel 13

 on enterFrame
   sprite (11).visible = false
   sprite (12).visible = false
   sprite(10).visible =  true


   sprite(13).movietime = 100

 -- place in frame 30 script to loop within sequence
 -- send it back to beginning +1 so don't restart sound/movie
-- the function marker("event0") returns the frame number of the beginning
-- adding 1 returns back to frame after beginning so sound won't be reset
-- keep looping sound by checking if done (soundbusy(2) = false) and replay
-- check if movie beyond the beginning of next segment, reset
-- (if sprite(13).movietime> 400 )

 on  exitFrame

  if soundbusy(2) = false then
  end if
  if sprite(13).movietime> 400 then
    sprite(13).movietime = 100
    sprite(13).movierate = 1
  end if

    go frame marker("event0")+1
 end exitFrame

-- place in script of frame 40
-- turn off other images (channels 20 and 22); turn on event 1 (channel 21)
-- start playing sound1
--   jump to position 400 of the movie in sprite channel 13

on enterFrame
  sprite (10).visible = false
  sprite (12).visible = false
  sprite(11).visible =  true


  sprite(13).movietime = 400

-- place in frame 50 script to loop within sequence
-- send it back to beginning +1 so don't restart sound/movie

on  exitFrame

  if soundbusy(2) = false then
  end if
  if sprite(13).movietime> 600 then
    sprite(13).movietime = 400
     sprite(13).movierate = 1
  end if

    go frame marker("event1")+1
 end exitFrame

-- place in script of frame 60
-- turn off other images (channels 20 and 21); turn on event 2 (channel 22)
-- start playing sound2
--   jump to position 600 of the movie in sprite channel 13

on enterFrame
  sprite (10).visible = false
  sprite (11).visible = false
  sprite(12).visible =  true


  sprite(13).movietime = 600

-- place in frame 70 script to loop within sequence
 -- send it back to beginning +1 so don't restart sound/movie

 on  exitFrame
  if soundbusy(2) = false then
  end if
  if sprite(13).movietime> 900 then
    sprite(13).movietime = 600
    sprite(13).movierate = 1
  end if

    go frame marker("event2")+1
 end exitFrame


VII Problems/Variations/Adjustments

These examples are good as far as they go.  You should realize, however, that there is much more possible and there are problems that might appear that will need adjustment.  Here are some examples: