物理エンジンライブラリ Box2DFlashAS3 ジョイント3(滑車ジョイント・マウスジョイント)
滑車ジョイントとマウスジョイントについてのメモ。
サンプルコードでは、距離ジョイントも使ってます。
ついでに任意で物体に力を加える方法も。
滑車ジョイント(PulleyJoint)
- 滑車の上に紐で繋がった物体をかけたようなジョイント。
- 二つの滑車を軸に、紐の長さ分だけ物体が動く。
- 紐の長さ、伸びやすさ(ゴムっぽくしたり)を指定できる。
使い方
- 滑車の場所(b2Vec2)を左右指定。
- 滑車に繋げる物体を指定(b2Body)。
- ジョイントの性質(b2PulleyJointDef)を設定(長さ等)
- ジョイントの初期化→ジョイント登録
マウスジョイント(MouseJoint)
- マウスと物体を繋ぐジョイント。
- マウス(座標)と他の物体を繋ぐだけ。
- マウスを動かしたら付いてくる、というような動作は自力で実装。
使い方
- マウス押した時に、マウス座標の下にある物体を取得(これは自力で作る)
- マウス座標、取得した物体、引っ張る力など設定(b2MouseJointDef)
- ジョイント作成(b2MouseJoint)
- 一定時間毎(EnterFrameイベントなど)マウスドラッグ時のマウス座標を反映させる
- マウス放したらジョイントを削除
物体に任意で力を加える
bb.ApplyForce(new b2Vec2(0, 200), bb.GetPosition());
(bbはb2Bodyオブジェクト。力を加える方向と、力を加える位置を指定する)
サンプルコード
※Box2Dのまとめたクラス(Box2DBase)を作って、基本クラスで使ってます。ソースは物理エンジンライブラリ Box2DFlashAS3(基本)にあります。
マウスジョイントの辺りは、Box2Dの付属コード、TestBedのTest.asのサンプルからほぼコピペです。
Actionscript:
-
public class StudyB2Joint3 extends B2Base
-
{
-
private var m_display_blocks:Sprite; //表示用
-
-
private var m_left_fook1:b2Body;
-
private var m_left_fook2:b2Body;
-
private var m_left_box1:b2Body;
-
private var m_left_box2:b2Body;
-
-
public function StudyB2Joint3() : void{
-
-
super(24, 10, 10);
-
-
//滑車ときのこ表示用
-
this.m_display_blocks = new Sprite();
-
this.addChild(this.m_display_blocks);
-
-
//水面
-
var water:MovieClip = new water_mc;
-
water.y = 350;
-
this.addChild(water);
-
-
//上下左右の壁作成
-
this.makeB2Walls();
-
-
//島作成
-
this.makeShima();
-
-
//滑車表示用のMC
-
var kassha_left_fook1:Sprite = new kinoko_fook_mc as Sprite;
-
var kassha_left_fook2:Sprite = new kinoko_fook_mc as Sprite;
-
var kassha_left_box1:Sprite = new board_100_mc as Sprite;
-
var kassha_left_box2:Sprite = new board_100_mc as Sprite;
-
-
var kassha_right_fook1:Sprite = new kinoko_fook_mc as Sprite;
-
var kassha_right_fook2:Sprite = new kinoko_fook_mc as Sprite;
-
var kassha_right_box1:Sprite = new board_50_mc as Sprite;
-
var kassha_right_box2:Sprite = new board_50_mc as Sprite;
-
-
//左の島の滑車
-
this.makeKassha(200, 175, 75, 175, 100, 10, 250,
-
kassha_left_fook1, kassha_left_fook2,
-
kassha_left_box1, kassha_left_box2);
-
//右の島の滑車
-
this.makeKassha(150, 450, 50, 150, 50, 10, 200,
-
kassha_right_fook1, kassha_right_fook2,
-
kassha_right_box1, kassha_right_box2);
-
-
//きのこ
-
this.makeKinokos();
-
-
//this.setDebugDraw();
-
-
this.addEventListener(Event.ENTER_FRAME, this.update, false, 0, true);
-
-
this.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown, false, 0, true);
-
this.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp, false, 0, true);
-
}
-
-
-
//上下左右の壁作成
-
private function makeB2Walls():void {
-
-
this.makeB2BodyBox(700, 10, 300, -50, 0, 0, 0.5, 0.5); //上
-
this.makeB2BodyBox(700, 10, 300, 550, 0, 0, 0.5, 0.5); //下
-
this.makeB2BodyBox(10, 600, -50, 250, 0, 0, 0.5, 0.5); //左
-
this.makeB2BodyBox(10, 600, 650, 250, 0, 0, 0.5, 0.5); //右
-
}
-
-
//島作成
-
private function makeShima():void {
-
-
this.makeB2BodyBox(100, 350, 175, 425, 0, 0, 0.5, 0.5);
-
this.makeB2BodyBox(100, 400, 450, 400, 0, 0, 0.5, 0.5);
-
}
-
-
//滑車作成
-
private function makeKassha(pulley_w:int, pulley_x:int, pulley_y:int,
-
fook_y:int,
-
box_w:int, box_h:int, box_y:int,
-
disp_fook1:Sprite = null, disp_fook2:Sprite = null,
-
disp_box1:Sprite = null, disp_box2:Sprite = null):void {
-
-
var x1:Number = pulley_x - (pulley_w / 2);
-
var x2:Number = pulley_x + (pulley_w / 2)
-
-
//フックに繋がる箱
-
var box1:b2Body = this.makeB2BodyBox(box_w, box_h, x1, box_y, 0, 1, 0.5, 0.5, disp_box1, this.m_display_blocks);
-
var box2:b2Body = this.makeB2BodyBox(box_w, box_h, x2, box_y, 0, 1, 0.5, 0.5, disp_box2, this.m_display_blocks);
-
-
//フック
-
var fook1:b2Body = this.makeB2BodyBox(10, 10, x1, fook_y, 0, 1, 0.5, 0.5, disp_fook1, this.m_display_blocks);
-
var fook2:b2Body = this.makeB2BodyBox(10, 10, x2, fook_y, 0, 1, 0.5, 0.5, disp_fook2, this.m_display_blocks);
-
-
//滑車
-
var ga1:b2Vec2 = this.createB2Vec(x1, pulley_y);
-
var ga2:b2Vec2 = this.createB2Vec(x2, pulley_y);
-
-
//滑車ジョイント作成
-
var pj_def:b2PulleyJointDef = new b2PulleyJointDef;
-
pj_def.Initialize(fook1, fook2, ga1, ga2, fook1.GetPosition(), fook2.GetPosition(), 1);
-
-
this.m_b2_world.CreateJoint(pj_def);
-
-
//フックは他のものと衝突しないようにする
-
fook1.GetShapeList().m_filter.maskBits = 0;
-
fook2.GetShapeList().m_filter.maskBits = 0;
-
-
//距離ジョイントで左の箱とフックつなぐ
-
var box1_anc_L:b2Vec2 = this.createB2Vec(x1 - (box_w / 2) + 5, box_y);
-
var box1_anc_R:b2Vec2 = this.createB2Vec(x1 + (box_w / 2) - 5, box_y);
-
-
var dj_def1_L:b2DistanceJointDef = new b2DistanceJointDef;
-
dj_def1_L.Initialize(fook1, box1, fook1.GetPosition(), box1_anc_L);
-
-
this.m_b2_world.CreateJoint(dj_def1_L);
-
-
var dj_def1_R:b2DistanceJointDef = new b2DistanceJointDef;
-
dj_def1_R.Initialize(fook1, box1, fook1.GetPosition(), box1_anc_R);
-
-
this.m_b2_world.CreateJoint(dj_def1_R);
-
-
//距離ジョイントで右の箱とフックつなぐ
-
var box2_anc_L:b2Vec2 = this.createB2Vec(x2 - (box_w / 2) + 5, box_y);
-
var box2_anc_R:b2Vec2 = this.createB2Vec(x2 + (box_w / 2) - 5, box_y);
-
-
var dj_def2_L:b2DistanceJointDef = new b2DistanceJointDef;
-
dj_def2_L.Initialize(fook2, box2, fook2.GetPosition(), box2_anc_L);
-
-
this.m_b2_world.CreateJoint(dj_def2_L);
-
-
var dj_def2_R:b2DistanceJointDef = new b2DistanceJointDef;
-
dj_def2_R.Initialize(fook2, box2, fook2.GetPosition(), box2_anc_R);
-
-
this.m_b2_world.CreateJoint(dj_def2_R);
-
}
-
-
-
//きのこを降らせる
-
private function makeKinokos():void {
-
-
//滑車の上に
-
this.makeKinoko(75, 225);
-
this.makeKinoko(257, 225);
-
-
this.makeKinoko(375, 175);
-
this.makeKinoko(525, 175);
-
-
//島の上に
-
this.makeKinoko(150, 0);
-
this.makeKinoko(200, 0);
-
-
this.makeKinoko(425, 0);
-
this.makeKinoko(475, 0);
-
}
-
-
//きのこ作成
-
private function makeKinoko(x:Number, y:Number):void {
-
-
var kinoko:Sprite;
-
var land = Math.random();
-
-
if (land>= 0.6) {
-
//普通のきのこ
-
kinoko = new kinoko_S1_MC as Sprite;
-
}
-
else if(land>= 0.3){
-
//怪しいきのこ
-
kinoko = new kinoko_S2_MC as Sprite;
-
}
-
else {
-
//しめじ
-
kinoko = new kinoko_S3_MC as Sprite;
-
}
-
-
this.makeB2BodyCircle(30, x, y, 1.0, 0.5, 0.5, kinoko, this.m_display_blocks);
-
}
-
-
-
//演算
-
private function update(e:Event):void{
-
-
//B2Boxの時間進める
-
this.m_b2_world.Step(this.m_b2_timeStep, this.m_b2_iterations);
-
-
//マウス座標設定
-
this.updateMouseWorld();
-
-
//マウスドラッグ時の設定
-
this.mouseDrag();
-
-
-
//滑車とフック・箱の間の線を引く
-
var self:StudyB2Joint3 = this;
-
//Body位置から描画位置移動する関数
-
var moveTo_Pos:Function = function(graphics:Graphics, anchor:b2Vec2):void {
-
-
var x:Number = anchor.x * self.m_b2_physcale;
-
var y:Number = anchor.y * self.m_b2_physcale;
-
-
graphics.moveTo(x, y);
-
}
-
-
//Body位置へ線を引く関数
-
var lineTo_Pos:Function = function(graphics:Graphics, anchor:b2Vec2):void {
-
-
var x:Number = anchor.x * self.m_b2_physcale;
-
var y:Number = anchor.y * self.m_b2_physcale;
-
-
graphics.lineTo(x, y);
-
}
-
-
//線表示用ディスプレイオブジェクト
-
var disp_gra:Graphics = this.m_display_blocks.graphics;
-
disp_gra.clear();
-
disp_gra.lineStyle(1, 0x9FD06F);
-
-
//worldに登録してあるBodyの動きを描画する
-
for (var bb:b2Body = this.m_b2_world.m_bodyList; bb; bb = bb.m_next){
-
if (bb.IsDynamic()) {
-
-
//Bodyの動きにあわせて中のSpriteを動かす
-
if (bb.m_userData is Sprite){
-
//メートルからピクセル単位に変換
-
bb.m_userData.x = bb.GetPosition().x * m_b2_physcale;
-
bb.m_userData.y = bb.GetPosition().y * m_b2_physcale;
-
bb.m_userData.rotation = bb.GetAngle() * (180/Math.PI);
-
}
-
-
//ジョイントを持ってるもののみ、線を描画
-
if (bb.m_jointList is b2JointEdge) {
-
for (var joint:b2Joint = bb.m_jointList.joint; joint; joint=joint.m_next){
-
//ジョイントのタイプで分岐)
-
if (joint.m_type == b2Joint.e_pulleyJoint) {
-
//滑車ジョイントの場合(滑車とフックを線で繋ぐ)
-
var joint_p:b2PulleyJoint = joint as b2PulleyJoint;
-
moveTo_Pos(disp_gra, joint_p.GetGroundAnchor1());
-
lineTo_Pos(disp_gra, joint_p.GetAnchor1());
-
moveTo_Pos(disp_gra, joint_p.GetGroundAnchor2());
-
lineTo_Pos(disp_gra, joint_p.GetAnchor2());
-
}
-
else {
-
//その他のジョイント(アンカー同士を線で繋ぐ)
-
//ここでは距離ジョイントとマウスジョイント
-
moveTo_Pos(disp_gra, joint.GetAnchor1());
-
lineTo_Pos(disp_gra, joint.GetAnchor2());
-
}
-
}
-
}
-
-
//水面に浮かぶような感じを出してみる(物体に任意で力を加える)
-
var bb_y:Number = bb.GetPosition().y * m_b2_physcale;
-
if (bb_y> 350) {
-
//水中は上方向に力を加える
-
var force:b2Vec2;
-
if (bb_y> 500) {
-
//深いところは多めに力を加える
-
force = new b2Vec2(0, -200);
-
}
-
else {
-
force = new b2Vec2(0, -100);
-
}
-
bb.ApplyForce(force, bb.GetPosition());
-
}
-
else if(bb_y <= 350 && bb_y> 250){
-
if (bb.GetLinearVelocity().y <0) {
-
//水面近くは下方向に力を加える(上方向に向かってる場合のみ)
-
bb.ApplyForce(new b2Vec2(0, 200), bb.GetPosition());
-
}
-
}
-
}
-
}
-
}
-
-
-
//マウスジョイント設定 ---------------------------------------------------------------------
-
//Box2D付属のGeneralのInputをほぼこコピペ
-
-
// world mouse position
-
private var m_mouseJoint:b2MouseJoint; //マウスジョイント
-
-
private var m_mouseXWorldPhys:Number; //メートル単位のマウス座標
-
private var m_mouseYWorldPhys:Number;
-
-
private var m_mouse_dowm:Boolean = false; //マウスボタン押されてるか
-
-
private function onMouseDown(e:Event):void {
-
this.m_mouse_dowm = true;
-
}
-
-
private function onMouseUp(e:Event):void {
-
this.m_mouse_dowm = false;
-
}
-
-
//マウス座標取得
-
public function updateMouseWorld():void{
-
//メートル単位のマウス座標に変換
-
m_mouseXWorldPhys = this.mouseX / this.m_b2_physcale;
-
m_mouseYWorldPhys = this.mouseY / this.m_b2_physcale;
-
}
-
-
//マウスドラッグ時
-
public function mouseDrag():void{
-
-
//マウスボタン押した(ジョイントまだ)
-
if (this.m_mouse_dowm && !this.m_mouseJoint){
-
-
var body:b2Body = GetBodyAtMouse(); //マウスの下の物体取得
-
-
if (body)
-
{
-
var md:b2MouseJointDef = new b2MouseJointDef();
-
md.body1 = this.m_b2_world.GetGroundBody();
-
md.body2 = body;
-
md.target.Set(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
-
md.maxForce = 300000.0 * body.GetMass();
-
md.timeStep = this.m_b2_timeStep;
-
m_mouseJoint = this.m_b2_world.CreateJoint(md) as b2MouseJoint;
-
body.WakeUp();
-
}
-
}
-
-
-
//マウスボタン離した
-
if (!this.m_mouse_dowm){
-
if (this.m_mouseJoint)
-
{
-
this.m_b2_world.DestroyJoint(m_mouseJoint);
-
m_mouseJoint = null;
-
}
-
}
-
-
-
//マウス押されてる状態(ジョイントされてる)
-
if (this.m_mouse_dowm && this.m_mouseJoint)
-
{
-
var p2:b2Vec2 = new b2Vec2(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
-
m_mouseJoint.SetTarget(p2);
-
}
-
}
-
-
-
//マウスの下の物体取得
-
private var mousePVec:b2Vec2 = new b2Vec2();
-
public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
-
// Make a small box.
-
mousePVec.Set(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
-
var aabb:b2AABB = new b2AABB();
-
aabb.lowerBound.Set(this.m_mouseXWorldPhys - 0.001, this.m_mouseYWorldPhys - 0.001);
-
aabb.upperBound.Set(this.m_mouseXWorldPhys + 0.001, this.m_mouseYWorldPhys + 0.001);
-
-
// Query the world for overlapping shapes.
-
var k_maxCount:int = 10;
-
var shapes:Array = new Array();
-
var count:int = this.m_b2_world.Query(aabb, shapes, k_maxCount);
-
var body:b2Body = null;
-
for (var i:int = 0; i <count; ++i)
-
{
-
if (shapes[i].GetBody().IsStatic() == false || includeStatic)
-
{
-
var tShape:b2Shape = shapes[i] as b2Shape;
-
var inside:Boolean = tShape.TestPoint(tShape.GetBody().GetXForm(), mousePVec);
-
if (inside)
-
{
-
body = tShape.GetBody();
-
break;
-
}
-
}
-
}
-
return body;
-
}
-
-
}


