Glimmr Canvas Animation

version 1/111030 by Erik Temple

  • Home page
  • Beginning
  • Previous
  • Next



  • Example: * Animation Rule Variations - This simple example shows how we can vary preset animations by adding rules to the animation rulebook. We'll have four variations on a simple horizontal motion animation. The "player" can choose from among these variation via a keypress menu. The chosen animation will play immediately, and once it finishes s/he will have a chance to choose another.

    We start with basic setup, creating a graphics window, a canvas, and some stuff to animate. This is primarily a round ball, implemented as a bitmap element so that we can avoid loading any external images. There are also two non-animated point primitives, to mark the beginning and end points of the ideal path of movement; these will primarily help us to see the differences between variations 2 and 3.

        "Animation Rule Variations"

        Include Glimmr Canvas Animation by Erik Temple.

        [Use Glimmr debugging.]
        [Use animation debugging.]

        There is a room.

        The graphics-window is a graphics g-window spawned by the main-window. The position of the graphics-window is g-placeabove. The measurement of the graphics-window is 50. The back-colour of the graphics-window is g-black.

        The graphics-window canvas is a g-canvas. The canvas-width is 500. The canvas-height is 300. The associated canvas of the graphics-window is the graphics-window canvas.

        When play begins:
            open up the graphics-window;
            [open up the console-window.]
        

        The associated canvas of a g-element is the graphics-window canvas.

        The Ball is a monochrome bitmap. The tint is g-White. The Ball is center-aligned. The bitmap-array is {
            { 0, 0, 1, 1, 1, 0, 0 },
            { 0, 1, 0, 0, 0, 1, 0 },
            { 1, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 1 },
            { 0, 1, 0, 0, 0, 1, 0 },
            { 0, 0, 1, 1, 1, 0, 0 } }.
        
        Initial-position indicator is a point primitive. The tint is g-red. The display-layer is 2.
        Secondary-position indicator is a point primitive. The tint is g-red. The display-layer is 2.

    Next, we define our animation track, and assign a few global variables to control the animation. (We use the globals so that we need only make changes in one place should we want to tweak things.)

    The after printing the banner text rule contains the entire flow of the "game": we enter a loop in which it is only possible for the player to press a number keyed to the menu, or to press "q" to quit. We store the number pressed in the "key-option" variable; we will use it later to decide which subsidiary animation rule to call. Then we invoke the animate phrase that will move the ball from its current position to the destination coordinates. We prevent the game from accepting any input until the animation completes by using the "delay input until all animations are complete" phrase. And after the animation completes, we swap the coordinates stored in the destination variable, so that the next animation will move the ball back where it came from.

        The movement track is an animation track.

        The motion-duration is a number variable. The motion-duration is 20.
        Base rate is a number variable. Base rate is 40.
        Rate interval is a number variable. Rate interval is 10.

        Initial position is a list of numbers variable. Initial position is {10, 100}.
        Secondary position is a list of numbers variable. Secondary position is {490, 100}.
        Destination is a list of numbers variable.

        When play begins:
            now the origin of the ball is the initial position;
            now the origin of the initial-position indicator is the initial position;
            now the origin of the secondary-position indicator is the secondary position.

        Key-option is a number variable.
        
        After printing the banner text:
            now the destination is the secondary position;
            say paragraph break;
            say "This demo shows some ways in which a simple animation can be changed using additional animation rules. Type a number to see the corresponding animation:[paragraph break]1. Simple motion along a path.[line break]2. Random noise added to a path[line break]3. Random noise added to a path, but nevertheless arriving at the originally specified ending point[line break]4. Gradual decrease in the frame rate.[paragraph break]Press Q to quit.[paragraph break]";
            while true is true:
                now key-option is the character code entered in the main-window minus 48;
                if key-option is 65["q" = 113 (48 + 65)], break;
                if key-option is less than 1 or key-option is greater than 4, next;
                animate the movement track as a motion animation targeting the Ball and ending at the destination at (base rate) ms per frame with a duration of (motion-duration) frames;
                delay input until all animations are complete;
                if the destination is the initial position:
                    now the destination is the secondary position;
                otherwise:
                    now the destination is the initial position;
            say "*** User exited demonstration ***";
            follow the immediately quit rule.

    The "key-option" variable, which contains the number of the menu that the player selected, is now used to decide which subsidiary animation to use. We don't need to run a second rule if the player pressed 1, since that corresponds to the unadorned movement invoked by the "animate..." phrase. The subsidiary rules run *after* the built-in preset animation rule, so they tweak the outcomes of that rule. (If we wanted our animation rules to run before the preset rule, we could make them "first" rules.)

    Option 2 randomly deforms the basic path of motion, and requires only a very simple rule that adds or subtracts a number between 1 and 10 from the x and y coordinates of the ball's origin. The "adjust the origin..." phrase used here is an easter egg phrase provided by GCA; we can also use "adjust the endpoint" for primitive elements. Note that this option will almost never finish on the destination coordinate specified, since we are adding noise to the chosen coordinate all the way along.

    Option 3 uses the same deformation as Option 2, but with a twist. We want to end on the originally specified destination point. This is tricky, because motion animations are completely relative: when the animation is set, up GCA calculates what we'll have to do to get to the destination, but doesn't save any information about the destination itself. To keep our eyes on the prize, then, we need to reconstruct the original destination coordinate, and reorient ourselves to it, every frame. This will cause the deformation to be a bit smoother than in Option 2, but still noticeable. Note that we only apply deformations for the first 3/4 of the animation's running time. That's so that we don't jump too unnaturally to the destination at the end if we are far off.

    Option 4 actually changes the rate of the animation itself. Once we're about 1/3 of the way through the allotted frames, the rule kicks in to change the rate of the timer tick, increasing the time between ticks and therefore slowing the apparent speed of the animation. For a motion animation, this is probably better done with an easing equation, but this kind of speed control might work nicely for a reel animation.

        Animation rule for the movement track when key-option is 2:
            adjust the origin of (the animation-target of the movement track) by (a random number between -10 and 10) and (a random number between -10 and 10).
        
        Animation rule for the movement track when key-option is 3:
            if current-frame of the movement track is less than (3 * (motion-duration / 4)):
                let end-x be start-x of the movement track plus delta-x of the movement track;
                let end-y be start-y of the movement track plus delta-y of the movement track;
                let X be a random number between -10 and 10;
                let Y be a random number between -10 and 10;
                adjust the origin of (the animation-target of the movement track) by (X) and (Y);
                now the start-x of the movement track is entry 1 of the origin of the animation-target of the movement track;
                now the start-y of the movement track is entry 2 of the origin of the animation-target of the movement track;
                now delta-x of the movement track is end-x minus start-x of the movement track;
                now delta-y of the movement track is end-y minus start-y of the movement track.

        Animation rule for the movement track when key-option is 4:
            if current-frame of the movement track is greater than (motion-duration / 3):
                let rate be (base rate) + ( (current-frame of the movement track - (motion-duration / 3)) * (rate interval) );
                time the movement track at (rate) ms per frame.