QuickBox2Dを試してみた 衝突

matsu45122010-08-05



今回は衝突の検出についてです。
衝突した位置と衝突時の力の大きさの取得をやってみました。

サンプル
衝突を検出した座標に円を描画。


ソース

package
{
	import com.actionsnippet.qbox.*;
	
	import flash.display.*;
	import flash.events.Event;
	
	import org.libspark.betweenas3.BetweenAS3;
	import org.libspark.betweenas3.tweens.ITween;
	
	[SWF(backgroundColor=0, width=500, height=500)]
	public class QB_5 extends MovieClip
	{
		private const SCALE:int = 30, BALL:int=10, W:int=500, H:int=500;
		private var sim:QuickBox2D;
		private var contact:QuickContacts;
		public function QB_5()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			sim = new QuickBox2D(this);
			sim.createStageWalls();
			
			for(var i:int = 0; i < BALL; i++)
				sim.addCircle({x:Math.random()*500/SCALE, y:Math.random()*500/SCALE, radius:(Math.random()*20+10)/SCALE});
			
			//衝突イベントの登録
			contact = sim.addContactListener();
			contact.addEventListener(QuickContacts.ADD, onContact);
			
			sim.mouseDrag();
			sim.start();
		}
		
		private function onContact(e:Event):void{
			var circle:Sprite = new Sprite();
			circle.graphics.lineStyle(3,0xFF0000);
			circle.graphics.drawCircle(0,0,5);
			addChild(circle);
			//衝突した座標の取得
			circle.x=contact.currentPoint.position.x*SCALE;
			circle.y=contact.currentPoint.position.y*SCALE;
			//力の取得
			var stl:Number = contact.currentPoint.velocity.Length()/5;
			//力の大きさに合わせて円を大きくする
			var t:ITween = BetweenAS3.to(circle, {scaleX:stl, scaleY:stl, alpha:0}, 1);
			t.onCompleteParams = [circle];
			t.onComplete = function(c:Sprite):void{removeChild(c)};
			t.play();
		}
	}
}

QuickBox2Dを試してみた Joint

matsu45122010-08-04

Jointを利用して棒人間でも作ろうと思ったけどなかなかうまくいかない。。。


サンプル

package
{
	import com.actionsnippet.qbox.QuickBox2D;
	import com.actionsnippet.qbox.QuickObject;
	
	import flash.display.MovieClip;
	
	public class QB_3 extends MovieClip
	{
		private var SCALE:int = 30;
		public function QB_3()
		{
			//計算結果を画面に描画
			var sim:QuickBox2D = new QuickBox2D(this);	
			
			//画面に合わせて壁を作る
			sim.createStageWalls(); 
			
			var head:QuickObject = sim.addCircle( { x:stage.stageWidth/2/SCALE, y:stage.stageHeight/2/SCALE, radius:5/SCALE, lineThickness:3, fillAlpha:0} );
			var body:QuickObject = sim.addBox( { x:stage.stageWidth/2/SCALE, y:(stage.stageHeight/2+10)/SCALE, width:1/SCALE, height:10/SCALE, fillColor:0} );
			var leftHand:QuickObject = sim.addBox( { x:body.x-1/SCALE, y:body.y, width:1/SCALE, height:5/SCALE, fillColor:0} );
			var rightHand:QuickObject = sim.addBox( { x:body.x+1/SCALE, y:body.y, width:1/SCALE, height:5/SCALE, fillColor:0} );
			var leftLeg:QuickObject = sim.addBox( { x:body.x-1/SCALE, y:body.y, width:1/SCALE, height:5/SCALE, fillColor:0} );
			var rightLeg:QuickObject = sim.addBox( { x:body.x+1/SCALE, y:body.y, width:1/SCALE, height:5/SCALE, fillColor:0} );
			
			//bodyとheadを繋げる。この二つのオブジェクトの衝突判定を無効にする。
			sim.addJoint({a:body.body, b:head.body, collideConnected:false, x1:stage.stageWidth/2/SCALE, y1:(stage.stageHeight/2+5)/SCALE});
			sim.addJoint({a:body.body, b:leftHand.body, collideConnected:false, x1:body.x, y1:body.y-2/SCALE, y2:leftHand.y-2/SCALE, x2:leftHand.x});
			sim.addJoint({a:body.body, b:rightHand.body, collideConnected:false, x1:body.x, y1:body.y-2/SCALE, y2:rightHand.y-2/SCALE, x2:rightHand.x});
			sim.addJoint({a:body.body, b:leftLeg.body, collideConnected:false, x1:body.x, y1:body.y+1/SCALE, y2:leftLeg.y-2/SCALE, x2:leftLeg.x});
			sim.addJoint({a:body.body, b:rightLeg.body, collideConnected:false, x1:body.x, y1:body.y+1/SCALE, y2:rightLeg.y-2/SCALE, x2:rightLeg.x});
			//シュミレーション開始
			sim.start(); 
			
			// オブジェをドラッグ出来るようになる
			sim.mouseDrag(); 
		}
	}
}

