Document Version : V1.3 - 2017.07.18 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


바디의 제거

바디를 제거하는 것은 간단하지만 주의해야 할 것이 있습니다.
운동을 하고 있는 바디를 제거하는 경우에는 tick 메서드 안의 step 이 진행되는 과정에서 nullptr 에러가 발생하게 되므로 물리 계산이 일어나지 않는 상태의 바디만을 제거해야 합니다. 이번 예제에서 사용되는 바디들은 운동을 하지 않는 상태이므로 그냥 제거해도 됩니다.


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


c:> cocos new Box2dEx17 -p com.study.box17 -l cpp  ↵


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

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


■ HelloWorldScene.h

■ HelloWorldScene.cpp

■ GLES-Render.h

■ GLES-Render.cpp



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

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

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


■  blocks.png


Box2dEx17은 Box2dEx02 - 디버그 모드까지 적용된 상태에서 시작합니다.




헤더 부분에는 터치를 처리할 메서드의 선언을 추가합니다.

[ 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:

    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);


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

                                          const char* spriteName, int type);

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

};


#endif // __HELLOWORLD_SCENE_H__



다음은 바디의 제거 프로젝트에서 Box2dEx02와 달라진 코드 부분입니다.
터치 리스너를 등록하고 onTouchBegan 메서드에서 바디를 제거하는 코드를 추가합니다.

[ HelloWorldScene.cpp  박스2D 바디의 제거 ]

#include "HelloWorldScene.h"


SceneHelloWorld::createScene()

{

     생략 : Box2dEx02의 코드와 같음 

}


bool HelloWorld::init()

{

     생략 : Box2dEx02의 코드와 같음 

}


bool HelloWorld::createBox2dWorld(bool debug)

{

     생략 : Box2dEx02의 코드와 같음 

}


void HelloWorld::setBox2dWorld()

{

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

    // 물리 객체의 바디와 해당 스프라이트를 추가한다.

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

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

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

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

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

}


HelloWorld::~HelloWorld()

{

     생략 : Box2dEx02의 코드와 같음 

}


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

{

     생략 : Box2dEx02의 코드와 같음 

}


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

{

     생략 : Box2dEx02의 코드와 같음 

}


void HelloWorld::onEnter()

{

    Scene::onEnter();


    // 싱글터치모드로 터치리스너 등록

    auto listener = EventListenerTouchOneByOne::create();

    listener->setSwallowTouches(true);

    listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBeganthis);


    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

}


void HelloWorld::onExit()

{

     생략 : Box2dEx02의 코드와 같음 

}


void HelloWorld::tick(float dt)

{

     생략 : Box2dEx02의 코드와 같음 

}


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

                             const char* spriteName, int type)

{

     생략 : Box2dEx02의 코드와 같음 

}


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

{

    Vec2 touchPoint = touch->getLocation();


    for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext())

    {

        if (b->GetUserData() != nullptr) {


            auto spriteData = (Sprite *)b->GetUserData();


            Rect rect = spriteData->getBoundingBox();

            if (rect.containsPoint(touchPoint)) {

                // 스프라이트 삭제

                this->removeChild(spriteData, true);


                // 물리객체 삭제

                _world->DestroyBody(b);


                break;

            }

        }

    }


    return true;

}




바디를 제거하는 것 자체는 어렵지 않지만 조금 복잡한 과정을 거쳐야 합니다.
  1. 터치 리스너에서 터치를 감지하고
  2. onTouchBegan 메서드에서 월드안의 모든 바디 객체를 찾아서 루프를 돌면서
  3. 해당 바디의 userData 에서 스프라이트 정보를 구해옵니다.
  4. 이후 해당 스프라이트 위치에 터치가 되었는지를 체크합니다.
  5. 터치가 되었다면 해당 바디의 스프라이트를 먼저 제거하고, 바디를 제거합니다.

    Vec2 touchPoint = touch->getLocation();


    for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext())

    {

        if (b->GetUserData() != nullptr) {


            auto spriteData = (Sprite *)b->GetUserData();


            Rect rect = spriteData->getBoundingBox();

            if (rect.containsPoint(touchPoint)) {

                // 스프라이트 삭제

                this->removeChild(spriteData, true);


                // 물리객체 삭제

                _world->DestroyBody(b);


                break;

            }

        }

    }



코드를 완성했으면 실행시켜 봅니다.






이제 바디를 마우스로 클릭해 보면 바디가 제거되는 것을 볼 수 있습니다.