인디 게임 강좌

전체보기

모바일 상단 메뉴

본문 페이지

[코코스2D] [Box2D] #15 - 충돌 처리

아이콘 내폰젤무거워
조회: 3419
2017-07-14 20:13:39

Document Version : V1.3 - 2017.07.14 with cocos2d-x 3.15.1

Document Version : V1.2 - 2015.06.08 with cocos2d-x 3.6

Document Version : V1.1 - 2014.03.14 with cocos2d-x 3.0beta2

Document Version : V1.0 - 2013.07.10 with cocos2d-x 2.1.4


제 책인 "시작하세요! Cocos2d-x 3.0 프로그래밍" 내용을  3.15.1 버전에 맞게 수정하여 올리고 있습니다.

이 글은 네이버카페 "Cocos2d-x 사용자 모임"에 동시에 게재되고 있습니다.


개발환경 : 

  • Windows7
  • Visual Studio Community 2017
  • Cocos2d-x 3.15.1
  • 사용 프로젝트 : proj.win32


충돌 처리

박스2D는 두 바디 사이의 충돌을 처리하기 위해서 b2ContactListener를 제공합니다. 충돌이 시작하면 충돌에 관한 진행 상황을 b2ContactListener에서 모니터링 하게 되고, 다음의 네 가지 이벤트가 순서대로 호출되는데 충돌을 처리하기 위해서 모든 콜백 함수를 정의할 필요는 없습니다. 필요한 콜백 함수만 정의를 하고 사용하면 됩니다.

종류설명
 Begin
 물리 시뮬레이션의 스텝에서 두 형태가 처음으로 접촉을 시작했을 때
 호출됩니다.
 해당 콜백 함수에서 FALSE 를 리턴하면 뒤이어 발생하는 이벤트를
 무시할 수 있습니다.
 그렇게 하면, preSolve, postSolve 콜백 함수는 호출되지 않으며
 발생한 힘(충돌)을 계산하지 않게 됩니다.
 그렇지만 end 콜백 함수는 여전히 호출됩니다.

 preSolve
 두 형태가 접촉을 계속하는 동안 호출됩니다.
 Begin에서 FALSE를 리턴하면 해당 시뮬레이션 스텝에서 무시되며,
 Begin에서 TRUE를 리턴하면 정상 처리됩니다.
 충돌 후 물리 계산이 되기 전입니다.

 postSolve
 두 형태가 접촉 후, 충돌을 통한 물리 계산을 이미 처리했을 때 호출됩니다.
 이 콜백 함수로 충돌력을 계산할 수 있는데 Impulse에 충돌의 충격량이
 전달되어 옵니다.

 End
 충돌 상황이 이번 스텝에서 해제됐을 때, 즉 두 바디의 접촉이 떨어졌을 때
 호출됩니다.



커맨드창을 열어 원하는 디렉터리로 이동한 후에, 다음과 같이 cocos 명령어를 이용하여 새로운 프로젝트를 생성합니다.


c:> cocos new Box2dEx14 -p com.study.box14 -l cpp  ↵


Box2dEx04의 모든 코드를 방금 만든 Box2dEx14에 적용시킵니다.

Box2dEx04의 Classes 폴더의 다음 파일들을 Box2dEx14의 Classes 폴더에 덮어 쓰면 됩니다.


■ HelloWorldScene.h

■ HelloWorldScene.cpp

■ GLES-Render.h

■ GLES-Render.cpp



그러고 나서 다음의 디렉터리에서 

{Cocos2d-x가 설치된 디렉터리} / tests / cpp-tests / Resources / Images

아래의 파일을 찾아 리소스 폴더에 추가합니다.


■  blocks.png


Box2dEx14는 Box2dEx04 - 마우스 조인트까지 적용된 상태에서 시작합니다.




헤더 부분에는 충돌을 처리하기 위한 메서드를 추가로 선언합니다.
로직에 필요한 변수도 하나 추가합니다.

[ HelloWorldScene.h  박스2D 충돌 처리 ]

#ifndef __HELLOWORLD_SCENE_H__

#define __HELLOWORLD_SCENE_H__


#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)

#pragma execution_character_set("utf-8")

#endif


#include "cocos2d.h"

#include "Box2D/Box2D.h"

#include <GLES-Render.h>


#define PTM_RATIO 32


using namespace cocos2d;


class HelloWorld

    public cocos2d::Scene

    public b2ContactListener

{

public:

    static cocos2d::Scene* createScene();

    virtual bool init();

    CREATE_FUNC(HelloWorld);


    Size winSize;

    Texture2D* texture;

    b2World* _world;


    // For debugging

    GLESDebugDraw* m_debugDraw;

    cocos2d::CustomCommand _customCmd;


    bool createBox2dWorld(bool debug);

    void setBox2dWorld();

    ~HelloWorld();

    virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform,

                               uint32_t flags) override;

    void onDraw(const cocos2d::Mat4& transform, uint32_t flags);


    void onEnter();

    void onExit();

    void tick(float dt);


    bool bDrag;

    b2Body* dragBody;

    b2MouseJoint* mouseJoint;

    b2Body* gbody;


    b2Body* addNewSprite(Vec2 point, Size size, b2BodyType bodytype,

                                           const char* spriteName, int type);

    b2Body* getBodyAtTab(Point p);

    bool onTouchBegan(Touch* touch, Event* event);

    void onTouchMoved(Touch* touch, Event* event);

    void onTouchEnded(Touch* touch, Event* event);


    int num;


    void BeginContact(b2Contact* contact);

    void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);

    void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);

    void EndContact(b2Contact* contact);

};


