2008年5月27日 (火)

FlashPlayer10 ローカルの画像を読み込み→カスタムフィルタ→別名保存

今度はローカルの画像を読み込んでみます。
読み込むまではテキストファイルの時と一緒。
違うのは読み込んだ後、Loader.loadBytes()でByteArrayから読み込まないといけないってことです。
そのLoaderインスタンスをaddChildしてあげればOKです。

これだけだとあれなので、PixelBenderで作成したカスタムフィルタをかけてみます。
前に作ったセピア色に変換するやつです。

カスタムフィルタをかけるのは前と変わらず。
まず読み込んだ画像と同じ大きさのBitmapDataを作成しておきます。
それにdrawしてapplyFilterでフィルタをかけます。

ここで注意。
こちらにも書かれてますが、loadBytesで読み込んだ直後はwidth、heightを取得できません。
Loader.contentLoaderInfoでEvent.INITが呼ばれた後は取得できるようです。

次に保存ですが、このままだと保存できません。
JPGEncoder」を使ってBitmapDataをByteArrayに変換してあげます。
それをFileReferenceのsave()に渡してあげればOKです。
やっぱり存在しているファイルに上書きするとIOErrorになるけど。。

package  {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.net.FileReference;
	import flash.events.*;
	import flash.display.Shader;
	import flash.filters.ShaderFilter;
	import flash.geom.Point;
	import flash.utils.ByteArray;
	import com.adobe.images.JPGEncoder;
	
	public class Main extends Sprite {
		[Embed(source = "test03.pbj", mimeType = "application/octet-stream")]
		private var shaderData:Class;
		private var ld:Loader;
		public function Main() {
			//読み込みボタン
			var bd1:BitmapData = new BitmapData(120, 60, false, 0xFF6600);
			var bm1:Bitmap = new Bitmap(bd1);
			var mc1:Sprite = new Sprite();
			mc1.addChild(bm1);
			mc1.x = 20;
			mc1.y = 20;
			addChild(mc1);
			mc1.buttonMode = true;
			mc1.addEventListener(MouseEvent.CLICK, selectFile);			
		}
		private function selectFile(e:MouseEvent):void {
			//読み込むファイルの選択
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.SELECT, loadFile);
			ff.browse();
		}
		private function loadFile(e:Event):void {
			//ファイル読み込み
			var ff:FileReference = FileReference(e.target);
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, setImage );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError");  } );
			ff.load();
		}
		private function setImage(e:Event):void {
			//ByteArrayに読み込まれたデータをLoaderで読み込み
			ld = new Loader();
			ld.loadBytes(e.target.data);
			addChild(ld);	//読み込んだ画像を表示
			ld.contentLoaderInfo.addEventListener(Event.INIT,initImage);
		}
		private function initImage(e:Event):void {
			//読み込んだ画像にカスタムフィルタ(セピア)をかける
			var shader:Shader = new Shader(ByteArray(new shaderData()));
			var shaderFilter:ShaderFilter = new ShaderFilter(shader);

			var bd:BitmapData = new BitmapData(ld.width, ld.height);
			var bm:Bitmap = new Bitmap(bd);
			bm.x = ld.width;
			bd.draw(ld);
			bd.applyFilter(bd, bd.rect, new Point(), shaderFilter);
			addChild(bm);	//カスタムフィルタをかけた画像を表示
			
			//JPGEncoderでBitmapDataをByteArrayに変換
			var jpgEncoder:JPGEncoder = new JPGEncoder(85);
			var jpgStream:ByteArray = jpgEncoder.encode(bd);
			saveImage(jpgStream);
		}
		private function saveImage(ba:ByteArray):void {
			//保存
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, function(e:Event):void { trace("complete");  } );
			ff.addEventListener(Event.CANCEL, function(e:Event):void { trace("cancel");  } );
			ff.addEventListener(Event.SELECT, function(e:Event):void { trace("select");  } );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError");  } );
			ff.save(ba,"newImage.jpg");
		}
	}
	
}

| | コメント (0) | トラックバック (1)

FlashPlayer10 テキストファイルを開く、保存する

前回のでテイストファイルをローカルに直接保存しましたが、今度は
ローカルのテキストファイルを開き、編集して保存してみます。

やってみたんだけど、「上書き保存」ができず。。
存在しているファイルと同じ名前で保存しようとするとIOErrorになります。
これはなんででしょう・・・仕様?

別名保存であれば下記のスクリプトでいけます。

