フロッキングを実装する前に・・・

フロッキングAIを実装しようと思い、
まず、操舵力の実装を行いました。


マウスを追いかけるようについてくる何か?
を作成しました。


実行結果は、こんな感じです。
wonderfl build flash online | 面白法人カヤック


ソースは、以下です。
rotationプロパティを利用して操舵力の実装をしました。

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	[SWF(width=800, height=600, backgroundColor=0xAADDFF)]

	/**
	 * 操舵力を実装する。
	 * @author SIBA
	 */
	public class Main03 extends Sprite
	{
		// ----------------------------
		//	メンバ変数
		// ----------------------------
		
		private var vehicle:Vehicle;
		
		
		// ----------------------------
		//	初期化
		// ----------------------------
		
		public function Main03()
		{
			vehicle = new Vehicle();
			vehicle.x = 300;
			vehicle.y = 400;
			addChild(vehicle);
			
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		
		// ----------------------------
		//	イベント
		// ----------------------------
		
		private function onEnterFrame(event:Event):void
		{
			vehicle.move(mouseX, mouseY);
		}
		
	}
}


import flash.geom.Point;
import flash.display.Sprite;
import flash.display.Graphics;

class Vehicle extends Sprite
{
	// --------------------------------
	//	定数
	// --------------------------------
	
	private const MAX_TURN_THETA:uint = 10;
	private const MAX_FOURCE:uint = 10;
	
	
	// --------------------------------
	//	メンバ変数
	// --------------------------------
	
	private var velocity:Number = 0;
	
	
	// --------------------------------
	//	初期化
	// --------------------------------
	
	public function Vehicle():void
	{
		initDesign();
	}
	
	private function initDesign():void
	{
		const g:Graphics = graphics;
		g.beginFill(0xFF5555);
		g.moveTo(-25, -20);
		g.lineTo(25, 0);
		g.lineTo(-25, 20);
		g.lineTo(-5, 0);
		g.lineTo(-25, -20);
		g.endFill();
	}
	
	
	// --------------------------------
	//	パブリックメソッド
	// --------------------------------
	
	public function move(mouseX:Number, mouseY:Number):void
	{
		velocity = Point.distance(new Point(mouseX, mouseY), new Point(x, y));
		velocity /= 10;
		velocity = velocity < MAX_FOURCE? velocity : MAX_FOURCE;
		
		var tempRotation:Number = Math.atan2(mouseY - y, mouseX - x)/Math.PI * 180;
		tempRotation -= rotation;
		if (tempRotation > 180) tempRotation -= 360;
		if (tempRotation < -180) tempRotation += 360;
		tempRotation /= 10;
		if (Math.abs(tempRotation) >= MAX_TURN_THETA)
		{
			tempRotation = tempRotation < 0? -MAX_TURN_THETA : MAX_TURN_THETA;
		}
		
		rotation += tempRotation;
		x += velocity * Math.cos(rotation/180 * Math.PI);
		y += velocity * Math.sin(rotation/180 * Math.PI);
	}
	
}

水面のエフェクト

またまたwonderflでForkしてみた。
最近、wonderflのプログラムを見て勉強しているので、
こんなことばっかりしています。


水面のようなものを作成するのがあったので、
それを使いました。
Forkする前はこんな感じです。
wonderfl build flash online | 面白法人カヤック


水面のようなものが表示できていてすごいなって思いました。
でも、もっと水面っぽく見せるために
透明度も付ければいいんじゃないか?
と思い、透明度をつけて実装しました。


それがこれです。
wonderfl build flash online | 面白法人カヤック


水面の下に画像を表示し、
DisplacementMapFilterを使ってゆがむようにしました。


ソースで一部わからないところがあるんですが、
誰か教えてくれないでしょうか?
波の伝播を処理している部分なのですが・・・。

field1[xx][yy] = (field2[xx][yy] * (2 - 4 * C1) - field1[xx][yy] * (1 - C2)
			+ (field2[xx][yy+1] + field2[xx][yy-1] 
			+ field2[xx-1][yy] + field2[xx+1][yy]) * C1) * CC;

これは何か波の公式でこの様になっているんですかね??
この部分についてだけが、分からなくて困っています。。。


