본문 바로가기
cocos2d-x

[cocos2d-x]#8 물리에 대해 알아보자.

by chunma1126 2025. 2. 18.

 

지난 시간에는 cocos2dx에서 어떻게 event들을 처리하는지 알아보았다.

이번시간에는 cocos2dx에서 어떻게 물리를 처리하는지에 대해 알아보도록 하겠다.

 

Scene Init

Physics를 쓰고 싶다면 먼저 Scene을 PhysicsWorld 로 초기화 해줘야 한다.

#pragma once

#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

class HelloWorld : public cocos2d::Scene
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    CREATE_FUNC(HelloWorld);


public:
    cocos2d::PhysicsWorld* physicsWorld;

    void SetUpPhysicsWorld(cocos2d::PhysicsWorld* _physicsWorld)
    {
        physicsWorld = _physicsWorld;
    }
};

저렇게 Scene의 멤버에 PhysicsWorld를 만들어 주고 Create 할때 설정해 주면 된다.

 

Scene* HelloWorld::createScene()
{
    auto scene = Scene::createWithPhysics();
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);

    auto layer = HelloWorld::create();
    layer->SetUpPhysicsWorld(scene->getPhysicsWorld());

    scene->addChild(layer);

    return scene;
}

저기에 setDebugDrawMask는 Debug용으로 사용되는 함수이다.

저렇게 하면 Physics의 충돌 영역 및 충돌범위등을 알수 있다.

 

이렇게 설정을 Scene을 Physics용으로 바꿔주었다면 이제 본격적으로 Physics를 사용할수 있다!

 

Edge

    auto edgeNode = Node::create();
    auto edgeBody = PhysicsBody::createEdgeBox(visibleSize , PHYSICSBODY_MATERIAL_DEFAULT,3);
  
    edgeNode->setPosition(Vec2(visibleSize.width / 2 + origin.x , visibleSize.height / 2 + origin.y));
    edgeNode->setPhysicsBody(edgeBody);

    addChild(edgeNode);

먼저 경계선을 만들어서 물체가 화면밖으로 튕겨 나가는걸 막아주어야 한다.

 

먼저 PhysicsBody를 만들어 주어야 한다.

Create할때 매개변수에 순선대로 크기,physicsMaterial,두께를 넣어줘야 한다.

 

여기서 처음 보는 게 PhysicsMaterial일 텐데, PhysicsMaterial은 물리 속성을 정의하는 구조체로,마찰력,탄성,밀도를 설정할 수 있다.이를 활용하면 물체가 얼마나 미끄러지는지, 얼마나 튕기는지 등을 조절할 수 있다.

cocos2dx에서 이미 정의된 PHYSICSBODY_MATERIAL_DEFAULT 도 있다.

const PhysicsMaterial PHYSICSBODY_MATERIAL_DEFAULT(0.1f, 0.5f, 0.5f);

 

다음과 같이 정의되어 있다.

 

다시 돌아가서 보면

 

저렇게 만든 physicsBody를 node에 설정해주면 된다.

 

Node에 적용시키기

   auto sprite = Sprite::create("Character.png");
   sprite->setPosition(Vec2(visibleSize.width /2 + origin.x , visibleSize.height/ 2 + origin.y));
   
   auto spriteBody = PhysicsBody::createCircle(sprite->getContentSize().width / 2 , PhysicsMaterial(0,1,0));
   sprite->setPhysicsBody(spriteBody);

먼저 Sprite를 Node를 하나 만들고 PhysicsBody를 만들어서 셋팅해주면 된다.

edge를 만들때와 똑같지만 PhysicsBody를 Create할때 edgeBox가 아닌 circle을 해주어야 한다.

 

AngularVelocity

    spriteBody->setAngularVelocity(400);
    spriteBody->setAngularDamping(1);
    spriteBody->setAngularVelocityLimit(30);

setAngularVelocity는 회전 속도를 설정하는 함수다. 단위는 degree/s로, 400이면 1초에 400도 회전한다.

setAngularDamping은 회전 감속을 설정한다. 값이 클수록 빠르게 회전이 멈춘다.

