인디 게임 강좌

전체보기

모바일 상단 메뉴

본문 페이지

[코코스2D] [Box2D] #05 - 마우스 조인트

아이콘 내폰젤무거워
조회: 1966
2017-07-05 22:32:21

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


마우스 조인트

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


c:> cocos new Box2dEx04 -p com.study.box04 -l cpp  ↵


Box2dEx02에서 한 것처럼 아래의 파일을 클래스 폴더에 추가합니다.

■ GLES-Render.h

■ GLES-Render.cpp



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

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

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


■  blocks.png


그리고 생성된 프로젝트는 앞에서와 같이 기본형으로 만들고, Box2dEx02에서 작성한 부분까지 코드를 작성하도록 합니다. Box2dEx02는 디버그모드까지 적용된 코드입니다.



이제 헤더에서는 다음과 같이 색으로 표시된 부분이 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);


    bool bDrag;

    b2Body* dragBody;

    b2MouseJoint* mouseJoint;

    b2Body* gbody;


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

                                            const char* spriteName, int type);

    b2Body* getBodyAtTab(Vec2 p);

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

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

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

};


#endif // __HELLOWORLD_SCENE_H__



다음은 마우스 조인트 프로젝트에서 HelloWorldScene 클래스 cpp의 풀 소스입니다.


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

    {

        srand((int)time(nullptr));

        this->setBox2dWorld();

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

    }


    return true;

}


bool HelloWorld::createBox2dWorld(bool debug)

{

     생략 : Box2dEx02의 코드와 같음 

}


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

{

     생략 : 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);

    listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMovedthis);

    listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEndedthis);


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

{

    // 바디데프 만들고 속성들을 지정한다.

    b2BodyDef bodyDef;

    bodyDef.type = bodytype;

    bodyDef.position.Set(point.x / PTM_RATIO, point.y / PTM_RATIO);


    if (spriteName) {

        if (strcmp(spriteName, "test") == 0) {

            int idx = (CCRANDOM_0_1() > .5 ? 0 : 1);

            int idy = (CCRANDOM_0_1() > .5 ? 0 : 1);

            Sprite* sprite = Sprite::createWithTexture(texture,

                                                                               Rect(32 * idx, 32 * idy, 3232));

            sprite->setPosition(point);

            this->addChild(sprite);


            bodyDef.userData = sprite;

        }

        else {

            Sprite* sprite = Sprite::create(spriteName);

            sprite->setPosition(point);

            this->addChild(sprite);


            bodyDef.userData = sprite;

        }

    }


    // 월드에 바디데프의 정보로 바디를 만든다.

    b2Body* body = _world->CreateBody(&bodyDef);


    // 바디에 적용할 물리 속성용 바디의 모양을 만든다.

    b2FixtureDef fixtureDef;

    b2PolygonShape dynamicBox;

    b2CircleShape circle;


    if (type == 0) {

        dynamicBox.SetAsBox(size.width / 2 / PTM_RATIO, size.height / 2 / PTM_RATIO);


        fixtureDef.shape = &dynamicBox;

    }

    else {

        circle.m_radius = (size.width / 2) / PTM_RATIO;


        fixtureDef.shape = &circle;

    }


    // Define the dynamic body fixture.

    fixtureDef.density = 1.0f;

    fixtureDef.friction = 0.3f;

    fixtureDef.restitution = 0.0f;


    body->CreateFixture(&fixtureDef);


    return body;

}


b2BodyHelloWorld::getBodyAtTab(Vec2 p)

{

    b2Fixture* fix;

    // 바디를 터치했는지 모든 바디 리스트를 돌면서 체크한다.

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

    {

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

            if (b->GetType() == b2_staticBodycontinue;

            fix = b->GetFixtureList();

            if (fix->TestPoint(b2Vec2(p.x / PTM_RATIO, p.y / PTM_RATIO))) {

                // 터치된 바디를 리턴한다.

                return b;

            }

        }

    }

    return nullptr;

}


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

{

    Vec2 touchPoint = touch->getLocation();


    if (b2Body* b = this->getBodyAtTab(touchPoint))

    {

        dragBody = b;

        bDrag = true;


        // 터치된 바디에 마우스 조인트를 생성한다.

        b2MouseJointDef md;

        md.bodyA = gbody;

        md.bodyB = dragBody;

        md.target.Set(dragBody->GetPosition().xdragBody->GetPosition().y);

        md.maxForce = 300.0 * dragBody->GetMass();


        mouseJoint = (b2MouseJoint *)_world->CreateJoint(&md);

    }


    return true;

}


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

{

    Vec2 touchPoint = touch->getLocation();


    if (bDrag) {

        mouseJoint->SetTarget(b2Vec2(touchPoint.x / PTM_RATIO, touchPoint.y / PTM_RATIO));

    }

}


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

{

    if (bDrag) {

        // 마우스 조인트를 제거한다.

        _world->DestroyJoint(mouseJoint);

        mouseJoint = nullptr;


        dragBody->SetAwake(true);

    }

    bDrag = false;

}




코드에서 보듯이 마우스 조인트의 대부분의 기능은 터치 이벤트 함수들에서 구현됩니다.
다만 조인트가 두 바디 사이의 연결을 만드는 것이라고 했기 때문에 Vec2(0, 0)에 gbody라는 이름의 형태와 속성이 없는 빈 바디를 하나 만들어 넣습니다. 그리고 탭이 발생한 지점에 바디가 있는지를 확인하고 바디가 있다면 그 바디와 gbody를 서로 마우스 조인트로 연결합니다. 탭이 된 바디B는 해당 탭이 된 위치에 마우스 조인트로 연결됩니다.
            

b2MouseJointDef md;

md.bodyA = gbody;

md.bodyB = dragBody;

md.target.Set(dragBody->GetPosition().xdragBody->GetPosition().y);

md.maxForce = 300.0 * dragBody->GetMass();


mouseJoint = (b2MouseJoint *)_world->CreateJoint(&md);



이후 onTouchMoved 메서드에서 터치가 이동됨에 따라 위치를 바꾸어 주게 됩니다.

mouseJoint->SetTarget(b2Vec2(touchPoint.x / PTM_RATIO, touchPoint.y / PTM_RATIO));



이후 사용자가 화면에서 터치된 손을 띄게 되면 onTouchEnded 메서드에서 이를 감지하여 조인트 마우스를 해제시킵니다.

_world->DestroyJoint(mouseJoint);
mouseJoint = nullptr;


자 이제 코드를 완성하였으면 실행을 시켜 보도록 합니다. 이제 화면에 보이는 바디를 마우스를 이용해 이리 저리 끌어 놓을 수 있을 것입니다. 위로 끌어 올리고 마우스를 놓는다면 중력에 의해 바디는 아래로 떨어져 내릴 것이다. 또한 힘껏 던지는 동작도 할 수 있을 것입니다.




앞으로 만들게 될 조인트 관련 예제들은 모두 마우스 조인트를 포함하고 있습니다. 그러므로 이 이후의 예제들은 Box2dEx04번 코드에 추가로 코드를 작성하면 됩니다.









Lv28 내폰젤무거워

모바일 게시판 하단버튼

댓글

새로고침
새로고침

모바일 게시판 하단버튼

지금 뜨는 인벤

더보기+

모바일 게시판 리스트

모바일 게시판 하단버튼

글쓰기

모바일 게시판 페이징