一応、ソースも載せておきます。

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.filters.DisplacementMapFilter;
	import flash.filters.DisplacementMapFilterMode;
	import flash.geom.Matrix;
	import flash.geom.Point;
	
	[SWF(width=800, height=600, backgroundColor=0xAADDFF)]

	/**
	 * 水面を作成する。
	 * @author SIBA
	 */
	public class Main03 extends Sprite
	{
		[Embed(source="images/test.jpg")] private var P1:Class;
		private var image:Bitmap = new P1();
				
		private const C1:Number = 0.5;
		private const C2:Number = 0.005;
		private const CC:Number = 1 / (1 + C2);
		private const SCALE:int = 10;
		private const FIELD_SIZE_X:int = 800 / SCALE + 2;
		private const FIELD_SIZE_Y:int = 600 / SCALE + 2;
		
		private var map:Bitmap;							// 水面の画像
		private var bitmapData:BitmapData;				// 水面のデータ
		private var field1:Array = [];					// 前回の水面データ
		private var field2:Array = [];					// 現在の水面データ
		private var dmf:DisplacementMapFilter;			// 水面のゆがみを作るFilter
		private var woterLayer:Sprite = new Sprite();	// 水面のレイヤ
		private var objectLayer:Sprite = new Sprite();	// オブジェクトのレイヤ
		
		public function Main03()
		{
			// レイヤーの配置
			addChild(objectLayer);
			addChild(woterLayer);
			objectLayer.graphics.drawRect(0, 0, 800, 600);
			
			// 水面の揺らぎを作るためのFilterを作成
			var channel:int = BitmapDataChannel.ALPHA;
			dmf = new DisplacementMapFilter(null, new Point(), channel, channel, 50, 50, DisplacementMapFilterMode.COLOR)
			
			// 画像の配置
			image.x = stage.stageWidth/2 - image.width/2;
			image.y = stage.stageHeight/2 - image.height/2;
			objectLayer.addChild(image);
			
			// 水面を表示するための画像を作成
			bitmapData = new BitmapData(FIELD_SIZE_X, FIELD_SIZE_Y, true, 0xFFFFFFFF);
			map = new Bitmap(bitmapData);
			map.scaleX = SCALE;
			map.scaleY = SCALE;
			woterLayer.addChild(map);
			
			// フィールドの初期化
			for (var i:int=0; i<FIELD_SIZE_X; i++)
			{
				field1[i] = [];
				field2[i] = [];
				for (var j:int=0; j<FIELD_SIZE_Y; j++)
				{
					field1[i][j] = 0.0;
					field2[i][j] = 0.0;
				}
			}
			
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
		}
		
		/**
		 * 波を立てる。
		 * @param px 波を立てる位置(x座標)
		 * @param py 波を立てる位置(y座標)
		 * @param power
		 */
		private function put(px:Number, py:Number, power:Number):void
		{
			// 立てる波の情報
			const r:uint = 5;
			const beginX:int = (px - r < 1) ? 1 : px - r;
			const endX:int = (px + r > FIELD_SIZE_X - 1) ? FIELD_SIZE_X - 1 : px + r;
			const beginY:int = (py - r < 1) ? 1 : py - r;
			const endY:int = (py + r > FIELD_SIZE_Y - 1) ? FIELD_SIZE_Y - 1 : py + r;
			
			// 波の起点を作成
			for (var xx:int=beginX; xx<endX; xx++)
			{
				for (var yy:int=beginY; yy<endY; yy++)
				{
					const d:Number = Point.distance(new Point(px, py), new Point(xx, yy));
					if (d < r)
					{
						var p:Number = power * Math.cos(Math.PI/2 * d/r);
						field1[xx][yy] += p;
						field2[xx][yy] += p;
					}
				}
			}
		}
		
		private function onMouseMove(event:MouseEvent):void
		{
			put(event.stageX/SCALE, event.stageY/SCALE, 15);
		}
		
		private function onEnterFrame(event:Event):void
		{
			// 初期化
			var xx:int = 0;
			var yy:int = 0;
			
			// 波を描く
			bitmapData.lock();
			for (xx=1; xx<FIELD_SIZE_X; xx++)
			{
				for (yy=1; yy<FIELD_SIZE_Y; yy++)
				{
					var c:uint = Math.abs(field2[xx][yy]);
					var a:uint = (c + 32 > 255) ? 255 : c + 32;
					var r:uint = (c + 128 > 255) ? 255 : c + 128;
					var g:uint = (c + 160 > 255) ? 255 : c + 160;
					var b:uint = (c + 192 > 255) ? 255 : c + 192;
					bitmapData.setPixel32(xx-1, yy-1, (a << 24) | (r << 16) | (g << 8) | b);
				}
			}
			bitmapData.unlock();

			// 波の動きを計算
			for (xx=1; xx<FIELD_SIZE_X-1; xx++)
			{
				for (yy=1; yy<FIELD_SIZE_Y-1; yy++)
				{
					field1[xx][yy] = (field2[xx][yy] * (2 - 4 * C1) - field1[xx][yy] * (1 - C2)
										+ (field2[xx][yy+1] + field2[xx][yy-1] 
										+ field2[xx-1][yy] + field2[xx+1][yy]) * C1) * CC;
				}
			}
			
			// 次へ
			const temp:Array = field1;
			field1 = field2;
			field2 = temp;
			
			// 全体に水面エフェクト
			var tempBmp:BitmapData = new BitmapData(map.width, map.height, true, 0x00FFFFFF);
			var mat:Matrix = new Matrix();
			mat.scale(map.scaleX, map.scaleY);
			tempBmp.draw(map, mat);
			dmf.mapBitmap = tempBmp
			objectLayer.filters = [dmf];
		}
		
	}
}

