サイトのトップへ戻る

Cocos2d-x ドキュメント 日本語訳

サイト内検索

物理演算

あなたのゲームは形が整ってきました。 あなたは Sprite オブジェクトやゲームプレイのメカニズムについて理解でき、 コーディングの努力が報われてきています。 ゲームがプレイできる形になったと感じ始めていることでしょう。 ゲームで現実世界の状況を再現する必要があると気づいた時、何をしますか? ご存知の通り、 衝突検知重力弾性摩擦のことです。 はい、お察しの通り。この章では物理演算物理演算エンジンの使用について説明します。 それでは、物理演算エンジンいつどこでなぜ 使用するのかを見ていきましょう。



物理演算はなんだか怖いです、本当に必要なのですか? いいえと言ってください!

物理演算は怖いものではありませんので逃げないでください! あなたのゲーム要件は、 物理演算エンジンを使う必要もないほどの簡単なものかもしれません。 Perhaps a combination of using a Node objects update() function, Rect objects and a combination of the containsPoint() or intersectsRect() functions might be enough for you? 例:

void update(float dt)
{
  auto p = touch->getLocation();
  auto rect = this->getBoundingBox();

  if(rect.containsPoint(p))
  {
      // do something, intersection
  }
}

非常にシンプルなゲーム要件の場合はこのメカニズムでも十分動作しますが、拡張性がありません。 Spriteオブジェクトが100個あって、それらが他のオブジェクトと交差しているかを常に確認し続けるとしたらどうでしょうか? できるでしょうが、CPU使用率とフレームレートはかなりの高負荷になります。 あなたのゲームはとてもプレイできるものではなくなるでしょう。 物理演算エンジンが私達に代わって、拡張可能でCPUにも優しい方法でこの懸念を解決してくれます。 Even though this might look foreign, let's take a look at a simple example and then nut and bolt the example, terminology and best practice together.

// create a static PhysicsBody
auto physicsBody = PhysicsBody::createBox(Size(65.0f , 81.0f ), PhysicsMaterial(0.1f, 1.0f, 0.0f));
physicsBody->setDynamic(false);

// create a sprite
auto sprite = Sprite::create("whiteSprite.png");
sprite->setPosition(Vec2(400, 400));

// sprite will use physicsBody
sprite->addComponent(physicsBody);

//add contact event listener
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

この例は簡単なものですが、複雑で恐ろしいものに見えます。 しかしよーく見てみれば恐ろしくも何ともありません。 上記コードで起こっている手順は以下の通りです。: PhysicsBody オブジェクトを作成。 Sprite オブジェクトを作成。 Sprite オブジェクトを PhysicsBody オブジェクトのプロパティに適用。 onContactBegin() イベントに対応するリスナーを作成。

Once we look step by step the concept starts to make sense. 物理演算エンジン の全ての詳細をより理解するには、後述する用語と概念を理解する必要があります。:



物理演算の用語と概念



Bodies

PhysicsBody はオブジェクトの物理的プロパティを保持しています。 物理的プロパティには masspositionrotationvelocitydampingがあります。 PhysicsBody オブジェクトはshapeの骨格となるものです。 PhysicsBody は、あなたがshape を取り付けるまではshape を持つことはありません。



Material

Material では物質属性を記述します:

-density:親bodyのmassプロパティを算出するのに使用されます。

-friction:各オブジェクトを他のオブジェクトとの兼ね合いでリアルに動かすために使用されます。

-restitution:オブジェクトをバウンドさせるために使用されます。この restitution 値は通常0から1.0の間で設定します。0だとバウンドはせず、1だと完璧にバウンドするということです。



Shapes

Shapes は衝突ジオメトリを記述します。 Shapes を bodiesに取り付けることで、bodyの 形状を定義します。 複雑な形状を定義するために、必要に応じて一つのbody に複数のshapes を取り付けることができます。 各shape はPhysicsMaterial オブジェクトと関連し、以下の属性を持ちます: typeareamassmomentoffsettag。 これらのうちのいくつかはあなたにとって馴染みの無いものかもしれません:

-type:円や四角形や多角形などのような、形状の種類を記述します。

-area: bodyのmass プロパティを算出するのに使用されます。 density値 と area値を基にして mass値が得られます。

