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

JapaneseFirefly

< ホタル >
定番ネタ?
別のネタの実装方法を考えている最中にhttp://wonderfl.net/c/28CUを見つけて
むしょうにホタルが作りたくなった。 過去にも同様のネタは見つけたけど見切り投稿。1月早いか
子供の頃に田舎で見た平家ボタルの群れを再現。点滅間隔や飛翔は源氏ボタルを参考にしました。
水路と草むら、右に木が1本立っている光景を心眼でとらえてください。
TODO:もう少し奥行き感が欲しいところ。とりあえず自分で決めたタイムアップがきたのでUP
20100506 微調整 マウスダウンすると時の流れが遅くなります
Get Adobe Flash player
by zendenmushi 06 May 2010
/**
 * Copyright zendenmushi ( http://wonderfl.net/user/zendenmushi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/cegt
 */

//  < ホタル >
//
// 定番ネタ?
// 別のネタの実装方法を考えている最中にhttp://wonderfl.net/c/28CUを見つけて
// むしょうにホタルが作りたくなった。 過去にも同様のネタは見つけたけど見切り投稿。1月早いか
//
// 子供の頃に田舎で見た平家ボタルの群れを再現。点滅間隔や飛翔は源氏ボタルを参考にしました。
// 水路と草むら、右に木が1本立っている光景を心眼でとらえてください。
//
// TODO:もう少し奥行き感が欲しいところ。とりあえず自分で決めたタイムアップがきたのでUP
//
// 20100506 微調整 マウスダウンすると時の流れが遅くなります
package 
{
	import adobe.utils.CustomActions;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.filters.BlurFilter;
	import flash.filters.GlowFilter;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.PerspectiveProjection;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.utils.getTimer;
	import net.hires.debug.Stats;
	
	/**
	 * ...
	 * @author TM
	 */
	[SWF(width=465,height=465,backgroundColor=0x000000,frameRate=60)]
	public class FireFlies extends Sprite
	{
		private var canvas : BitmapData;
		private var back : BitmapData;

		private var fireflyImage : BitmapData;
		private var matrix3D : Matrix3D = new Matrix3D();
		private var axis : Vector3D = new Vector3D();
		private var zeroPoint : Point = new Point(0, 0);
		private var appearCounter : int = 0;
		private var nextAppear : int = 0;
		private var syncInterval : int = 0;
		private var first : Boolean = true;
		private var mouseIsDown : Boolean = false;

		private var lastUpdate : uint = 0;
		private var updateInterval : Number = 0;
		private var mouseReleased : Boolean = false;
		
		private var fireflyRender : FireFlyRender;
		private var flies : Vector.<FireFly> = new Vector.<FireFly>;
		private var freep : int = -1;
		private const itemlimit : int = 2000;

		
		private var loaders : Array;
   		private const url:Array = [
			//"./firefly2.png"
			"http://assets.wonderfl.net/images/related_images/d/dd/dd4a/dd4a59866907a0ae71c7e03eb1669dd86fbb8ac0"
			];
			
		public function FireFlies()
		{
            Wonderfl.capture_delay( 50 );
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e : Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
       		loaders = new Array();
			for (var i : int = 0; i < url.length; i++) {
				loaders[i] = new Loader();
				loaders[i].contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
				loaders[i].load(new URLRequest(url[i]), new LoaderContext(true));
			}
		}
		
		private var loaded_count : int = 0;
		private function loaded(e : Event = null) : void
		{
			loaded_count++;
			if (loaded_count < url.length) return;
			addEventListener(Event.ENTER_FRAME, enterFrame);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown); // スクリーンショットがあまりにもさびしいので、マウスダウンしている間、光を大きくする
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);

			fireflyImage = new BitmapData(72*2, 72, true, 0);
			back = Bitmap(loaders[0].content).bitmapData;
			var r : Rectangle = new Rectangle(0, back.height - 72, 72*2, 72);
			fireflyImage.copyPixels(back, r , zeroPoint, null, null, true); // ホタル飛翔イメージ切り出し
			back.fillRect(r, 0xff000000);
			
			fireflyRender = new FireFlyRender(fireflyImage);
			
			addChild(Bitmap(loaders[0].content));

			canvas = new BitmapData(stage.stageWidth * 2, stage.stageHeight * 2, true, 0); // ホタルの光はstageの縦横2倍のキャンバスに描いて、縮小表示
			var bmp : Bitmap = new Bitmap( canvas );
			bmp.scaleX = 0.5;
			bmp.scaleY = 0.5;
			addChild(bmp);
			
			//addChild(new Stats());
			
			//addChild(new Bitmap(fireflyRender.pre));
			
			ScreenCenter.x = stage.stageWidth / 2;
			ScreenCenter.y = stage.stageHeight / 2;
			ScreenSize.x = stage.stageWidth;
			ScreenSize.y = stage.stageHeight;
		
			graphics.clear();
			graphics.beginFill(0, 1);
			graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
			graphics.endFill();
			
			axis.x = 1;
			axis.y = 0;
			axis.z = 0;
			matrix3D.identity();
			matrix3D.appendRotation(0, axis);
			matrix3D.appendTranslation(0, 0, 100);
			
			first = true;
			nextAppear = 60 * 3;
		}
		
		private function mouseUp(e:MouseEvent):void 
		{
			mouseIsDown = false;
			mouseReleased = true;
		}
		
		private function mouseDown(e:MouseEvent):void 
		{
			mouseIsDown = true;
			mouseReleased = false;
		}
		
		private function newItem(x : Number, y : Number, z : Number, dx : Number = 0, dy : Number = 0, dz : Number = 0 ) : FireFly
		{
			var cnt : int = flies.length;
			if (((itemlimit > 0) && cnt >= itemlimit) && (freep >= cnt-1)) return null;
			
			freep++;

			if (freep == cnt) {
				flies[cnt] = new FireFly(x,y,z, syncInterval, dx,dy,dz);
			} else {
				flies[freep].reGenerate(x, y, z, false, syncInterval, dx,dy,dz);
			}
			flies[freep].index = freep;
			flies[freep].visible = true;

			
			return flies[freep];
		}
		private function remove(index : int) : void
		{
			var cnt : int = flies.length;
			var temp : FireFly = flies[index];
			var lastp : int = freep;

			temp.visible = false;

			if (lastp != index) {
				flies[index] = flies[lastp];
				flies[index].index = index;
				flies[lastp] = temp;
			}
			freep = lastp - 1;
			
		}
		
		private function enterFrame(e:Event):void 
		{
			syncInterval = (syncInterval + 1) % 180;
			
			appearCounter++;
			if (appearCounter > nextAppear) {
				if (first) {
					newItem(0, 0, -100, 0, 0, 1).bodyVisible = true;
					first = false;
				} else {
					newItem(Math.random() * 400 - 200, Math.random() * 50, -Math.random() * 50 + 25);
				}
				appearCounter = 0;
				nextAppear -= 20;
				if (nextAppear < 1) nextAppear = 1;
			}
			
			canvas.fillRect(canvas.rect, 0);
			canvas.lock();
			
			for (var i : int = freep; i >= 0; i--) {
				var vanish : Boolean = !flies[i].stepFrame(matrix3D, back);
				if (!vanish) {
					if (mouseIsDown) {
						// スクリーンショットがあまりにもさびしいので、マウスダウンしている間、光を大きくする。かなり反則
						flies[i].size *= 4;
					}
					fireflyRender.renderSmall(canvas, flies[i]);
				} else {
					remove(i);
				}
			}
			// 手前のホタル
			for (i = freep; i >= 0; i--) {
				fireflyRender.renderLarge(canvas, flies[i]);
			}
			canvas.unlock();
		
			var tick : uint = getTimer();
			var interval : uint = tick - lastUpdate;
			
			function wait(msec : uint) : void {
				while ( interval < msec ) { // ウェイトタイマー。 Flashはビジーループ大丈夫?
					tick = getTimer();
					interval = tick - lastUpdate;
				}
			}
			if (mouseIsDown) {
				wait( 50 );
				updateInterval = interval;
			} else if (mouseReleased) { // マウスボタンを離した際にゆっくりと速度を戻す
				//trace(interval, updateInterval );
				updateInterval -= 0.5;
				if (interval < updateInterval) {
					wait( updateInterval );
				} else {
					mouseReleased = false;
				}
			}
			lastUpdate = tick;
			
		}
	}
	
}

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.filters.GlowFilter;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Utils3D;
import flash.geom.Vector3D;
import flash.media.Video;

