フラッシュ上の物理学。 AS3のNapeでラグドールを作成する

画像
物理学をフラッシュで作成する必要がありましたが、1つの小さなニュアンスが必要でした。 ステージ上に10〜15個のレグドルを表示する必要がありました(知らない人のために。レグドルは人形です。人体の模倣)。 人気のあるBox2Dでこれを実装しようとすると、Box2Dで実行できないという残念な結論に至りました。 ちょっとしたグーグルが比較的新しいNape()エンジンを見つけました。 これは一般にこの問題を解決しました。
驚いたことに、私はハブでNapeの言及を見つけることができず、ハブでの作業の始まりを説明することにしました。
カットの下で、regdolの作成の説明、Box2DとNapeでの比較のための例、および例のソースコード。



プロジェクトはFlashDevelopで作成されます。 プロジェクト全体へのリンクを以下に示します。
Napeをダウンロードしてプロジェクトに接続するのは簡単なので、説明しません。

1.世界の創造。

最も重要な瞬間。 コンストラクターで、ライブラリローダーを登録する必要があります。
public function Main(): void
{
new Boot();
….........................
}


* This source code was highlighted with Source Code Highlighter .
public function Main(): void
{
new Boot();
….........................
}


* This source code was highlighted with Source Code Highlighter .


Napeの世界は、3つのクラスで説明されています。
UniformSpaceは、オブジェクトが眠れない世界です。
UniformSleepSpaceは、力が作用しなければオブジェクトが眠りに落ちる世界です。 これにより、プロセッサリソースが節約されます。
BruteSpaceは、オブジェクトが眠ることができない境界線のないスペースです。

理解できるように、最も一般的なのはUniformSleepSpaceです。 それに基づいてプロジェクトを構築します。
// .
var gravity:Vec2 = new Vec2(0, 250);
// . , .
var world:UniformSleepSpace = new UniformSleepSpace( new AABB(0,0, 800, 600), 25, gravity);


* This source code was highlighted with Source Code Highlighter .
// .
var gravity:Vec2 = new Vec2(0, 250);
// . , .
var world:UniformSleepSpace = new UniformSleepSpace( new AABB(0,0, 800, 600), 25, gravity);


* This source code was highlighted with Source Code Highlighter .


Box2Dとは異なり、Napeは物理的な世界と画面上の画像との間でスケーリングしません。 すべてはピクセルで計算されます。

世界の創造は終わりました。 どうやらこれについて複雑なことは何もない。

2.静的ボディを作成します。

私たちの場合、それは一つです-これは地球です。

静的プラットフォームを作成する
パラメーターでは、サイズ、空間内の位置、速度、およびボディのタイプへのポインターを示します(true-静的、false-動的)
physObject = Tools.createBox(400, 500, 700, 20, 0, 0, 0, true , Material.Steel);
world.addObject(physObject); //
addChild(physObject.graphic); //


* This source code was highlighted with Source Code Highlighter .
physObject = Tools.createBox(400, 500, 700, 20, 0, 0, 0, true , Material.Steel);
world.addObject(physObject); //
addChild(physObject.graphic); //


* This source code was highlighted with Source Code Highlighter .


Napeは、Material.SteelやMaterial.Woodなどのデフォルトマテリアルを追加しました。 これにより、プロトタイプ作成と開発が非常に容易になります。

また、Box2Dとは異なり、デフォルトのグラフィックを作成する必要はありません。 オブジェクトにはグラフィックプロパティがあります。 グラフィックをインストールしない場合、デフォルトのプリミティブが表示されます。

3.世界を立ち上げる。
世界を表示するには、ENTER_FRAMEハンドラーを作成し、その中のUniformSleepSpaceインスタンスのstep()メソッドを呼び出します。
シミュレートされた時間をパラメーターとして送信します(例:1 / 30.0)

この段階で、すでにプロジェクトを開始でき、1つのオブジェクトが画面に表示されます-地球。

4.ラグドールを作成します。

人形の体は次のようになります。

画像

人形の物理的なつながりは赤で強調表示されています。

物理エンジンをさらに使用して作業しやすくするために、アクターオブジェクトを作成します。これは、物理世界と表示された画像との間の接続として機能します。

public class Actor extends EventDispatcher
{
//
protected var _body:PhysObj;
// (, )
protected var _costume:DisplayObject;

public function Actor(myBody:PhysObj, myCostume:DisplayObject)
{
_body = myBody;
_costume = myCostume;

if (_body != null )
{
updateLook();
}
}

public function Update(): void
{
// , ,
if (!_body.sleep)
{
updateLook();
}
}

//
private function updateLook(): void
{
var PosX:Number = _body.px;
var PosY:Number = _body.py;
_costume.x = PosX * Main.SCALE;
_costume.y = PosY * Main.SCALE;
}

}