setAngularVelocityLimit는 회전 속도의 최대치를 설정한다. 30으로 제한하면 아무리 힘을 줘도 30 degree/s 이상 회전하지 않는다.

 

Dynamic

    spriteBody->setDynamic(false);

Dynamic을 false로 설정하면 해당 객체는 물리 엔진의 영향을 받지 않고 고정된 상태가 된다. 주로 배경이나 벽 같은 요소에 사용된다.

 

Velocity

    spriteBody->setVelocity(Vec2(200, 0));
    spriteBody->setLinearDamping(0);

setVelocity를 사용하면 객체의 속도를 설정할 수 있다. 위 코드는 x축 방향으로 초당 200 픽셀의 속도를 갖도록 한다.

setLinearDamping은 선형 감속 값을 설정한다. 값이 클수록 속도가 빨리 줄어든다. 0이면 감속이 없다.

 

Apply

    spriteBody->applyForce(Vec2(-1200, 0));
    spriteBody->applyTorque(4000);
    spriteBody->applyImpulse(Vec2());

applyForce는 일정한 힘을 가하는 함수다. (-1200, 0)이면 왼쪽으로 지속적인 힘이 가해진다.

applyTorque는 회전력을 가하는 함수다. 4000이면 객체가 강하게 회전한다.

applyImpulse는 순간적인 힘(충격)을 가하는 함수다. applyForce와 다르게 단발성으로 작용한다.

 

Collision

spriteBody->setCollisionBitmask(1);

이렇게 유니티에서 layer를 설정해 주는 것처럼 Bitmask를 설정해줄수 있다.

 

auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::OnTriggerEnter, this);
getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener,this);

이렇게 EventListener를 만들어서 이벤트를 등록해주고 그걸 dispatcher에 등록해준다.

 

PhysicsEvent는 총4개가 존재한다.이해하기 쉽게 유니티의 함수들과 비교해보도록 하겠다.

이벤트 cocos2dx unity
충돌 시작 onContactBegin OnCollisionEnter / OnTriggerEnter
충돌 해결 전 onContactPreSolve 없음
충돌 해결 후  onContactPostSolve OnCollisionStay (완전히 동일하지 않음)
충돌 종료 onContactSeparate OnCollisionExit / OnTriggerExit 

 

메서드의 호출 흐름을 예시를 통해 살펴보도록 하자.

 

충돌 시작 (onContactBegin): 공이 벽에 닿음.

 

충돌 해결 전 (onContactPreSolve): 공과 벽이 충돌했으니, 튕겨나가도록 속도를 변경하거나, 충돌을 무시하려면 충돌을 취소할 수 있음.

 

충돌 해결 후 (onContactPostSolve): 공이 벽에 충돌한 후 실제 물리적 변화가 적용되고 튕겨나가는지, 혹은 벽에 힘이 가해졌는지 등을 처리.

 

충돌 종료 (onContactSeparate): 공과 벽이 떨어지면서 충돌이 끝남.

 

이제 이런 이벤트들에만 메소드를 등록시키면 충돌 처리를 할수 있다.

 

다음은 내가 onContactBegin에 등록한 예시 함수이다.

bool HelloWorld::OnTriggerEnter(cocos2d::PhysicsContact& contact)
{
    PhysicsBody* a = contact.getShapeA()->getBody();
    PhysicsBody* b = contact.getShapeB()->getBody();

    if (1 == a->getCollisionBitmask() && 2 == b->getCollisionBitmask() || 2 == a->getCollisionBitmask() && 1 == b->getCollisionBitmask())
    {
        CCLOG("Hi");
    }


    return true;
}

return이 true가 되면 충돌 처리가 되고 return false가 되면 두 물체는 충돌이 무시된다.

 

오늘은 cocos2dx의 Physics에 대해 알아보았다.

생각보다 내용이 유니티와 비슷해서 놀랐다.

 

다음시간 부터는 지금까지 배운걸 토대로 간단한 프로젝트를 만들고 그 과정을 공유해 보도록 하겠다!