interactive scripting - CGR C/092 - fall 2004
Philip van Allen - v a n a l l e n @ a r t c e n t e r . e d u

room 142, Monday 4:00pm-7:00pm
all materials on this web site © copyright 2004, Philip van Allen
 
week 08d - proj 3 , keyboard, collision detection, move clips

directional moves: 


moving things in any direction

Reference: http://www.moock.org/webdesign/lectures/ff2001sfMotion/

We've seen how easy it is to move a movieClip left, right, up or down. Just add or subtract from the _x (right, left) or _y (up, down) properties. But what if you want to move something in an arbitrary direction? For example, suppose you have a bug on the screen and want it to move in whatever direction it's pointed, which might 37 degrees down.

You can point the bug simply by changing the _rotation property, as we've already done. To actually move the bug in this direction, you use a little trigonometry to translate the _rotation property (which is in degrees from -180 to 180) into x and y offests. These offsets are then added to the current position, which moves the bug in the direction it's pointed (note that the 0 degrees value for _rotation is the unrotated position, and points right).

In the following example, the LEFT arrow key rotates the bug counter clockwise, and the RIGHT arrow clockwise. The UP key moves the bug forward, and the DOWN key moves the bug backward. In addition to the bug, several extra displays are added to indicate the current x,y position (in the lower center of the circle), the x,y offsets used to move the clip (next to the front of the red arrow), and the current angle of _rotation (next to the black arrow).

bug_move.fla



code walk : 

rotating the bug

The following code resides in the movieClip of the bug. It does the following:

  • rotates the bug when a left or right arrow keys are pressed
  • moves the bug when the up or down arrow keys are pressed
    • to perform the actual move, it calls a function that's defined in the main timeline
  • runs code that is not part of the techniques being taught, but may be interesting
    • counter-rotates the displays of current x y, offset x y, and grids so they stay in the same position while the bug rotates.
    onClipEvent (enterFrame) {
        if (Key.isDown(Key.RIGHT)) {
            this._rotation += 5;
        } else if (Key.isDown(Key.LEFT)) {
            this._rotation -= 5;
        }
        if (Key.isDown(Key.UP)) {
            _root.moveClip(10, this._rotation, this);
        } else if (Key.isDown(Key.DOWN)) {
            _root.moveClip(-10, this._rotation, this);
        }
        // The following code is specific to this demonstration
        // and rotates the informational clips inside this clip in the opposite direction
        // so they stay horizontal. The code is commented out on this page so you can copy
        // the code and not worry about it. 
        // this.grid._rotation = this._rotation * -1;
        // this.offsetxy._rotation = this._rotation * -1;
        // this.degreeDisplay._rotation = this._rotation * -1;
    }

The first section of code rotates the movieClip as we've done before. It watches for the RIGHT or LEFT keys to be pressed, and changes the clip rotation by 5 degrees correspondingly.

if (Key.isDown(Key.RIGHT)) {
    this._rotation += 5;
} else if (Key.isDown(Key.LEFT)) {
    this._rotation -= 5;
}

Next, the code watches for the UP or DOWN keys to be pressed. If either one is, it calls a function defined in frame one of the main timeline. This function moves a clip by the distance passed (in this case 10 pixels), in the direction indicated by the angle passed (in degrees), and it moves the clip that's passed.

if (Key.isDown(Key.UP)) {
    _root.moveClip(10, this._rotation, this);
} else if (Key.isDown(Key.DOWN)) {
    _root.moveClip(-10, this._rotation, this);
}
         

There are three informational movieClips inside the main bug movieClip. The last bit of code counter rotates these clips so they appear to remain stationary, while the bug rotates. They are actually rotating in exactly the opposite direction that the bug is rotating. This is accomplished by setting the rotation of the inner clips to the opposite of the main clips _rotation property. This turns out to be very simple. Just multiply the main clip's rotation by -1. For example, if the current clip has a rotation of 90 degrees, multiplying it by -1 results in -90, which is the opposite position of rotation (since the rotation runs from -180 to +180).