var ScreenCenter : Point = new Point();
var ScreenSize : Point = new Point();

class FireFlyLuminous
{
	public static function compute(param : int, /*inout*/ colorRGB : Vector.<int>) : uint
	{
		if (param < 0x40) {
			colorRGB[0] += (0xc0 - colorRGB[0]) / 8;
			colorRGB[1] += (0xff - colorRGB[1]) / 8;
			colorRGB[2] += (0x00 - colorRGB[2]) / 8;
			return colorRGB[0] << 16 | colorRGB[1] << 8 | colorRGB[2];
		} else {
			if (colorRGB[0] > 16) {
				colorRGB[0] += (0x0 - colorRGB[0]) / 16;
				colorRGB[1] += (0x0 - colorRGB[1]) / 32;
				colorRGB[2] += (0x0 - colorRGB[2]) / 32;
			} else {
				colorRGB[0] += (0x0 - colorRGB[0]) < 0 ? -1 : 0;
				colorRGB[1] += (0x0 - colorRGB[1]) < 0 ? -1 : 0;
				colorRGB[2] += (0x0 - colorRGB[2]) < 0 ? -1 : 0;
			}
			return colorRGB[0] << 16 | colorRGB[1] << 8 | colorRGB[2];
		}
	}
}

class FireFlyRender
{
	private var sp : Sprite = new Sprite();
	private var grp : Graphics = sp.graphics;
	private var tempRect : Rectangle = new Rectangle();
	private var destPoint : Point = new Point();
	private var matrix2D : Matrix = new Matrix();
	private var glow : GlowFilter = new GlowFilter(0xffff00, 1, 8, 8);
	
