Recipe 11.5.
Springs
Problem
You want
an object to jump to and
settle at a specific point, as if it were attached by a spring or
rubber band.
Solution
Use Hooke's Law, the formula for a
spring.
Discussion
Hooke's Law describes the forces at work in a
spring. In simple terms, it says that the force applied by the
spring (acceleration) is
proportional to how far it is stretched. This makes total sense.
For example, if you barely pull on a rubber band, it snaps back
lightly. But if you pull it back as far as you can, it snaps back
with enough force to be painful.
Obviously, springs have different amounts of
"springiness" or tension. Some are easy to stretch and won't snap
back too strongly. Others require a lot more force to pull, and
will spring back with an equally strong force. A number can be used
to represent each spring's strength. The variable _k
represents this constant, and it is generally a small fraction of
1. A value such as 0.1 or 0.2 works well.
When a spring is modeled with ActionScript, you
also need to specify a target point that the spring will pull the
object to. Finally, you need to apply some damping or friction. In
the real world, as an object springs back and forth, it loses a bit
of energy and eventually comes to rest somewhere. If you don't add
dampening to your code, the object just springs back and forth
forever. To apply damping, multiply the velocity values by a
fraction, such as 0.95. This removes 5 percent of its speed on each
frame, eventually slowing it down to a stop. Here is an example,
with all these principles in place:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Spring extends Sprite {
private var _sprite:Sprite;
private var _vx:Number = 20;
private var _vy:Number = 0;
private var _k:Number = .1;
private var _damp:Number = .94;
private var _targetX:Number = 200;
private var _targetY:Number = 200;
public function Spring( ) {
_sprite = new Sprite( );
_sprite.graphics.beginFill(0x0000ff, 100);
_sprite.graphics.drawCircle(0, 0, 25);
_sprite.graphics.endFill( );
_sprite.x = 0;
_sprite.y = 0;
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(event:Event):void {
var ax:Number = (_targetX - _sprite.x) * _k;
var ay:Number = (_targetY - _sprite.y) * _k;
_vx += ax;
_vy += ay;
_sprite.x += _vx;
_sprite.y += _vy;
_vx *= _damp;
_vy *= _damp;
}
}
}
In this example, the spring's force, represented
by the variable _k, is set to 0.1. The variable
_damp is set to 0.94; this variable controls the
damping or friction. The target point 200, 200 is
stored in the variables _targetX and
_targetY.
In the onEnterFrame method, get the
distance from the target to the object's current position. This
tells you how far the spring is stretched. Multiply this by
_k, the spring's strength. This gives you the force (or
acceleration) to apply. Add this to the velocity and add the
velocity to the position.
Finally, apply the damping by multiplying the
velocity by the damp variable.
When you test this, you should see the sprite
spring quickly to the target point, go past it, and spring back.
Eventually it settles down and comes to rest.
The target point does not have to be stationary.
You can easily alter the previous example to use the mouse
coordinates as a target by changing the two lines that determine
the acceleration:
var ax:Number = (mouseX - _sprite.x) * _k;
var ay:Number = (mouseY - _sprite.y) * _k;
This gives you a very smooth, interactive
spring.
Try using different values for _k and
_damp to see how you can alter the spring's
properties.
See Also
Recipes 11.1,
11.2,
and 11.4
for information velocity and
acceleration.
|