인디 게임 강좌

전체보기

모바일 상단 메뉴

본문 페이지

[코코스2D] [Box2D] #03 - 바디(Body)의 종류

아이콘 내폰젤무거워
조회: 2852
추천: 1
2017-07-03 22:19:49

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


바디(Body)의 종류

박스2D에는 사용하는 바디는 다음과 같이 세 종류로 구분할 수 있습니다.
 
종류설명
  정적 바디
  Static Body
  특정 위치에 고정되어 있는 바디입니다.
  다른 강체와의 충돌에도 고정된 위치와 각도는 변하지 않습니다.
  (충돌에 따른 물리 연산을 하지 않습니다.)
  키네마틱 바디
  Kinematic Body
  정적 바디와 같은 성격의 정적 객체이지만 속도와 방향을 지정해서 이동시킬 수
  있습니다.
  (충돌에 따른 물리 연산을 하지 않습니다.)
  동적 바디
  Dynamic Body
  중력의 힘을 받는 동적 객체로서 이동과 회전이 가능합니다.
  다른 강체와 충돌하면 마찰력, 반발력에 의해 충돌 반응을 일으킵니다. 
  (충돌에 따른 물리 연산을 하게 됩니다. ex: 튕김 현상 )


그리고 모든 바디는 충돌을 체크하기 위한 형태와 물리적인 특성인 밀도, 마찰력, 반발력을 가지고 있습니다.

이제 세가지 특징을 비교하기 위한 프로젝트를 만들어 보기로 하겠습니다.

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


c:> cocos new Box2dEx03 -p com.study.box03 -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 onTouchBegan(Touch* touch, Event* event);

    void addNewSpriteAtPosition2(Vec2 location);

};


#endif // __HELLOWORLD_SCENE_H__




다음으로는 cpp 부분을 수정합니다.
setBox2dWorld 메서드를 추가해 Static Body와 Kinematic Body를 생성하는 코드 부분을 추가해 주고,
addNewSpriteAtPosition2 메서드에서 Dynamic Body를 추가하는데 Box2dEx02 와 비슷한 이름을 사용했지만 내용이 많이 바뀌었으므로 자세히 보고 코드를 수정해 주어야 합니다.


다음은 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()

{

    // staticBody 스프라이트를 추가한다.

    Sprite* pSprite1 = Sprite::createWithTexture(textureRect(006464));

    pSprite1->setPosition(Vec2(winSize.width / 2winSize.height / 2));

    this->addChild(pSprite1);


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

    b2BodyDef bodyDef1;

    bodyDef1.type = b2_staticBody;

    bodyDef1.position.Set(winSize.width / 2 / PTM_RATIO,

                                        winSize.height / 2 / PTM_RATIO);

    bodyDef1.userData = pSprite1;


    b2Body* body1 = _world->CreateBody(&bodyDef1);


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

    b2PolygonShape staticBox;

    // 바디의 크기 지정 - 상자의 크기에서 가운데 위치를 지정한다.

    staticBox.SetAsBox((pSprite1->getContentSize().width / 2) / PTM_RATIO,

                                     (pSprite1->getContentSize().height / 2) / PTM_RATIO);



    b2FixtureDef fixtureDef1;

    fixtureDef1.shape = &staticBox;

    fixtureDef1.density = 1.0f;


    body1->CreateFixture(&fixtureDef1);



    // kinematicBody 스프라이트를 추가한다.

    Sprite* pSprite2 = Sprite::createWithTexture(textureRect(006432));

    pSprite2->setPosition(Vec2(0100));

    this->addChild(pSprite2);


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

    b2BodyDef bodyDef2;

    bodyDef2.type = b2_kinematicBody;

    bodyDef2.position.Set(0100.0f / PTM_RATIO);

    bodyDef2.linearVelocity = b2Vec2(1.0f0);

    bodyDef2.userData = pSprite2;


    b2Body* body2 = _world->CreateBody(&bodyDef2);


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

    b2PolygonShape kinematicBox;

    // 바디의 크기 지정 - 상자의 크기에서 가운데 위치를 지정한다.

    kinematicBox.SetAsBox((pSprite2->getContentSize().width / 2) / PTM_RATIO,

                                            (pSprite2->getContentSize().height / 2) / PTM_RATIO);



    b2FixtureDef fixtureDef2;

    fixtureDef2.shape = &kinematicBox;

    fixtureDef2.density = 1.0f;


    body2->CreateFixture(&fixtureDef2);

}


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

{

     생략 : Box2dEx02의 코드와 같음 

}


void HelloWorld::onExit()

{

     생략 : Box2dEx02의 코드와 같음 

}


void HelloWorld::tick(float dt)

{

    int velocityIterations = 8;

    int positionIterations = 3;


    // Step : 물리 세계를 시뮬레이션한다.

    _world->Step(dt, velocityIterations, positionIterations);


    // 만들어진 객체 만큼 루프를 돌리면서 바디에 붙인 스프라이트를 여기서 제어한다.

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

    {

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

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


            spriteData->setPosition(Vec2(b->GetPosition().x * PTM_RATIO,

                                                            b->GetPosition().y * PTM_RATIO));

            spriteData->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));

        }

        // --------------------------------------------------------------------

        // 키네마틱 바디 좌우로 이동시키기

        if (b->GetType() == b2_kinematicBody) {

            b2Vec2 v = b->GetPosition();

            if (v.x*PTM_RATIO > winSize.width) {

                // 왼쪽으로 이동

                b->SetLinearVelocity(b2Vec2(-1.0f0));

           }

            else if (v.x < 0) {

                // 오른쪽으로 이동

                b->SetLinearVelocity(b2Vec2(1.0f0));

            }

        }

    }

}


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