QuickBox2Dを試してみた オブジェクトの生成


今回はオブジェクトの生成とマウスドラッグとか。


QuickBox2Dダウンロード
http://actionsnippet.com/?page_id=1391


公式サンプルをちょっとだけ改造したものです。
デモ


ソース

package
{
	import com.actionsnippet.qbox.QuickBox2D;
	import com.actionsnippet.qbox.QuickObject;
	
	import flash.display.MovieClip;
	import flash.events.Event;
	
	public class QB_1 extends MovieClip
	{
		private var bar:QuickObject;
		
		public function QB_1()
		{
			//計算結果を画面に描画  コメントアウト外すとデバッグモード
			var sim:QuickBox2D = new QuickBox2D(this/*, { debug:true }*/);	
			
			//各パラメタのデフォルト値の設定
			sim.setDefault({fillColor:0xFF, lineAlpha:0, radius:1.5}); 
			
			//画面に合わせて壁を作る
			sim.createStageWalls(); 
			
			//四角を作る。isBullet:trueで計算の項精度化
			sim.addBox( { x:5, y:5, width:1, height:1, lineColor:0xFF0000, fillColor:0xFFFF00, isBullet:true} ); 
			
			//円を作る
			sim.addCircle( { x:4, y:8, radius:2, lineThickness:3, lineAlpha:0.5} ); 
			
			//densityを0にすることで固定オブジェになる
			bar = sim.addBox( { x:stage.stageWidth/2/30, y:stage.stageHeight/2/30, width:4, height:.3, angle:0.3, density:0, fillColor:0xCC0000 } );
			
			//多角形の生成
			sim.addPoly( { x:15, y:8, verts:[[0, 0, 2, 2, 0, 2]], fillColor:0x00FF00} );

			//シュミレーション開始
			sim.start(); 
			
			// オブジェをドラッグ出来るようになる
			sim.mouseDrag(); 
			
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		private function onEnterFrame(e:Event):void{
			bar.angle += 0.02;
		}
	}
}

addChildとcopyPixels

画面上に大量弾を表示させて動かす場合、addChildとBitmapData.copyPixelsを使うのではどのくらい実行速度が違うのかを試してみました。


こちら

下の数字が現在画面上に存在する弾の数で、スライダーを動かすと弾の量を変更できます。


ソース

package
{
	import com.bit101.components.*;
	
	import flash.display.*;
	import flash.events.*;
	import flash.filters.GlowFilter;
	import flash.geom.*;
	import flash.text.TextField;

	[SWF(backgroundColor=0, frameRate=60, width=500, height=700)]
	public class CopyPixelsTest extends Sprite
	{
		
		private var nt:TextField = new TextField();
		private var list:Vector.<A>;
		private var list2:Vector.<B>;
		private var angle:Number = 0;
		private var n:int = 50;
		private var bmpData:BitmapData;
		private var view:Bitmap, viewData:BitmapData;
		private var rect:Rectangle;
		private var bw_2:int, bh_2:int, w:int=500, h:int=500, w_2:int=250, h_2:int=250;
		private var point:Point = new Point();
		private var slider:HSlider;
		
		public function CopyPixelsTest()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			list = new Vector.<A>;
			list2 = new Vector.<B>;
			viewData = new BitmapData(w,h, false, 0);
			view = new Bitmap(viewData);
			
			normal();
			
			new PushButton(this, 300, 600, "addChild", normal);
			new PushButton(this, 300, 630, "copyPixels", copy);
			slider = new HSlider(this, 300, 660, slide);
			slider.maximum = 200;
			slider.minimum = 10;
			slider.value = 50;
			addChild(nt);
			
			nt.y = 550;
			nt.x = 150;
			nt.textColor = 0xFFFFFF;
			addChild(new Stats()).y = 550;
		}
		
		private function slide(e:Event):void{
			n = int(slider.value);
		}
		
		private function normal(e:MouseEvent=null):void{
			resetCopy();
			addEventListener(Event.ENTER_FRAME, onFrame1);
		}
		
		private function resetCopy():void{
			removeEventListener(Event.ENTER_FRAME, onFrame2);
			viewData.fillRect(viewData.rect, 0);
			//removeChild(view);
			list2 = new Vector.<B>;
		}
		
		private function onFrame1(e:Event):void{
			nt.text = list.length.toString();
			for(var i:int = 0; i < n; i++){
				var a:A = new A();
				a.vx = Math.cos(angle)*5;
				a.vy = Math.sin(angle)*5;
				a.x = w_2;
				a.y = h_2;
				addChild(a);
				list.push(a);
				angle += 0.015;
			}
			i = list.length;
			while(i--){
				a = list[i];
				a.x += a.vx;
				a.y += a.vy;
				if(a.x < 0 || a.y < 0 || w < a.x || h < a.y){
					removeChild(a);
					list.splice(i,1);
					a = null;
				}
			}
		}
		
		
		private function copy(e:MouseEvent):void{
			resetNormal();
			addChild(view);
			
			var m:Matrix = new Matrix();
			m.createGradientBox(20,20,0,-10,-10);
			var sp:Sprite = new Sprite();
			sp.graphics.beginGradientFill(GradientType.RADIAL, [0xFFFFFF, 0xFF0000], [1.0,1.0], [0, 255], m);
			sp.graphics.drawCircle(0,0,10);
			sp.graphics.endFill();
			bmpData = new BitmapData(sp.width, sp.height, true, 0xFFFFFF);
			bmpData.draw(sp, new Matrix(1, 0, 0, 1, sp.width/2, sp.height/2));
			rect = bmpData.rect;
			bw_2 = bmpData.width/2;
			bh_2 = bmpData.height/2;
			
			addEventListener(Event.ENTER_FRAME, onFrame2);
		}
		
		private function resetNormal():void{
			removeEventListener(Event.ENTER_FRAME, onFrame1);
			for(var i:int = 0; i < list.length; i++){
				removeChild(list[i]);
			}
			list = new Vector.<A>();
		}
		
		private function onFrame2(e:Event):void{
			nt.text = list2.length.toString();
			viewData.lock();
			viewData.colorTransform(viewData.rect, new ColorTransform(0.8, 0.8, 0.8));
			for(var i:int = 0; i < n; i++){
				var a:B = new B();
				a.vx = Math.cos(angle)*5;
				a.vy = Math.sin(angle)*5;
				a.x = w_2;
				a.y = h_2;
				list2.push(a);
				angle += 0.015;
			}
			i = list2.length;
			while(i--){
				a = list2[i];
				a.x += a.vx;
				a.y += a.vy;
				if(a.x < 0 || a.y < 0 || w < a.x || h < a.y){
					list2.splice(i,1);
					a = null;
					continue;
				}
				
				point.x = a.x-bw_2;
				point.y = a.y-bh_2;
				viewData.copyPixels(bmpData, rect, point);
			}
			viewData.unlock();
		}
	}
}
import flash.display.*;
import flash.filters.GlowFilter;
import flash.geom.Matrix;