package  {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.net.FileReference;
	import flash.events.*;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	public class Main extends Sprite{
		private var txt:TextField;
		public function Main() {
			//テキスト入力
			txt = new TextField();
			txt.width = 400;
			txt.multiline = true;
			txt.text = "";
			txt.border = true;
			txt.type = TextFieldType.INPUT;
			txt.x = 20;
			txt.y = 20;
			addChild(txt);
			
			//読み込みボタン
			var bd1:BitmapData = new BitmapData(120, 60, false, 0xFF6600);
			var bm1:Bitmap = new Bitmap(bd1);
			var mc1:Sprite = new Sprite();
			mc1.addChild(bm1);
			mc1.x = 20;
			mc1.y = 200;
			addChild(mc1);
			mc1.buttonMode = true;
			mc1.addEventListener(MouseEvent.CLICK, selectFile);			

			//保存ボタン
			var bd2:BitmapData = new BitmapData(120, 60, false, 0x000000);
			var bm2:Bitmap = new Bitmap(bd2);
			var mc2:Sprite = new Sprite();
			mc2.addChild(bm2);
			mc2.x = 160;
			mc2.y = 200;
			addChild(mc2);
			mc2.buttonMode = true;
			mc2.addEventListener(MouseEvent.CLICK, saveFile);			
		}
		private function selectFile(e:MouseEvent):void {
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.SELECT, loadFile);
			ff.browse();

		}
		private function loadFile(e:Event):void {
			var ff:FileReference = FileReference(e.target);
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, setText );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError");  } );
			ff.load();
		}
		private function setText(e:Event):void {
			txt.text = e.target.data;
		}
		private function saveFile(e:MouseEvent):void {
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, function(e:Event):void { trace("complete");  } );
			ff.addEventListener(Event.CANCEL, function(e:Event):void { trace("cancel");  } );
			ff.addEventListener(Event.SELECT, function(e:Event):void { trace("select");  } );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError",e.target,e.text);  } );
			ff.save(txt.text,"saveTextTest.txt");

		}
	}
	
}

| | コメント (0) | トラックバック (0)

FlashPlayer10 ファイルを直接保存

FlashPlayer10からローカルに直接ファイルを保存できるようになったのでテスト。

ファイル操作は今までと同様FileReferenceを使います。(初めて使う。。)
今まではサーバーへアップロードするくらいの機能だったと思うけど、新しく
loadとsaveというメソッドが追加されて、ローカルのファイルを直接開いたり、
直接書き込んだりすることができます。
今回はsaveを使います。

saveはドキュメントによるとこう書いてあります。

function save(data:*, defaultFileName:String = null):void

第1引数で保存するデータを渡します。型はなんでもいいっぽいですが今回は普通にStringを渡してみます。
第2引数は保存するファイル名の初期値です。保存の際にユーザーが書き換えることができます。省略可です。

private function _save(data:ByteArray, defaultFileName:String)
となってるのでデータはやっぱりByteArrayに変換されてから保存されるんですね。

ソースは以下のとおり。
最初「OPEN」とかのイベントを設定しなかったらPlayer落ちた。

package  {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.net.FileReference;
	import flash.events.*;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	public class Main extends Sprite{
		private var txt:TextField;
		public function Main() {
			//テキスト入力
			txt = new TextField();
			txt.width = 400;
			txt.multiline = true;
			txt.text = "ここにテキストを入力。\n入力したら下の黒い四角をクリックすると保存します。";
			txt.border = true;
			txt.type = TextFieldType.INPUT;
			txt.x = 20;
			txt.y = 20;
			addChild(txt);
			
			//保存ボタン
			var bd:BitmapData = new BitmapData(120, 60, false, 0x000000);
			var bm:Bitmap = new Bitmap(bd);
			var mc:Sprite = new Sprite();
			mc.addChild(bm);
			mc.x = 20;
			mc.y = 200;
			addChild(mc);
			mc.buttonMode = true;
			mc.addEventListener(MouseEvent.CLICK, saveFile);			
		}
		private function saveFile(e:MouseEvent):void {
			var ff:FileReference = new FileReference();
			ff.addEventListener(Event.OPEN, function(e:Event):void { trace("open");  } );
			ff.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void { trace("progress");  } );
			ff.addEventListener(Event.COMPLETE, function(e:Event):void { trace("complete");  } );
			ff.addEventListener(Event.CANCEL, function(e:Event):void { trace("cancel");  } );
			ff.addEventListener(Event.SELECT, function(e:Event):void { trace("select");  } );
			ff.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void { trace("ioError");  } );
			ff.save(txt.text,"saveTextTest.txt");

		}
	}
	
}

| | コメント (0) | トラックバック (0)

2008年5月26日 (月)

PixelBenderでカスタムフィルタ(セピア)

PixelBenderというもので作成したものをカスタムフィルタとして使えるようになった。
ということでちょっとやってみる。

PixelBenderをダウンロードしてチュートリアルとか他の人のサンプル見たけどよく分からず。。
とりあえずはPixelBenderの部分はGalleryにあるセピア色に変換するやつを使わせてもらって
ActionScriptのほうでどう書いたらそれが使えるのか、というのをやってみました。

