flash on 2009-6-5
/**
* Copyright hacker_n2ylj3ym ( http://wonderfl.net/user/hacker_n2ylj3ym )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/zRa8
*/
package {
import Box2D.Collision.Shapes.*;
import Box2D.Collision.b2AABB;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.Joints.*;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2World;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.text.TextField;
import flash.utils.Timer;
public class Box2DGame2 extends Sprite {
private var world:b2World;
private var joint1:b2RevoluteJoint;
private var joint2:b2RevoluteJoint;
private var textfield:TextField;
private var payTextfield:TextField;
private var coinTextfield:TextField;
private var money:int = 30;
private var payMoney:int = 0;
private var floorList:Array;
private static const DRAW_SCALE:Number = 100;
public function Box2DGame2():void {
init();
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
textfield = new TextField();
this.addChild(textfield);
}
public function init():void {
initWorld();
initWall();
initBar();
initPin();
initFloor();
initButton();
}
/**
* world設定
*/
private function initWorld():void {
// 外枠を定義する
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set(-100, -100);
worldAABB.upperBound.Set(100, 100);
// 重力加速度
var gravity:b2Vec2 = new b2Vec2(0, 10);
// 外枠と重力を指定して、物理エンジン全体をセットアップする
world = new b2World(worldAABB, gravity, true);
// 描画設定
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.m_sprite = this;
debugDraw.m_drawScale = DRAW_SCALE; // 1mを100ピクセルにする
debugDraw.m_fillAlpha = 1; // 不透明度
debugDraw.m_lineThickness = 1; // 線の太さ
debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
world.SetDebugDraw(debugDraw);
}
/**
* ジョイントバーの設置
*/
private function initBar():void {
joint1 = createJointBar(1.23, 0);
joint2 = createJointBar(1.67, 0);
}
private function createJointBar(x:Number, y:Number):b2RevoluteJoint {
// 動くバーの支点となるオブジェクト
var point:b2Body = createBox(0.01, 0.01, x, y, 0, true);
// 動くバー
var bar:b2Body = createBox(0.02, 0.4, x, y + 0.4);
// 回転ジョイントの設定
var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
jointDef.enableMotor = true;
jointDef.motorSpeed = 0; // ここで初期速度を設定すると、joint.SetMoterSpeed()の指定時におかしくなる?
jointDef.maxMotorTorque = 300000; // 謎。 0だと動かない
jointDef.Initialize(bar, point, point.GetPosition()); // pointにbarをくっつける
var joint:b2RevoluteJoint = world.CreateJoint(jointDef) as b2RevoluteJoint;
joint.SetMotorSpeed(1); // こっちで初期スピードの設定をする
return joint;
}
/**
* ピンの設置
*/
private function initPin():void {
for (var i:int = 0; i < 7; i++) {
for (var j:int = 0; j < 10; j++) {
if (i % 2 == 1 && j == 9) {
continue;
}
var x:Number = j*0.3 + (i % 2 == 1 ? 0.15 : 0) + 0.05;
var y:Number = i*0.3 + 1.3;
// createCircle(0.01, x, y, true);
createBox(0.01, 0.01, x, y, Math.PI/4, true);
}
}
}
/**
* 床の設置 消したり出したりする
*/
private function initFloor():void {
floorList = [];
for (var j:int = 0; j < 10; j++) {
var x:Number = j*0.3 + 0.05;
var y:Number = 6*0.3 + 1.3;
// 横の壁
createBox(0.01, 0.2, x, y + 0.2, 0, true);
if (j < 9) {
// こっちが床
floorList.push(createBox(0.15, 0.01, x + 0.15, y + 0.4, 0, true));
}
}
}
/**
* 壁の設置
*/
private function initWall():void {
createBox(0.05, 3, 3.1, 3, 0, true);
createBox(0.05, 3, 4.2, 3, 0, true);
createBox(1, 3, -1.25, 3, 0, true);
// createBox(0.5, 0.6, 3.6, 3.5, 0, true);
createBox(0.07, 0.07, 3.15, 0.8, Math.PI/4, true);
createBox(0.07, 0.07, 3.15, 0.7*3 + 0.2, Math.PI/4, true);
createBox(0.07, 0.07, 4.15, 0.6*2 + 0.2, Math.PI/4, true);
createBox(0.01, 0.01, 3.65 , 0.8*0.5, Math.PI/4, true);
createBox(0.01, 0.01, 3.65 , 0.8*1.5, Math.PI/4, true);
createBox(0.01, 0.01, 3.65 , 0.8*2.5, Math.PI/4, true);
createBox(0.01, 0.01, 3.65 , 0.8*3.5, Math.PI/4, true);
}
/**
* ボタン作成
*/
private function initButton():void {
// コイン投入ボタン
var button:Sprite = new Sprite();
button.graphics.beginFill(0xFFFFFF);
button.graphics.drawRoundRectComplex(320, 300, 80, 30, 5, 5, 5, 5);
button.graphics.endFill();
button.buttonMode = true;
button.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
coinTextfield = new TextField();
coinTextfield.text = '投入: 残' + money+'枚';
coinTextfield.selectable = false;
coinTextfield.x = 325;
coinTextfield.y = 305;
button.addChild(coinTextfield);
addChild(button);
// 払い出しボタン
var buttonPay:Sprite = new Sprite();
buttonPay.graphics.beginFill(0xFFFFFF);
buttonPay.graphics.drawRoundRectComplex(320, 340, 80, 30, 5, 5, 5, 5);
buttonPay.graphics.endFill();
buttonPay.buttonMode = true;
buttonPay.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
if (payMoney) {
var timer:Timer = new Timer(150, payMoney);
timer.addEventListener(TimerEvent.TIMER, function (et:TimerEvent):void {
createCircle(0.12, Math.random()*0.5 + 3.3, -1);
});
timer.addEventListener(TimerEvent.TIMER_COMPLETE, function(et:TimerEvent):void {
timer.stop();
});
timer.start();
payMoney = 0;
dropCoins();
}
});
payTextfield = new TextField();
payTextfield.selectable = false;
payTextfield.text = '払戻: 0枚';
payTextfield.x = 330;
payTextfield.y = 345;
buttonPay.addChild(payTextfield);
addChild(buttonPay);
}
/**
* クリック時の処理
*/
private function onMouseDown(event:MouseEvent):void {
// クリックされた場所に何か落とす
if (money > 0) {
var circle:b2Body = createCircle(0.12, 1.5, -1);
circle.m_userData.name = 'ball';
money--;
coinTextfield.text = '投入: 残' + money.toString() +'枚';
}
}
/**
* フレームごとの更新処理
*/
private function update():void {
// バーを左右に振る
var angle:int = joint1.GetJointAngle() / Math.PI * 180;
if (angle < -50) {
joint1.SetMotorSpeed(0.8);
} else if (50 < angle) {
joint1.SetMotorSpeed(-0.8);
}
// 2つめのバーは1つ目のバーと同じ動きをさせる
joint2.SetMotorSpeed(joint1.GetMotorSpeed());
var check_list:Array = new Array(9);
var hit_list:Array = new Array(9);
for (var i:int = 0; i < hit_list.length; i++) {
hit_list[i] = 0;
}
// spriteを描画しつつ点数の計算も行う 処理がすごく怪しい
for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
// 物体の位置を更新する処理。中身は後述
if (!b.m_userData || !(b.m_userData as Object).hasOwnProperty('sprite')) {
continue;
}
try {
if ((b.m_userData as Object).hasOwnProperty('sprite')) {
var sprite:Sprite = b.m_userData.sprite as Sprite;
sprite.x = b.GetWorldCenter().x * DRAW_SCALE;
sprite.y = b.GetWorldCenter().y * DRAW_SCALE;
sprite.rotation = b.GetAngle() * 180 / Math.PI;
// 終了判定
for (var j:int = 0; j < 9; j++) {
var x:Number = j*0.3 + 0.05 + 0.15;
var y:Number = 6*0.3 + 1.3 + 0.35;
// 床に落ちた下のコイン
if (sprite.hitTestPoint(x * DRAW_SCALE, y * DRAW_SCALE)) {
hit_list[j] = 1;
if (check_list[j] == 1) {
dropCoins();
}
}
// 床に重なったコインの上のコイン
if (sprite.hitTestPoint(x * DRAW_SCALE, (y - 0.35) * DRAW_SCALE)) {
check_list[j] = 1;
}
}
}
} catch (e:Error) {
}
}
calculatePayment(hit_list);
}
/**
* 払い戻し枚数の計算
* 2枚隣合っていたら4枚、3枚隣り合っていたら8枚,4枚隣り合っていたら16枚・・・
*/
private function calculatePayment(hit_list:Array):void {
payMoney = 0;
var result:String = hit_list.join('');
hit_list = null;
result = result.replace(/0+/g, '0');
var list:Array = result.split('0');
for (var i:int = 0; i < list.length; i++) {
var value:String = list[i];
if (value.length > 1) {
payMoney += Math.pow(2, value.length);
}
}
payTextfield.text = "払戻: " + payMoney.toString() + "枚";
}
/**
* 床を消してコインを全部落とす
*/
private function dropCoins():void {
for each (var floorBox:b2Body in floorList) {
// world.DestroyBodyでb2bodyオブジェクトを削除
world.DestroyBody(floorBox);
}
floorList = new Array();;
// 1秒後に再構築
var timer:Timer = new Timer(1000, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, function(te:TimerEvent):void {
initFloor();
timer.stop();
});
timer.start();
}
/**
* 毎フレーム呼び出される
*/
private function enterFrameHandler(event:Event):void {
if (world == null) {
return;
}
update();
world.Step(1 / 48, 10);
}
//////////////////////////////////////////////////////////////////////////////////
// ここから下はオブジェクト生成用メソッド
//////////////////////////////////////////////////////////////////////////////////
/**
* 四角形を作成
*/
private function createBox(width:Number, height:Number, x:Number, y:Number, angle:Number = 0, is_static:Boolean=false):b2Body {
var boxBodyDef:b2BodyDef = new b2BodyDef();
boxBodyDef.position.Set(x, y);
var body:b2Body = world.CreateBody(boxBodyDef);
body.CreateShape(createBoxShapeDef(width, height, angle, is_static));
if (!is_static) {
body.SetMassFromShapes();
}
body.m_userData = new Object();
body.m_userData.name = 'box';
return body;
}
/**
* 円作成
*/
private function createCircle(radius:Number, x:Number, y:Number, is_static:Boolean = false):b2Body {
// 円の場所を設定する
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(x, y);
// 円の大きさなどを設定する
var shapeDef:b2CircleDef = new b2CircleDef();
shapeDef.radius = radius;
shapeDef.density = 1; // 密度 [kg/m^2]
shapeDef.restitution = 0.3; // 反発係数、通常は0~1
shapeDef.density = is_static ? 0 : 1;
// 円を動く物体として作る
var body:b2Body = world.CreateBody(bodyDef);
body.CreateShape(shapeDef);
if (!is_static) {
body.SetMassFromShapes();
}
body.m_userData = new Object();
body.m_userData.name = 'circle';
body.m_userData.sprite = createCircleSprite(x, y, radius);
this.addChild(body.m_userData.sprite);
return body;
}
private function createCircleSprite(x:Number, y:Number, radius:Number):Sprite {
var s:Sprite = new Sprite();
s.graphics.beginFill(0x0000FF, 0);
s.graphics.drawCircle(0, 0, radius * DRAW_SCALE);
s.graphics.endFill();
s.x = x * DRAW_SCALE;
s.y = y * DRAW_SCALE;
s.addEventListener(Event.ENTER_FRAME, function(e:Event):void {
if (s.y >= 500) {
s.parent.removeChild(s);
if (315 <= s.x && s.x <= 405) {
money++;
coinTextfield.text = '投入: 残' + money.toString() +'枚';
}
}
});
return s;
}
/**
* 箱の形を設定
*/
private function createBoxShapeDef(width:Number, height:Number, angle:Number = 0, is_static:Boolean = false):b2PolygonDef {
var boxShapeDef:b2PolygonDef= new b2PolygonDef();
if (angle) {
// 傾いた物体
boxShapeDef.SetAsOrientedBox(width, height, new b2Vec2(0, 0), angle);
} else {
// まっすぐな物体
boxShapeDef.SetAsBox(width, height);
}
boxShapeDef.density = is_static ? 0 : 1;
boxShapeDef.restitution = 0.3; // 反発係数、通常は0~1
return boxShapeDef;
}
}
}