In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Trig-free ballistics 2

Get Adobe Flash player
by John_Blackburne 02 Jan 2012
// forked from John_Blackburne's Trig-free ballistics
// forked from John_Blackburne's New ballistic demo
package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    
    import flash.geom.Matrix;

        import flash.events.MouseEvent;
        import flash.text.TextField; 
           [SWF(backgroundColor="#000000", frameRate="30")]
    public class Main extends Sprite {

        private const fSize:Number =  8.0;
        
        private const fSpeed:Number= 10.0;
        private const fGunX:Number = 250.0;
        private const fGunY:Number = 350.0;
        
        private const fInitX:Number =400.0;
        private const fInitY:Number =400.0;
        
        private const fSpeed2:Number= fSpeed * fSpeed;
        
        // gravity: positive as y measured downwards. Adjusted for the frame rate
        private var fGravity:Number = 10 / stage.frameRate;

        private var gun1:Bitmap, gun2:Bitmap;
        private var ball1:Shape, ball2:Shape, target:Shape;
        private var vx1:Number, vy1:Number;
        private var vx2:Number, vy2:Number;
        private var tx:Number, ty:Number;
        
        private var fTime1:Number, fTime2:Number, fTime:Number;
        
        private var txt: TextField;
                        
        public function Main():void {
            stage.addEventListener(MouseEvent.MOUSE_DOWN, click);
            txt = new TextField();
            txt.width = txt.height = 465;
            txt.textColor = 0xffffff;
            addChild(txt);
            addEventListener(Event.ENTER_FRAME, update);
            MakeThings();
            init();
        }
        
        private function MakeThings():void {
            var data:BitmapData;
                        
            ball1 = new Shape();
            ball1.graphics.beginFill(0xffffaf);
            ball1.graphics.drawCircle(0, 0, fSize);
            ball1.graphics.endFill();
            addChild(ball1);

            ball2 = new Shape();
            ball2.graphics.beginFill(0xafffff);
            ball2.graphics.drawCircle(0, 0, fSize);
            ball2.graphics.endFill();
            addChild(ball2);

            target = new Shape();
            target.graphics.lineStyle(1, 0xff8000);
            target.graphics.drawCircle(0, 0, fSize);
            addChild(target);
            
            data = new BitmapData(fSize * 3, fSize);
            data.fillRect(data.rect, 0xffffffaf);
            gun1 = new Bitmap(data);
            addChild(gun1);

            data = new BitmapData(fSize * 3, fSize);
            data.fillRect(data.rect, 0xffafffff);
            gun2 = new Bitmap(data);
            addChild(gun2);
        }
        
        private function DrawCoverage():void {
            // draw the border of the region shots can reach.
            // Note that the upper missile path always touches this
            var iX:int, fDist:Number; fSpeed2:Number;
            graphics.beginFill(0x804040);
            for (iX = 0; iX < stage.stageWidth; iX++) {
                fDist = iX - fGunX;
                graphics.drawRect(iX, fGunY +
                    0.5 * (fGravity * fDist * fDist / fSpeed2 - fSpeed2 / fGravity),
                    1, 1);
            }
            // draw trajectories of missiles fired horizontally from gun.
            // The code tests against this to decide whether to shoot up or down.
            // the two curves are the same, separated the maximum height of shots
            graphics.beginFill(0x404040);
            for (iX = 0; iX < stage.stageWidth; iX++) {
                fDist = iX - fGunX;
                graphics.drawRect(iX, fGunY +
                    0.5 * (fGravity * fDist * fDist / fSpeed2),
                    1, 1);
            }
        }
        
        private function RotateGun(gun:Bitmap, x:Number, y:Number):void {
            gun.transform.matrix = new Matrix(x, y, -y, x, fGunX + y * fSize / 2, fGunY - x * fSize / 2);
        }
 
        private function init(_x:Number = fInitX, _y:Number = fInitY):void {
            var fDist:Number, fHeight:Number, fGravity2:Number, 
                fDisc:Number, fB:Number;
            // entry point
            // create a ball to throw
            // create the target
            graphics.clear();
            graphics.beginFill(0);
            graphics.drawRect(0, 0, 500, 500);
            DrawCoverage();
            
            txt.text = "";


            target.x = tx = _x;
            target.y = ty = _y;

            graphics.beginFill(0x404040, 1);
            graphics.drawCircle(fGunX, fGunY, fSize);
 
            ball1.x = ball2.x = fGunX;
            ball1.y = ball2.y = fGunY;
            
            // work out the velocity

            // get the range
            fDist = tx - fGunX;
            fHeight = ty - fGunY;
            
            // get the discriminant
            fGravity2 = fGravity * fGravity;
            fB = fSpeed2 + fHeight * fGravity;
            fDisc = fB * fB - fGravity2 * (fDist * fDist + fHeight * fHeight);

            // if the discriminant is negative then cannot solve/outside of coverage area
            if (fDisc < 0) {
                fTime = 1000;
                txt.appendText("Too far/too high for launch speed");
                return;
            }
            
            fDisc = Math.sqrt(fDisc);
            
            // calculate the times and velocities
            // first solution: shorter time, faster/more direct shot
            fTime1 = Math.sqrt(2 * (fB - fDisc) / fGravity2);
            vx1 = fDist / fTime1;
            vy1 = -Math.sqrt(fSpeed2 - vx1 * vx1);
            // check in case below height where need to aim downwards
            if (2 * fHeight > fDist * fDist * fGravity / fSpeed2) {
                vy1 =-vy1;
            }
            RotateGun(gun1, vx1 / fSpeed, vy1 / fSpeed);
            txt.appendText ("\n'angle' 1: " +
                String( 180 / Math.PI * Math.atan2(vx1, -vy1)));

            // second solution: longer time, higher/looping shot
            fTime2 = Math.sqrt(2 * (fB + fDisc) / fGravity2);
            vx2 = fDist / fTime2;
            vy2 = -Math.sqrt(fSpeed2 - vx2 * vx2);
            RotateGun(gun2, vx2 / fSpeed, vy2 / fSpeed);
            txt.appendText ("\n'angle' 2: " +
                String( 180 / Math.PI * Math.atan2(vx2, -vy2)));

            fTime = 0;
        }

        private function click(e:MouseEvent):void {
            init(e.stageX, e.stageY);
        }
        
        private function update(e:Event):void {
            fTime++;
            if (fTime < fTime1) {
                update1();
            } else {
                ball1.x = tx;
                ball1.y = ty;
            }
            if (fTime < fTime2) {
                update2();
            } else {
                ball2.x = tx;
                ball2.y = ty;
            }
        }
 
        private function update1():void {
            ball1.x = fGunX + vx1 * fTime;
            ball1.y = fGunY + vy1 * fTime + 0.5 * fGravity * fTime * fTime;
            graphics.beginFill(0xffffaf, 0.1);
            graphics.drawCircle(ball1.x, ball1.y, fSize);
        }

        private function update2():void {
            ball2.x = fGunX + vx2 * fTime;
            ball2.y = fGunY + vy2 * fTime + 0.5 * fGravity * fTime * fTime;
            graphics.beginFill(0xafffff, 0.1);
            graphics.drawCircle(ball2.x, ball2.y, fSize);
        }
    }
}