	// プリレンダリングするサイズ範囲
	private const PreRenderStartSize : Number = 1.5;
	private const PreRenderEndSize : Number = 2;
	private var pre : BitmapData;
	private var prerenderedPos : Vector.<int> = new Vector.<int>;
	
	private var fireflyBody : BitmapData;
	private var colorTrans : ColorTransform = new ColorTransform(1, 1, 1, 0.8); // 20100506
	private var vertices : Vector.<Number>;
	private var uvs : Vector.<Number>;
	private var faces : Vector.<int>;
	
	
	public function FireFlyRender(fireflyImage : BitmapData)
	{
		var i : int, j : int, k : int, ss : Number;
		
		fireflyBody = fireflyImage;
		
		vertices = new Vector.<Number>;
		uvs =  new Vector.<Number>;
		uvs[0] = 0; uvs[1] = 0;
		uvs[2] = 0.5; uvs[3] = 0;
		uvs[4] = 0.5; uvs[5] = 1;
		uvs[6] = 0; uvs[7] = 1;

		faces =  new Vector.<int>;
		faces[0] = 0; faces[1] = 1; faces[2] = 3;
		faces[3] = 1; faces[4] = 2; faces[5] = 3;
		
		sp.blendMode = BlendMode.ADD;
		
		var h : int = 0;
		for (i = 0; i <= (PreRenderEndSize-PreRenderStartSize)*2; i++) {
			prerenderedPos[i] = h;
			h += (PreRenderStartSize+i*0.5)*2+2;
		}
		pre = new BitmapData( PreRenderEndSize * 2 * 128, h, true, 0);
		
		var lumi : Vector.<int> = new Vector.<int>;
		
		for (j = 0; j <=  (PreRenderEndSize-PreRenderStartSize)*2; j++) {
			lumi[0] = 0;
			lumi[1] = 0;
			lumi[2] = 0;
			ss = PreRenderStartSize + j * 0.5;
			k = 0;
			for (i = 0; i < 180; i++) {
				var color : uint = FireFlyLuminous.compute(i, lumi);
				
				if ((i < 12) || (i >= 64)) {
					k++;
					
					matrix2D.identity();
					matrix2D.translate( k*(PreRenderEndSize+1)*2 + PreRenderEndSize, prerenderedPos[j]+ss+1 );

					glow.color = color;
					glow.blurX = 4;
					glow.blurY = 4;
					sp.filters = [glow];

					grp.clear();
					grp.lineStyle(0, 0, 0);
					grp.beginFill( color, 1 );
					grp.drawCircle(0, 0, ss);
					grp.endFill();
					pre.draw( sp , matrix2D);
				}
				
			}
		}
	}
	
