#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);
~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 addNewSpriteAtPosition(Vec2 location);
};
#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
return HelloWorld::create();
}
bool HelloWorld::init()
{
if ( !Scene::init() )
{
return false;
}
// 스프라이트와 겹쳐지면 디버그 모드를 표현할 수 없다.
// auto wlayer = LayerColor::create(Color4B(255, 255, 255, 255));
// this->addChild(wlayer);
/////////////////////////////
// 윈도우 크기를 구한다.
winSize = Director::getInstance()->getWinSize();
// 이미지의 텍스쳐를 구한다.
texture = Director::getInstance()->getTextureCache()->addImage("SpinningPeas.png");
// 월드 생성
if (this->createBox2dWorld(true))
{
this->schedule(schedule_selector(HelloWorld::tick));
}
return true;
}
bool HelloWorld::createBox2dWorld(bool debug)
{
// 월드 생성 시작 ---------------------------------------------------------
// 중력의 방향을 결정한다.
b2Vec2 gravity = b2Vec2(0.0f, -30.0f);
_world = new b2World(gravity);
_world->SetAllowSleeping(true);
_world->SetContinuousPhysics(true);
// 디버그 드로잉 설정
if (debug) {
// 적색 : 현재 물리 운동을 하는 것
// 회색 : 현재 물리 운동량이 없는 것
m_debugDraw = new GLESDebugDraw(PTM_RATIO);
_world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2Draw::e_shapeBit;
//flags += b2Draw::e_jointBit;
//flags += b2Draw::e_aabbBit;
//flags += b2Draw::e_pairBit;
//flags += b2Draw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);
}
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0);
b2Body *groundBody = _world->CreateBody(&groundBodyDef);
b2EdgeShape groundEdge;
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &groundEdge;
// 아래쪽.
groundEdge.Set(b2Vec2(0, 0), b2Vec2(winSize.width / PTM_RATIO, 0));
groundBody->CreateFixture(&boxShapeDef);
// 왼쪽
groundEdge.Set(b2Vec2(0, 0), b2Vec2(0, winSize.height / PTM_RATIO));
groundBody->CreateFixture(&boxShapeDef);
// 위쪽
groundEdge.Set(b2Vec2(0, winSize.height / PTM_RATIO),
b2Vec2(winSize.width / PTM_RATIO, winSize.height / PTM_RATIO));
groundBody->CreateFixture(&boxShapeDef);
// 오른쪽
groundEdge.Set(b2Vec2(winSize.width / PTM_RATIO, winSize.height / PTM_RATIO),
b2Vec2(winSize.width / PTM_RATIO, 0));
groundBody->CreateFixture(&boxShapeDef);
// 월드 생성 끝 ---------------------------------------------------------
return true;
}
HelloWorld::~HelloWorld()
{
delete _world;
_world = nullptr;
}
void HelloWorld::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
Scene::draw(renderer, transform, flags);
_customCmd.init(_globalZOrder, transform, flags);
_customCmd.func = CC_CALLBACK_0(HelloWorld::onDraw, this, transform, flags);
renderer->addCommand(&_customCmd);
}
void HelloWorld::onDraw(const Mat4 &transform, uint32_t flags)
{
Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when seting matrix stack");
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);
GL::enableVertexAttribs(cocos2d::GL::VERTEX_ATTRIB_FLAG_POSITION);
_world->DrawDebugData();
CHECK_GL_ERROR_DEBUG();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
void HelloWorld::onEnter()
{
Scene::onEnter();
// 싱글터치모드로 터치리스너 등록
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}
void HelloWorld::onExit()
{
Scene::onExit();
}
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()));
}
}
}
bool HelloWorld::onTouchBegan(Touch *touch, Event * event)
{
auto touchPoint = touch->getLocation();
// 터치된 지점에 새로운 물리 객체의 바디와 해당 스프라이트를 추가한다.
addNewSpriteAtPosition(touchPoint);
return true;
}
void HelloWorld::addNewSpriteAtPosition(Vec2 location)
{
// 스프라이트를 파라미터로 넘어온 위치에 만든다.
// Sprite* pSprite = Sprite::createWithTexture(texture, Rect(0, 0, 37, 37));
// pSprite->setPosition(Vec2(location.x, location.y));
// this->addChild(pSprite);
// 바디데프 만들고 속성들을 지정한다.
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(location.x / PTM_RATIO, location.y / PTM_RATIO);
// 보통은 유저데이터에 스프라이트를 연결하는데
// 여기서는 아무 데이타를 넣지 않고 디버그 드로잉만 수행한다.
// bodyDef.userData = pSprite;
bodyDef.userData = nullptr;
// 월드에 바디데프의 정보로 바디를 만든다.
b2Body* body = _world->CreateBody(&bodyDef);
// 바디에 적용할 물리 속성용 바디의 모양을 만든다. 여기서는 원을 만든다.
b2CircleShape circle;
// 바디의 크기 지정 - 원의 경우엔 반지름
circle.m_radius = 0.5f;
b2FixtureDef fixtureDef;
// 모양
fixtureDef.shape = &circle;
// 밀도
fixtureDef.density = 1.0f;
// 마찰력
fixtureDef.friction = 0.2f;
// 반발력
fixtureDef.restitution = 0.6f;
body->CreateFixture(&fixtureDef);
}
void HelloWorld::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
Scene::draw(renderer, transform, flags);
_customCmd.init(_globalZOrder, transform, flags);
_customCmd.func = CC_CALLBACK_0(HelloWorld::onDraw, this, transform, flags);
renderer->addCommand(&_customCmd);
}
void HelloWorld::onDraw(const Mat4 &transform, uint32_t flags)
{
Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when seting matrix stack");
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);
GL::enableVertexAttribs(cocos2d::GL::VERTEX_ATTRIB_FLAG_POSITION);
_world->DrawDebugData();
CHECK_GL_ERROR_DEBUG();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}