PixelBenderのGalleryから。
http://labs.adobe.com/wiki/index.php/Sepia_hydra

ソースをコピペしてPixelBenderに貼り付け。
「nameSpace」は「namespace」(全部小文字)に変更。
「evaluatePixel()」には引数は渡せないっぽいので外に出してあげます。(ソースが古い?)

 :
input image3 source;
output pixel3 result;
void evaluatePixel()
 :

なんかイメージをロードしてセピア色になることを確認。
ByteCode(.pbj)で書き出します。

ASのほうではEmbedを使って埋め込むっぽいです。
[Embed(source = "test03.pbj", mimeType = "application/octet-stream")]
private var shaderData:Class;

カスタムフィルタを使うためにShaderとShaderFilterをimportします。
import flash.display.Shader;
import flash.filters.ShaderFilter;

フィルタを作成します。
var shader:Shader = new Shader(ByteArray(new shaderData()));
var shaderFilter:ShaderFilter = new ShaderFilter(shader);

この「shaderFilter」をBlurFilterとかと同じ感じで使ってあげればいいのかなと。
今回はBitmapDataにapplyFilterでこのカスタムフィルタを適用しています。
下記の<Main.as>のようなソースになります。

これでパブリッシュ、再生すると・・・エラーが出ます。。
「ArgumentError: Error #2162: The Shader output type is not compatible for this operation.」

PixelBenderで作成したやつのoutputがおかしいっぽいです。
ソース内の「image3」「pixel3」ですが、この「3」というのはRGBの3つという意味みたいです。たぶん。
これを「image4」「pixel4」にするとalphaも加わって「4」になります。
これでまた書き出しなおしてやってみるとエラーが出なくなりました!

(参考)http://www.libspark.org/browser/as3/Astro/ShaderFilter/CalcShader/src/CalcShader.as

以下、ソースです。
PixelBenderのほうのソースは基本コピペなのでここには載せないでおきます。

次はオリジナルのフィルタを・・・。

<Main.as>
package  {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.Shader;
	import flash.filters.ShaderFilter;
	import flash.geom.Point;
	import flash.utils.ByteArray;
	[SWF(width=600,height=400,backgroundColor=0xFFFFFF,frameRate=30)]


	public class Main extends Sprite {
		[Embed(source = "test03.pbj", mimeType = "application/octet-stream")]
		private var shaderData:Class;
		[Embed(source = "10075215.jpg")]
		private var Image0 : Class;
		
		public function Main() {
			var bmp : Bitmap = new Image0();
			var shader:Shader = new Shader(ByteArray(new shaderData()));
			var shaderFilter:ShaderFilter = new ShaderFilter(shader);
			var bd:BitmapData = new BitmapData(bmp.width, bmp.height, false, 0x000000);
			var bm:Bitmap = new Bitmap(bd);
			addChild(bm);
			bd.draw(bmp);
			bd.applyFilter(bd, bd.rect, new Point(), shaderFilter);
		}	
	}	
}


| | コメント (0) | トラックバック (0)

2008年5月21日 (水)

FlashPlayer10 軽くさわってみる。

FlashPlayer10のベータ版が出たってことでみんないろいろ試してるようなので俺もちょっと。。といってもあまり時間とれず。

まずどうやったらできるのか、ってとこで調べてたけど、↓ここが分かりやすかったので参考にしました。

http://blog.bk-zen.com/2008/05/16/62

FlashDepelop+Flex3SDKでやります。手軽にテストできていいですな。

ここのサンプル参考にとりあえず3D回転するのだけ作った。載せるほどのものでもないけども。

package  {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	
	[SWF(width=400,height=450,backgroundColor=0xFFFFFF,frameRate=30)]

	public class Main extends MovieClip {
		private var bd:BitmapData;
		private var bm:Bitmap;
		private var mc:Sprite;
		public function Main() {
			bd = new BitmapData(400, 300, false, 0x000000);
			bm = new Bitmap(bd);
			mc = new MovieClip();
			mc.addChild(bm);
			bm.x = -200;
			bm.y = -150;
			this.addChild(mc);
			mc.addEventListener(Event.ENTER_FRAME, loop);
			mc.x = 200;
			mc.y = 200;
		}
		private function loop(e:Event):void {
			e.target.rotationX += 5;
		}
	}
}

| | コメント (0) | トラックバック (0)

2008年5月 9日 (金)

JavascriptでProcessing

Processingをjsに移行したらしい。
http://ejohn.org/blog/processingjs/

IEじゃ動かなくて基本FireFoxなのと、動作もやっぱり重いのでまだ実用的ではないな。

でも、とりあえずこれ作ったやつすごい。

| | コメント (0) | トラックバック (0)

2008年4月30日 (水)

Box2D FLASH その8 BitmapDataでエフェクト2

