본문 바로가기

Cocos2d-x v3.17/고급 기능

[Cocos2d-x 고급기능]물리엔진 충돌판정

반응형

    충돌

    당신은 교통사고가 나거나 어떤 물체와 충돌한적이 있나요?


    강체객체는 서로 충돌이 됩니다.그것들이 서로 접촉하는순간 충돌이 발생하는겁니다.

    충돌이 발생하였을때 일련의 이벤트가 발생하지만 완전히 무시할수도 있습니다.


    충돌 필터

    충돌 필터는 어떤것과 충돌하게 할건지 아님 충돌하지 않게할건지 선택할수 있게 해줍니다.엔진은 이런 사용을 위하여 비트마스크를 사용해 충돌필터링을 지원합니다.

    Cocos2d-x 32종류의 충돌종류을 지원하여  도형에 속한 충돌종류을 지정할수 있고

     모양과 충돌  수있는 충돌종류 지정할 수도 있습니다.

     충돌종류 마스킹을 통해 수행됩니다.

    예시:

    C++

    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

    }


    당신은 비트마스크를 검사하여 충돌종류을 검사하고 충돌을 확정시킬수 있습니다.


    C++

    if ((shapeA->getCategoryBitmask() & shapeB->getCollisionBitmask()) == 0

       || (shapeB->getCategoryBitmask() & shapeA->getCollisionBitmask()) == 0)

    {

       // shapes can't collide

       ret = false;

    }




    충돌 그룹 사용하면 전체 그룹 인덱스를 지정할  있습니다.

    동일한 그룹 인덱스가 항상 충돌 (양수 인덱스)하거나 항상 충돌하지 않는 (음수 인덱스와 0 인덱스그룹 인덱스 모양을 가질  있습니다.

    그룹인덱스가 다른 모양을 하고 있다면 충돌종류와 마스킹을 통해 필터링할수 있습니다.

    그룹 필터링이 충돌종류 필터링보다 높은 우선순위를 가지고 있습니다.


    Contract/joint

    앞서 말한 용어들을 기억해보세요.

    관절(joint) 각기 다른 강체를 연결하는 방식입니다.

    인체의 관절같이 다른위치에 연결되있는것처럼요.

    • 다른 강체를 관절로 연결해도 강체는 정적일수도 있습니다.
    • joint(관절)클래스는 모두 PhysicsJoint  자식클래스입니다.
    • joint->setCollisionEnable(false) 메서드를 통해서 연결되있는 강체들이 충돌하는것을 막을수 있습니다.

    관절를 정의할때 당신이 제공해야할 기하데이터가 있습니다.

    대다수의 관절은 모두 피벗으로 정의됩니다.

    나머지 일부 관절에도 각자 정의되어있을겁니다.

    • PhysicsJointFixed고정 관절입니다.두개의 강체가 특정한 점위에 고정되있고 만약 나중에 분리되어야 하는 복합강체를 사용할때 적합한 방식입니다.
    • PhysicsJointLimit두개강체의 최대거리를 제한하여 줄로 연결되있는것처럼 사용합니다.
    • PhysicsJointPin두개의 강체가 독립적으로 하나의 피벗을 기준으로 회전이 있을수 있습니다.핀으로 찍어놓은것처럼!
    • PhysicsJointDistance두개의 강체간에 거리를 설정합니다.
    • PhysicsJointSpring스프링관절입니다.스프링으로 두개의 강체를 연결한것처럼 서로 늘어나거나 줄어듭니다.
    • PhysicsJointRotarySpring스프링관절같지만 위치값으로 영향을 받는것이 아닌 서로 회전의 영향만 받습니다.
    • PhysicsJointRotaryLimitJointLimit 비슷하지만 두개강체의 위치가 아닌 회전에있어서만 영향을 줍니다.
    • PhysicsJointRatchet:与套筒扳手的工作类似소켓렌치같은 작용을 합니다
    • PhysicsJointGear 강체의 각속도비율을 유지시켜줍니다
    • PhysicsJointMotor 강체의 각속도를 일정하게 유지시킵니다.


  • 충돌검사

    충돌(Contact) 물리엔진이 생성한 두개의 도형이 충돌하는것을 관리하기 위한 객체입니다.

    Contact 객체는 우리가 직접 생성하는것이 아니라 자동으로 생성됩니다.

    여기에 관련된 용어를 소개합니다.

    • contact point:두개의 도형이 서로 부딪히는 .
    • contact normal:충돌 법선은 하나의 도형이 충돌된 다른 도형을 가리키는 단위벡터입니다.

    당신은 contact객체에서 physicsShape 얻어올수 있습니다.아래처럼 얻어옵니다.


    C++

    bool onContactBegin(PhysicsContact& contact)

    {

        auto bodyA = contact.getShapeA()->getBody();

        auto bodyB = contact.getShapeB()->getBody();

        return true;

    }


    콘텍트 리스너(Contact Listener) 통해서 충돌을 감지하세요.

    콘텍트 리스너는 아래 네가지 이벤트를 처리합니다 :  begin, pre-solve, post-solve, separate

    • begin 이벤트를 받을땐 두개의 도형이 접촉을 시작할때 입니다.콜백함수가 true 반환하면 충돌이 계속 처리될수 있게 하고,false 반환한다면 물리엔진은  충돌자체를 생략해버립니다.preSolve() postSolve()콜백함수도 생략해버리구요.

    하지만 두개도형이  이상 충돌하지 않을땐 separate이벤트를 받을수 있습니다.

    • pre-solve 이벤트를 받을땐  도형이 접촉해있을때입니다.만약 콜백함수가 false 반환한다면 물리엔진은 이번충돌을 생략해버립니다.true 반환한다면 충돌은 계속 처리할겁니다.그외에 당신은 setRestitution(),setFriction(),setSurfaceVelocity()메서드를 사용하여 회복계수,마찰,표면속도등을 설정하여 충돌속성을 커버할수 있습니다.
    • post-solve이미 두개의 도형이 접촉해있고 충돌이 이미 처리되있을때 이이벤트가 불립니다.
    • separate 이상 접촉하지 않을때 이이벤트가 불립니다.

    EventListenerPhysicsContactWithBodies, EventListenerPhysicsContactWithShapes, EventListenerPhysicsContactWithGroup  통해 당신이 원하는 강체,도형,그룹의 이벤트를 처리할수 있고 물리충돌과 연관된 비트 마스크를 설정해야 합니다.


    주의만약 아무 설정도 하지 않은 상황에서 이벤트리스너를 생성하면 충돌이벤트를 받지 못합니다.


    예시

    C++

    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;

    }

    출처: <http://cocos2d-x.org/docs/cocos2d-x/zh/physics/collisions.html>

     


반응형