#endif // __HELLOWORLD_SCENE_H__




다음은 충돌 처리 프로젝트에서 Box2dEx04와 달라진 코드 부분입니다.
init 메서드에서 충돌 처리를 위한 리스너 설정을 추가하고, 충돌 처리를 위해 헤더에 추가한 메서드들을 정의합니다.

[ HelloWorldScene.cpp  박스2D 충돌 처리 ]

#include "HelloWorldScene.h"


SceneHelloWorld::createScene()

{

    return HelloWorld::create();

}


bool HelloWorld::init()

{

    if ( !Scene::init() )

    {

        return false;

    }

    

    /////////////////////////////


    // 윈도우 크기를 구한다.

    winSize = Director::getInstance()->getWinSize();


    // 이미지의 텍스쳐를 구한다.

    texture = Director::getInstance()->getTextureCache()->addImage("blocks.png");


    // 월드 생성

    if (this->createBox2dWorld(true))

    {

        // 월드에 충돌함수 클래스를 리스너에 추가함

        _world->SetContactListener((b2ContactListener *)this);


        srand((int)time(nullptr));

        this->setBox2dWorld();

        this->schedule(schedule_selector(HelloWorld::tick));

    }


    return true;

}


bool HelloWorld::createBox2dWorld(bool debug)

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::setBox2dWorld()

{

    // 마우스 조인트 바디를 생성해서 월드에 추가한다.

    bDrag = false;

    gbody = this->addNewSprite(Vec2(00), Size(00), b2_staticBodynullptr0);


    // 바디를 생성해서 월드에 추가한다.

    this->addNewSprite(Vec2(240160), Size(3232), b2_dynamicBody"test"0);

}


HelloWorld::~HelloWorld()

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::onDraw(const Mat4 &transform, uint32_t flags)

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::onEnter()

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::onExit()

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::tick(float dt)

{

     생략 : Box2dEx04의 코드와 같음 

}


b2BodyHelloWorld::addNewSprite(Vec2 point, Size size, b2BodyType bodytype,

                             const char* spriteName, int type)

{

     생략 : Box2dEx04의 코드와 같음 

}


b2BodyHelloWorld::getBodyAtTab(Vec2 p)

{

     생략 : Box2dEx04의 코드와 같음 

}


bool HelloWorld::onTouchBegan(Touch* touch, Event* event)

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::onTouchMoved(Touch* touch, Event* event)

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::onTouchEnded(Touch* touch, Event* event)

{

     생략 : Box2dEx04의 코드와 같음 

}


void HelloWorld::BeginContact(b2Contact* contact)

{

    num = 0;

    log("Contact : Begin");

}


void HelloWorld::PreSolve(b2Contact* contact, const b2Manifold* oldManifold)

{

    log("Contact : PreSolve");

}


void HelloWorld::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)

{

    log("Contact : PostSolve .. %d"num++);


    b2Fixture* fixA = contact->GetFixtureA();

    b2Fixture* fixB = contact->GetFixtureB();


    b2Body* bodyA = fixA->GetBody();

    b2Body* bodyB = fixB->GetBody();


    if (bodyA->GetType() == b2_dynamicBody || bodyB->GetType() == b2_dynamicBody)

    {

        log("Contact : impulse .. %f", impulse->normalImpulses[0]);

    }

}


void HelloWorld::EndContact(b2Contact* contact)

{

    log("Contact : End");

}





PostSolve 메서드에 파라미터로 들어오는 contact 매개 변수를 통해서 충돌이 일어난 두 바디의 Fixture를 구할 수 있고, Fixture로부터 Body를 구할 수 있습니다.

void HelloWorld::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)

{

    b2Fixture* fixA = contact->GetFixtureA();

    b2Fixture* fixB = contact->GetFixtureB();


    b2Body* bodyA = fixA->GetBody();

    b2Body* bodyB = fixB->GetBody();

}




코드를 완성하고 실행을 하면 공중에서 바디가 하나 만들어지고 중력의 영향을 받아 바닥으로 떨어질 것입니다.




이 때 콘솔창(출력창)의 로그를 보면 다음과 같습니다. 처음에는 아무리 찾아봐도 End가 없을 것입니다. 바닥과의 충돌인데 반발력을 주지 않아서 계속 바닥과 맞닫아 있기 때문에 End 이벤트는 발생하지 않습니다.
그리고 계속해서 preSoleve 와 postSolve 이벤트가 반복적으로 발생함을 알 수 있습니다. 이는 물리적인 운동량이 없어질 때까지 반복이 됩니다.




이제 바디를 마우스로 잡아서 끌어 올리면 End 이벤트가 발생하는 것을 볼 수 있습니다.













Lv28 내폰젤무거워

모바일 게시판 하단버튼

댓글

새로고침
새로고침

모바일 게시판 하단버튼

지금 뜨는 인벤

더보기+

모바일 게시판 리스트

모바일 게시판 하단버튼

글쓰기

모바일 게시판 페이징