Cocoa部さんのcocos2d-xでBOX2Dを使う入門編( http://cocoabu.com/box2d-00 )
をやろうとしたらいきなりハマったのでメモ。
結論から書くとBox2Dを導入してDebugDrawを実行するステップは,
- cocos2d/extensions/cocos-ext.h 内のショートカットに
# ifndef __COCOS2D_EXT_H__
# define __COCOS2D_EXT_H__
# include "ExtensionMacros.h"
# include "GUI/CCControlExtension/CCControlExtensions.h"
# include "GUI/CCScrollView/CCScrollView.h"
# include "GUI/CCScrollView/CCTableView.h"
# include "GUI/CCEditBox/CCEditBox.h"
# include "../external/Box2D/Box2D.h" // ←Add!!
// Physics integration
# include "physics-nodes/CCPhysicsDebugNode.h"
# include "physics-nodes/CCPhysicsSprite.h"
# include "assets-manager/AssetsManager.h"
# endif /* __COCOS2D_EXT_H__ */
にBox2Dへのパスを追加
(これでextensionのヘッダファイルをインポートすればBox2Dも呼び出せるようになる)
-
GL-ESRender.h, GL-ESRender.cppをインポート
テストの中に入っているものでもいいし、私は
http://blog.csdn.net/tian2kong/article/details/20386213
から拝借しました。そのままClassesフォルダの中に入れるだけでよいです。 -
使いたいSceneファイル内で呼び出す
# ifndef __GAMESCENE_SCENE_H__
# define __GAMESCENE_SCENE_H__
# include "cocos2d.h"
# include "extensions/cocos-ext.h"
# include "Box2D.h"
# include "GLES-Render.h"
# define PTM_RATIO 32.0 // 32px = 1m in Box2D
class GameScene : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags) override;
// implement the "static create()" method manually
CREATE_FUNC(GameScene);
private:
b2World* world;
GLESDebugDraw* debugDraw;
~GameScene();
void initPhysics();
void update(float dt);
};
# endif // __GAME_SCENE_H__
# include "GameScene.h"
# include "Box2D/Box2D.h"
USING_NS_CC;
Scene* GameScene::createScene()
{
auto scene = Scene::create();
auto layer = GameScene::create();
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool GameScene::init()
{
//////////////////////////////
// 1. super init first
if (!Layer::init()){
return false;
}
this->initPhysics();
// Example box2d object to confirm debugDraw works
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(200/PTM_RATIO, 300/PTM_RATIO);
b2Body *body = world->CreateBody(&bodyDef);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(50/PTM_RATIO, 50/PTM_RATIO);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
body->CreateFixture(&fixtureDef);
this->scheduleUpdate();
return true;
}
void GameScene::update(float dt) {
int velocityIterations = 10;
int positionIterations = 10;
this->world->Step(dt, velocityIterations, positionIterations);
}
void GameScene::initPhysics()
{
b2Vec2 gravity;
gravity.Set(0.0f, 1.0f);
this->world = new b2World(gravity);
this->debugDraw = new GLESDebugDraw( PTM_RATIO );
this->world->SetDebugDraw(debugDraw);
uint32 flags = 0;
flags += b2Draw::e_shapeBit;
// flags += b2Draw::e_jointBit;
// flags += b2Draw::e_aabbBit;
// flags += b2Draw::e_pairBit;
// flags += b2Draw::e_centerOfMassBit;
this->debugDraw->SetFlags(flags);
// this->addChild(B2DebugDrawLayer::create(this->world, PTM_RATIO), 9999);
}
void GameScene::draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformUpdated) {
Layer::draw(renderer, transform, transformUpdated);
Director* director = Director::getInstance();
GL::enableVertexAttribs( cocos2d::GL::VERTEX_ATTRIB_FLAG_POSITION );
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
world->DrawDebugData();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
GameScene::~GameScene() {
delete this->debugDraw;
this->debugDraw = NULL;
delete this->world;
this->world = NULL;
}
以上です。
DebugDrawを表示するためにSceneのdrawメソッドをオーバーライドしてやるところがミソです。
(オーバーライドすべきdrawメソッドの仕様が結構頻繁にわっていて、古い情報だとうまくいきません…)
うまくいくと、生成したBox2DのオブジェクトがDebugDrawで描画されているはずです。
# 画像はiOSシミュレータ上のもの。
Box2D自体の使い方は変わってないはずなので, これでデバッグのやり方まで整えばあとは巷にある情報で勉強が進められるかな…。