package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; // Import the wonderful APE classes; Notice that Im such a lazy dude that just // import all. import org.cove.ape.*; public class SpringConnectionExample extends Sprite { // Sprite array to hold reference to sprites and particles private var sprites :Array; // Group that will have all our particles private var particles :Group; // To hold a reference to the current selected ball private var selectedBall :Ball; // Number of balls to create private var numBalls :Number = 10; public function SpringConnectionExample() { super(); this.initMain(); } private function initMain():void { // An array to hold a reference to our sprites and physic particles this.sprites = new Array(); // Init APE this.initAPE(); // Create our sprites and its physic particles this.createSprites(); // Create the spring constraints this.createSpringConstraints(); // Some listeners to be able to move our sprites this.stage.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDownHandler); this.stage.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUpHandler); } private function onMouseDownHandler(e:MouseEvent):void { // If mouse is pressed down over a ball sprite hold a reference to it // in our previously define variable. Which will be use to move it around // while the mouse remains pressed. if ( e.target is Ball ) { this.selectedBall = Ball(e.target); } } private function onMouseUpHandler(e:MouseEvent):void { // When mouse relased we set this value to null in order to stop its movement // during the enterframe handler. this.selectedBall = null; } private function createSprites():void { // Craete numBalls number of Balls for ( var a:uint = 0; a < numBalls; a ++ ) { // Just a sprite with a circle drawn in it, with a radius of 15 var ball:Ball = new Ball(15); // Now we create our particle for the physics; since we are using a rounded (or circular) // object, we use the APE's CircleParticle class, and passed as arguments the x(0), y(0), // radius (matches our ball sprite radius), and whether is position is fixed or not; in this // case it is not because it will be moving around the stage. var ballParticle:CircleParticle = new CircleParticle(0, 0, ball.radius, false); // Here we link our physic particle with its render, which is our ball sprite ballParticle.setDisplay( ball, 0, 0); // Asign a random position to our particle ( and ball sprite ) ballParticle.px = Math.random()*this.stage.stageWidth/2; ballParticle.py = Math.random()*this.stage.stageHeight/2; // Ok, in this example, in the Ball class it is declared a public property to // hold a reference to its physics particle for later use ball.physics = ballParticle; // Push to the sprites array the reference of the ball and its related physics // particle this.sprites.push( {render:ball, physics:ballParticle} ); } } private function createSpringConstraints():void { // Here we just loop through our sprites array to add a spring constraint // , or connect if you will, our balls together. for ( var a:uint = 0; a < this.sprites.length; a ++ ) { if ( (a+1) < this.sprites.length ) { // Get our particles from our array var particleA:AbstractParticle = this.sprites[a].physics; var particleB:AbstractParticle = this.sprites[a+1].physics; // create the spring to connect our two particles together var spring:SpringConstraint = new SpringConstraint( particleA, particleB ); spring.restLength = 100; spring.rectScale = 0.1; // Add the constraint to our APE particle group this.particles.addConstraint(spring); } // We are adding our current particles after the springs so when we run // our movie the springs are positioned on the back of our balls (haha sounds // funny) and not over them, so it appears that they are actually connected. this.particles.addParticle( this.sprites[a].physics ); } } // Function to call APE initialization methods private function initAPE():void { // Initialize APE Engine, as you probably have noticed in other tutos // a quarter (1/4) is passed as agrument to the init method, but we dont // actually need that because .25 it's the default value APEngine.init(); APEngine.damping = 0.97; // Set APE container to be THIS; it could be any other displayobjectcontainer. // We are just useing here our main class to act as the container. APEngine.container = this; // Create our particle group and add it to APE. // We passed "true" as argument because we want it to be collidable; to detect and // handle its internal particle collisions; default value is false this.particles = new Group(true); APEngine.addGroup( this.particles ); // Add and event listner to update APE every frame this.addEventListener(Event.ENTER_FRAME, this.updateAPE); } private function updateAPE(e:Event=null):void { // Here we check if there is a selected ball to update its position if ( this.selectedBall != null ) { this.moveBall(); } // Update APE APEngine.step(); APEngine.paint(); } // This function is used to update a ball's position when is selected // by pressing the mouse down over it. private function moveBall():void { // Basically we update its position adding a little force to its corresponding // physics particle, according the current mouse coordinates. var vx:Number = this.mouseX - this.selectedBall.physics.px; var vy:Number = this.mouseY - this.selectedBall.physics.py; this.selectedBall.physics.addForce( new VectorForce(false, vx, vy) ); } } }