Physics2DのCreateRagdollScene

PS Suite SDKの話。
Physics2Dのサンプルとしてある、Physics2DSample。
その中の、
CreateRagdollScene(Ragdollの基本的な生成方法を示したサンプルシーンです。)
について。

[Tips and notice]

まず全文。

This sample shows the way to create very simple ragdoll style object.

Ragdoll is composed of many primitives. (boxes and spheres)

You can determine which parts has collision detection inside one ragdoll by AddIgnorePair(...).
This is more convenient to construct the ragdoll compared with usual collision filter or group filter.

But should be care about the fact that a lot of ignorePair makes the performance of broadphase become worse.
And once that the interpenetration happens, then it is difficult for the ragdoll to restore the original relative position
if there are a lot of ignorePair inside the ragdoll.
(interpenetration free means that it is also easy for the ragdoll to restore the original position
because there is no collision detection and it is free for any parts of the ragdoll to go forward and go back.)

google翻訳を駆使した意訳(間違ってるかもしれないので注意を。)

このサンプルでは簡単なラグドールの作成の方法を紹介します。

ラグドールは多くのプリミティブで構成されます。

AddIgnorePair(..)を使って、ラグドール内でどこに衝突検出を持つのか決めることが可能です。
この方法の方が、Filterを使うよりもラグドール作成において便利です。

しかし、多くのignorePairはパフォーマンスを悪くするので気を付けてください。
また、ignorePairを使用し、万一ラグドールがこんがらがってしまったとき、元に戻るのが困難ですので注意してください。
(衝突検出をけせば、衝突することがないので重なってもこんがらがることがないし、元に簡単に戻ります。)

作成手順

  1. PhysicsShapeの作成
  2. 頭と体
  3. 左上部
  4. 右上部
  5. 左下部
  6. 右下部
  7. 一部の衝突を消す

PhysicsShapeの作成

151行目から

// body center
Vector2 body_width = new Vector2(2.0f, 2.5f);
sceneShapes[4] = new PhysicsShape(body_width);
// head
float head_width = 1.5f;
sceneShapes[5] = new PhysicsShape(head_width);
// arm 1, 2
Vector2 arm_width = new Vector2(1.5f, 0.75f);
sceneShapes[6] = new PhysicsShape(arm_width);
// hand
float hand_width = 0.75f;
sceneShapes[7] = new PhysicsShape(hand_width);
// leg 1, 2
Vector2 leg_width = new Vector2(0.75f, 2.0f);
sceneShapes[8] = new PhysicsShape(leg_width);

PhysicsBodyの作成

頭と体を作成し、Jointでつなげる。
219行目から

// create body
Vector2 center_pos = new Vector2(-10.0f + 20.0f * i, 5.0f);
				
sceneBodies[numBody] = new PhysicsBody(sceneShapes[4], 5.0f);
sceneBodies[numBody].position = center_pos;
//一部省略				

// create head
sceneBodies[numBody] = new PhysicsBody(sceneShapes[5], 1.0f);
sceneBodies[numBody].position = center_pos + new Vector2(0.0f, body_width.Y) + new Vector2(0, head_width);
//一部省略	
	
// creat joint between body and head
bodyA = sceneBodies[body_index];
bodyB = sceneBodies[head_index];
sceneJoints[numJoint] = new PhysicsJoint(bodyA, bodyB, bodyB.position + new Vector2(0.0f, -head_width), (uint)body_index, (uint)head_index);
sceneJoints[numJoint].axis1Lim = new Vector2(1.0f, 0.0f); //移動はしない
sceneJoints[numJoint].axis2Lim = new Vector2(0.0f, 1.0f);
sceneJoints[numJoint].angleLim = 1;
sceneJoints[numJoint].angleLower = PhysicsUtility.GetRadian(-10.0f); //10度の回転を許可
sceneJoints[numJoint].angleUpper = PhysicsUtility.GetRadian(10.0f);
numJoint++;

左上部

247行目から。
arm1(肩から肘)、arm2(肘から手首)、handを作成しJointでつなげる。

// create left arm1					
sceneBodies[numBody] = new PhysicsBody(sceneShapes[6], 1.0f);
sceneBodies[numBody].position = center_pos + new Vector2(-body_width.X, 0.5f*body_width.Y) + new Vector2(-arm_width.X, 0.0f);
//一部省略	
				
// creat joint between body and left arm1 体とくっつける				
bodyA = sceneBodies[body_index];
bodyB = sceneBodies[larm1_index];
sceneJoints[numJoint] = new PhysicsJoint(bodyA, bodyB, bodyB.position+ new Vector2(+arm_width.X, 0.0f), (uint)body_index, (uint)larm1_index);
sceneJoints[numJoint].axis1Lim = new Vector2(1.0f, 0.0f); //移動はしない
sceneJoints[numJoint].axis2Lim = new Vector2(0.0f, 1.0f);
sceneJoints[numJoint].angleLim = 1;
sceneJoints[numJoint].angleLower = PhysicsUtility.GetRadian(-45.0f); //45度の回転を許可
sceneJoints[numJoint].angleUpper = PhysicsUtility.GetRadian(45.0f);
numJoint++;
					