class A extends Shape{
	public var vx:Number, vy:Number;
	public function A():void{
		var m:Matrix = new Matrix();
		m.createGradientBox(20,20,0,-10,-10);
		graphics.beginGradientFill(GradientType.RADIAL, [0xFFFFFF, 0xFF0000], [1.0,1.0], [0, 255], m);
		graphics.drawCircle(0,0,10);
		graphics.endFill();
	}
}

class B{
	public var x:Number, y:Number, vx:Number, vy:Number;
}

100行パズル

100行パズル



ソースコード大体100行くらいでパズルを作ってみました。
画像の分割数はスライダーで調整できます。
shuffleボタンで入れ替えてからゲーム開始(特にエフェクトとかはありません)。


100行パズル


ソースコード

package
{
	import com.bit101.components.*;
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	[SWF(backgroundColor=0x0, width=500, height=600)]
	public class Pazzle extends Sprite
	{
		[Embed(source="Sunset.jpg")]
		private var Picture:Class;
		private var bmp:Bitmap, bmpData:BitmapData;
		private var bmpList:Array=[];
		private var rowsSlider:HSlider, columnSlider:HSlider;
		private var blank:Panel=null;
		
		public function Pazzle()
		{
			//画像をBitmapに
			var picture:Bitmap = new Picture();
			var sx:Number = 400/picture.width, sy:Number = 400/picture.height;
			bmpData = new BitmapData(400, 400);
			bmpData.draw(picture, new Matrix(sx, 0, 0, sy));
			bmp = new Bitmap(bmpData);
			//ボタンとスライダーの生成
			new PushButton(this, 50, 550, "shuffle", shuffle);
			rowsSlider = new HSlider(this, 200, 550, split);
			columnSlider = new HSlider(this, 350, 550, split);
			rowsSlider.maximum = columnSlider.maximum = 10;
			rowsSlider.minimum = columnSlider.minimum = 1;
			rowsSlider.value = columnSlider.value = 3;
			//まずは3x3で分割
			split();
		}
		
		//シャッフル!!
		private function shuffle(e:MouseEvent):void{
			var l:int = bmpList.length;
			var newArr:Array = bmpList;
			while(l--) if(!bmpList[l].visible)bmpList[l].visible=true;
			l = bmpList.length;
			while(l){
				var m:int = Math.floor(Math.random()*l);
				l--;
				var n:Panel = newArr[l];
				newArr[l] = newArr[m];
				var tx:int = n.x, ty:int = n.y, ti:int = n.i, tj:int = n.j;
				n.x = newArr[l].x; n.y = newArr[l].y; n.i = newArr[l].i; n.j = newArr[l].j;
				newArr[l].x = tx;  newArr[l].y = ty; newArr[l].i = ti; newArr[l].j = tj;
				newArr[m] = n;
			}
			//一番左上に来たものを消す
			bmpList[0].visible=false;
			blank = bmpList[0];
			bmpList = newArr;
		}
		
		//分割!
		private function split(e:Event=null):void{
			for(var i:int = 0; i < bmpList.length; i++){
				removeChild(bmpList[i]);
				bmpList[i].removeEventListener(MouseEvent.CLICK, onClick);
			}
			
			bmpList = [];
			var rows:int=rowsSlider.value, columns:int=columnSlider.value;
			var w:int = bmp.width/columns, h:int = bmp.height/rows;
			//画像を分割して配置
			var rect:Rectangle = new Rectangle(0,0,w,h);
			for(i = 0; i < rows; i++){
				for(var j:int = 0; j < columns; j++){
					var bd:BitmapData = new BitmapData(w, h);
					rect.x = j*w; rect.y = i*h;
					bd.copyPixels(bmpData, rect, new Point(0,0));
					var b:Panel = new Panel(bd, i*columns+j, i, j);
					b.addEventListener(MouseEvent.CLICK, onClick);
					b.x = j*w+j*5; b.y = i*h+i*5+50;
					addChild(b);
					bmpList.push(b);
				}
			}
		}
		
		private function onClick(e:MouseEvent):void{
			if(blank==null)return;
			var p:Panel = e.target as Panel;
			//移動可能なら入れ替え
			var d:int = Math.abs(p.i-blank.i)+Math.abs(p.j-blank.j);
			if(d != 1) return;
			swap(p,blank);
		}
		
		//入れ替え!
		private function swap(p1:Panel, p2:Panel):void{
			var tx:int = p1.x, ty:int = p1.y, ti:int = p1.i, tj:int = p1.j;
			p1.x = p2.x; p1.y = p2.y; p1.i = p2.i; p1.j = p2.j;
			p2.x = tx; p2.y = ty; p2.i = ti; p2.j = tj;
			bmpList[p1.i*columnSlider.value+p1.j] = p1;
			bmpList[p2.i*columnSlider.value+p2.j] = p2;
		}
	}
}

import flash.display.*;
class Panel extends Sprite{
	public var id:int, i:int, j:int;
	public function Panel(bmpData:BitmapData, id:int, i:int, j:int){
		addChild(new Bitmap(bmpData));
		this.id = id; this.i = i; this.j = j;
	}
}

jsdo.itを試してみた。

jsdo.itというサービスを利用してみました。
wonderflのJavascript版ですね。


早速色々と投稿。コードと実際動いているものはリンクで飛べば見れます。

particle



particle2



particle3




Colorful Spring Particle JS version

これは以前wonderflに投稿したものJavascriptに移植してみました。
Glowの効果はグラデーションで作成。
あとグラデーションの作り方はASよりもJSの方が簡単でした。


今回Javascript + HTML5で作ってみて、意外と簡単に再現できるものだと感じました。
でもBlurとかGlowの効果を作るのが面倒くさいです。ただやり方を知らないだけかもしれませんが。

jsdo.itのおかげで今JSが熱いです!