-mass: the quantity of matter that a body contains, as measured by its acceleration under a given force or by the force exerted on it by a gravitational field.

-moment: 角加速度のために必要なトルクを決定します。

-offset: offset from the body’s center of gravity in body local coordinates.

-tag: 開発者が形状を簡単に識別するために使用されます。全ての Nodeには識別のためのタグを割り当て、簡単にアクセスできるということを覚えておいて下さい。

Cocos2dxでは以下のような様々な shapes を用意しています:

-PhysicsShape: PhysicsShape 基底クラスを実装したShapes

-PhysicsShapeCircle: 円は立体です。You cannot make a hollow circle using the circle shape.

-PhysicsShapePolygon: Polygon shapes are solid convex polygons.

-PhysicsShapeBox: Box shape is one kind of convex polygon.

-PhysicsShapeEdgeSegment: A segment shape.

-PhysicsShapeEdgePolygon: Hollow polygon shapes. A edge-polygon shape consists of multiple segment shapes.

-PhysicsShapeEdgeBox:Hollow box shapes. A edge-box shape consists of four segment shapes.

-PhysicsShapeEdgeChain: The chain shape provides an efficient way to connect many edges together.



Contacts/Joints

Contacts オブジェクトとjoint オブジェクトでは、各bodyをお互いにどのようにして取り付けるかを記述します。



World

world コンテナには物理bodyを追加して、それらを再現します。 bodies, shapesconstraints を world に追加して、全体としてworldを更新します。 world はそれらがどのように相互作用するかを全て制御します。 Much of the interaction with the physics API will be with a PhysicsWorld object.

ここでは覚えるべきことがたくさんあるので、必要に応じてそれら用語を見直すようにしてください。



Physics World and Physics Body



PhysicsWorld

PhysicsWorld オブジェクトは、物理演算を再現する時に使用される中核となるものです。 あなたが住んでいる世界と同じ様に、 PhysicsWorldでも同時にたくさんのことが起こっています。 PhysicsWorldScene レベルで深く組み込まれています because of it's many facets。 Let's use a simple example that we can all relate to. あなたの家にキッチンはありますか? これは、あなたのphysics worldとして考えてください! 今あなたの world には、食べ物やナイフや調理器具のようなPhysicsBodyオブジェクトがあります! これらのbodies は この world内でお互いに相互作用します。 These objects touch and also react to those touches. 例: ナイフを使って食べ物を切り、それを調理器具の中に入れます。 Does the knife cut the food? Maybe. Maybe not. Perhaps it isn't the correct type of knife for the job.

以下を使用して、 PhysicsWorld を内包する Sceneを作成することができます:

auto scene = Scene::createWithPhysics();

全てのPhysicsWorld はそれに関連するプロパティを持っています:

-gravity: この世界に適用される重力。 既定値は Vec2(0.0f, -98.0f)です。.

-speed: 物理演算世界のスピードを設定します。スピードとは、シミュレーションを実行する速さのことです。既定値は 1.0です。

-updateRate: 物理演算世界の更新速度を設定します。更新速度とは EngineUpdateTimes/PhysicsWorldUpdateTimesの値です。

-substeps: 物理演算世界の更新におけるサブステップ数を設定します。

PhysicsWorldの更新処理は、 ステッピングと呼ばれます。 既定では、PhysicsWorld updates through time automatically. これは 自動ステッピングと呼ばれます。 自動ステッピングは毎フレーム自動的に発生します。 setAutoStep(false)を設定することで、PhysicsWorldauto stepingを無効にすることができます。 無効にした場合は、step(time)を設定して手動でPhysicsWorld更新します。 Substeps are used to step the PhysicsWorld forward multiple times using a more precise time increment than a single frame. This allows for finer grained control of the stepping process including more fluid movements.



PhysicsBody

PhysicsBody オブジェクトは positionvelocityの情報を持っています。 PhysicsBodyオブジェクトには forces,movement, damping ,impulses (他にも色々)を適用できます。 PhysicsBody静的動的にすることができます。 静的 body はシミュレーション下では動かず、無限の質量を持っているかのように振舞います。 動的 body は完全なシミュレートを行うことができます。 動的 body はユーザーによって手動で動かすことができますが、通常はforcesに従って動きます。 動的 body は全ての種類の body と衝突することができます。 Node には、PhysicsBody()Nodeオブジェクトを関連付けるsetPhysicsBody()が実装されています。