// create left arm2	
sceneBodies[numBody] = new PhysicsBody(sceneShapes[6], 1.0f);
sceneBodies[numBody].position = center_pos + new Vector2(-body_width.X, 0.5f*body_width.Y) + new Vector2(-arm_width.X, 0.0f) + new Vector2(-2.0f*arm_width.X, 0.0f);
//一部省略	
			
// creat joint between left arm1 and left arm2	腕同士をくっつける			
bodyA = sceneBodies[larm1_index];
bodyB = sceneBodies[larm2_index];
sceneJoints[numJoint] = new PhysicsJoint(bodyA, bodyB, 0.5f*(bodyA.position + bodyB.position), (uint)larm1_index, (uint)larm2_index);
sceneJoints[numJoint].axis1Lim = new Vector2(1.0f, 0.0f); //移動はしない
sceneJoints[numJoint].axis2Lim = new Vector2(0.0f, 1.0f);
sceneJoints[numJoint].angleLim = 1;
sceneJoints[numJoint].angleLower = PhysicsUtility.GetRadian(-45.0f); //10度の回転を許可
sceneJoints[numJoint].angleUpper = PhysicsUtility.GetRadian(45.0f);
numJoint++;
					
// create left hand
sceneBodies[numBody] = new PhysicsBody(sceneShapes[7], 1.0f);
sceneBodies[numBody].position = center_pos + new Vector2(-body_width.X, 0.5f*body_width.Y) + new Vector2(-4.0f*arm_width.X, 0.0f) + new Vector2(-hand_width, 0.0f);
//一部省略	
					
// creat joint between left arm2 and left hand	腕と手をくっつける			
bodyA = sceneBodies[larm2_index];
bodyB = sceneBodies[lhand_index];
sceneJoints[numJoint] = new PhysicsJoint(bodyA, bodyB, bodyB.position + new Vector2(hand_width, 0), (uint)larm2_index, (uint)lhand_index);
sceneJoints[numJoint].axis1Lim = new Vector2(1.0f, 0.0f); //移動はしない
sceneJoints[numJoint].axis2Lim = new Vector2(0.0f, 1.0f);
sceneJoints[numJoint].angleLim = 1;
sceneJoints[numJoint].angleLower = PhysicsUtility.GetRadian(-10.0f); //10度の回転を許可
sceneJoints[numJoint].angleUpper = PhysicsUtility.GetRadian(10.0f);
numJoint++;	

右上部

左下部

右下部

内容は違っても方法は一緒なので省略。

一部の衝突を消す

PhysicsScene.AddIgnorePairを使用して衝突検出を消す

void AddIgnorePair (uint index1, uint index2)
衝突を無視するペアをリストに加える

index1 剛体ペアの一方
index2 剛体ペアのもう一方
まず左のラグドール

449行目から。

AddIgnorePair((uint)body_index, (uint)head_index);   //体・頭
					
AddIgnorePair((uint)body_index, (uint)rarm1_index);  //体・右腕1
AddIgnorePair((uint)rarm1_index, (uint)rarm2_index); //右腕1・右腕2
AddIgnorePair((uint)rarm2_index, (uint)rhand_index); //右腕2・右手

AddIgnorePair((uint)body_index, (uint)larm1_index);  //左上部で上と一緒
AddIgnorePair((uint)larm1_index, (uint)larm2_index);
AddIgnorePair((uint)larm2_index, (uint)lhand_index);

AddIgnorePair((uint)body_index, (uint)rleg1_index);  //体・右足1
AddIgnorePair((uint)rleg1_index, (uint)rleg2_index); //右足1・右足2

AddIgnorePair((uint)body_index, (uint)lleg1_index);  //左下部で上と一緒
AddIgnorePair((uint)lleg1_index, (uint)lleg2_index);

この状態では、足同士の判定衝突は残ったまま。
強い力がかかった時に、万一足が絡まってしまうと、元に戻るのは困難。

真ん中のラグドール

469行目から。

sceneBodies[body_index].collisionFilter = 1;
sceneBodies[head_index].collisionFilter = 1;
sceneBodies[larm1_index].collisionFilter = 1;
sceneBodies[larm2_index].collisionFilter = 1;
sceneBodies[lhand_index].collisionFilter = 1;
sceneBodies[rarm1_index].collisionFilter = 1;
sceneBodies[rarm2_index].collisionFilter = 1;
sceneBodies[rhand_index].collisionFilter = 1;
sceneBodies[lleg1_index].collisionFilter = 1;
sceneBodies[lleg2_index].collisionFilter = 1;
sceneBodies[rleg1_index].collisionFilter = 1;
sceneBodies[rleg2_index].collisionFilter = 1;

collisionFilterをすべてのパーツに適用。
体の中で衝突はしない。
比較的安定するようになる。

最後の右のラグドール

487行目から。
衝突はすべて行われる。
そのせいでとても不安定。テストのためだけに使う。