Bitmapのエフェクトをさくーしゃさんの「第11回 大阪てら子でつくったやつ」っぽいのでやってみました。

中身はマウスでドラッグできます。ソースはコチラ

このエフェクトおもしろいですね。Pixelのでこういう使い方もあるんだーって感じです。頭使うといろいろできそうです。

| | コメント (0) | トラックバック (0)

2008年4月28日 (月)

Box2D FLASH その7 BitmapDataでエフェクト

Boxを複数作ってBitmapDataでなんかやってみました。

ちゃんとやればキレイな感じになるかも。当たった時にカコーンとかキーンとかってSE鳴らしたい。

ソースはこちら

| | コメント (0) | トラックバック (0)

2008年4月27日 (日)

Box2D FLASH その6 2つのオブジェクトをつなげる

Jointを使って2つのオブジェクトをつなげます。

今までのはオブジェクトが1個だけだったのでもう1個作成します。

//2つめのBOX作成
bodyDef.userData = new BoxFace();
bodyDef.userData.width = boxDef.extents.x * 2;
bodyDef.userData.height = boxDef.extents.y * 2;
bodyDef.position.x = 400;
bodyDef.position.y = 200;
var body2 = m_world.CreateBody(bodyDef);
addChild(bodyDef.userData);

1つめのBOXの横に同じBOXを作成します。
すでに1つ作成しているので、違う部分のみでできます。
userDataのとこも同じだけど表示するインスタンス自体はその数だけ
作らないといけません。

●Jointの作成
では2つのBOXをつなげます。

//Jointの作成
var jd:b2RevoluteJointDef = new b2RevoluteJointDef();
jd.enableLimit = true;
jd.lowerAngle = -50 / (180/Math.PI);
jd.upperAngle = 50 / (180/Math.PI);
jd.anchorPoint.Set(200, 200);
jd.body1 = body1;
jd.body2 = body2;
m_world.CreateJoint(jd);

> var jd:b2RevoluteJointDef = new b2RevoluteJointDef();
新しいJointを作成します。
なぜ「RevoluteJoint」という名前なのかは分かりません。

> jd.enableLimit = true;
Jointの曲がる角度を制限するか無制限にするかの設定です。
trueにするとこの後に設定する角度の範囲内で動きます。
falseにすると360度動きます。

> jd.lowerAngle = -50 / (180/Math.PI);
> jd.upperAngle = 50 / (180/Math.PI);
enableLimitをtrueにした場合にここで指定する角度の範囲内で
動くようになります。
上記の場合だと-50度~50度の範囲で動きます。

> jd.anchorPoint.Set(200, 200);
2つのオブジェクトがこの点を支点として動きます。
単に2つのオブジェクトをつなげる場合はどちらかのオブジェクトの
位置と同じにしておきます。
数字を(300, 100)とかに変えてみると違いが分かります。

> jd.body1 = body1;
> jd.body2 = body2;
つなげるオブジェクトを指定します。
body1、body2は型は「b2Body」です。CreateBodyの戻り値になってます。

> m_world.CreateJoint(jd);
作成したJointを登録します。

1つのJointでつなげられるオブジェクトは2つまでです。
複数のJointを使用すれば複数のオブジェクトをつなげることができます。

●Jointの描画
以上ので動くと思いますが、動作を分かりやすくするためJoint自体も
描画してあげます。
Jointを描画する機能もBox2D自体には無いので自作することになりますが
サンプルのものをそのまま使えば大丈夫です。

毎フレームで以下を実行します。

//Jointの描画
for (var jj:b2Joint = m_world.m_jointList; jj; jj = jj.m_next){
	DrawJoint(jj);
}