	public function renderSmall(target : BitmapData, firefly : FireFly) : void
	{
		if (!firefly.culling) {
			// PreRenderStartSizeより小さいサイズはfillrectで描画
			if (firefly.size < PreRenderStartSize) {
				tempRect.x = firefly.x * 2 - firefly.size;
				tempRect.y = firefly.y * 2 - firefly.size;
				tempRect.width = firefly.size*2;
				tempRect.height = firefly.size*2;
				target.fillRect( tempRect, 0xff000000 | firefly.color);
			}
		} else { // 空に被っている場合は、影のみ表示
			if (firefly.size > 2) {
				tempRect.x = firefly.x * 2 - 1;
				tempRect.y = firefly.y * 2 - 1;
				tempRect.width = 2;
				tempRect.height = 2;
				target.fillRect( tempRect, 0xff202020 );
			}
		}
	}

	public function renderLarge(target : BitmapData, firefly : FireFly) : void
	{
		if (!firefly.culling) {
			// PreRenderEndSize以上はプリレンダ
			if ((firefly.size >= PreRenderStartSize) && (firefly.size <= PreRenderEndSize)) {
				if (firefly.blink < 180) {
					var k : int = firefly.blink >= 64 ? firefly.blink - 64 + 12 : firefly.blink < 12 ? firefly.blink : 12;
					
					tempRect.x = k * (PreRenderEndSize+1) * 2;
					tempRect.y = prerenderedPos[((firefly.size - PreRenderStartSize) * 2) >> 0];
					tempRect.width = (PreRenderEndSize+1) * 2;
					tempRect.height = firefly.size * 2+1;
					destPoint.x = firefly.x * 2 - PreRenderEndSize;
					destPoint.y = firefly.y * 2 - firefly.size;
					target.copyPixels(pre, tempRect, destPoint, null, null, true);
				}
			} else if (firefly.size > PreRenderEndSize) {
				// PreRenderEndSizeより大きいサイズはSpriteで描画
				matrix2D.identity();
				matrix2D.translate( firefly.x*2, firefly.y*2 );

				glow.color = firefly.color;
				var blurSize : int = firefly.size / 2 < 4 ? 4 : firefly.size / 2;
				glow.blurX = blurSize ;
				glow.blurY = blurSize ;
				sp.filters = [glow];

				grp.clear();
				grp.lineStyle(0, 0, 0);
				grp.beginFill( firefly.color, 1 );
				grp.drawCircle(0, 0, firefly.size);
				grp.endFill();
				
				colorTrans.alphaMultiplier = 1;
				target.draw( sp , matrix2D, colorTrans );

				if ((firefly.z < 20) && (firefly.size < 200) && firefly.bodyVisible) { // 最初のホタルは体も表示
					var scale : Number = firefly.size / 12;// (20 - firefly.z) / 20;
					vertices[0] = -36*scale; vertices[1] = -36*scale;
					vertices[2] =  36*scale; vertices[3] = -36*scale;
					vertices[4] =  36*scale; vertices[5] =  36*scale;
					vertices[6] = -36*scale; vertices[7] =  36*scale;
					
					if (firefly.blink & 1) {
						uvs[0] = 0;
						uvs[2] = 0.5;
						uvs[4] = 0.5;
						uvs[6] = 0;
					} else {
						uvs[0] = 0.5+1/144;
						uvs[2] = 1;
						uvs[4] = 1;
						uvs[6] = 0.5+1/144;
					}

					sp.filters = [];
					grp.clear();
					grp.beginBitmapFill( fireflyBody );
					grp.drawTriangles(vertices, faces, uvs);
					grp.endFill();
					colorTrans.alphaMultiplier = (((firefly.color & 0xff00) >> 8) / 255.0) * ((20 - firefly.z) / 20); // 20100506
					target.draw( sp , matrix2D, colorTrans );
				}
				
			}
		}
	}

}

class FireFly
{
	public var index : int;
	public var visible : Boolean = false;
	public var x : Number;
	public var y : Number;
	public var z : Number;
	public var color : uint;
	
	public var bodyVisible : Boolean = false;
	
	public var size : Number;
	public var culling : Boolean;
	public var blink : int = 0;
	
	private var blinkInterval : int = 180;
	private var luminousColor : Vector.<int> = new Vector.<int>;
	private var flystate : int = 0;
	private var landState : int = 0;
	private var basepos : Vector3D = new Vector3D();
	private var basedir : Vector3D = new Vector3D();
	private var flypos :  Vector.<Number> = new Vector.<Number>;
	private var flyposCam :  Vector.<Number> = new Vector.<Number>;
	private var hiding : Boolean = false;
	private var laucnhTimer : uint = 0;
	private var flytimer : uint = 0;
	
	
	public function FireFly(x : Number, y : Number, z : Number, blinkInit : int, dx : Number = 0, dy : Number = 0, dz : Number = 0 )
	{
		//filters = [glow];
		flypos[0] = 0;
		flypos[1] = 0;
		flypos[2] = 0;

		flyposCam[0] = 0;
		flyposCam[1] = 0;
		flyposCam[2] = 0;

		reGenerate(x, y, z, false, blinkInit, dx, dy, dz);
	}
	
