Balancing the Scales

April 3, 2001
by Will Turnage

Dear Multimedia Handyman,

I need to make a balance (a weighing device) activity for an educational children's project. The students will be given a weight, and then they have to put weight on the weighing pan to balance it out. Can you help me figure out how to do this?

Raru

Raru,

I think at one point we've all found ourselves sitting in front of the computer with no clue how to begin a project. It might be some little Shockwave idea you had, or it could be a project for a major client, but still, you find yourself with a task in front of you and you have no idea where to begin. I think we've all been there, and judging from the number of emails I get from people asking me to code their projects for them, a lot of find ourselves frequently in that position.

So how do you go about solving this problem? Basically, the way to solve it is to break down your project into smaller and smaller pieces, and develop those separately. For instance, with the weighing project you described above, you could break it down into these smaller movies:

1. A movie that assigns a fixed weight to one half of a scale
2. A movie where weights are simply dragged onto one side of a scale.
3. A movie that actually displays the balance between two weights.

Once you have successfully built all three smaller movies, then you can put them together into a single movie that does everything. Let's start with the first movie.

This movie has a scale and a single button in it. When you click the button, it assigns a new number to the scale on the right. The code for this movie is really simple. There is a single behavior on the button that contains the following code:

on mouseUp me

sendSprite (2, #setCurrentWeight, random (99))

end

On sprite 2 (which is the sprite containing the right hand side of the scale) is another behavior which contains the following code:

property pCurrentWeight

on setCurrentWeight me, newWeight

pCurrentWeight = newWeight
member ("rightSideWeight").text = string (pCurrentWeight)

end

When the user clicks the button, it generates a random number between 1 and 99. That number is sent to sprite 2, which stores it in a property to be used later. Finally, it converts the number to a string and displays that string in a text field.

The first step of your project is now complete, so you can begin working on the second problem of dragging weights onto the left-hand side of the scale. Your finished movie should look like this:

This movie only contains two behaviors, one that goes on each of the weights, and the second which goes on the left-hand side of the scale to keep track of the current weight on the left hand side. The first behavior you should write will allow you to drag the weights around the screen and add them to the left-hand side of the scale. The behavior should contain the following code:

property pSprRef
property pDrag
property pStartLoc
property pWeight

on beginSprite me

pSprRef = sprite (me.spriteNum)
pStartLoc = pSprRef.loc
pWeight = value (pSprRef.member.text)
pSprRef.cursor = 260
pDrag = false

end

on prepareFrame me

if pDrag then
pSprRef.loc = the mouseLoc
end if

end

on mouseDown me

pDrag = true
pSprRef.cursor = 290

end

on mouseUp me

pSprRef.cursor = 260
pDrag = false
if pSprRef.intersects (3) then
else
pSprRef.loc = pStartLoc
end if

end

When this behavior begins, it initializes all of its properties. First it creates a variable for the sprite that it's attached to. Next it stores the original starting location of the sprite in a variable. Then it calculates a mass for a weight by getting the value of the number that is contained in its own text field. Finally, it sets the cursor of its sprite equal to the hand so that the user knows it can be dragged around the screen.

When the user clicks on the weight, it triggers a flag saying that the weight can now be dragged around the screen. Then it changes the cursor of the sprite to the closed hand. Now, since the pDrag variable is set to true, on each frame it will set the location of the weight equal to the location of your mouse on screen.

Once you let go of the mouse, it returns the cursor to the open hand position and sets pDrag to false, meaning that the weight can no longer be dragged around the screen. Next it checks to see if the weight is on top of sprite 3, which is the sprite for the left-hand side of the scale. If the weight intersects the scale, then it leaves the weight where it is, and sends a message to add the value of its weight to the left-hand side of the scale. If the weight doesn't intersect with the left-hand side of the scale, then it automatically returns the weight to its starting position on the screen.

So now you've got a movie for the left-hand side of the scale and the right hand side of the scale, so all you need is a third movie to simulate the balance between two sides of a scale.

Again, there are very few behaviors in this sprite. When you click on the button, it generates random numbers for the left and right sides of the scales. The other main behavior is attached to the line connecting the left and right side of the scale. It contains the following code:

property pLeftWeight
property pRightWeight

on prepareFrame me

if pRightWeight > 0 then

weightDifference = pRightWeight - pLeftWeight
newRotation = (weightDifference / pRightWeight)

if newRotation > 1 then
newRotation = 30
else if newRotation < -1 then
newRotation = -30
else
newRotation = newRotation * 30
end if

rotationDifference = newRotation - sprite (me.spriteNum).rotation

if abs (rotationDifference) < 1 then
pSprRef.rotation = newRotation
else
pSprRef.rotation = pSprRef.rotation + (rotationDifference / 6.0)
end if

if pSprRef.rotation > 0 then
sprite(2).loc = point (sprite (me.spriteNum).right, sprite (me.spriteNum).bottom)
sprite(3).loc = point (sprite (me.spriteNum).left, sprite (me.spriteNum).top)
else if pSprRef.rotation < 0 then
sprite (2).loc = point(sprite (me.spriteNum).right, sprite (me.spriteNum).top)
sprite (3).loc = point(sprite (me.spriteNum).left, sprite (me.spriteNum).bottom)
else
sprite (2).loc = point (sprite (me.spriteNum).right, sprite (me.spriteNum).bottom)
sprite (3).loc = point (sprite (me.spriteNum).left, sprite (me.spriteNum).bottom)
end if
end if

end

The code for this behavior starts by checking to make sure that there's a weight on the right hand side of the scale. Then it calculates the difference between the left and right sides and divides that number into the right hand weight to determine how much you are going to rotate the line that balances the two scales. If this number is positive, then you'll rotate the line in a positive way making it look like the right hand side is heavier. If this number is negative then you'll rotate the balancing line in a negative direction making the left-hand side look heavier. Finally, if the number is zero, then there will be no rotation and both sides will appear even.

Next, you take this new rotation number and convert it to a number between -30 and 30 degrees. This is done to restrict the balances of the scale from going way out of whack when one side is heavier than the other side.

Once you've calculated what the rotation of the line should be, then all that's left to do is to make the line appear to move like a real scale. To do this, you just take a small fraction of the difference between the current rotation of the line and the rotation that the line should be at. Then you just add this fraction to the current rotation to make the line appear to move. If the difference between rotations is less than one, then rather than calculate small differences, you can just go ahead and assign it to its final rotation.

The final step is to move the different sides of the scales. Based on the current rotation of the balancing line, you set the location of the sprites to either the top or bottom of the sprite based on which side is heavier.

Now that you have three different movies demonstrating the different functions, you can combine the code into one single movie that will look like this.

A sample Director 8 movie is available for download in Mac or Windows format.

Will Turnage is a multimedia programmer based in New York City. In addition to his weekly role as Director Online's Multimedia Handyman, he is also the Technology Director for Sony Music's Client Side Technologies Group. You can read more about his work at http://will.turnage.com/.