それでは、静的PhysicsBodyオブジェクトと五つの動的PhysicsBodyを四角い形状で作成してみましょう:

auto physicsBody = PhysicsBody::createBox(Size(65.0f, 81.0f),
                        PhysicsMaterial(0.1f, 1.0f, 0.0f));
physicsBody->setDynamic(false);

//create a sprite
auto sprite = Sprite::create("whiteSprite.png");
sprite->setPosition(s_centre);
addChild(sprite);

//apply physicsBody to the sprite
sprite->addComponent(physicsBody);

//add five dynamic bodies
for (int i = 0; i < 5; ++i)
{
    physicsBody = PhysicsBody::createBox(Size(65.0f, 81.0f),
                    PhysicsMaterial(0.1f, 1.0f, 0.0f));

    //set the body isn't affected by the physics world's gravitational force
    physicsBody->setGravityEnable(false);

    //set initial velocity of physicsBody
    physicsBody->setVelocity(Vec2(cocos2d::random(-500,500),
                cocos2d::random(-500,500)));
    physicsBody->setTag(DRAG_BODYS_TAG);

    sprite = Sprite::create("blueSprite.png");
    sprite->setPosition(Vec2(s_centre.x + cocos2d::random(-300,300),
                s_centre.y + cocos2d::random(-300,300)));
    sprite->addComponent(physicsBody);

    addChild(sprite);
}

結果は、静止した PhysicsBody とそれに衝突する五つの追加 PhysicsBodyオブジェクトになります。



衝突

あなたは自動車事故に遭ったことはありますか? 何かと衝突したことはありますか? 車と同じ様に、PhysicBodyオブジェクトでも接触が発生します。 衝突 とは、PhysicBodyオブジェクトが他のオブジェクトと接触した時に発生するものです。 衝突 が発生すると、それを無視したり、それを引き金としてイベントを実行したりできます。



衝突をフィルタリングする

衝突をフィルタリングすることで、shape間の衝突を有効にしたり妨げたりできます。 この物理演算エンジン では、 カテゴリーとグループのビットマスクを使用した衝突のフィルタリングをサポートしています。

サポートしている衝突カテゴリーの数は32です。 各 shape ごとに、どの衝突カテゴリーに属するかを設定することができます。 また、各shape が衝突可能なのはどの衝突カテゴリーなのかを設定することもできます。 この設定はビットマスクによって行います。例えば:

auto sprite1 = addSpriteAtPosition(Vec2(s_centre.x - 150,s_centre.y));
sprite1->getPhysicsBody()->setCategoryBitmask(0x02);    // 0010
sprite1->getPhysicsBody()->setCollisionBitmask(0x01);   // 0001

sprite1 = addSpriteAtPosition(Vec2(s_centre.x - 150,s_centre.y + 100));
sprite1->getPhysicsBody()->setCategoryBitmask(0x02);    // 0010
sprite1->getPhysicsBody()->setCollisionBitmask(0x01);   // 0001

auto sprite2 = addSpriteAtPosition(Vec2(s_centre.x + 150,s_centre.y),1);
sprite2->getPhysicsBody()->setCategoryBitmask(0x01);    // 0001
sprite2->getPhysicsBody()->setCollisionBitmask(0x02);   // 0010

auto sprite3 = addSpriteAtPosition(Vec2(s_centre.x + 150,s_centre.y + 100),2);
sprite3->getPhysicsBody()->setCategoryBitmask(0x03);    // 0011
sprite3->getPhysicsBody()->setCollisionBitmask(0x03);   // 0011

以下のように、category および collisionのビットマスクについて確認・比較を行うことで、衝突可能かどうかを確認することができます。:

if ((shapeA->getCategoryBitmask() & shapeB->getCollisionBitmask()) == 0
   || (shapeB->getCategoryBitmask() & shapeA->getCollisionBitmask()) == 0)
{
   // shapes can't collide
   ret = false;
}