	public function reGenerate(x : Number, y : Number, z : Number, launch : Boolean, blinkInit : int, dx : Number = 0, dy : Number = 0, dz : Number = 0 ) : void
	{
		luminousColor[0] = 0;
		luminousColor[1] = 0;
		luminousColor[2] = 0;
		
		flytimer = 60*5;
		
		basepos.x = x;
		basepos.y = y;
		basepos.z = z;
		
		if ((dx == 0) && (dy == 0) && (dz == 0)) {
			var rad : Number = Math.random() * Math.PI * 2;
			basedir.x = Math.cos(rad);
			if (launch) {
				basedir.y = -Math.random();
			} else {
				basedir.y = Math.random() * 2 - 1;
				blink =  blinkInit +  int(Math.random()*2)*60;
			}
			basedir.z = Math.sin(rad);
		} else {
			basedir.x = dx;
			basedir.y = dy;
			basedir.z = dz;
			blink = 0;
		}
		basedir.normalize();
		
		blinkInterval = 180 + Math.random() * 10;
		
		flystate = 0;// Math.random() * 255;
		hiding = false;
	}
	
	public function stepFrame(camera : Matrix3D, back : BitmapData) : Boolean
	{
		//graphics.clear();
		blink = (blink + 1) % blinkInterval;

		flystate = (flystate + 1) & 0xff;
		var rad : Number = (flystate * Math.PI / 128);
		
		basepos.x += basedir.x/10;
		basepos.y += basedir.y/10;
		basepos.z += basedir.z/10;
		if ((basepos.z < -50) && (basedir.z < 0) && (blink >= 179)) {
			if (basepos.z < -80) {
				basedir.z = -basedir.z;
			} else if (Math.random() > 0.5) {
				basedir.z = -basedir.z;
			}
		}
		if (flytimer > 0) flytimer--;
		else if (laucnhTimer > 0) {
			laucnhTimer--;
			if (laucnhTimer == 0) {
				reGenerate(basepos.x, basepos.y, basepos.z, true, blink);
				flystate = landState;
			}
		}
		if ((basepos.x > 400) || (basepos.x < -400) /*|| (basepos.y > 0)*/ || (basepos.y < -100) || (basepos.z > 80) /*|| (basepos.z < -80)*/) {
			hiding = true;
		}

		flypos[0] = basepos.x;
		if (laucnhTimer == 0) flypos[1] = basepos.y - (Math.sin( rad )*16 + Math.cos(rad*4)*4)/10;
		flypos[2] = basepos.z;
		camera.transformVectors( flypos, flyposCam );

		culling = flyposCam[2] < 0;
		var land : Boolean = false;
		if (!culling) {
			var scale : Number = 100 / flyposCam[2];
			flyposCam[0] *= scale;
			flyposCam[1] *= scale;
			//flyposCam[2] *= scale;
			x = flyposCam[0] + ScreenCenter.x;
			y = flyposCam[1] + ScreenCenter.y;
			z = flyposCam[2];
		
			culling = (y < 0);
			var dat : uint = back.getPixel32(x, y) & 0xffffff;
			if (dat == 0) { // 背景が黒ピクセル部分は木or草。 z座標によって、木の向こうか手前(あるいは止まるか)を判断
				if (z > 60) culling = true;
				else if (z > 40) land = true;
			} else if (dat != 0x010101) { // 背景が0x010101部分以外は光らせない
				culling = true;
			}
		}
		
		if (((basepos.y > 50) || land) && (flytimer == 0) && (laucnhTimer == 0)) { // 着地
			basedir.x = 0;
			basedir.y = 0;
			basedir.z = 0;
			laucnhTimer = 60*10; // 10秒止まる
			landState = flystate;
		}
		
		
		if (!culling) {
			
			color = FireFlyLuminous.compute(blink, luminousColor);
			size = 100 / flyposCam[2];
			if (size >= 2) {
				
				if ((x < 0) || (x > ScreenSize.x)) {
					hiding = true;
				}
			}
		}
		
		if (hiding && (blink >= 179)) {
			return false;
		}
		return true;
	}
}