BeInteractive!

«Prev | 1 | Next»
2007年 08月 28日

一個前のエントリで出した、大量のパーティクルを描画ってヤツですが、眠くてやる気が出ないのでなんとなく解説しちゃいます。多分応用は効くはず。

まず大切なのは、こんなもの真面目に全部描画しないことです。たぶん、次のSWFを見ればその意味が分かると思います。

つまり、四分円の分だけパーティクル(ドット)を描画して、applyFilterでグローをかけて、あとはこの四分円を回転させながらコピーすることで円を作るのです。これだけで、一気にバーティクルの量が四倍に!!

というわけで、25,000描画していると言いましたが、実際には1/4の6,400ぐらい分の計算量しかかかっていないというわけです。節約節約。

俺が作ったヤツは手抜きしてるのでつなぎ目が不自然だけど、これを奇麗にすれば多分実用に耐えるはず。あなたも今日から四倍のパーティクルを出してみよう。

以下ソース

package
{
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.geom.Point;
	import flash.filters.BlurFilter;
	import flash.filters.GlowFilter;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.getTimer;
	import flash.display.StageScaleMode;

	// default-size 800 800
	public class Particle extends Sprite
	{
		public function Particle()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.frameRate = 60;
			
			_bitmap = new BitmapData(400, 400, true);
			_master = new BitmapData(800, 800, false);
			
			_matrix00 = new Matrix();
			_matrix00.rotate(180 / 180 * Math.PI);
			_matrix00.translate(400, 400);
			_matrix01 = new Matrix();
			_matrix01.rotate(90 / 180 * Math.PI);
			_matrix01.translate(400, 400);
			_matrix10 = new Matrix();
			_matrix10.rotate(270 / 180 * Math.PI);
			_matrix10.translate(400, 400);
			_matrix11 = new Matrix();
			_matrix11.translate(400, 400);
			
			addChild(new Bitmap(_master));
			
			_field = new TextField();
			_field.textColor = 0xFFFFFF;
			_field.autoSize = TextFieldAutoSize.LEFT;
			_time = getTimer();
			
			addChild(_field);
			
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		private var _bitmap:BitmapData;
		private var _master:BitmapData;
		private var _step:uint = 0;
		private var _rad:Number = 0;
		
		private var _matrix00:Matrix;
		private var _matrix01:Matrix;
		private var _matrix10:Matrix;
		private var _matrix11:Matrix;
		
		private var _zero:Point = new Point(0, 0);
		private var _filter:GlowFilter = new GlowFilter(0xFFFFFF, 1.0, 8, 8, 5, 1.0);
		
		private var _field:TextField;
		private var _time:uint;
		
		private function enterFrameHandler(event:Event):void
		{
			_step = (_step + 1) % 4;
			
			_rad = (_rad + (0.2 / 180 * Math.PI));
			
			if (_rad > (2 / 180 * Math.PI)) {
				_rad -= (2 / 180 * Math.PI);
			}
			
			_master.lock();
			
			var numParticles:uint = drawBitmap(_bitmap, _step, _rad);
			drawMaster(_master, _bitmap);
			
			_master.unlock();
			
			var t:uint = getTimer();
			
			_field.text = numParticles * 4 + ' particles / ' + Math.floor(1000 / (t - _time)) + ' fps';
			
			_time = t;
		}
		
		private function drawBitmap(bitmap:BitmapData, step:Number, rad:Number):uint
		{
			var numParticles:uint = 0;
			var rect:Rectangle = new Rectangle(0, 0, 2, 2);
			
			bitmap.fillRect(bitmap.rect, 0x00000000);
			
			for (var r:Number = rad; r <= (Math.PI / 2); r += (2 / 180 * Math.PI)) {
				var cosR:Number = Math.cos(r);
				var sinR:Number = Math.sin(r);
				for (var s:Number = step; s <= 570; s += 4) {
					++numParticles;
					bitmap.setPixel32(cosR * s, sinR * s, 0xFFFFFFFF);
				}
			}
			
			bitmap.applyFilter(bitmap, bitmap.rect, _zero, _filter);
			
			return numParticles;
		}
		
		private function drawMaster(dest:BitmapData, source:BitmapData):void
		{
			dest.fillRect(dest.rect, 0x00000000);
			dest.draw(source, _matrix00);
			dest.draw(source, _matrix01);
			dest.draw(source, _matrix10);
			dest.draw(source, _matrix11);
		}
	}
}

を描画するってのをやってみてたら、多すぎてわけわからなくなりました。パーティクルっていうかドットだけど。

約25,000ドット + ブラー効果付きです。これって多い方なのかな?多いっていうならやり方解説する。

2007年 08月 27日

なんか技術系の話題は久々?

今日ご紹介するのはBitmapData.lock/unlockメソッドですが、皆さんご存知でしょうか。ドキュメントを見ると、

この BitmapData オブジェクトが変更されたときに、BitmapData オブジェクトを参照するすべてのオブジェクト (たとえば Bitmap オブジェクト) が更新されないように、イメージをロックします。パフォーマンスを向上させるには、setPixel() メソッドまたは setPixel32() メソッドを何度も呼び出す前後に、このメソッドを unlock() メソッドとともに使用してください。

と書いてあります。

多分ほとんどの人がBitmapDataを表示するために、addChild(new Bitmap(bitmapData))とかやってると思いますが、実はsetPixelやsetPixelsをすると、画面の表示を更新するために、Bitmap(BitmapDataではない)の更新処理も入ります。setPixelを何度も呼び出せばその分だけ更新処理が入ります。しかし、例えばENTER_FRAMEハンドラ内でsetPixelを何度も呼び出してBitmapDataを更新するときなど、この何度も更新処理が入る、というのは明らかに無駄です。ENTER_FRAME1回につき1回更新処理が入ればいいわけですから。

というわけで、この更新処理を制御するためのメソッドがlock/unlockです。

早速次のコードで速度比較をしてみましょう。

package
{
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.utils.getTimer;
	import flash.display.Bitmap;

	public class LockUnlock extends Sprite
	{
		public function LockUnlock()
		{
			var bitmap:BitmapData = new BitmapData(800, 800);
			
			addChild(new Bitmap(bitmap));
			
			for (var i:uint = 0; i < 3; ++i) {
				trace(drawBitmap(bitmap) + 'ms');
			}
		}
		
		private function drawBitmap(bitmap:BitmapData):uint
		{
			var start:uint = getTimer();
			
			var w:uint = bitmap.width;
			var h:uint = bitmap.height;
			
			for (var x:uint = 0; x < w; ++x) {
				for (var y:uint = 0; y < h; ++y) {
					bitmap.setPixel(x, y, 0xFFFFFF);
				}
			}
			
			return getTimer() - start;
		}
	}
}

addChildした状態の800x800のBitmapDataに対して、全ピクセルに対してsetPixelをする時間を計測x3をやってみます。結果は次の通り。

224ms
208ms
203ms

次に、lock/unlock処理を加えてみます。

bitmap.lock();
for (var i:uint = 0; i < 3; ++i) {
	trace(drawBitmap(bitmap) + 'ms');
}
bitmap.unlock();

結果は次の通り。

129ms
131ms
136ms

差は歴然ですのでお試しあれ。

«Prev | 1 | Next»

カテゴリ

タグ

アーカイブ

最新コメント

最新トラックバック