2Dメタボール
ActionScript3のビットマップ操作周りのライブラリの充実っぷりは凄いと思います.
ここまで充実したライブラリを見たことが無いです.
上の2Dメタボールの場合,他言語で普通にプログラムしたら
結構な時間がかかると思うけど,ActionScript3だと3時間位.
すげー.
以下ソースコード.
ライセンスはPublic Domainです.
//---------------------------------------- // 2D metaball + bump-map // written by keim +Si+ // licence: PD //---------------------------------------- package { import flash.display.*; import flash.events.*; import flash.geom.*; import flash.filters.*; import flash.text.*; [SWF(width="240", height="240", backgroundColor="#406080", frameRate=30)] public class main_bump extends Sprite { static public var base:BitmapData; // base image static private var pixels:BitmapData; // displaying image static private var screen:Bitmap; // displaying image static private var balls:Array; // meta-balls array static private var ptBallsCenter:Point = new Point(); // balls center position static private var matFace:Matrix = new Matrix(); // face matrix static private var conv:ConvolutionFilter; // convolution filter for bump static private var colt:ColorTransform; // color matrix for cutout static private var face:BitmapData; // face image public function main_bump() { var i:int; // bitmaps base = new BitmapData(240, 240, false); pixels = new BitmapData(240, 240, false); screen = new Bitmap(pixels); // create filter instance colt = new ColorTransform(32,4,1.6,1,-1280,-320,-320,0); conv = new ConvolutionFilter(3, 3, [-2.5,-2.5,0,-2.5,0,2.5,0,2.5,2.5], 1, 128); // draw meta-ball gradation ball.pat = new BitmapData(ball.R*2,ball.R*2,false,0); var shp:Shape = new Shape(); var mtx:Matrix = new Matrix(); mtx.createGradientBox(ball.R*2,ball.R*2,0,0); shp.graphics.beginGradientFill(GradientType.RADIAL, [0x808080,0x000000], [1,1], [0,255], mtx); shp.graphics.drawRect(0,0,ball.R*2,ball.R*2); shp.graphics.endFill(); base.draw(shp); base.draw(shp, null, null, BlendMode.MULTIPLY); var blr:BlurFilter = new BlurFilter(16, 16); ball.pat.applyFilter(base, ball.pat.rect, ball.pat.rect.topLeft, blr); // draw face face = new BitmapData(48, 16, true, 0x00000000); var tf:TextField = new TextField(); tf.width = 48; tf.height = 16; tf.defaultTextFormat = new TextFormat("MS Gothic", 12, 0x000000, null, null, null, null, null, TextFormatAlign.CENTER); tf.text = "o゚ω゚o"; face.draw(tf); // create ball instance balls = new Array(16); for (i=0; i<balls.length; i++) { balls[i] = new ball(); } // set sprite/event addChild(screen); stage.quality = StageQuality.HIGH; stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; stage.doubleClickEnabled = true; stage.addEventListener(Event.ENTER_FRAME, _onEnterFrame); stage.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, _onMouseUp); stage.addEventListener(MouseEvent.DOUBLE_CLICK, _onDoubleClick); } private function _onEnterFrame(event:Event) : void { var i:int, j:int; // execute ball.mouseX = mouseX; ball.mouseY = mouseY; for (i=0; i<balls.length; i++) { ball(balls[i]).gravity(); } for (i=0; i<balls.length-1; i++) for (j=i+1; j<balls.length; j++) { ball(balls[i]).interact(ball(balls[j])); } for (i=0; i<balls.length; i++) { ball(balls[i]).run(); } calcBallCenter(); // draw base.fillRect(pixels.rect,0x000000); for (i=0; i<balls.length; i++) { ball(balls[i]).draw(base); } pixels.applyFilter(base, pixels.rect, pixels.rect.topLeft, conv); base.colorTransform(pixels.rect, colt); pixels.draw(base,null,null,BlendMode.MULTIPLY); drawFace(); } private function _onMouseDown(event:MouseEvent) : void { for (var i:int=0; i<balls.length; i++) { ball(balls[i]).check_hold(event.localX, event.localY); } } private function _onMouseUp(event:MouseEvent) : void { for (var i:int=0; i<balls.length; i++) { ball(balls[i]).hold = false; } } private function _onDoubleClick(event:MouseEvent) : void { for (var i:int=0; i<balls.length; i++) { ball(balls[i]).blast(); } } private function calcBallCenter() : void { ptBallsCenter.x = 0; ptBallsCenter.y = 0; for (var i:int=0; i<balls.length; i++) { ptBallsCenter.offset(balls[i].x, balls[i].y); } var dv:Number=1/balls.length; ptBallsCenter.x *= dv; ptBallsCenter.y *= dv; } private function drawFace() : void { matFace.identity(); matFace.translate(-24, -8); matFace.rotate(Math.atan2(balls[0].y-ptBallsCenter.y, balls[0].x-ptBallsCenter.x)); matFace.translate(ptBallsCenter.x, ptBallsCenter.y); pixels.draw(face, matFace, null, null, null, true); } } } import flash.display.*; import flash.geom.*; class ball { static internal var pat:BitmapData; static internal var R :Number = 48; // radius static internal var K :Number = 0.025; // spring static internal var K2:Number = 0.3; // dumper static internal var D :Number = 48; // comfortable distance static internal var G :Number = 0.8; // gravity static internal var HR:Number = 32*32; // holding radius ^ 2 internal var mat:Matrix = new Matrix(); internal var ax:Number = 0; internal var ay:Number = 0; internal var vx:Number = 0; internal var vy:Number = 0; internal var x:Number = 0; internal var y:Number = 0; internal var hold:Boolean = false; internal var holdX:Number = 0; internal var holdY:Number = 0; static internal var mouseX:Number = 0; static internal var mouseY:Number = 0; function ball() { reset(); } internal function reset() : void { x = Math.random()*R+120; y = Math.random()*R; vx = 0; vy = 0; ax = 0; ay = 0; hold = false; } internal function run() : void { if (hold) { x = mouseX+holdX; y = mouseY+holdY; } else { x += vx + ax * 0.5; y += vy + ay * 0.5; vx += ax - vx * K2; vy += ay - vy * K2; if (y>240) { vy=-vy; y=480-y; } if (x<0) { vx=-vx; x=-x; } else if (x>240) { vx=-vx; x=480-x; } } mat.identity(); mat.translate(x-R, y-R); } internal function draw(base:BitmapData) : void { base.draw(pat, mat, null, BlendMode.ADD); } internal function gravity() : void { ax = 0; ay = G; } internal function interact(b:ball) : void { var dx:Number = b.x - x; var dy:Number = b.y - y; var l:Number = Math.sqrt(dx*dx+dy*dy); var f:Number = (D-l)*K/l; dx *= f; dy *= f; ax -= dx; ay -= dy; b.ax += dx; b.ay += dy; } internal function check_hold(x_:Number, y_:Number) : void { holdX = x - x_; holdY = y - y_; if (holdX*holdX+holdY*holdY < HR) { hold = true; } } internal function blast() : void { x = Math.random()*240; y = Math.random()*240; } }