Gravity - revamped in faux 3D
Space to clear screen.
Click to add ball (with momentum based on mouse movement).
Added a mini map at the upper right corner that shows the real (2D) coordinates of the balls. You may want too look at it while creating new balls (to get the momentum right).
package {
import flash.net.*;
import flash.events.KeyboardEvent;
import flash.filters.ConvolutionFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.BlurFilter;
import flash.geom.Matrix;
import flash.ui.Mouse;
import flash.geom.Point;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.display.AVM1Movie;
import flash.display.Sprite;
public class FlashTest extends Sprite {
public function FlashTest() {
addChild(new Bitmap(_bmd));
for each (var p:Point in start) {
placeWell(p, 10000);
}
addChild(_miniMap);
_miniMap.x = 465-MMSIZE-10;
_miniMap.y = 5;
addChild(_lineSpr);
spawnRandomBalls(100);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseD);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseU);
stage.addEventListener(Event.ENTER_FRAME, onEF);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
private function placeWell(p:Point, str:int) :void {
var spr:Sprite = new Sprite();
spr.graphics.beginFill(0xFFFFFF);
spr.graphics.drawCircle(0, 0, SPRWELLSIZE-RAD);
spr.graphics.endFill();
addChild(spr);
spr.x = p.x;
spr.y = toDispCoords(p.y);
spr.addEventListener(MouseEvent.MOUSE_DOWN, dragWell);
spr.addEventListener(MouseEvent.MOUSE_UP, dropWell);
wells.push({sprite:spr, strength:str, realX:p.x, realY:p.y});
}
private function spawnRandomBalls(count:int) :void{
var i:int;
var v:Number = 10;
for(i=0; i<count; i++) {
var x:Number = RAD+Math.random()*(stage.stageWidth-2*RAD);
var y:Number = RAD+Math.random()*(stage.stageHeight-2*RAD);
var angle:Number = 2*Math.PI*Math.random();
spawnBall(x, y, v*Math.cos(angle), v*Math.sin(angle));
}
}
private function spawnBall(X:Number, Y:Number, VX:Number, VY:Number):void {
var myBall:Ball = new Ball(X, Y, VX, VY);
addChild(myBall);
_balls.push(myBall);
}
private var _miniMap:Sprite = new Sprite();
private const MMSIZE:int = 100;
private var mouseV:Point = new Point();
private var lastMousePos:Point = new Point();
private var _center:Point = new Point(stage.stageWidth/2, stage.stageHeight/2);
private var _lineSpr:Sprite = new Sprite();
private const RAD:Number = 10;
private const SPRWELLSIZE:Number = 30;
private const WELLSIZE:Number = SPRWELLSIZE*SPRWELLSIZE;
private var start:Array = [_center]; // startpoints
private var wells:Array = new Array(); // sprites
private var _bmd:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
private var _balls:Array = new Array();
private var mD:Boolean = true;
private function dragWell(e:MouseEvent):void {
mD = true;
(e.target as Sprite).startDrag(true);
}
private function dropWell(e:MouseEvent):void {
mD = false;
(e.target as Sprite).stopDrag();
}
private function onKeyDown(e:KeyboardEvent):void {
if(e.keyCode == 32) {
clearBalls();
}
}
private function mouseD(e:MouseEvent):void {
spawnBall(stage.mouseX, stage.mouseY, mouseV.x, mouseV.y);
}
private function mouseU(e:MouseEvent):void {
}
private function onEF(e:Event):void {
mouseSpeed();
move();
//bounds();
gravity();
bmdFilters();
updateWellPos();
}
private function mouseSpeed():void {
mouseV.x = (mouseV.x + stage.mouseX-lastMousePos.x)/2;
mouseV.y = (mouseV.y + stage.mouseY-lastMousePos.y)/2;
lastMousePos.x = stage.mouseX;
lastMousePos.y = stage.mouseY;
Mouse.hide();
_lineSpr.x = stage.mouseX;
_lineSpr.y = stage.mouseY;
_lineSpr.graphics.clear();
_lineSpr.graphics.lineStyle(1,0xFFFFFF);
_lineSpr.graphics.moveTo(0,0);
_lineSpr.graphics.lineTo(-mouseV.x<<1, -mouseV.y<<1);
}
private function move():void {
var i:int;
for(i=0; i<_balls.length; i++) {
for each (var p:Object in wells){
var sq:Number = distSq(_balls[i], p);
if(sq<WELLSIZE || sq>64000000) {
removeChild(_balls[i]);
_balls.splice(i,1);
return;
}
}
_balls[i].realX += _balls[i].vx;
_balls[i].realY += _balls[i].vy;
_balls[i].x = _balls[i].realX;
_balls[i].y = toDispCoords(_balls[i].realY);
_balls[i].scaleX = _balls[i].scaleY = toDispScale(_balls[i].realY);
if(_balls[i].realY<wells[0].realY && wells[0].sprite.hitTestObject(_balls[i])) {
_balls[i].visible = false;
} else {
_balls[i].visible = true;
}
_bmd.setPixel(_balls[i].x, _balls[i].y , 0xFFFFFF);
}
}
private function gravity():void {
for each (var p:Object in wells) {
var i:int;
for(i=0; i<_balls.length; i++) {
var dx:Number = p.realX-_balls[i].realX;
var dy:Number = p.realY-_balls[i].realY;
var rSq:Number = dx*dx+dy*dy;
rSq = Math.sqrt(rSq);
var angle:Number = Math.atan2(dy,dx);
var f:Number = p.strength*_balls[i].mass/rSq;
_balls[i].vx += Math.cos(angle)*f/_balls[i].mass; // fx = Math.cos(angle)*f
_balls[i].vy += Math.sin(angle)*f/_balls[i].mass; // fy = Math.sin(angle)*f
}
}
}
private var cmf:ColorMatrixFilter = new ColorMatrixFilter( [ 1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 0.95, 0]);
private function bmdFilters():void {
_bmd.applyFilter(_bmd, _bmd.rect, new Point(0,0), cmf);
}
private function updateWellPos():void {
for each (var p:Object in wells) {
p.realX = p.sprite.x;
p.realY = toRealCoords(p.sprite.y);
p.sprite.scaleX = p.sprite.scaleY = toDispScale(p.realY);
}
drawMiniMap();
}
private function drawMiniMap():void {
_miniMap.graphics.clear();
_miniMap.graphics.lineStyle(1, 0xFFFFFF);
_miniMap.graphics.beginFill(0xFFFFFF, 0.1);
_miniMap.graphics.drawRect(0, 0, MMSIZE, MMSIZE);
_miniMap.graphics.endFill();
_miniMap.graphics.drawCircle(wells[0].realX/465*MMSIZE, wells[0].realY/465*MMSIZE, 3);
for each (var b:Ball in _balls) {
if(b.realX>=0 && b.realX<465 && b.realY>=0 && b.realY<465) {
_miniMap.graphics.drawCircle(b.realX/465*MMSIZE, b.realY/465*MMSIZE, 1);
}
}
_miniMap.graphics.lineStyle(1,0xFF0000);
_miniMap.graphics.drawCircle(stage.mouseX/465*MMSIZE, stage.mouseY/465*MMSIZE, 1);
}
private function bounds():void {
var i:int;
for(i=0; i<_balls.length; i++) {
if(_balls[i].realX<RAD || _balls[i].realX>stage.stageWidth-RAD) {
_balls[i].vx *= -1;
_balls[i].realX += _balls[i].vx;
}
if(_balls[i].realY<RAD || _balls[i].realY>stage.stageHeight-RAD) {
_balls[i].vy *= -1;
_balls[i].realY += _balls[i].vy;
}
}
}
private function distSq(ball:Ball, well:Object):Number {
var dx:Number = well.realX - (ball.realX + ball.vx);
var dy:Number = well.realY - (ball.realY + ball.vy);
return dx*dx+dy*dy;
}
private function clearBalls():void {
for(var i:int = 0; i<_balls.length; i++) {
removeChild(_balls[i]);
}
_balls = new Array();
}
private function toDispCoords(y:Number):Number {
return (y/465-0.5)*70+232;
}
private function toRealCoords(y:Number):Number {
return 465*((y-232)/70+0.5);
}
private function toDispScale(y:Number):Number {
y = y/500+1;
return y>.05?y:.05;
}
}
}
import flash.geom.Point;
import flash.display.Sprite;
class Ball extends Sprite{
public function Ball(X:Number, Y:Number, VX:Number, VY:Number) {
createGraphics();
realX = X;
realY = Y;
//x = X;
//y = Y;
vx = VX;
vy = VY;
mass = 100;
}
public var realX:Number;
public var realY:Number;
public var mass:Number;
public var vx:Number;
public var vy:Number;
private const RADIUS:Number = 5;
private function createGraphics():void {
var r1:int = Math.random()*255;
var r2:int = (255-r1) - Math.random()*(255-r1);
var r3:int = 255-r1-r2;
this.graphics.beginFill((r1<<16)|(r2<<8)|r3);
this.graphics.drawCircle(0,0,RADIUS);
this.graphics.endFill();
}
}