関数「DrawJoint」はいろいろなタイプのJointを描画するようになっています。
前回やったマウスとのJointもこれで描画されるようになります。

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;

	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Dynamics.Joints.*;
	import Box2D.Common.Math.*;
	public class Box2D0430Test05 extends Sprite {
		public var m_world:b2World;
		public var m_iterations:int = 1;
		public var m_timeStep:Number = 1/60;
		public var m_sprite:Sprite;
		public var m_physScale:Number = 1;
		private var m_mouseJoint:b2MouseJoint;
		function Box2D0430Test05() {
			init();
		}
		public function init():void {
			//Box2D 計算&描画用のENTER_FRAMEの設定
			addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
			
			//シェイプ描画用のSpriteを作成
			m_sprite = new Sprite();
			addChild(m_sprite);
			
			//Box2Dのステージを作成
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.minVertex.Set(-1000.0, -1000.0);
			worldAABB.maxVertex.Set(1000.0, 1000.0);
			var gravity:b2Vec2 = new b2Vec2(0.0, 100.0);	//重力(X方向、Y方向)
			var doSleep:Boolean = false;	//これまだ不明。。
			m_world = new b2World(worldAABB, gravity, doSleep);

			//BOXの作成
			var boxDef:b2BoxDef = new b2BoxDef();
			boxDef.extents.Set(40, 20);
			boxDef.density = 0.2;
			boxDef.friction = 0.8;
			boxDef.restitution = 0.6;
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.AddShape(boxDef);
			bodyDef.position.x = 200;
			bodyDef.position.y = 200;
			bodyDef.userData = new BoxFace();	//BoxFaceはリンケージしてあるもの
			bodyDef.userData.width = boxDef.extents.x * 2;
			bodyDef.userData.height = boxDef.extents.y * 2;
			var body1:b2Body = m_world.CreateBody(bodyDef);
			addChild(bodyDef.userData);
			
			//2つめのBOX作成
			bodyDef.userData = new BoxFace();
			bodyDef.userData.width = boxDef.extents.x * 2;
			bodyDef.userData.height = boxDef.extents.y * 2;
			bodyDef.position.x = 400;
			bodyDef.position.y = 200;
			var body2 = m_world.CreateBody(bodyDef);
			addChild(bodyDef.userData);
			
			//Jointの作成
			var jd:b2RevoluteJointDef = new b2RevoluteJointDef();
			jd.enableLimit = true;
			jd.lowerAngle = -50 / (180/Math.PI);
			jd.upperAngle = 50 / (180/Math.PI);
			jd.anchorPoint.Set(400, 200);
			jd.body1 = body1;
			jd.body2 = body2;
			m_world.CreateJoint(jd);
			
			//地面の作成
			//var boxDef:b2BoxDef = new b2BoxDef();
			boxDef.extents.Set(400, 20);
			boxDef.density = 0.0; //ここを0にすると動かない!
			boxDef.friction = 0.8;
			boxDef.restitution = 0.6;
			//var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.AddShape(boxDef);
			bodyDef.position.x = 400;
			bodyDef.position.y = 500;
			bodyDef.userData = { };
			bodyDef.userData.fillColor = 0x000000;
			m_world.CreateBody(bodyDef);
			//天井
			bodyDef.position.y = 0;
			m_world.CreateBody(bodyDef);
			//左の壁
			boxDef.extents.Set(20, 300);
			bodyDef.position.x = 0;
			bodyDef.position.y = 300;
			m_world.CreateBody(bodyDef);
			//右の壁
			bodyDef.position.x = 800;
			m_world.CreateBody(bodyDef);
			
			//マウスイベントを登録
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, mUp);
		}
		
		public function Update(e:Event):void {
			//Box2Dに計算をさせる
			m_world.Step(m_timeStep, m_iterations);
			
			//シェイプを全て削除
			this.m_sprite.graphics.clear();
			
			for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next) {
				if (bb.m_userData is Sprite) {
					//bb.m_userDataがSprite、またはMovieClipの時
					bb.m_userData.x = bb.m_position.x;
					bb.m_userData.y = bb.m_position.y;
					bb.m_userData.rotation = bb.m_rotation * (180/Math.PI);
				} else {
					//上記以外の時。この場合はシェイプ
					for (var s:b2Shape = bb.GetShapeList(); s != null; s = s.GetNext()) {
						drawShape(s, bb.m_userData.fillColor);
					}   
				}
			}
			//Jointの描画
			for (var jj:b2Joint = m_world.m_jointList; jj; jj = jj.m_next){
				DrawJoint(jj);
			}
		}
		
		public function drawShape(_shape:b2Shape, _fillColor:int = 0xFFFFFF):void {
			//シェイプの描画 このへんはコピペで。。
			var i:int;
			var v:b2Vec2;
			var poly:b2PolyShape = _shape as b2PolyShape;
			var tV:b2Vec2 = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
			m_sprite.graphics.beginFill(_fillColor, 1);
			m_sprite.graphics.lineStyle(0, 0, 0);
			m_sprite.graphics.moveTo(tV.x * m_physScale, tV.y * m_physScale);
			for (i = 0; i < poly.m_vertexCount; ++i){
				v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
				m_sprite.graphics.lineTo(v.x * m_physScale, v.y * m_physScale);
			}
			m_sprite.graphics.lineTo(tV.x * m_physScale, tV.y * m_physScale);
			m_sprite.graphics.endFill();
		}

		private function mDown(e:MouseEvent):void {
			var body:b2Body = GetBodyAtMouse();	//マウスの下にあるオブジェクトを取得
			if (body) {
				var md:b2MouseJointDef = new b2MouseJointDef();
				md.body1 = m_world.m_groundBody;
				md.body2 = body;
				md.target.Set(mouseX, mouseY);
				md.maxForce = 30000.0 * body.m_mass;
				md.timeStep = m_timeStep;
				m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
				body.WakeUp();
				
				addEventListener(Event.ENTER_FRAME, MouseDrag);
			}
		}
		private function mUp(e:MouseEvent):void {
			if (m_mouseJoint) {
				m_world.DestroyJoint(m_mouseJoint);
				m_mouseJoint = null;
				removeEventListener(Event.ENTER_FRAME, MouseDrag);
			}
		}
		public function MouseDrag(e:Event):void{
			var p2:b2Vec2 = new b2Vec2(mouseX, mouseY);
			m_mouseJoint.SetTarget(p2);
		}

		private var mousePVec:b2Vec2 = new b2Vec2();
		public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
			// Make a small box.
			mousePVec.Set(mouseX, mouseY);
			var aabb:b2AABB = new b2AABB();
			aabb.minVertex.Set(mouseX - 0.001, mouseY - 0.001);
			aabb.maxVertex.Set(mouseX + 0.001, mouseY + 0.001);
			// Query the world for overlapping shapes.
			var k_maxCount:int = 10;
			var shapes:Array = new Array();
			var count:int = m_world.Query(aabb, shapes, k_maxCount);
			var body:b2Body = null;
			for (var i:int = 0; i < count; ++i) {
				if (shapes[i].m_body.IsStatic() == false || includeStatic) {
					var inside:Boolean = shapes[i].TestPoint(mousePVec);
					if (inside) {
						body = shapes[i].m_body;
						break;
					}
				}
			}
			return body;
		}
		public function DrawJoint(joint:b2Joint):void {
			var b1:b2Body = joint.m_body1;
			var b2:b2Body = joint.m_body2;
			var x1:b2Vec2 = b1.m_position;
			var x2:b2Vec2 = b2.m_position;
			var p1:b2Vec2 = joint.GetAnchor1();
			var p2:b2Vec2 = joint.GetAnchor2();
			m_sprite.graphics.lineStyle(1,0x44aaff,1/1);
			switch (joint.m_type) {
			case b2Joint.e_distanceJoint:
			case b2Joint.e_mouseJoint:
				m_sprite.graphics.moveTo(p1.x * m_physScale, p1.y * m_physScale);
				m_sprite.graphics.lineTo(p2.x * m_physScale, p2.y * m_physScale);
				break;
				
			case b2Joint.e_pulleyJoint:
				var pulley:b2PulleyJoint = joint as b2PulleyJoint;
				var s1:b2Vec2 = pulley.GetGroundPoint1();
				var s2:b2Vec2 = pulley.GetGroundPoint2();
				m_sprite.graphics.moveTo(s1.x * m_physScale, s1.y * m_physScale);
				m_sprite.graphics.lineTo(p1.x * m_physScale, p1.y * m_physScale);
				m_sprite.graphics.moveTo(s2.x * m_physScale, s2.y * m_physScale);
				m_sprite.graphics.lineTo(p2.x * m_physScale, p2.y * m_physScale);
				break;
				
			default:
				if (b1 == m_world.m_groundBody) {
					m_sprite.graphics.moveTo(p1.x * m_physScale, p1.y * m_physScale);
					m_sprite.graphics.lineTo(x2.x * m_physScale, x2.y * m_physScale);
				} else if (b2 == m_world.m_groundBody) {
					m_sprite.graphics.moveTo(p1.x * m_physScale, p1.y * m_physScale);
					m_sprite.graphics.lineTo(x1.x * m_physScale, x1.y * m_physScale);
				} else{
					m_sprite.graphics.moveTo(x1.x * m_physScale, x1.y * m_physScale);
					m_sprite.graphics.lineTo(p1.x * m_physScale, p1.y * m_physScale);
					m_sprite.graphics.lineTo(x2.x * m_physScale, x2.y * m_physScale);
					m_sprite.graphics.lineTo(p2.x * m_physScale, p2.y * m_physScale);
				}
			}
		}
	}
}