* This source code was highlighted with Source Code Highlighter .
public class Actor extends EventDispatcher
{
//
protected var _body:PhysObj;
// (, )
protected var _costume:DisplayObject;

public function Actor(myBody:PhysObj, myCostume:DisplayObject)
{
_body = myBody;
_costume = myCostume;

if (_body != null )
{
updateLook();
}
}

public function Update(): void
{
// , ,
if (!_body.sleep)
{
updateLook();
}
}

//
private function updateLook(): void
{
var PosX:Number = _body.px;
var PosY:Number = _body.py;
_costume.x = PosX * Main.SCALE;
_costume.y = PosY * Main.SCALE;
}

}


* This source code was highlighted with Source Code Highlighter .


クラスActorを展開して、人体の「Head」オブジェクトを作成しましょう

public class Head extends Actor
{
public var head:PhysObj;

[Embed(source = '../../../../lib/ragdol.swf' , symbol = 'Ragdoll' )]
public var _headSprite: Class;

public function Head(parent:DisplayObjectContainer , location:Point, dimension:Point, initVel:Point)
{
var radius:Number = dimension.y / 2;

// HEAD -------------------------------------------------------------------------------
var headSprite:Sprite = new _headSprite();
headSprite.scaleX = radius * 2 / headSprite.width;
headSprite.scaleY = radius * 2 / headSprite.height;
//
if (!Main.gebug) parent.addChild(headSprite);

//
head = Tools.createCircle(location.x, location.y, radius, 0, 0, 0, false , true , Main.RagdolMaterial);
//
Main.world.addObject(head);
parent.addChild(head.graphic);

//
super(head, headSprite);
}

}


* This source code was highlighted with Source Code Highlighter .
public class Head extends Actor
{
public var head:PhysObj;

[Embed(source = '../../../../lib/ragdol.swf' , symbol = 'Ragdoll' )]
public var _headSprite: Class;

public function Head(parent:DisplayObjectContainer , location:Point, dimension:Point, initVel:Point)
{
var radius:Number = dimension.y / 2;

// HEAD -------------------------------------------------------------------------------
var headSprite:Sprite = new _headSprite();
headSprite.scaleX = radius * 2 / headSprite.width;
headSprite.scaleY = radius * 2 / headSprite.height;
//
if (!Main.gebug) parent.addChild(headSprite);

//
head = Tools.createCircle(location.x, location.y, radius, 0, 0, 0, false , true , Main.RagdolMaterial);
//
Main.world.addObject(head);
parent.addChild(head.graphic);

//
super(head, headSprite);
}

}


* This source code was highlighted with Source Code Highlighter .


体のすべての部分は同じように記述されます。
スペースを節約するために、ここではすべての部分について説明しません。 それらはいろいろな形で見ることができます。

全身を記述するRagdollオブジェクトを作成します。 それらの間の接続。

public class Ragdol extends Actor
{
//
public var _actors:Array;
// .
private var pj:PivotJoint;

// . .
public var rost:Number = 200.0;

// . .

public function Ragdol(parent:DisplayObjectContainer , loc:Point, initVel:Point)
{
// . .
var maxBias:Number = 0.1;
var maxForce:Number = 1e+9;

// .

//
_actors.push( new Head(parent, new Point( head_x, head_y ), new Point(0, dhead), new Point(0, 0)));
_actors.push( new Torso1(parent, new Point( torso1_x, torso1_y ), new Point(ttorso, dtorso1), new Point(0, 0)));
_actors.push( new Torso2(parent, new Point( torso2_x, torso2_y ), new Point(ttorso, dtorso2), new Point(0, 0)));
_actors.push( new Torso3(parent, new Point( torso3_x, torso3_y ), new Point(ttorso, dtorso3), new Point(0, 0)));

_actors.push( new ArmLup(parent, new Point( l_arm_up_x, l_arm_up_y ), new Point(tarm, darm), new Point(0, 0)));
_actors.push( new ArmLmid(parent, new Point( l_arm_low_x, l_arm_low_y ), new Point(tarm, darm), new Point(0, 0)));
_actors.push( new ArmRup(parent, new Point( r_arm_up_x, r_arm_up_y ), new Point(tarm, darm), new Point(0, 0)));
_actors.push( new ArmRmid(parent, new Point( r_arm_low_x, r_arm_low_y ), new Point(tarm, darm), new Point(0, 0)));

_actors.push( new LegLup(parent, new Point(l_leg_up_x, l_leg_up_y), new Point(tleg, dleg), new Point(0, 0)));
_actors.push( new LegLlow(parent, new Point(l_leg_low_x, l_leg_low_y), new Point(tleg, dleg), new Point(0, 0)));
_actors.push( new LegRup(parent, new Point(r_leg_up_x, r_leg_up_y), new Point(tleg, dleg), new Point(0, 0)));
_actors.push( new LegRlow(parent, new Point(r_leg_low_x, r_leg_low_y), new Point(tleg, dleg), new Point(0, 0)));

//
//Head to torso1
pj = new PivotJoint(_actors[0].head, _actors[1].torso1, new Vec2(head_x, head_y + dhead/2));
pj.maxBias = maxBias;
pj.maxForce = maxForce;
Main.world.addConstraint(pj);

// .
// . .

super( null , null );

}

}