this.grid._rotation = this._rotation * -1;
this.offsetxy._rotation = this._rotation * -1;
this.degreeDisplay._rotation = this._rotation * -1;

 

 

code walk : 

moveClip() function

To see how the clip is actually moved, we look at the code for the function moveClip. Though it should be noted that you don't really have to understand the inner workings of this function. As long as you know the right arguments to pass it, you can simply copy the code and use it. That's the great thing about functions--their inner workings are transparent to their users.

The function has the format:

moveClip(distance, angleInDegrees, clip)

The arguments are defined as:

  • distance is the number of pixels to move the clip
  • angleInDegrees is the angle of direction to move the clip, where 0 is to the right, 90 is down, 180 and -180 are to the left, and -90 is up. The angle can be obtained directly from the _rotation property of any clip. Zero is the default, unrotated angle of a clip. So the direction of any graphic you use with this technique should be to the right by default, as is true in this bug example.
  • clip is the actual clip to be moved

Here's the code:

function moveClip(distance, angleInDegrees, aClip)            {
    var radians = (angleInDegrees / 180) * Math.PI;
    var moveX = Math.cos(radians) * distance;
    var moveY = Math.sin(radians) * distance;
    aClip._x += moveX;
    aClip._y += moveY;
}

The first line of code converts the _rotation angle to a different unit of measure that can be used with the trig functions sine and cosine

var radians = (angleInDegrees / 180) * Math.PI;

The next two lines use the sine and cosine trig functions to obtain the x and y offsets needed to move the clip the distance specified.

var moveX = Math.cos(radians) * distance;
var moveY = Math.sin(radians) * distance;

And the last lines add the offset values to the current position of the clip specified in the arguments, which moves it to its new position.

aClip._x += moveX;
aClip._y += moveY;

 

 

making a game : 


with a moving bug & collision detection

The next example uses a moving bug in combination with the collision detection techniques we learned earlier. The bug moves as in the above example: LEFT rotates counter-clockwise, RIGHT rotates clockwise, UP moves forward, DOWN moves backward. In addition to moving, the bug can hurl blue spit by hitting the SPACEBAR. If the blue spit collides with a sandwich, you slime the sandwich and get a point.

bug_game.fla

This game does the following things:

  • rotates and moves the bug
  • makes the bug hurl spit blue spit balls
    • it does this using attachMovie() for the spit ball object
    • the new spit balls are given the same location and rotation as the bug
    • a function is assigned to the new ball's enterFrame event handler with code that
      • moves the spit ball in the direction it's pointed (the direction the bug is pointed at the time it is launched)
      • checks to see if the spit ball has collided with a sandwich
  • the sandwiches are two frame movieClips, and if a spit ball collides with a sandwich
    • check to see if the sandwich has already been slimed. if not
      • switch the sandwich to it's slimed frame
      • add to the score
    • regardless of a previous hit, delete the spit ball from the stage

     

 
code walk :  

the bug

The bug moves around as in the above example. The addition is its ability to hurl spit balls. Here's the code, minus the code to orient and move the bug--which is the same as the above:

onClipEvent(load) {
    var spitCount = 0; // init counter for the number of spit balls
    var newClipName; // declare var for the name of new spit balls
}
onClipEvent (enterFrame) {
    // the same code goes here as above for orienting
    // and moving the bug on button presses
    // 
    // code for spitting
    if (Key.isDown(Key.SPACE)) {
        spitCount++; // increment the spit count
        newClipName = "spit" + spitCount; // generate the new clip name
        _root.attachMovie("spit", newClipName, spitCount); 
        _root[newClipName]._rotation = this._rotation; 
        _root[newClipName]._x = this._x;
        _root[newClipName]._y = this._y;
        _root[newClipName].onEnterFrame = function () {
            _root.moveClip(15, this._rotation, this);
            _root.collideTarget(this);
        }
    }
}

The load event handler initializes the spitCount variable and declares the newClipName variable. The spitCount variable is used in creating unique names and depths for each new spit ball.

var spitCount = 0; // init counter for the number of spit balls
var newClipName; // declare var for the name of new spit balls