| | コメント (0) | トラックバック (0)

2008年4月26日 (土)

Box2D FLASH その5 マウスでドラッグ

位置に移動させるとかstartDragを使うとかありますが、そういう感じでは
ないみたいです。
(こういうのと同じ感覚でできるやり方もあるかもしれないけど未確認)

Box2Dでは「Joint」というものでオブジェクト同士をつなげることができます。
マウスでオブジェクトをドラッグする場合はマウスとオブジェクトを
この「Joint」でつなげることによって「マウスでオブジェクトを引っ張る」
ことができ、「オブジェクトをマウスでドラッグする」ように見せられます。

●ドラッグの対象となるオブジェクトを取得
ドラッグを開始させるタイミングMOUSE_DOWNイベントが発生した時になります。
各オブジェクトにMOUSE_DOWNイベントを登録すれば良さそうですが、
このサンプルでは違います。(でもこの方法もできそうな気がします)
サンプルではstageに対してMOUSE_DOWNイベントを登録し、MOUSE_DOWNされたら
その時マウスの下にあるオブジェクトを調べています。

Box2Dにそういう関数が用意されているわけではないみたいで、
サンプルでは「GetBodyAtMouse」という関数の中にこの処理が
がーっと書かれています。
中身はなんとなく分かるけど、ここは「おまじない」としてしまいます。。