{

    auto touchPoint = touch->getLocation();


    // 터치된 지점에 새로운 물리 객체의 바디와 해당 스프라이트를 추가한다.

    addNewSpriteAtPosition2(touchPoint);


    return true;

}


void HelloWorld::addNewSpriteAtPosition2(Vec2 location)

{

    int nNum = rand() % 3;


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

    b2BodyDef bodyDef;

    bodyDef.type = b2_dynamicBody;

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

    bodyDef.userData = nullptr;


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

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



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

    if (nNum == 0) {

        b2PolygonShape dynamicBox;

        dynamicBox.SetAsBox(0.8f0.8f);


        b2FixtureDef fixtureDef;

        fixtureDef.shape = &dynamicBox;

        fixtureDef.density = 1.0f;

        fixtureDef.friction = 0.3f;

        fixtureDef.restitution = 0.0f;


        body->CreateFixture(&fixtureDef);

    }

    else if (nNum == 1) {

        b2PolygonShape dynamicBox;

        b2Vec2 tri[3];


        tri[0].x = -.5;

        tri[0].y = 0.0;


        tri[1].x = .5;

        tri[1].y = 0.0;


        tri[2].x = 0;

        tri[2].y = 1.0;


        dynamicBox.Set(tri, 3);


        b2FixtureDef fixtureDef;

        fixtureDef.shape = &dynamicBox;

        fixtureDef.density = 1.0f;

        fixtureDef.friction = 0.3f;

        fixtureDef.restitution = 1.0f;


        body->CreateFixture(&fixtureDef);


    }

    else {

        b2CircleShape dynamicCircle;

        dynamicCircle.m_radius = 1.0;


        b2FixtureDef fixtureDef;

        fixtureDef.shape = &dynamicCircle;

        fixtureDef.density = 1.0f;

        fixtureDef.friction = 0.2f;

        fixtureDef.restitution = 1.0f;


        body->CreateFixture(&fixtureDef);

    }

}




위 코드에서 보면 바디를 다음과 같이 세가지 타입으로 생성하였습니다.

[ 정적 바디 만들기 ]
 

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

b2BodyDef bodyDef1;

bodyDef1.type = b2_staticBody;

bodyDef1.position.Set(winSize.width/2/PTM_RATIOwinSize.height/2/PTM_RATIO);

bodyDef1.userData = pSprite1;

    

b2Body* body1 = _world->CreateBody(&bodyDef1);


[ 키네마틱 바디 만들기 ]
 

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

b2BodyDef bodyDef2;

bodyDef2.type = b2_kinematicBody;

bodyDef2.position.Set(0100.0f/PTM_RATIO);

bodyDef2.linearVelocity = b2Vec2(1.0f0);

bodyDef2.userData = pSprite2;

    

b2Body* body2 = _world->CreateBody(&bodyDef2);

키네마틱 바디는 앞의 정적 바디를 만들 때와는 다르게 선형 속도값을 벡터값으로 추가 지정해 주었습니다. 위의 코드는 X축으로 1.0의 힘을 받는다는 표현입니다. Y축으로 힘을 주지 않았기 때문에 프로그램이 실행되면 X축으로만 이동하게 될 것입니다.


[ 동적 바디 만들기 ]
 

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

b2BodyDef bodyDef;

bodyDef.type = b2_dynamicBody;

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

bodyDef.userData = NULL;

    

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

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


자 이제 코드를 완성하였으면 실행을 시켜 보도록 합니다.





정적 바디는 화면 한 가운데 고정되어 있고, 그 바로 아래서 키네마틱 바디가 좌우로 이동을 하고 있습니다. 그리고 우리가 화면에 마우스 클릭으로 동적 객체를 만들어내면 바디들끼리 부딪치면서 동적 객체는 충돌 후 물리반응을 일으키지만 정적 객체는 충돌 후 물리반응을 하지 않습니다. 즉 화면상에서 위치의 변경이나 회전을 하지 않는다는 뜻입니다. 마우스 클릭으로 동적 객체를 여러 개 만들어내면서 확인해 보기 바랍니다.


정적 객체와 키네마틱 객체의 위치가 겹치면 어떻게 될까요?

궁금하면 위의 코드에서 키네마틱 바디가 생성되는 위치를 조금 위로 만들어서 테스트해 볼 수 있을 것입니다. 이처럼 물리 엔진을 사용할 때는 많은 값들을 조정해보고 실제로 눈으로 보면서 값의 변화에 따른 시뮬레이션의 결과가 어떻게 되는지 확인해 보는 작업이 중요합니다.








Lv28 내폰젤무거워

모바일 게시판 하단버튼

댓글

새로고침
새로고침

모바일 게시판 하단버튼

지금 뜨는 인벤

더보기+

모바일 게시판 리스트

모바일 게시판 하단버튼

글쓰기

모바일 게시판 페이징