絵具が混ざり合うような感じ

wonderflでForkして見た。


Forkする前はこんな感じのです。
wonderfl build flash online | 面白法人カヤック


このくっつく感じがいいなぁと思いました。


でも、これは同じ色がくっつくって仕様になっているようで、
どんな色でも混ざり合うようにしてみました。


それがこれです。
wonderfl build flash online | 面白法人カヤック


動きはランダムにしてしまったので、元のやつより変な動きをします。
ソース載せておきます。

package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.GradientType;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.geom.Point;
	
	[SWF(width=800, height=600, backgroundColor=0xAADDFF)]

	/**
	 * ボールとボールがくっつく。
	 * @author SIBA
	 */
	public class Main01 extends Sprite
	{
		
		private var BALL_NUMBER:int = 100;				// 表示するボールの数
		
		private var balls:Array = [];					// ボールを格納している配列
		private var ballLayer:Sprite = new Sprite();	// ボールを置くコンテナ
		private var dispImage:Bitmap;
		
		public function Main01()
		{
			// ボールを置くコンテナを設定
			ballLayer.filters = [new BlurFilter(30, 30, 1)];
			addChild(ballLayer);
			
			// 表示するイメージを設定
			dispImage = new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x00FFFFFF));
			addChild(dispImage);
			
			// ボールを作成
			for (var i:int=0; i<BALL_NUMBER; i++)
			{
				var ball:Sprite = new Sprite();
				ball.graphics.beginFill(Math.random()*0xFFFFFF);
				ball.graphics.drawCircle(0, 0, 50);
				ball.graphics.endFill();
				
				ball.x = Math.random()*stage.stageWidth;
				ball.y = Math.random()*stage.stageHeight;
				
				balls[balls.length] = ball;
				ballLayer.addChild(ball);
			}
			
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		private function onEnterFrame(event:Event):void {
			// ボールの移動
			for (var i:int=0; i<BALL_NUMBER; i++)
			{
				var ball:Sprite = balls[i] as Sprite;
				ball.x += Math.random()*2-1;
				ball.y += Math.random()*2-1;
			}
			
			// 画像の変更
			const bitmapData:BitmapData = dispImage.bitmapData;
			bitmapData.fillRect(bitmapData.rect, 0x00FFFFFF);
			bitmapData.draw(ballLayer);
			bitmapData.threshold(bitmapData, bitmapData.rect, new Point(), "<", 0x80000000, 0xFFFFFFFF, 0xFF000000, true);
		}
		
	}
}

ActionScript 3.0による数学・物理学表現

今日、本屋に行ってきたら、このような本を見つけました。


今はお金がなくて買えないですけど、
「ほしいなぁ」
って思いましたww


Treeコンポーネントで右クリックしてもアイテムを選択できる

Treeコンポーネントコンテキストメニューで右クリック機能をつけても、
右クリックした場所のアイテムを選択できず、
少し困ったことがありました。


一応、解決策をあげておきます。
TreeコンポーネントのイベントでitemRollOverがあるので、
それでロールオーバーされているアイテムを保持しておき、
右クリックしたときに、
その保持したアイテムに選択アイテムを変更する。
といった手順で右クリックでアイテムを選択できるようにしました。


