package { import flash.display.GradientType; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.events.KeyboardEvent; import flash.events.TimerEvent; import flash.geom.Matrix; import flash.text.TextField; import flash.text.TextFieldType; import flash.utils.Timer; import org.cove.ape.*; [SWF(width="1024", height="768",backgroundColor="#ffffff", frameRate="150")] public class ApeBreakOut extends Sprite { private var physicsGroup:Group; private var walls:Group; private var ceeling:RectangleParticle; private var pongParticle:RectangleParticle; private var pongAnchor:CircleParticle; private var ballParticle:CircleParticle; private var pongSprite:Sprite; private var pongSpring:SpringConstraint; private var ballSprite:Sprite; private var gravity:IForce; private var score:int; private var targetsArray:Array; private var leftWall:RectangleParticle; private var rightWall:RectangleParticle; private var floor:RectangleParticle; private var _moving:Boolean; private var _dir:Number; private var damp:TextField; private var grav:TextField; private var ballMass:TextField; private var springStifness:TextField; private var pongHitForce:TextField; private var dampVal:TextField; private var gravityVal:TextField; private var ballMassVal:TextField; private var springStifnessVal:TextField; private var pongHitForceVal:TextField; // Tweekable variables used // I haven't included the elasticity or friction values of the various elements // So to check the effect of those you have to change them yourself for each element, sorry guys! private var DAMPING:Number = 0.9992 private var PONG_HIT:Number = -300; private var GRAVITY:Number = 10; private var SPRING_STIFNESS:Number = 0.8 private var BALL_MASS:Number = 0.6; public function ApeBreakOut() { score = 0; _moving = false; APEngine.init(0.25); APEngine.container = this; pongSprite = new Sprite; var matr:Matrix = new Matrix(); matr.createGradientBox(60, 10, 0, 0, 0); pongSprite.graphics.beginGradientFill(GradientType.RADIAL,[0xFF0000, 0x000000], [1, 1], [0x00, 0xFF], matr, SpreadMethod.PAD); pongSprite.graphics.drawRoundRect(0,0,60,10, 5,5); pongSprite.graphics.endFill(); ballSprite = new Sprite; ballSprite.graphics.beginFill(0x00ff00); ballSprite.graphics.drawCircle(0,0,10); ballSprite.graphics.endFill(); physicsGroup = new Group(true); walls = new Group(); // Create Boundary Walls (fixed elements, duh!) floor = new RectangleParticle(512, 550, 1024, 50,0,true); leftWall = new RectangleParticle(0, 395, 100, 768,0,true); rightWall = new RectangleParticle(1000, 395, 100, 768,0,true); ceeling = new RectangleParticle(512, -45, 1024, 100, 0, true); // Paint Walls floor.setFill(0xaaaaaa, 0.3); leftWall.setFill(0xbbbbbb); rightWall.setFill(0xbbbbbb); ceeling.setFill(0xccffcc); walls.addParticle(floor); walls.addParticle(ceeling); walls.addParticle(leftWall); walls.addParticle(rightWall); // Create gravity force, which should be a massless force so it's the same for every object // and of course, it only has a y component to it... gravity = new VectorForce(false, 0, GRAVITY) APEngine.addForce(gravity); // Damping is something like the energy decay of the physics system // To get a good idea of its effects, mess with it a little APEngine.damping = DAMPING; // Create the targets createTargets(); // Create Pong // The pong will move because it's attached to an anchor // that is user controlled and connected to the pong by a spring // not the simplest way to do it of course // I just wanted to add a spring for demo purposes pongParticle = new RectangleParticle(492, 525, 60, 10, 0, false, 2, 0, 0); // Here I add the event that will make the pong hit the ball pongParticle.addEventListener(CollisionEvent.COLLIDE, handleCollision); // Here I set a sprite as the pong display, so what you actually see isn't what the engine is considering // in it's calculations pongParticle.setDisplay(pongSprite, -30, -5); physicsGroup.addParticle(pongParticle); // Notice that unlike the pong, the anchor is a fixed element // this way it only moves when I want it to move pongAnchor = new CircleParticle(497, 600, 10, true) pongAnchor.setFill(0xFFFF00, 1); // If I don't set this to true it stays put! pongAnchor.alwaysRepaint = true; // If I don't add this it still works fine, you just can't see the anchor element physicsGroup.addParticle(pongAnchor); // Once again to get the idea of stifness try messing with it's value in the demo pongSpring = new SpringConstraint(pongAnchor, pongParticle, SPRING_STIFNESS); physicsGroup.addConstraint(pongSpring); // This is the ball ballParticle = new CircleParticle(500, 350, 5, false, BALL_MASS, 0.6, 0); ballParticle.setFill(0x0F0055); physicsGroup.addParticle(ballParticle); // Now that the groups have all the elements I just add them to the engine and that's it // WARNING: // Notice that I have tried to keep loads of different groups to // keep things tidy, it didn't work out all that well // some elements must be on the same group in order to work properly // so if your having problems try getting rid of some groups a bunching things togeher APEngine.addGroup(physicsGroup); APEngine.addGroup(walls); // Only one of these is needed, I think it doesn't really matter which walls.addCollidable(physicsGroup); //physicsGroup.addCollidable(walls); // This is the timer that will call step() where the APEEngine goes to work var t:Timer = new Timer(10); t.addEventListener(TimerEvent.TIMER, step); t.start(); stage.addEventListener(KeyboardEvent.KEY_DOWN, move); stage.addEventListener(KeyboardEvent.KEY_UP, stopMove); stage.addEventListener(KeyboardEvent.KEY_DOWN,updateVals); // UI Stuff initLabels(); } private function createTargets():void { // Create targets // I'll make 40 of them // Fixed rectangleParticles that dissapear as the pong hits them... targetsArray = []; var count:int = 0; var row:int = 0; var target:RectangleParticle; var Xpos:int = 362; var Ypos:int = 100; var color:int = 0x111111; var inc:int = 0x021111; for (var i:int = 0; i<40; i++) { // Create the particle itself target = new RectangleParticle(Xpos, Ypos, 30, 10, 0, true); // Add an event to handle collisions target.addEventListener(CollisionEvent.COLLIDE, handleCollision); // Add particle to group physicsGroup.addParticle(target); // paint it just for kicks target.setFill(color); // keep track of it and handle position and color offsets targetsArray.push(target); count++; Xpos += 30; color+=inc; if(count == 10) { row++; Xpos = 362 + row*15; Ypos+= 10; count = 0; color=0x111111; } } } // Everytime the ball hits a target or the pong, a CollisionEvent is raised and delt with here private function handleCollision(e:CollisionEvent):void { if(e.target == pongParticle && e.collidingItem == ballParticle) ballParticle.addForce(new VectorForce( true, (ballParticle.px - pongParticle.px)*10 , PONG_HIT)); if(e.target != ballParticle && e.collidingItem == ballParticle) { for each (var t:RectangleParticle in targetsArray) { if(e.target == t ) { // Remove the target physicsGroup.removeParticle(RectangleParticle(e.target)); // Throw the ball somewhere else! ballParticle.addForce(new VectorForce(true, Math.random()*10, Math.abs(Math.random()*10))); e.target.removeEventListener(CollisionEvent.COLLIDE, handleCollision); targetsArray[e.target] = null; score++; } } } } private function step(e:TimerEvent):void { var esq:IForce = new VectorForce(true,-2,0); var dir:IForce = new VectorForce(true,2,0); var x:Number = pongAnchor.px; // if this happens the ball has gone through walls... if(ballParticle.py < 0 || ballParticle.py > 768 || ballParticle.px < 0 || ballParticle.px > 1024) { trace("OUT OF BOUNDS! - Physics Error?!"); // reset the balls' position ballParticle.px = 512; ballParticle.py = 300; } // this handles the pongs' position so it doesn't go through walls if(_moving) { if((_dir > 0 && x < 937) || (_dir < 0 && x > 61)) if(x + _dir < 937 && x + _dir > 61) pongAnchor.px += _dir; _dir = _dir + _dir*0.1; } else _dir = 0; // Every target destroyed... wait for reset if(score == 30) { stage.addEventListener(KeyboardEvent.KEY_DOWN,reset); score = 0; } // I've done my work, now it's APEs turn // First tell it to calculate stuff APEngine.step(); // Then tell it to paint stuff APEngine.paint(); } // AND THAT'S IT FOLKS! // Nothing very important from here on... private function move(e:KeyboardEvent):void { if(!_moving) _moving = true; switch(e.charCode) { case(97): case(65): _dir = -5; break; case(115): case(83): _dir = +5; break; } } private function reset(e:KeyboardEvent):void { if(e.charCode == 114 || e.charCode == 82) { pongAnchor.px = 497; pongAnchor.py = 600; pongParticle.px = 492; pongParticle.py = 535; createTargets(); stage.removeEventListener(KeyboardEvent.KEY_DOWN,reset); } } private function stopMove(e:KeyboardEvent):void { if(_moving) _moving = false; } // UI Stuff, nevermind it... private function updateVals(e:KeyboardEvent):void { switch(e.charCode) { case(13): DAMPING = Number(dampVal.text); APEngine.damping = DAMPING GRAVITY = Number(gravityVal.text); APEngine.removeForce(gravity); gravity = new VectorForce(false, 0, GRAVITY) APEngine.addForce(gravity); BALL_MASS = Number(ballMassVal.text); ballParticle.mass = BALL_MASS; SPRING_STIFNESS = Number(springStifnessVal.text); pongSpring.stiffness = SPRING_STIFNESS; PONG_HIT = Number(pongHitForceVal.text); break; } } // Just the textfield stuff // Don't even look at it, not worth it ;) private function initLabels():void { var x:Number = 50; var y:Number = 575; var xx:Number = 130; var yy:Number = 575; damp= new TextField(); damp.height = 20; damp.selectable = false; damp.text = "Damping: "; damp.x = x; damp.y = y; dampVal = new TextField(); dampVal.textColor = 0xff0000; dampVal.type = TextFieldType.INPUT; dampVal.height = 20; dampVal.text = DAMPING.toString(); dampVal.x = xx; dampVal.y = y; y = y + damp.height; grav= new TextField(); grav.height = 20; grav.selectable = false; grav.text = "Gravity: "; grav.x = x; grav.y = y; gravityVal = new TextField(); gravityVal.textColor = 0xff0000; gravityVal.type = TextFieldType.INPUT; gravityVal.height = 20; gravityVal.text = GRAVITY.toString(); gravityVal.x = xx; gravityVal.y = y; y = y + grav.height; ballMass= new TextField(); ballMass.height = 20; ballMass.selectable = false; ballMass.text = "Ball Mass: "; ballMass.x = x; ballMass.y = y; ballMassVal = new TextField(); ballMassVal.textColor = 0xff0000; ballMassVal.type = TextFieldType.INPUT; ballMassVal.height = 20; ballMassVal.text = BALL_MASS.toString(); ballMassVal.x = xx; ballMassVal.y = y; y = y + ballMass.height; springStifness= new TextField(); springStifness.height = 20; springStifness.selectable = false; springStifness.text = "Spring Stifness: "; springStifness.x = x; springStifness.y = y; springStifnessVal = new TextField(); springStifnessVal.textColor = 0xff0000; springStifnessVal.type = TextFieldType.INPUT; springStifnessVal.height = 20; springStifnessVal.text = SPRING_STIFNESS.toString(); springStifnessVal.x = xx; springStifnessVal.y = y; y = y + springStifness.height; pongHitForce= new TextField(); pongHitForce.height = 20; pongHitForce.selectable = false; pongHitForce.text = "Pong Hit Force: "; pongHitForce.x = x; pongHitForce.y = y; pongHitForceVal = new TextField(); pongHitForceVal.textColor = 0xff0000; pongHitForceVal.type = TextFieldType.INPUT; pongHitForceVal.height = 20; pongHitForceVal.text = PONG_HIT.toString(); pongHitForceVal.x = xx; pongHitForceVal.y = y; y = y + pongHitForce.height; stage.addChild(damp); stage.addChild(grav); stage.addChild(ballMass); stage.addChild(springStifness); stage.addChild(pongHitForce); stage.addChild(dampVal); stage.addChild(gravityVal); stage.addChild(ballMassVal); stage.addChild(springStifnessVal); stage.addChild(pongHitForceVal); } } }