[Box2d] Mechanical Clock (機械式時計)
Mechanical Clock with Box2D(機械式時計)
*
* 最初はぜんまいが巻かれていない状態です。
* 左上のボタンをクリックすると最初の数秒間リューズにトルクを加え、ぜんまいが巻かれ、時計が動き始めます。
* 途中から、30秒間で秒針(四番車)が何度回転するかを計測します。
*
* http://flashjp.com/mecclock
*
*
/**
* Copyright tatsuya ( http://wonderfl.net/user/tatsuya )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/b9Ls
*/
/* Mechanical Clock with Box2D(機械式時計)
*
* 最初はぜんまいが巻かれていない状態です。
* 左上のボタンをクリックすると最初の数秒間リューズにトルクを加え、ぜんまいが巻かれ、時計が動き始めます。
* 途中から、30秒間で秒針(四番車)が何度回転するかを計測します。
*
* http://flashjp.com/mecclock
*
* */
package {
import flash.display.*;
import flash.events.*;
import flash.text.*;
[SWF(width=465, height=465, backgroundColor=0xFFFFFF, frameRate=50)]
public class clockfl extends Sprite {
public var sheets:Sprite = new Sprite();//全体
public var b2Sheet:Sprite = new Sprite();//box2Dのデバッグを描く
public var backgroundSheet:Sprite = new Sprite();//背景用画像を描く
public var textSheet:Sprite = new Sprite();//説明文を書く
public var clock:Clock;//Clockクラスのオブジェクト
public var fStart:Boolean = false;//実行時はtrue
public var appTorqueInfo:Sprite;//トルクを加えている時の矢印など
public var evaluTF:TextField;//計測結果表示用テキストフィールド
public function clockfl() {
if (stage) {
init();
} else {
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init():void {
stage.addChild(sheets);
sheets.x = 465 / 2;
sheets.y = 465 / 2;
sheets.addChild(backgroundSheet);
sheets.addChild(b2Sheet);
sheets.addChild(textSheet);
drawBackground();//背景を描く
drawTexts();//説明文などを描く
setButtons();//ボタンをセットする
main();//メインの実行部分
}
private function drawBackground():void {//背景を描く
//var b2bg:Sprite = new Sprite();
var b2bg:Shape = new Shape();
b2bg.graphics.lineStyle(0, 0x555555);
b2bg.graphics.beginFill(0x666666);
b2bg.graphics.drawRoundRect(-465/2,-465/2,465,465,10);
b2bg.graphics.endFill();
b2bg.graphics.lineStyle(0, 0xAAAAAA);
b2bg.graphics.moveTo( -465 / 2, 0);
b2bg.graphics.lineTo( 465 / 2, 0);
b2bg.graphics.moveTo(0, -465 / 2);
b2bg.graphics.lineTo( 0, 465 / 2);
b2bg.graphics.drawCircle(0, 0, 440 / 2);
backgroundSheet.addChild(b2bg);
drawCrossLine( -8.371 * 10 , 10.2764 * 10 , 5);//アンクルの回転中心を描く
drawCrossLine( -11.971 * 10 , 7.35 * 10 , 5);//テンプの回転中心を描く
function drawCrossLine(x:Number , y:Number , length:Number):void {
var lines:Shape = new Shape();
var length:Number = 5;
lines.graphics.lineStyle(0, 0xffffff, 0.3);
lines.graphics.moveTo(x - length, y);
lines.graphics.lineTo(x + length, y);
lines.graphics.moveTo(x, y - length);
lines.graphics.lineTo(x, y + length);
backgroundSheet.addChild(lines);
}
}
private function setButtons():void {//ボタンをセットする
var button1:Sprite = new Sprite();//スタート|ストップボタン
button1.graphics.lineStyle(0, 0xffffff);
button1.graphics.beginFill(0xeeeeee);
button1.graphics.drawRoundRect(0,0, 100,20 , 7);
button1.graphics.endFill();
button1.x = -225;
button1.y = -225;
button1.buttonMode = true;
button1.useHandCursor = true;
button1.addEventListener(MouseEvent.CLICK , bt1ClickHandler);
backgroundSheet.addChild(button1);
var bt1Text:TextField = new TextField();
bt1Text.width = 100;
bt1Text.height = 20;
bt1Text.mouseEnabled = false;
button1.addChild(bt1Text);
var bt1TF:TextFormat = new TextFormat();
bt1TF.font ="verdana";
bt1TF.size = 12;
bt1TF.align = "center";
bt1Text.defaultTextFormat = bt1TF;
bt1Text.text = "click to start";
function bt1ClickHandler(e:MouseEvent):void {//ボタンがクリックされた時の処理
fStart = !fStart;//状態を切り替える
if (fStart) {
bt1Text.text = "click to stop";
}else {
bt1Text.text = "click to start";
}
}
}
private function drawTexts():void {//説明文などを描く
//各部品の名称を書く
var tfmt:TextFormat = new TextFormat();
tfmt.font ="verdana";
tfmt.size = 12;
tfmt.color = 0xffffff;
setText(145,-135,"リューズ");
setText(135, -90, "丸穴車");
setText(-60, -180, "角穴車");
setText( -180, -115, "香箱車");
setText(65, 5, "二番車(分針)");
setText(60, 60, "三番車");
setText(55, 120, "四番車(秒針)");
setText( -90, 155, "ガンギ車");
setText( -140, 120, "アンクル");
setText( -180, 60, "テンプ");
function setText(x:Number , y:Number , text:String):void {
var tf:TextField = new TextField();
tf.x = x;
tf.y = y;
tf.text = text;
tf.mouseEnabled = false;
tf.setTextFormat(tfmt);
textSheet.addChild(tf);
}
//now applying torque!の画像作成
appTorqueInfo = new Sprite();
appTorqueInfo.visible = false;//デフォルトは表示しない
textSheet.addChild(appTorqueInfo);
var color:Number = 0xffe4e1;
var arrow:Shape = new Shape();
var r:Number = 25;
arrow.graphics.lineStyle(3, color, 0.99);
arrow.graphics.moveTo(r/2 , -r*1.732/2);
arrow.graphics.curveTo(0, -r * 2 / 1.732 , -r / 2, -r * 1.732 / 2);
arrow.graphics.lineTo(-r/2+7 , -r*1.732/2-7);
appTorqueInfo.addChild(arrow);
appTorqueInfo.x = 12.97 * 10;
appTorqueInfo.y = -12.64 * 10;
var textInfo:TextField = new TextField();
textInfo.text = "now\napplying\ntorque!"
textInfo.x = -75;
textInfo.y = -75;
textInfo.width = 150;
textInfo.height = 66;
textInfo.mouseEnabled = false;
appTorqueInfo.addChild(textInfo);
var textInfoFmt:TextFormat = new TextFormat();
textInfoFmt.font ="verdana";
textInfoFmt.size = 12;
textInfoFmt.color = color;
textInfoFmt.align = "center";
textInfoFmt.bold = true;
textInfo.setTextFormat(textInfoFmt);
//計測用テキストフィールドの設定
evaluTF = new TextField();
evaluTF.x = -230;
evaluTF.y = 210;
evaluTF.width = 400;
evaluTF.defaultTextFormat = tfmt;
textSheet.addChild(evaluTF);
}
private function main():void {//メインの実行部分
//Clockクラスのオブジェクト作成
clock = new Clock(b2Sheet);
//初期画面用に実行する(これがないと寂しいので)
clock.makeStep();
var count:uint = 0;
var sttAngle:Number;//秒針の角度(計測用)
var endAngle:Number;//秒針の角度(計測用)
// 毎フレームの処理のリスナー設定
addEventListener(Event.ENTER_FRAME , EFHandler);
function EFHandler(e:Event):void {// 毎フレームの処理
if (fStart){//実行時のみ処理する
clock.makeStep();
count++;
if (count < 250) {//最初の250ステップだけ、リューズを巻き上げる
clock.wind();
appTorqueInfo.visible = true;
}else{
appTorqueInfo.visible = false;
}
//精度を測定してみる
if (count < 1000) {
evaluTF.text = "もうすぐ精度を測定します";
}else if (count == 1000) {
sttAngle = clock._angle;//測定開始時の秒針の角度[ラジアン]
}else if (count < 2500) {
evaluTF.text = "測定中 (" + ((count - 1000) / 1500 * 100).toFixed(1) + "%)";
}else if (count == 2500) {
endAngle = clock._angle;//測定終了時の秒針の角度[ラジアン]
var dAngle:Number = endAngle-sttAngle;//実測した秒針の角度の変化[ラジアン]
var dAngleDeg:Number = dAngle / Math.PI * 180;
var theoAngle:Number = Math.PI;//理論値では1500ステップ=30秒=半回転する→π[ラジアン]
var result:Number = (dAngle-theoAngle) / theoAngle * 100;//理論値との比
evaluTF.text = "計測終了 30秒での秒針の回転角度は"+dAngleDeg.toFixed(2)+"°(理論値と比べ "+result.toFixed(2)+"%)";
}
}
}
}
}
}
//Clockクラス
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.text.*;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
internal class Clock {
private const SCALE:Number = 10;//px/m よって1mが10px
private var world:b2World;
private var ground:b2Body;
private var crown:Crown;
private var maruana:Maruana;
private var kakuana:Kakuana;
private var koubako:Koubako;
private var gear2:Gear2;
private var gear3:Gear3;
private var gear4:Gear4;
private var gangi:Gangi;
private var anchor:Anchor;
private var tempu:Tempu;
////////////////////////////////////////////////////////////////////
public function Clock(drawSheet:Sprite) {// コンストラクタ
setBase();//基本設定
setParts();//各部品設定
function setBase():void {//基本設定
// シミュレーションする座標の範囲を指定する
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set(-100.0, -100.0);
worldAABB.upperBound.Set(100.0, 100.0);
// 重力を定義する
var gravity:b2Vec2 = new b2Vec2(0.0, 0.0);
// 世界のインスタンスを作成する
world = new b2World(worldAABB, gravity, true);
// DebugDraw を有効にする
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.m_sprite = drawSheet;// b2bg;
debugDraw.m_drawScale = SCALE;
debugDraw.m_fillAlpha = 0.1;
debugDraw.m_lineThickness = 1;
debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
world.SetDebugDraw(debugDraw);
//goundの設定
ground= world.GetGroundBody();
}
function setParts():void {//各部品設定
//各部品のオブジェクト作成
crown= new Crown(world , ground);//リューズ
maruana = new Maruana(world , ground);//丸穴車
kakuana = new Kakuana(world , ground);//角穴車
koubako = new Koubako(world , ground);//香箱車
gear2 = new Gear2(world , ground);//二番車…分針
gear3 = new Gear3(world , ground);//三番車
gear4 = new Gear4(world , ground);//四番車…秒針
gangi = new Gangi(world , ground);//ガンギ車
anchor = new Anchor(world , ground);//アンクル
tempu = new Tempu(world , ground);//テンプ
//ギアジョイント設定
setGearJoint(crown.body , maruana.body , crown.revoluteJoint , maruana.revoluteJoint , 1);//リューズと丸穴車
setGearJoint(maruana.body , kakuana.body , maruana.revoluteJoint , kakuana.revoluteJoint , 1);//丸穴車と角穴車
setGearJoint(koubako.body , gear2.body1 , koubako.revoluteJoint , gear2.revoluteJoint1 , 1 / 6);//香箱車と二番車(の内側)
setGearJoint(gear2.body2 , gear3.body1 , gear2.revoluteJoint2 , gear3.revoluteJoint1 , 1 / 8);//二番車(の外側)と三番車(の内側)
setGearJoint(gear3.body2 , gear4.body1 , gear3.revoluteJoint2 , gear4.revoluteJoint1 , 1 / 7.5);//三番車(の外側)と四番車(の内側)
setGearJoint(gear4.body2 , gangi.body1 , gear4.revoluteJoint2 , gangi.revoluteJoint1 , 1 / 10);//四番車(の外側)とガンギ車(の内側)
//ディスタンスジョイントを設定する(干渉回避用)
setDistanceJoint(gangi.body2 , gear4.body2);//ガンギ車(の外側)と四番車(の外側)が干渉しないようにディスタンスジョイントでつなぐ
setDistanceJoint(gangi.body2 , gear3.body2);//ガンギ車(の外側)と三番車(の外側)が干渉しないようにディスタンスジョイントでつなぐ
function setGearJoint(b1:b2Body , b2:b2Body , j1:b2RevoluteJoint , j2:b2RevoluteJoint , r:Number):void {//ギアジョイントを設定する
var gJointDef1:b2GearJointDef = new b2GearJointDef();
gJointDef1.body1 = b1;
gJointDef1.body2 = b2;
gJointDef1.joint1 = j1;
gJointDef1.joint2 = j2;
gJointDef1.ratio = r;
world.CreateJoint(gJointDef1);
}
function setDistanceJoint(b1:b2Body , b2:b2Body):void {//ディスタンスジョイントを設定する(干渉回避用)
var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
jointDef.Initialize(b1, b2, b1.GetWorldCenter() , b2.GetWorldCenter());
world.CreateJoint(jointDef);
}
}
}////////////////////////////////////////////////////////////////////// コンストラクタここまで
////////////////////////////////////////////////////////////////////
public function makeStep():void {//Box2Dで時間を進める
//Box2Dで時間を進める
world.Step(1 / 50, 10);
//角穴車-香箱車の角度差でトルクを決める
var theKakuanaAngle:Number = kakuana.body.GetAngle();
var theKoubakoAngle:Number = koubako.body.GetAngle();
var torque:Number = (theKakuanaAngle - theKoubakoAngle) * 3000;
//kakuana.body.ApplyTorque(-torque);
//↑本来ならこういうトルクがかかるはずですが、逆回転防止のコハゼという機構の代わりに、この行を消すことで角穴車が逆回転しないようにしています
koubako.body.ApplyTorque(torque);
//テンプを回転させる
var tempuAngle:Number = tempu.body.GetAngle();//テンプの回転角
torque = tempuAngle* tempu._kr;//回転角とばね定数でトルクを決める
tempu.body.ApplyTorque(-torque);//トルクを与える
}////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
public function wind():void {//リューズを巻く
crown.body.ApplyTorque(-3000);
}////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
public function get _angle():Number {//秒針の角度を返す(計測用)
return gear4.body2.GetAngle();
}////////////////////////////////////////////////////////////////////
}
//各部品の座標・形状等の物性を扱うための内部クラス・共通関数
import Box2D.Dynamics.*;
import Box2D.Dynamics.Joints.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import flash.geom.Point;
internal class Crown {//竜頭(リューズ)部分
public var xx:Number = 12.97;//x座標[m]
public var yy:Number = -12.64;//y座標[m]
public var radius:Number = 2.92/2;//半径[m]
public var density:Number = 1;//密度[kg/m^2]
public var body:b2Body;
public var revoluteJoint:b2RevoluteJoint;
public function Crown(world:b2World , ground:b2Body) {
body = makeGearBody(world , xx , yy , radius , density);
revoluteJoint = makeRevoluteJoint(world , ground , body);
}
}
internal class Maruana {//丸穴車
public var xx:Number = 8.58;//x座標[m]
public var yy:Number = -8.25;//y座標[m]
public var radius:Number = 9.39/2;//半径[m]
public var density:Number = 1;//密度[kg/m^2]
public var body:b2Body;
public var revoluteJoint:b2RevoluteJoint;
public function Maruana(world:b2World , ground:b2Body) {
body = makeGearBody(world , xx , yy , radius , density);
revoluteJoint = makeRevoluteJoint(world , ground , body);
}
}
internal class Kakuana {//角穴車
public var xx:Number = -3.99;//x座標[m]
public var yy:Number = -10.22;//y座標[m]
public var radius:Number = 15.86/2;//半径[m]
public var density:Number = 1;//密度[kg/m^2]
public var body:b2Body;
public var revoluteJoint:b2RevoluteJoint;
public function Kakuana(world:b2World , ground:b2Body) {
body = makeGearBody(world , xx , yy , radius , density);
revoluteJoint = makeRevoluteJoint(world , ground , body);
}
}
internal class Koubako {//香箱車
public var xx:Number = -3.99;//x座標[m]
public var yy:Number = -10.22;//y座標[m]
public var radius:Number = 19.22/2;//半径[m]
public var density:Number = 0.005;//密度[kg/m^2]
public var body:b2Body;
public var revoluteJoint:b2RevoluteJoint;
public function Koubako(world:b2World , ground:b2Body) {
body = makeGearBody(world , xx , yy , radius , density , 0);
revoluteJoint = makeRevoluteJoint(world , ground , body);
}
}
internal class Gear2 {//二番車
public var xx:Number = 0;//x座標[m]
public var yy:Number = 0;//y座標[m]
public var radius1:Number = 2.65/2;//半径[m] radius1 < radius2 のこと!
public var radius2:Number = 12.24/2;//半径[m]
public var density:Number = 0.001550;//密度[kg/m^2]
public var body1:b2Body;
public var body2:b2Body;
public var revoluteJoint1:b2RevoluteJoint;
public var revoluteJoint2:b2RevoluteJoint;
public function Gear2(world:b2World , ground:b2Body) {
body1 = makeGearBody(world , xx , yy , radius1 , density , 0);
revoluteJoint1 = makeRevoluteJoint(world , ground , body1);
body2 = makeGearBody(world , xx , yy , radius2 , density , 0);
revoluteJoint2 = makeRevoluteJoint(world , ground , body2);
//body1,2をdistanceJointでつなぐ
setDistanceJoint(world , body1, body2 , xx , yy , radius1);
}
}
internal class Gear3 {//三番車
public var xx:Number = 0;//x座標[m]
public var yy:Number = 7.03;//y座標[m]
public var radius1:Number = 1.85/2;//半径[m] radius1 < radius2 のこと!
public var radius2:Number = 10.75/2;//半径[m]
public var density:Number = 0.005;//密度[kg/m^2]
public var body1:b2Body;
public var body2:b2Body;
public var revoluteJoint1:b2RevoluteJoint;
public var revoluteJoint2:b2RevoluteJoint;
public function Gear3(world:b2World , ground:b2Body) {
body1 = makeGearBody(world , xx , yy , radius1 , density , 0);
revoluteJoint1 = makeRevoluteJoint(world , ground , body1);
body2 = makeGearBody(world , xx , yy , radius2 , density , 0);
revoluteJoint2 = makeRevoluteJoint(world , ground , body2);
//body1,2をdistanceJointでつなぐ
setDistanceJoint(world , body1, body2 , xx , yy , radius1);
}
}
internal class Gear4 {//四番車
public var xx:Number = 0;//x座標[m]
public var yy:Number = 13.03;//y座標[m]
public var radius1:Number = 1.28/2;//半径[m] radius1 < radius2 のこと!
public var radius2:Number = 9.85/2;//半径[m]
public var density:Number = 0.005;//密度[kg/m^2]
public var body1:b2Body;
public var body2:b2Body;
public var revoluteJoint1:b2RevoluteJoint;
public var revoluteJoint2:b2RevoluteJoint;
public function Gear4(world:b2World , ground:b2Body) {
body1 = makeGearBody(world , xx , yy , radius1 , density , 0);
revoluteJoint1 = makeRevoluteJoint(world , ground , body1);
body2 = makeGearBody(world , xx , yy , radius2 , density , 0);
revoluteJoint2 = makeRevoluteJoint(world , ground , body2);
//body1,2をdistanceJointでつなぐ
setDistanceJoint(world , body1, body2 , xx , yy , radius1);
}
}
internal class Gangi {//ガンギ車
public var xx:Number = -5.69;//x座標[m]
public var yy:Number = 12.47;//y座標[m]
public var radius1:Number = 1.51/2;//半径[m] radius1 < radius2 のこと!
public var radius2:Number = 3.0360;//半径[m]
public var density:Number = 0.004800;//密度[kg/m^2]
public var body1:b2Body;
public var body2:b2Body;
public var revoluteJoint1:b2RevoluteJoint;
public var revoluteJoint2:b2RevoluteJoint;
public function Gangi(world:b2World , ground:b2Body) {
body1 = makeGearBody(world , xx , yy , radius1 , density , 0);
revoluteJoint1 = makeRevoluteJoint(world , ground , body1);
body2 = makeGangi(world , xx , yy , radius2 , density);
revoluteJoint2 = makeRevoluteJoint(world , ground , body2);
//body1,2をdistanceJointでつなぐ
setDistanceJoint(world , body1, body2 , xx , yy , radius1);
}
}
internal class Anchor {//アンクル
public var xx:Number = -8.371;//支点x座標[m]
public var yy:Number = 10.2764;//支点y座標[m]
public var density:Number = 0.000240;//密度[kg/m^2]
public var body:b2Body;
public var w:Number = 0.404 , h:Number = 0.82;//つめの形状
public function Anchor(world:b2World , ground:b2Body , l1:Number = 1.79 , l2:Number = 1.79 , thetaL1deg:Number = 32.5,thetaL2deg:Number=149.0 ,thetaTdeg:Number=2) {
//物体の定義
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(xx , yy);//座標[m]
bodyDef.angularDamping = 0;
bodyDef.angle = -0.888;
var thetaT:Number = Math.PI / 180 * thetaTdeg;//ラジアンに変換
var thetaL1:Number = Math.PI / 180 * thetaL1deg;//ラジアンに変換
var thetaL2:Number = Math.PI / 180 * thetaL2deg;//ラジアンに変換
//物体を作る
body = world.CreateBody(bodyDef);
// 形の定義を作る(1)右側
var shapeDef:b2PolygonDef = new b2PolygonDef();
shapeDef.vertexCount = 4;
//右下座標
var RCPt1:Point = new Point(0, 0);//回転中心1
var RCPt2:Point = new Point(l1, 0);//右下の回転前
var aPt:Point;
RCPt2 = RotatePoint(RCPt2 , RCPt1, thetaL1);//RCPt1(原点)を中心に回転させる
shapeDef.vertices[0].Set(RCPt2.x , RCPt2.y);//右下
aPt = RotatePoint(new Point(l1-w , 0) , RCPt1,thetaL1);//左下
aPt = RotatePoint(aPt, RCPt2, thetaT);
shapeDef.vertices[1].Set(aPt.x , aPt.y);//左下
aPt = RotatePoint(new Point(l1-w , -h) , RCPt1,thetaL1);//左上
aPt = RotatePoint(aPt, RCPt2, thetaT);
shapeDef.vertices[2].Set(aPt.x , aPt.y);//左上
aPt = RotatePoint(new Point(l1 , -h) , RCPt1,thetaL1);//右上
aPt = RotatePoint(aPt, RCPt2, thetaT);
shapeDef.vertices[3].Set(aPt.x , aPt.y);//右上
shapeDef.density = density; // 密度 [kg/m^2]
shapeDef.friction = 0;
shapeDef.restitution = 0;
// 形を物体に追加する
body.CreateShape(shapeDef);
// 形の定義を作る(2)左側
shapeDef.vertexCount = 4;
RCPt2 = new Point(l2, 0);//左上の回転前
RCPt2 = RotatePoint(RCPt2 , RCPt1, thetaL2);//RCPt1(原点)を中心に回転させる
shapeDef.vertices[0].Set(RCPt2.x , RCPt2.y);//左上
aPt = RotatePoint(new Point(l2+w, 0) , RCPt1,thetaL2);//右上
aPt = RotatePoint(aPt, RCPt2, thetaT);
shapeDef.vertices[1].Set(aPt.x , aPt.y);//右上
aPt = RotatePoint(new Point(l2+w , h) , RCPt1,thetaL2);//右下
aPt = RotatePoint(aPt, RCPt2, thetaT);
shapeDef.vertices[2].Set(aPt.x , aPt.y);//右下
aPt = RotatePoint(new Point(l2 , h) , RCPt1,thetaL2);//左下
aPt = RotatePoint(aPt, RCPt2, thetaT);
shapeDef.vertices[3].Set(aPt.x , aPt.y);//左下
shapeDef.density = density; // 密度 [kg/m^2]
shapeDef.friction = 0;
shapeDef.restitution = 0;
// 形を物体に追加する
body.CreateShape(shapeDef);
// 形の定義を作る(3)上の左側
var tDeg:Number = 39.8;
var tRad:Number = Math.PI / 180 * tDeg;
w = 0.3;
h = 0.52;
var wAbs:Number = 0.30;// 0.60;//中心線からの距離
var hAbs:Number = 3.85;
RCPt1 = new Point(-wAbs, -hAbs);//回転中心
shapeDef.vertexCount = 4;
shapeDef.vertices[0].Set( -wAbs , -hAbs);//右下
aPt = RotatePoint(new Point(-wAbs-w , -hAbs) , RCPt1, -tRad);//左下
shapeDef.vertices[1].Set(aPt.x , aPt.y);//左下
aPt = RotatePoint(new Point(-wAbs-w , -hAbs - h) , RCPt1, -tRad);//左上
shapeDef.vertices[2].Set(aPt.x , aPt.y);//左上
aPt = RotatePoint(new Point(-wAbs , -hAbs-h) , RCPt1, -tRad);//右上
shapeDef.vertices[3].Set(aPt.x , aPt.y);//右上
shapeDef.density = density;// 密度 [kg/m^2]
shapeDef.friction = 0;
shapeDef.restitution = 0;
// 形を物体に追加する
body.CreateShape(shapeDef);
// 形の定義を作る(4)上の右側
shapeDef.vertexCount = 4;
RCPt1 = new Point(wAbs, -hAbs);//回転中心
shapeDef.vertices[0].Set(wAbs , -hAbs);//左下
aPt = RotatePoint(new Point(wAbs , -hAbs-h) , RCPt1, tRad);//左上
shapeDef.vertices[1].Set(aPt.x , aPt.y);//左上
aPt = RotatePoint(new Point(wAbs+w , -hAbs - h) , RCPt1, tRad);//右上
shapeDef.vertices[2].Set(aPt.x , aPt.y);//右上
aPt = RotatePoint(new Point(wAbs+w , -hAbs) , RCPt1, tRad);//右下
shapeDef.vertices[3].Set(aPt.x , aPt.y);//右下
shapeDef.density = density; // 密度 [kg/m^2]
shapeDef.friction = 0;
shapeDef.restitution = 0;
// 形を物体に追加する
body.CreateShape(shapeDef);
// 重さ・重心を計算する
body.SetMassFromShapes();
//RevoluteJointの設定
var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
jointDef.Initialize(ground, body, new b2Vec2(xx, yy));
jointDef.lowerAngle = - Math.PI / 180 * 5;
jointDef.upperAngle = + Math.PI / 180 * 5;
jointDef.enableLimit = true;//振幅の制限設定の可否
//実際にジョイントを作る
world.CreateJoint(jointDef);
function RotatePoint(pt:Point , cpt:Point , rad:Number):Point {//点ptをcptを中心に回転させる
var xl1:Number = pt.x - cpt.x;
var yl1:Number = pt.y - cpt.y;
var x2:Number = cpt.x + Math.cos(rad) * xl1 - Math.sin(rad) * yl1;
var y2:Number = cpt.y + Math.sin(rad) * xl1 + Math.cos(rad) * yl1;
return new Point(x2, y2);
}
}
}
internal class Tempu {//テンプ
public var xx:Number = -11.971;//x座標[m]
public var yy:Number = 7.35;//y座標[m]
public var radius:Number = 0.16;//半径[m]
public var density:Number = 0.0002;//密度[kg/m^2]
public var _kr:Number;//ねじりばね定数[N・m/rad]
public var body:b2Body;
public function Tempu(world:b2World , ground:b2Body) {
var _m:Number , _r:Number;//慣性モーメント計算用
//物体の定義
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(xx , yy);//座標[m]
bodyDef.angularDamping = 0;
bodyDef.isBullet = true;
//物体を作る
body = world.CreateBody(bodyDef);
var shapeDef:b2CircleDef = new b2CircleDef();
shapeDef.radius = radius;
shapeDef.density = density;
shapeDef.friction = 0;
shapeDef.restitution = 0;
var tl:Number = 0.15;//テンプ中心とアンクル支点を内分する割合(0だとテンプ中心)
shapeDef.localPosition.Set((11.971-8.371)*tl,(10.2764-7.35)*tl);//ここに質量がある
//チェック用
_m = Math.PI * radius * radius * density;//慣性モーメント計算用m
_r = Math.sqrt(Math.pow((11.971 - 8.371) * tl, 2) + Math.pow((10.2764 - 7.35) * tl, 2));
_kr = _m * _r * _r * Math.pow(2.5 * 2 * Math.PI , 2)*2;//*2はカウンター込み
// 形を物体に追加する
body.CreateShape(shapeDef);
//カウンター
shapeDef.localPosition.Set((11.971-8.371)*(-tl),(10.2764-7.35)*(-tl));//ここに質量がある
body.CreateShape(shapeDef);// 形を物体に追加する
//外側に二つ錘をつける
var l:Number = 2.2;//半径
var lrad:Number = (39.2894+90) * Math.PI / 180;
radius = 0.30;
shapeDef.radius =radius;
shapeDef.density = density;
shapeDef.friction = 0;
shapeDef.restitution = 0;
shapeDef.localPosition.Set(l*Math.cos(lrad),l*Math.sin(lrad));//ここに質量がある
body.CreateShape(shapeDef);// 形を物体に追加する
shapeDef.localPosition.Set(-l*Math.cos(lrad),-l*Math.sin(lrad));//ここに質量がある
body.CreateShape(shapeDef);// 形を物体に追加する
_m = Math.PI * radius * radius * density;//慣性モーメント計算用m
_kr += _m * l * l * Math.pow(2.5 * 2 * Math.PI , 2)*2;
// 重さ・重心を計算する
body.SetMassFromShapes();
//RevoluteJointの設定
var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
jointDef.Initialize(ground, body, new b2Vec2(xx,yy));
world.CreateJoint(jointDef);
}
}
function makeGearBody(world:b2World , xx:Number , yy:Number , radius:Number , density:Number , angularDamping:Number=3):b2Body {//単純なギア作成
//物体の定義
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(xx , yy);//座標[m]
bodyDef.angularDamping = angularDamping;
//物体を作る
var body:b2Body = world.CreateBody(bodyDef);
// 形の定義を作る
var shapeDef:b2CircleDef = new b2CircleDef();
shapeDef.radius = radius;
shapeDef.density = density; // 密度 [kg/m^2]
shapeDef.friction = 0;
// 形を物体に追加する
body.CreateShape(shapeDef);
// 重さ・重心を計算する
body.SetMassFromShapes();
return body;
}
function makeRevoluteJoint(world:b2World , ground:b2Body , body:b2Body):b2RevoluteJoint {//body(歯車)を固定するRevoluteJointの作成
//RevoluteJointの設定
var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
jointDef.Initialize(ground, body, body.GetWorldCenter());
//実際にジョイントを作る
var joint:b2RevoluteJoint = b2RevoluteJoint(world.CreateJoint(jointDef));
return joint;
}
function makeGangi(world:b2World , xx:Number , yy:Number , radius:Number , density:Number):b2Body {//ガンギ車外側作成
//半径radius上の円に15個の小さい円を作成する
const R:Number = 0.1;
const N:uint = 15;
var ii:uint;
var shapeDef:b2CircleDef;
var rad:Number;
var offsetR:Number = Math.PI / 180 * 10;
//物体の定義
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(xx , yy);//座標[m]
bodyDef.angularDamping = 0;
bodyDef.isBullet = true;
//物体を作る
var body:b2Body = world.CreateBody(bodyDef);
for (ii = 0; ii < N; ii++) {
makeDot(ii);
}
// 重さ・重心を計算する
body.SetMassFromShapes();
return body;
function makeDot(nn:uint):void {
// 形の定義を作る
rad = (360 / N * nn) / 180 * Math.PI;
shapeDef = new b2CircleDef();
shapeDef.radius = R;
shapeDef.density = density; // 密度 [kg/m^2]
shapeDef.localPosition.Set(offsetR+radius*Math.cos(rad), radius*Math.sin(rad));
shapeDef.friction = 0;
shapeDef.restitution = 0;
body.CreateShape(shapeDef);// 形を物体に追加する
}
}
function setDistanceJoint(world:b2World , body1:b2Body, body2:b2Body , cx:Number , cy:Number , r:Number):void {//二番車など内外に歯を持つギアの固定のためのJoint設定
var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
jointDef.Initialize(body1, body2, new b2Vec2(cx+r, cy) , new b2Vec2(cx, cy+r));
world.CreateJoint(jointDef);
}