The code for rotating and moving the bug is the same as above, and is omitted for brevity. The next section of code handles hurling the spit balls when the user presses the space bar.

if (Key.isDown(Key.SPACE)) {

The next code increments the spitCount, generates a unique name based on spitCount, and creates a new instance of the spit symbol with that name and a depth of spitCount.

spitCount++; 
newClipName = "spit" + spitCount;
_root.attachMovie("spit", newClipName, spitCount); 
         

The code then sets the new spit ball instance to have the same rotation and x,y position as the bug. Note the syntax for referring to the new instance. It uses the brackets around the name of clip with no period before the bracket. This is similar to the array notation we used last week.

_root[newClipName]._rotation = this._rotation; 
_root[newClipName]._x = this._x;
_root[newClipName]._y = this._y;

The next section of code assigns an enterFrame event handler for the new spit ball instance so it will move on its own and check for collisions. The code after this line is what will be run each enterFrame event for the spit ball. This code only sets up the spit ball event handler, it does not run the code in the function.

_root[newClipName].onEnterFrame = function () {

Two functions are run in a spit ball clip every enterFrame. The first one is the moveClip() function we've reviewed above. The second function checks to see if the spit ball clip has collided with a sandwich.

_root.moveClip(15, this._rotation, this);
_root.collideTarget(this);

 

 
code walk : 

the functions

In frame one of the main timeline, we put some initialization code and the function definitions.

var scoreText = 0;
var targetArray = [sandwich0, sandwich1, sandwich2, ... sandwich9];
function collideTarget(aClip) {
    lastTarget = _root.targetArray.length - 1; // get the array length
    for (i = 0; i <= lastTarget; i++) {
        // did the spit ball collide with a sandwich?
        if (_root.targetArray[i].hitTest(aClip._x, Clip._y, true)) {
            if (_root.targetArray[i]._currentFrame == 1) {
                _root.targetArray[i].gotoAndStop("splat");
                _root.scoreText++;
            }
            aClip.removeMovieClip();
        }
    }
}
function moveClip(distance, angleInDegrees, aClip) {
    // same code desscribed above
}

The initialization code sets the score to zero, and puts all of the sandwich instances in one arrary. With all of the sandwich instances in the array, it is simple to cycle through the array in a loop, checking each sandwich for a collision.

var scoreText = 0;
var targetArray = [sandwich0, sandwich1, sandwich2, ... sandwich9];

The collideTarget() function checks a clip (a spit ball) against every instance of a sandwich in the array we just created.

function collideTarget(aClip) {

in order to set the number of times through the loop, we check the number of elements in the array, using the .length property of the array. The loop uses this number minus one because arrays start at zero.

lastTarget = _root.targetArray.length - 1; // get the array length
for (i = 0; i <= lastTarget; i++) {

Next the code checks for a collision of an instance of the sandwich with the registration point (_x, _y) of the clip. To do this, we use the hitTest() method of the instance of the sandwich clip. The spit ball x,y is checked against an element in the targetArray, which refers to an instance of a sandwich. The last argument of the hitTest method is set to true, so there is only a collision if the spit ball x,y contacts the actual graphics of the sandwich.

if (_root.targetArray[i].hitTest(aClip._x, aClip._y, true)) {

Another test is required to make sure the user doesn't get a score for hitting a sandwich that has already been hit by a spit ball. The code checks for the current frame number of the sandwich instance. If it's on frame one, it has not been hit yet. If it is not on frame one, it is on the "splat" frame which indicates it has been hit.

if (_root.targetArray[i]._currentFrame == 1) { // been hit before?

If the sandwich has not been hit (i.e. it is on frame one), then the code sends the sandwich to the "splat" frame, and increments the score.

_root.targetArray[i].gotoAndStop("splat");
_root.scoreText++;

Finally, if the spit ball hit this sandwich instance, the ball is deleted regardless of a previous hit or not. This makes the game more difficult, and prevents the spit ball from passing "through" the sandwich.

aClip.removeMovieClip();
           
           
 

all materials on this web site © copyright 2004, Philip van Allen

top