●Jointの作成
ドラッグの対象となるオブジェクトを取得できたら、マウスとそのオブジェクトを
Jointでつなげます。
関数「mDown」内の↓この部分です。

var md:b2MouseJointDef = new b2MouseJointDef();
md.body1 = m_world.m_groundBody;
md.body2 = body;
md.target.Set(mouseX, mouseY);
md.maxForce = 30000.0 * body.m_mass;
md.timeStep = m_timeStep;
m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
body.WakeUp();

> var md:b2MouseJointDef = new b2MouseJointDef();
Jointを作成します。マウスとつなげるので「b2MouseJointDef」を使います。

> md.body1 = m_world.m_groundBody;
> md.body2 = body;
つなげるオブジェクトをここで指定します。
マウスとつなげる場合はこうする、と思ってください。。

> md.target.Set(mouseX, mouseY);
ターゲットです。通常はマウスの座標を入れます。
ちょっとずらしたい場合とかはいじってください。

> md.maxForce = 30000.0 * body.m_mass;
マウス動かしたときのオブジェクトが引っ張られる力です。
この値を大きくするとマウス動かしたときにすぐマウスの位置にきます。
逆に小さくすると少し遅れてマウスの位置に来るようになります。

> md.timeStep = m_timeStep;
1回の処理で経過させる時間です。
通常は「m_world.Step(m_timeStep, m_iterations);」で指定する
「m_timeStep」と同じでいいと思います。

> m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
作成したJointを登録させます。

> body.WakeUp();
これはまだよく分からない。。

Jointが作成されました。
マウスは動くのでJointのターゲットは常に更新する必要があります。
そのためのENTER_FRAMEを用意します。

addEventListener(Event.ENTER_FRAME, MouseDrag);

内容はコチラ。

public function MouseDrag(e:Event):void{
	var p2:b2Vec2 = new b2Vec2(mouseX, mouseY);
	m_mouseJoint.SetTarget(p2);
}

Jointのターゲットをその時のマウスの座標に変更します。
これでマウスにJointが引っ張られて、それによってつながっている
オブジェクトも引っ張られていきます。

マウスを離したらJointを削除します。

private function mUp(e:MouseEvent):void {
	if (m_mouseJoint) {
		m_world.DestroyJoint(m_mouseJoint);
		m_mouseJoint = null;
		removeEventListener(Event.ENTER_FRAME, MouseDrag);
	}
}

これでドラッグできるようになりました。

テストしにくいので、天井と壁も作成してます。

//天井
bodyDef.position.y = 0;
m_world.CreateBody(bodyDef);
//左の壁
boxDef.extents.Set(20, 300);
bodyDef.position.x = 0;
bodyDef.position.y = 300;
m_world.CreateBody(bodyDef);
//右の壁
bodyDef.position.x = 800;
m_world.CreateBody(bodyDef);