* This source code was highlighted with Source Code Highlighter .
public class Ragdol extends Actor
{
//
public var _actors:Array;
// .
private var pj:PivotJoint;

// . .
public var rost:Number = 200.0;

// . .

public function Ragdol(parent:DisplayObjectContainer , loc:Point, initVel:Point)
{
// . .
var maxBias:Number = 0.1;
var maxForce:Number = 1e+9;

// .

//
_actors.push( new Head(parent, new Point( head_x, head_y ), new Point(0, dhead), new Point(0, 0)));
_actors.push( new Torso1(parent, new Point( torso1_x, torso1_y ), new Point(ttorso, dtorso1), new Point(0, 0)));
_actors.push( new Torso2(parent, new Point( torso2_x, torso2_y ), new Point(ttorso, dtorso2), new Point(0, 0)));
_actors.push( new Torso3(parent, new Point( torso3_x, torso3_y ), new Point(ttorso, dtorso3), new Point(0, 0)));

_actors.push( new ArmLup(parent, new Point( l_arm_up_x, l_arm_up_y ), new Point(tarm, darm), new Point(0, 0)));
_actors.push( new ArmLmid(parent, new Point( l_arm_low_x, l_arm_low_y ), new Point(tarm, darm), new Point(0, 0)));
_actors.push( new ArmRup(parent, new Point( r_arm_up_x, r_arm_up_y ), new Point(tarm, darm), new Point(0, 0)));
_actors.push( new ArmRmid(parent, new Point( r_arm_low_x, r_arm_low_y ), new Point(tarm, darm), new Point(0, 0)));

_actors.push( new LegLup(parent, new Point(l_leg_up_x, l_leg_up_y), new Point(tleg, dleg), new Point(0, 0)));
_actors.push( new LegLlow(parent, new Point(l_leg_low_x, l_leg_low_y), new Point(tleg, dleg), new Point(0, 0)));
_actors.push( new LegRup(parent, new Point(r_leg_up_x, r_leg_up_y), new Point(tleg, dleg), new Point(0, 0)));
_actors.push( new LegRlow(parent, new Point(r_leg_low_x, r_leg_low_y), new Point(tleg, dleg), new Point(0, 0)));

//
//Head to torso1
pj = new PivotJoint(_actors[0].head, _actors[1].torso1, new Vec2(head_x, head_y + dhead/2));
pj.maxBias = maxBias;
pj.maxForce = maxForce;
Main.world.addConstraint(pj);

// .
// . .

super( null , null );

}

}


* This source code was highlighted with Source Code Highlighter .


5.レドルの使用。

最も簡単なテストでは、マウスクリックハンドラーを追加します。
private function onClick(e:MouseEvent): void
{
_ragdolActors.push( new Ragdol( this , new Point(mouseX, mouseY), new Point(0, 0)));
}


* This source code was highlighted with Source Code Highlighter .
private function onClick(e:MouseEvent): void
{
_ragdolActors.push( new Ragdol( this , new Point(mouseX, mouseY), new Point(0, 0)));
}


* This source code was highlighted with Source Code Highlighter .


これにより、画面にregdolが追加されます。

物理的な世界が機能するために、更新ハンドラーについて説明します。

private function update(e:Event): void
{
//
world.step(timeStep);

//
for ( var i: int = 0; i < _ragdolActors.length; i++ ) {

for ( var r: int = 0; r < _ragdolActors[i]._actors.length; r++ ) {
_ragdolActors[i]._actors[r].Update();
}

}

}

* This source code was highlighted with Source Code Highlighter .
private function update(e:Event): void
{
//
world.step(timeStep);

//
for ( var i: int = 0; i < _ragdolActors.length; i++ ) {

for ( var r: int = 0; r < _ragdolActors[i]._actors.length; r++ ) {
_ragdolActors[i]._actors[r].Update();
}

}

}

* This source code was highlighted with Source Code Highlighter .

ラグドールを作成する最も簡単な例は終わりです。

次のリンクは例です。
Box2Dのレドル
solverit.ru/swf
うなじのレグドル
solverit.ru/swf2

両方の例のソースは、物理ライブラリが異なることを除いて同一です。 そのため、パフォーマンスの違いを視覚的に推定できます。

FlashDevelopのプロジェクトソース
solverit.ru/files/RagdollNape.zip

PSもちろん、うなじはまだ非常に若いです。 そして、例えば、眠りに落ちるオブジェクトにはいくつかの問題がありますが、彼の可能性は非常に大きいです。

Source: https://habr.com/ru/post/J104176/


All Articles