衝突グループを使うことで、グループインデックスを設定できるようになります。 全てのshapeに同じグループインデックスを付けて常に衝突可能にしたり(正のインデックス)、決して衝突しないように (負のインデックスと0のインデックス)することができます。 異なるグループインデックスを持つshape間の衝突は、カテゴリーとビットマスクに従ってフィルターされます。 言い換えれば、group でのフィルタリングはcategory でのフィルタリングよりも優先度が高いです。



Contacts/Joints

Recall from the terminology above that joints are how contact points are connected to each other. Yes, you can think of it just like joints on your own body. Each joint type has a definition that derives from PhysicsJoint. All joints are connected between two different bodies. One body may be static. You can prevent the attached bodies from colliding with each other by joint->setCollisionEnable(false). Many joint definitions require that you provide some geometric data. Often a joint will be defined by anchor points. The rest of the joint definition data depends on the joint type.

-PhysicsJointFixed: A fixed joint fuses the two bodies together at a reference point. Fixed joints are useful for creating complex shapes that can be broken apart later.

-PhysicsJointLimit: A limit joint imposes a maximum distance between the two bodies, as if they were connected by a rope.

-PhysicsJointPin: A pin joint allows the two bodies to independently rotate around the anchor point as if pinned together.

-PhysicsJointDistance: Set the fixed distance with two bodies

-PhysicsJointSpring: Connecting two physics bodies together with a spring

-PhysicsJointGroove: Attach body a to a line, and attach body b to a dot

-PhysicsJointRotarySpring: Likes a spring joint, but works with rotary

-PhysicsJointRotaryLimit: Likes a limit joint, but works with rotary

-PhysicsJointRatchet: Works like a socket wrench

-PhysicsJointGear: Keeps the angular velocity ratio of a pair of bodies constant

-PhysicsJointMotor: Keeps the relative angular velocity of a pair of bodies constant



衝突検出

Contacts とは 物理演算エンジンによって作成される、二つのshape間の衝突を管理するためのオブジェクトです。 Contact オブジェクトはユーザーによっては作成されず、自動的に作成されます。 以下に、 contactsに関連する用語をいくつか記載します。

-contact point: contact point とは、二つの shapes が接触した場所です。

-contact normal: contact normal とは、一つのshapeからもう一つのshapeまでの単位ベクトルです。

contactからはPhysicsShapeを取得することができます。そのPhysicsShapeから bodiesを取得できます。

bool onContactBegin(PhysicsContact& contact)
{
    auto bodyA = contact.getShapeA()->getBody();
    auto bodyB = contact.getShapeB()->getBody();
    return true;
}

contact listenerを実装することで、contacts にアクセスすることができます。 この contact listener は、 begin, pre-solve, post-solve , separateといった複数のイベントをサポートしています。

-begin: このステップになって初めて、二つの shapes が接触を開始します。コールバックからtrueを返すと通常通りこの衝突を処理し、falseを返すと物理演算エンジンにこの衝突を一切無視させます。 あなたがfalseを戻り値として返した場合は、 preSolve() コールバックとpostSolve() コールバックは呼ばれなくなりますが、shapeの接触が終了した時にはseparateイベントをちゃんと受け取ります。

-pre-solve: このステップの間は二つのshapes の接触は続いています。コールバックからfalseを返すと物理演算エンジンはこのステップの衝突を無視し、trueを返した場合は通常通り衝突を処理します。 また、setRestitution()setFriction()setSurfaceVelocity() を使って衝突値を上書きし、反発値や摩擦値や表面速度値をカスタムすることができます。

-post-solve:二つ shapes の接触は続いており、この衝突に対する応答は処理された。

-separate: このステップになって初めて、二つの shapes の接触が終了します。

また、 EventListenerPhysicsContactWithBodiesEventListenerPhysicsContactWithShapesEventListenerPhysicsContactWithGroupを使って、あなたの興味があるbodiesや shapes や groupsのイベントを受け取ることもできます。 Besides this you also need to set the physics contact related bitmask value, as the contact event won't be received by default, even if you create the relative EventListener.

例:

bool init()
{
    //create a static PhysicsBody
    auto sprite = addSpriteAtPosition(s_centre,1);
    sprite->setTag(10);
    sprite->getPhysicsBody()->setContactTestBitmask(0xFFFFFFFF);
    sprite->getPhysicsBody()->setDynamic(false);

    //adds contact event listener
    auto contactListener = EventListenerPhysicsContact::create();
    contactListener->onContactBegin = CC_CALLBACK_1(PhysicsDemoCollisionProcessing::onContactBegin, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

    schedule(CC_SCHEDULE_SELECTOR(PhysicsDemoCollisionProcessing::tick), 0.3f);
    return true;

    return false;
}

void tick(float dt)
{
    auto sprite1 = addSpriteAtPosition(Vec2(s_centre.x + cocos2d::random(-300,300),
      s_centre.y + cocos2d::random(-300,300)));
    auto physicsBody = sprite1->getPhysicsBody();
    physicsBody->setVelocity(Vec2(cocos2d::random(-500,500),cocos2d::random(-500,500)));
    physicsBody->setContactTestBitmask(0xFFFFFFFF);
}

bool onContactBegin(PhysicsContact& contact)
{
    auto nodeA = contact.getShapeA()->getBody()->getNode();
    auto nodeB = contact.getShapeB()->getBody()->getNode();

    if (nodeA && nodeB)
    {
        if (nodeA->getTag() == 10)
        {
            nodeB->removeFromParentAndCleanup(true);
        }
        else if (nodeB->getTag() == 10)
        {
            nodeA->removeFromParentAndCleanup(true);
        }
    }

    //bodies can collide
    return true;
}



Queries

あなたはある位置に立って周りを見回したことはありますか? そうするとあなたの 近くにあるものと遠くにあるものが見えます。 見えたものが自分からどれくらいの距離にあるのかを目測することができます。 物理演算エンジンでは、これと同じタイプの空間クエリが実装されています。 PhysicsWorld オブジェクトでは、現在point queryiesray castsrect queriesをサポートしています。



Point Queries

何かに触れたとき、例えば机など、これはpoint queryとして考えることができます。 point queryを使用することで、点から一定の距離内にshapeが存在するかを確認することができます。 Point queries are useful for things like mouse picking and simple sensors. You can also find the closest point on a shape to a given point or find the closest shape to a point.



Ray Cast

あなたが周りを見回している時、あなたの視界に入ったものはきっとあなたの注意を引くことでしょう。 この時、あなたは気づかないうちにray castを行っているのです。 あなたは自分の興味を引くものを見つけるまで周囲を見渡しました。 このようにして、shape をray castして最初に交差したshapeの点情報を取得できます。 例:

void tick(float dt)
{
    Vec2 d(300 * cosf(_angle), 300 * sinf(_angle));
    Vec2 point2 = s_centre + d;
    if (_drawNode)
    {
        removeChild(_drawNode);
    }
    _drawNode = DrawNode::create();

    Vec2 points[5];
    int num = 0;
    auto func = [&points, &num](PhysicsWorld& world,
        const PhysicsRayCastInfo& info, void* data)->bool
    {
        if (num < 5)
        {
            points[num++] = info.contact;
        }
        return true;
    };

    s_currScene->getPhysicsWorld()->rayCast(func, s_centre, point2, nullptr);

    _drawNode->drawSegment(s_centre, point2, 1, Color4F::RED);
    for (int i = 0; i < num; ++i)
    {
        _drawNode->drawDot(points[i], 3, Color4F(1.0f, 1.0f, 1.0f, 1.0f));
    }
    addChild(_drawNode);

    _angle += 1.5f * (float)M_PI / 180.0f;
}



Rect Queries

Rect queries では、範囲内にあるshapes を大まかに確認するための高速な方法が使えます。 実装はとても簡単です:

auto func = [](PhysicsWorld& world, PhysicsShape& shape, void* userData)->bool
{
    //Return true from the callback to continue rect queries
    return true;
}

scene->getPhysicsWorld()->queryRect(func, Rect(0,0,200,200), nullptr);

ロゴスマッシュを行う際にrect query を使ったいくつかの例:



物理演算を無効にする

内蔵の 物理演算エンジン を使用するのは良い考えです。 これは堅牢で高度なエンジンです。 しかし、あなたが別の物理演算エンジンを使いたい場合は、それも可能です。 必要な作業は、base/ccConfig.h内で CC_USE_PHYSICS を無効にすることだけです。