boxDef、bodyDefはすでに作成してあるので、違う部分だけ
設定しなおしてCreateBodyに渡してあげればOKです。

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Dynamics.Joints.*;
	import Box2D.Common.Math.*;
	public class Box2D0430Test04 extends Sprite {
		public var m_world:b2World;
		public var m_iterations:int = 1;
		public var m_timeStep:Number = 1/60;
		public var m_sprite:Sprite;
		public var m_physScale:Number = 1;
		private var m_mouseJoint:b2MouseJoint;
		function Box2D0430Test04() {
			init();
		}
		public function init():void {
			//Box2D 計算&描画用のENTER_FRAMEの設定
			addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
			
			//シェイプ描画用のSpriteを作成
			m_sprite = new Sprite();
			addChild(m_sprite);
			
			//Box2Dのステージを作成
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.minVertex.Set(-1000.0, -1000.0);
			worldAABB.maxVertex.Set(1000.0, 1000.0);
			var gravity:b2Vec2 = new b2Vec2(0.0, 100.0);	//重力(X方向、Y方向)
			var doSleep:Boolean = false;	//これまだ不明。。
			m_world = new b2World(worldAABB, gravity, doSleep);

			//BOXの作成
			var boxDef:b2BoxDef = new b2BoxDef();
			boxDef.extents.Set(40, 20);
			boxDef.density = 0.2;
			boxDef.friction = 0.8;
			boxDef.restitution = 0.6;
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.AddShape(boxDef);
			bodyDef.position.x = 200;
			bodyDef.position.y = 200;
			bodyDef.userData = new BoxFace();	//BoxFaceはリンケージしてあるもの
			bodyDef.userData.width = boxDef.extents.x * 2;
			bodyDef.userData.height = boxDef.extents.y * 2;
			var body:b2Body = m_world.CreateBody(bodyDef);
			addChild(bodyDef.userData);
			
			//地面の作成
			//var boxDef:b2BoxDef = new b2BoxDef();
			boxDef.extents.Set(400, 20);
			boxDef.density = 0.0; //ここを0にすると動かない!
			boxDef.friction = 0.8;
			boxDef.restitution = 0.6;
			//var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.AddShape(boxDef);
			bodyDef.position.x = 400;
			bodyDef.position.y = 500;
			bodyDef.userData = { };
			bodyDef.userData.fillColor = 0x000000;
			m_world.CreateBody(bodyDef);
			//天井
			bodyDef.position.y = 0;
			m_world.CreateBody(bodyDef);
			//左の壁
			boxDef.extents.Set(20, 300);
			bodyDef.position.x = 0;
			bodyDef.position.y = 300;
			m_world.CreateBody(bodyDef);
			//右の壁
			bodyDef.position.x = 800;
			m_world.CreateBody(bodyDef);
			
			//マウスイベントを登録
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, mUp);
		}
		
		public function Update(e:Event):void {
			//Box2Dに計算をさせる
			m_world.Step(m_timeStep, m_iterations);
			
			//シェイプを全て削除
			this.m_sprite.graphics.clear();
			
			for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next) {
				if (bb.m_userData is Sprite) {
					//bb.m_userDataがSprite、またはMovieClipの時
					bb.m_userData.x = bb.m_position.x;
					bb.m_userData.y = bb.m_position.y;
					bb.m_userData.rotation = bb.m_rotation * (180/Math.PI);
				} else {
					//上記以外の時。この場合はシェイプ
					for (var s:b2Shape = bb.GetShapeList(); s != null; s = s.GetNext()) {
						drawShape(s, bb.m_userData.fillColor);
					}   
				}
			}
		}
		
		public function drawShape(_shape:b2Shape, _fillColor:int = 0xFFFFFF):void {
			//シェイプの描画 このへんはコピペで。。
			var i:int;
			var v:b2Vec2;
			var poly:b2PolyShape = _shape as b2PolyShape;
			var tV:b2Vec2 = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
			m_sprite.graphics.beginFill(_fillColor, 1);
			m_sprite.graphics.lineStyle(0, 0, 0);
			m_sprite.graphics.moveTo(tV.x * m_physScale, tV.y * m_physScale);
			for (i = 0; i < poly.m_vertexCount; ++i){
				v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
				m_sprite.graphics.lineTo(v.x * m_physScale, v.y * m_physScale);
			}
			m_sprite.graphics.lineTo(tV.x * m_physScale, tV.y * m_physScale);
			m_sprite.graphics.endFill();
		}
		
		private function mDown(e:MouseEvent):void {
			var body:b2Body = GetBodyAtMouse();	//マウスの下にあるオブジェクトを取得
			if (body) {
				var md:b2MouseJointDef = new b2MouseJointDef();
				md.body1 = m_world.m_groundBody;
				md.body2 = body;
				md.target.Set(mouseX, mouseY);
				md.maxForce = 30000.0 * body.m_mass;
				md.timeStep = m_timeStep;
				m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
				body.WakeUp();
				
				addEventListener(Event.ENTER_FRAME, MouseDrag);
			}
		}
		private function mUp(e:MouseEvent):void {
			if (m_mouseJoint) {
				m_world.DestroyJoint(m_mouseJoint);
				m_mouseJoint = null;
				removeEventListener(Event.ENTER_FRAME, MouseDrag);
			}
		}
		public function MouseDrag(e:Event):void{
			var p2:b2Vec2 = new b2Vec2(mouseX, mouseY);
			m_mouseJoint.SetTarget(p2);
		}

		private var mousePVec:b2Vec2 = new b2Vec2();
		public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
			// Make a small box.
			mousePVec.Set(mouseX, mouseY);
			var aabb:b2AABB = new b2AABB();
			aabb.minVertex.Set(mouseX - 0.001, mouseY - 0.001);
			aabb.maxVertex.Set(mouseX + 0.001, mouseY + 0.001);
			// Query the world for overlapping shapes.
			var k_maxCount:int = 10;
			var shapes:Array = new Array();
			var count:int = m_world.Query(aabb, shapes, k_maxCount);
			var body:b2Body = null;
			for (var i:int = 0; i < count; ++i) {
				if (shapes[i].m_body.IsStatic() == false || includeStatic) {
					var inside:Boolean = shapes[i].TestPoint(mousePVec);
					if (inside) {
						body = shapes[i].m_body;
						break;
					}
				}
			}
			return body;
		}
	}
}

| | コメント (1) | トラックバック (0)

«Box2D FLASH その4 MovieClipの表示