package {
import Box2D.Collision.Shapes.b2CircleDef;
import Box2D.Collision.Shapes.b2PolygonDef;
import Box2D.Collision.Shapes.b2Shape;
import Box2D.Collision.b2AABB;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.Joints.b2MouseJoint;
import Box2D.Dynamics.Joints.b2MouseJointDef;
import Box2D.Dynamics.Joints.b2RevoluteJointDef;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2World;
import General.FpsCounter;
import General.Input;
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.getTimer;
[SWF(width=640, height=480, backgroundColor=0x666666, frameRate=30)]
public class FallBall extends Sprite
{
public static var SCREEN_C:uint = 0x666666,
SCREEN_W:int = 640,
SCREEN_H:int = 480,
NX_SCALE:Number = 30.0,
NX_ITER:Number = 10,
NX_STEP:Number = 1/30,
NX_BALL_NUM:int = 10,
NX_BALL_R:int = 10;
private var base:Sprite;
private var balls:Array;
private var world:b2World;
private var mousejoint:b2MouseJoint;
private var mousepos:b2Vec2 = new b2Vec2;
private var mousepos_scale:b2Vec2 = new b2Vec2;
private var input:Input;
static public var fpsCounter:FpsCounter = new FpsCounter();
private var tf:TextField;
private var is_start:Boolean = false;
public function FallBall()
{
initFallBase();
initFallWorld();
}
private function initFallBase():void
{
// ベース画面設定
this.base = new Sprite();
this.addChild( this.base );
// 入力初期化
this.input = new Input( this.base );
// 文字
var fmt:TextFormat = new TextFormat();
fmt.size = 50;
this.tf = new TextField();
this.tf.text = "Click Start";
this.tf.autoSize = TextFieldAutoSize.CENTER;
this.tf.x = SCREEN_W / 2 - tf.width / 2;
this.tf.y = SCREEN_H / 2 - tf.height / 2;
this.tf.setTextFormat( fmt );
this.base.addChild( this.tf );
this.is_start = false;
// fps
fpsCounter.x = 15;
fpsCounter.y = 30;
this.addChildAt( fpsCounter, 0 );
// イベント設定
this.base.addEventListener( Event.ENTER_FRAME, procFallBall, false, 0, true );
}
/**
* @brief ワールド初期化
*/
private function initFallWorld():void
{
// 衝突の世界を構築
// 判定パラメータ設定
var worldaabb:b2AABB = new b2AABB();
worldaabb.lowerBound.Set( -1000.0, -1000.0 );
worldaabb.upperBound.Set( 1000.0, 1000.0 );
// 重力設定
var gravity:b2Vec2 = new b2Vec2( 0.0, 10.0 );
// 判定外のオブジェクトをスリープさせるか
var doSleep:Boolean = true;
this.world = new b2World( worldaabb, gravity, doSleep );
/*
// デバッグ表示
var dbgDraw:b2DebugDraw = new b2DebugDraw();
dbgDraw.m_sprite = this.base;
dbgDraw.m_drawScale = NX_SCALE;
dbgDraw.m_fillAlpha = 0.8;
dbgDraw.m_lineThickness = 1.0;
dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
this.world.SetDebugDraw(dbgDraw);
*/
}
/**
* @brief ボール初期化
*/
private function initFallBall():void
{
// ボール格納先
if( this.balls == null )
{
this.balls = new Array();
}
var color:Array = [ 0xf0f0f0, 0x232323 ];
var wpos:int = SCREEN_W;
var hpos:int = 50;
var ballshape:b2CircleDef = new b2CircleDef();
var ballbody:b2BodyDef = new b2BodyDef();
for( var i:int = 0; i < NX_BALL_NUM; ++i )
{
// シェイプを作成
ballshape.density = 2;
ballshape.restitution = 0.5;
ballshape.friction = 0.2;
ballshape.radius = (Math.random() * NX_BALL_R + NX_BALL_R) / NX_SCALE;
// ボディを作成
ballbody.position.Set( (Math.random()*wpos-20 + 20) / NX_SCALE, (Math.random()*hpos+hpos) / NX_SCALE );
// 表示部分を作成
var ballview:Sprite = new Sprite();
ballview.graphics.beginFill( color[Math.floor( Math.random() * color.length )] );
ballview.graphics.drawCircle( 0, 0, ballshape.radius * NX_SCALE );
ballview.graphics.endFill();
ballview.x = ballbody.position.x * NX_SCALE;
ballview.y = ballbody.position.y * NX_SCALE;
this.base.addChild( ballview );
// 実体を作成
var ball:b2Body = this.world.CreateBody( ballbody );
ball.SetUserData( ballview );
ball.CreateShape( ballshape );
ball.SetMassFromShapes();
this.balls.push( ball );
}
// 100個までに制限
while( this.balls.length > 100 )
{
var destroybody:b2Body = b2Body( this.balls.shift() );
var destroyview:Sprite = Sprite( destroybody.GetUserData() );
this.world.DestroyBody( destroybody );
this.base.removeChild( destroyview );
}
}
/**
* @brief 床や障害物を初期化
*/
private function initFallWall():void
{
var polyshape:b2PolygonDef = new b2PolygonDef();
var polybody:b2BodyDef = new b2BodyDef();
var polyview:Sprite;
var poly:b2Body;
var color:uint = 0xe0e0e0;
var alpha:Number = 0.7;
var i:int;
// 床 (bottom)
polyshape.SetAsBox( SCREEN_W/2/NX_SCALE , 10/2/NX_SCALE );
polybody.position.Set( SCREEN_W/2/NX_SCALE, (SCREEN_H-5)/NX_SCALE );
// 表示部分を作成
polyview = new Sprite();
polyview.graphics.beginFill( color, alpha );
polyview.graphics.drawRect( -SCREEN_W/2, -5, SCREEN_W, 10 );
polyview.graphics.endFill();
polyview.x = polybody.position.x * NX_SCALE;
polyview.y = polybody.position.y * NX_SCALE;
this.base.addChild( polyview );
// 実体を作成
poly = this.world.CreateBody( polybody );
poly.CreateShape( polyshape );
poly.SetMassFromShapes();
// 床 (top)
polyshape.SetAsBox( SCREEN_W/2/NX_SCALE , 10/2/NX_SCALE );
polybody.position.Set( SCREEN_W/2/NX_SCALE, 5/NX_SCALE );
// 表示部分を作成
polyview = new Sprite();
polyview.graphics.beginFill( color, alpha );
polyview.graphics.drawRect( -SCREEN_W/2, -5, SCREEN_W, 10 );
polyview.graphics.endFill();
polyview.x = polybody.position.x * NX_SCALE;
polyview.y = polybody.position.y * NX_SCALE;
this.base.addChild( polyview );
// 実体を作成
poly = this.world.CreateBody( polybody );
poly.CreateShape( polyshape );
poly.SetMassFromShapes();
// 床 (left)
polyshape.SetAsBox( 10/2/NX_SCALE , SCREEN_H/2/NX_SCALE );
polybody.position.Set( 5/NX_SCALE, SCREEN_H/2/NX_SCALE );
// 表示部分を作成
polyview = new Sprite();
polyview.graphics.beginFill( color, alpha );
polyview.graphics.drawRect( -5, -SCREEN_H/2, 10, SCREEN_H );
polyview.graphics.endFill();
polyview.x = polybody.position.x * NX_SCALE;
polyview.y = polybody.position.y * NX_SCALE;
this.base.addChild( polyview );
// 実体を作成
poly = this.world.CreateBody( polybody );
poly.CreateShape( polyshape );
poly.SetMassFromShapes();
// 床 (right)
polyshape.SetAsBox( 10/2/NX_SCALE , SCREEN_H/2/NX_SCALE );
polybody.position.Set( (SCREEN_W-5)/NX_SCALE, SCREEN_H/2/NX_SCALE );
// 表示部分を作成
polyview = new Sprite();
polyview.graphics.beginFill( color, alpha );
polyview.graphics.drawRect( -5, -SCREEN_H/2, 10, SCREEN_H );
polyview.graphics.endFill();
polyview.x = polybody.position.x * NX_SCALE;
polyview.y = polybody.position.y * NX_SCALE;
this.base.addChild( polyview );
// 実体を作成
poly = this.world.CreateBody( polybody );
poly.CreateShape( polyshape );
poly.SetMassFromShapes();
// たれさがる物体
// ジョイント設定
var joint_num:int = 7;
var leftsize_w:int = 48;
var leftsize_h:int = 15;
var initial_w:int = SCREEN_W - leftsize_w * joint_num;
var initial_h:int = 300;
var resize_val:Number = 0.9;
var ground:b2Body = this.world.GetGroundBody();
var prev:b2Body = ground;
var anchor:b2Vec2 = new b2Vec2();
// シェイプを作成
polyshape.density = 5;
polyshape.friction = Math.random();
polyshape.SetAsBox( leftsize_w/2/NX_SCALE, leftsize_h/2/NX_SCALE );
// ジョインを作成
var joint:b2RevoluteJointDef = new b2RevoluteJointDef();
joint.enableLimit = true;
joint.lowerAngle = -15 * Math.PI/180.0;
joint.upperAngle = 15 * Math.PI/180.0;
for( i = 0; i < joint_num; ++i )
{
// ボディを作成
polybody.position.Set( (initial_w + (i*leftsize_w*resize_val + leftsize_w/2))/NX_SCALE, (leftsize_h + initial_h)/NX_SCALE );
// 表示部分を作成
polyview = new Sprite();
polyview.graphics.beginFill( color, alpha );
polyview.graphics.drawRect( -leftsize_w/2, -leftsize_h/2, leftsize_w, leftsize_h );
polyview.graphics.endFill();
polyview.rotation = polybody.angle * 180 / Math.PI;
polyview.x = polybody.position.x * NX_SCALE;
polyview.y = polybody.position.y * NX_SCALE;
this.base.addChild( polyview );
// 実体を作成
poly = this.world.CreateBody( polybody );
poly.SetUserData( polyview );
poly.CreateShape( polyshape );
poly.SetMassFromShapes();
anchor.Set( (initial_w + i*leftsize_w*resize_val)/NX_SCALE, polybody.position.y );
joint.Initialize( prev, poly, anchor );
this.world.CreateJoint( joint );
prev = poly;
}
// 最後にもっかいグラウンドにくっつける
anchor.Set( (initial_w + joint_num*leftsize_w*resize_val)/NX_SCALE, polybody.position.y );
joint.Initialize( prev, ground, anchor );
this.world.CreateJoint( joint );
// つみあがっている箱たち
// シェイプを作成
var box_size:int = 20;
var box_num:int = 10;
polyshape.density = 1.0;
polyshape.friction = 0.5;
polyshape.restitution = 0.2;
polyshape.SetAsBox( box_size/2/NX_SCALE, box_size/2/NX_SCALE );
for( var w:int = 0; w < 3; ++w )
{
for( i = 0; i < box_num; ++i )
{
// ボディを作成
polybody.position.Set( (w*100 + Math.random()+50)/NX_SCALE, (SCREEN_H-box_size*2 - i*(box_size+5))/NX_SCALE );
// 表示部分を作成
polyview = new Sprite();
polyview.graphics.beginFill( color, alpha );
polyview.graphics.drawRect( -box_size/2, -box_size/2, box_size, box_size );
polyview.graphics.endFill();
polyview.x = polybody.position.x * NX_SCALE;
polyview.y = polybody.position.y * NX_SCALE;
this.base.addChild( polyview );
// 実体を作成
poly = this.world.CreateBody( polybody );
poly.SetUserData( polyview );
poly.CreateShape( polyshape );
poly.SetMassFromShapes();
}
}
}
/**
* @brief 実行と更新。
*/
private function procFallBall( ev:Event ):void
{
if( this.is_start == false )
{
if( Input.mousePressed )
{
this.base.removeChild( this.tf );
this.is_start = true;
initFallBall();
initFallWall();
}
return;
}
// 入力情報をもとに更新
mousepos.Set( Input.mouseX, Input.mouseY );
mousepos_scale.Set( Input.mouseX / NX_SCALE, Input.mouseY / NX_SCALE );
// R が押された?
if( Input.isKeyPressed( 82 ) )
{
// リセット
this.balls = null;
while( this.base.numChildren )
{
this.base.removeChildAt( 0 );
}
this.world = null;
this.mousejoint = null;
initFallWorld();
initFallBall();
initFallWall();
}
// space が押された?
if( Input.isKeyPressed( 32 ) )
{
// ボール追加
initFallBall();
}
// 衝突判定更新
var physStart:uint = getTimer();
this.world.Step( NX_STEP, NX_ITER );
fpsCounter.updatePhys( physStart );
var it:b2Body;
for( it = this.world.GetBodyList(); it != null; it = it.GetNext() )
{
// 表示物体を更新
var usrdata:* = it.GetUserData();
if( usrdata == null ) continue;
usrdata.x = it.GetPosition().x * NX_SCALE;
usrdata.y = it.GetPosition().y * NX_SCALE;
usrdata.rotation = it.GetAngle() * 180 / Math.PI;
}
updateMouseDrag();
// General update.
Input.update();
fpsCounter.update();
}
/**
* @brief マウスに引っ張られる処理
*/
private function updateMouseDrag():void
{
var usrdata:Sprite;
if( Input.mouseDown && !this.mousejoint )
{
var body:b2Body = getBodyAtMouse();
if( body )
{
var md:b2MouseJointDef = new b2MouseJointDef();
md.body1 = this.world.GetGroundBody();
md.body2 = body;
md.target.SetV( this.mousepos_scale );
md.maxForce = 300.0 * body.GetMass();
md.timeStep = NX_STEP;
this.mousejoint = this.world.CreateJoint( md ) as b2MouseJoint;
var line:Sprite = new Sprite();
line.graphics.lineStyle( 2, 0xe0e090, 0.7 );
line.graphics.moveTo( this.mousepos.x, this.mousepos.y );
line.graphics.lineTo( body.GetUserData().x, body.GetUserData().y );
this.base.addChild( line );
this.mousejoint.SetUserData( line );
body.WakeUp(); // sleep から起こす。
}
}
if( !Input.mouseDown )
{
if( this.mousejoint )
{
// 引っ張るライン削除
usrdata = this.mousejoint.GetUserData() as Sprite;
usrdata.graphics.clear();
this.base.removeChild( usrdata );
// マウスジョイントも削除
this.world.DestroyJoint( this.mousejoint );
this.mousejoint = null;
}
}
if( this.mousejoint )
{
// 引っ張るライン設定
usrdata = this.mousejoint.GetUserData() as Sprite;
usrdata.graphics.clear();
usrdata.graphics.lineStyle( 2, 0xe0e090, 0.7 );
usrdata.graphics.moveTo( this.mousepos.x, this.mousepos.y );
usrdata.graphics.lineTo( this.mousejoint.GetBody2().GetUserData().x, this.mousejoint.GetBody2().GetUserData().y );
this.mousejoint.SetTarget( this.mousepos_scale );
}
}
/**
* @brief 掴んだ物体を取得
*/
private var mousePVec:b2Vec2 = new b2Vec2();
private function getBodyAtMouse():b2Body
{
// ちっっさな領域を作成
mousePVec.SetV( this.mousepos_scale );
var aabb:b2AABB = new b2AABB();
aabb.lowerBound.Set( this.mousepos_scale.x - 0.001, this.mousepos_scale.y - 0.001 );
aabb.upperBound.Set( this.mousepos_scale.x + 0.001, this.mousepos_scale.y + 0.001 );
// 上で作成した領域とぶつかっているやつを取得
var k_maxCount:int = 10;
var shapes:Array = new Array();
var count:int = this.world.Query( aabb, shapes, k_maxCount );
var body:b2Body = null;
for( var i:int = 0; i < count; ++i )
{
if( shapes[i].GetBody().IsStatic() == false )
{
var tShape:b2Shape = shapes[i] as b2Shape;
var inside:Boolean = tShape.TestPoint( tShape.GetBody().GetXForm(), mousePVec );
if( inside )
{
body = tShape.GetBody();
break;
}
}
}
return body;
}
}
}