ソースを載せておきます。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml">
	<mx:XMLList id="list">
		<treeList label="test1">
			<treeList label="test1-1"/>
			<treeList label="test1-2"/>
			<treeList label="test1-3"/>
			<treeList label="test1-4"/>
		</treeList>
		<treeList label="test2">
			<treeList label="test2-1"/>
			<treeList label="test2-2"/>
			<treeList label="test2-3"/>
			<treeList label="test2-4"/>
		</treeList>
	</mx:XMLList>
	
	<mx:Script>
		<![CDATA[
			import mx.events.ListEvent;
			
			private var rollOverIndex:int = -1;
			
			private function onRightMouseDown(event:MouseEvent):void {
				tree.selectedIndex = rollOverIndex;
			}
			
			private function onItemRollOver(event:ListEvent):void {
				rollOverIndex = event.rowIndex;
			}
		]]>
	</mx:Script>

	<mx:Tree id="tree" width="200" height="100%" dataProvider="{list}" labelField="@label"
		rightMouseDown="onRightMouseDown(event)" itemRollOver="onItemRollOver(event)"/>
</mx:WindowedApplication>

変数へのバインディングと双方向バインディング

BindingってクラスがFlexにはあったんですね。
知らなかったです。
これを利用すると簡単に双方向バインディングが実現できる。


今から紹介するソースは、
双方向バインディングの例ではないですけど、
これに使えると思ったソースです。


今まで、僕はmxmlのScriptタグないで宣言した変数へのバインディングは、
テキストフィールドの値が変更されたときのイベントである
changeイベントの送出があったときに
テキストフィールドの値を変数へ代入するというように実装していました。


しかし、それだと、コードが長くなって大変でした。
以下のようなコードが必要で、Changeイベントの送出をとるために、
コンポーネントにもchange属性を設定しなくてはいけない。

<mx:Script>
	<![CDATA[
		private var text:String = "";
		
		public function onChange(event:Event):void {
			text = textInput.text;
		}
	]]>
</mx:Script>



だけど、Bindingを使うと、以下の1行だけでOKである。
テキストフィールドが1個の場合は上の例でも大丈夫だが、
テキストフィールドの数が多いと、下の例の方がコードがすっきりします。

<mx:Binding source="textInput.text" destination="text"/>



一応、Bindingを使用したサンプルコードを載せておきます。

<?xml version="1.0" encoding="utf-8"?>
<!-- 変数へのバインディング -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
	
	<mx:Script>
		<![CDATA[
			[Bindable]
			private var text:String = "";
		]]>
	</mx:Script>
	
	<mx:Binding source="tinTest.text" destination="text"/>
	
	<mx:TextInput id="tinTest" fontSize="14"/>
	<mx:Label text="{text}" fontSize="14"/>
</mx:Application>

NativeWindowは再表示できない・・・。

NativeWindowを一度closeしてしまうと、
NativeWindowのプロパティであるclosedがfalseになるようで、
再度openメソッドを呼び出しても表示してくれません。


Windowって前の状態を保持したまま置いておきたい場合はないのだろうか?


解決策としては、これしか考えられなかった。
NativeWindowは新しく作るしかないようだが、
その上に乗せるコンポーネントを保持するようにした。


プログラムソースはこんな感じです。


リスト1:メイン

<?xml version="1.0" encoding="utf-8"?>
<!-- 再度Windowを表示する -->
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
	paddingTop="20" paddingBottom="20" paddingLeft="20" paddingRight="20">
	
	<mx:Script>
		<![CDATA[
			import mx.core.Window;
			import components.MyCanvas;
			import mx.containers.Canvas;
			
			private var canvas:MyCanvas = null;
			
			private function onClick(event:MouseEvent):void {
				if (canvas == null) {
					canvas = new MyCanvas();
				}
				var window:Window = new Window();
				window.width = 400;
				window.height = 300;
				window.open();
				window.addChild(canvas);
			}
		]]>
	</mx:Script>
	
	<mx:Button label="Windowを開く" click="onClick(event)"/>
</mx:WindowedApplication>



スト2:キャンバス

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%">
	<mx:VBox width="100%" height="100%"
		paddingTop="20" paddingBottom="20" paddingLeft="20" paddingRight="20">
		<mx:Spacer height="100%"/>
		<mx:Button label="Window"/>
	</mx:VBox>
</mx:Canvas>