がおまる開発ブログ

cocos2d-xやlevelhelperを使って iPhone/Androidアプリの作り方を解説します

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Box2Dのジョイントを試す:マウスジョイント編

Box2Dには物体同士をくっつけたりするジョイントという機能があります。
いくつかジョイント機能のうち今回はマウスジョイントをお知らせします。

マウスジョイント
マウスジョイントとはマウスで物体を掴んで、
タッチ位置を起点に物体を振り回したりドラッグしたり出来ます。
結構面白い動きになるので、発想によっては色々と応用が効きそうです。

実際の動きはこんな感じ!


LevelHelperを利用してますが、
cocos2d-xでも利用できるソースですので、ご利用ください!

SpriteHelperを開いて、オブジェクトにBox2Dを付与していきます。
s_20130209_0.png

LevelHelperに配置していきます。
s_20130209_1.png

続いてXcodeでソースを書いていきます。

#include "cocos2d.h"
#include "Box2D.h"
#include "LevelHelperLoader.h"

class HelloWorld : public cocos2d::CCLayer {
public:
~HelloWorld();
HelloWorld();

// returns a Scene that contains the HelloWorld as the only child
static cocos2d::CCScene* scene();

void initPhysics();
virtual void draw();
virtual void ccTouchesBegan(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
virtual void ccTouchesMoved(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
virtual void ccTouchesCancelled(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
virtual void ccTouchesEnded(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
void update(float dt);

private:
b2World* world;
LevelHelperLoader* lh;

// マウスジョイント変数定義
b2MouseJoint* mouseJoint;

// マウスジョイント取得
b2MouseJoint* getMouseJoint(cocos2d::CCPoint p);

// マウス移動処理
void setMouseTarget(CCPoint p);

// マウスジョイント破棄
void setDestroyMouseJoint();


};

#endif // __HELLO_WORLD_H__



HelloWorld::HelloWorld()
{
setTouchEnabled( true );

CCSize s = CCDirector::sharedDirector()->getWinSize();

// 初期化
this->initPhysics();

scheduleUpdate();
}

HelloWorld::~HelloWorld()
{
delete lh;
lh = NULL;

delete world;
world = NULL;

delete m_debugDraw;
}

// 初期処理
void HelloWorld::initPhysics()
{

CCSize s = CCDirector::sharedDirector()->getWinSize();

b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
world = new b2World(gravity);

// Do we want to let bodies sleep?
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);

// LevelHelper初期化
lh = new LevelHelperLoader("GaomarTest.plhs");
LHSettings::sharedInstance()->setLhPtmRatio(PTM_RATIO);
lh->addObjectsToWorld(world, this);

if(lh->hasPhysicBoundaries())
lh->createPhysicBoundaries(world);

if(!lh->isGravityZero())
lh->createGravity(world);

// マウスジョイント変数初期化
mouseJoint = NULL;
}

// タッチダウン
void HelloWorld::ccTouchesBegan(CCSet* touches, CCEvent* event)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();

//Add a new body/atlas sprite at the touched location
CCSetIterator it;
CCTouch* touch;

for( it = touches->begin(); it != touches->end(); it++)
{
touch = (CCTouch*)(*it);

if(!touch)
break;

CCPoint location = touch->getLocationInView();
location = CCDirector::sharedDirector()->convertToGL(location);

// マウスジョイント取得
mouseJoint = this->getMouseJoint(location);
}
}

// タッチ移動時
void HelloWorld::ccTouchesMoved(CCSet* touches, CCEvent* event)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();

//Add a new body/atlas sprite at the touched location
CCSetIterator it;
CCTouch* touch;

for( it = touches->begin(); it != touches->end(); it++)
{
touch = (CCTouch*)(*it);

if(!touch)
break;

CCPoint location = touch->getLocationInView();
location = CCDirector::sharedDirector()->convertToGL(location);

if (mouseJoint != NULL) {

// マウスジョイントがあれば、オブジェクト移動させる
this->setMouseTarget(location);

} else {
// 無いのでマウスジョイント取得する
mouseJoint = this->getMouseJoint(location);

}
}
}

// タッチキャンセル時
void HelloWorld::ccTouchesCancelled(CCSet* touches, CCEvent* event)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();

//Add a new body/atlas sprite at the touched location
CCSetIterator it;
CCTouch* touch;

for( it = touches->begin(); it != touches->end(); it++)
{
touch = (CCTouch*)(*it);

if(!touch)
break;

// マウスジョイント破棄
this->setDestroyMouseJoint();

}
}


// タッチアップ時
void HelloWorld::ccTouchesEnded(CCSet* touches, CCEvent* event)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();

//Add a new body/atlas sprite at the touched location
CCSetIterator it;
CCTouch* touch;

for( it = touches->begin(); it != touches->end(); it++)
{
touch = (CCTouch*)(*it);

if(!touch)
break;

// マウスジョイント破棄
this->setDestroyMouseJoint();

}
}

////////////////////////////////////////////////////////////////////////////////
// マウスジョイント取得
b2MouseJoint* HelloWorld::getMouseJoint(CCPoint p) {
b2Vec2 locationWorld = b2Vec2(p.x/PTM_RATIO, p.y/PTM_RATIO);

// ANIMALタグだけを取得
CCArray* spritesWithTag = lh->spritesWithTag(ANIMAL);
CCObject* data = NULL;
CCARRAY_FOREACH(spritesWithTag, data) {
// スプライト取得
LHSprite* spr = (LHSprite*)data;

// スプライトがタッチされたかどうか?
if (spr->isTouchedAtPoint(p)) {
// スプライトに付与されているBodyを取得
b2Body* b = spr->getBody();
b2MouseJointDef md;
b2BodyDef groundBodyDef;
b2MouseJoint* mouseJoint;
b2Body* groundBody = world->CreateBody(&groundBodyDef);
md.bodyA = groundBody;//ground Body
md.bodyB = b;
md.target = locationWorld;
md.collideConnected = true;
md.maxForce = 1000.0f * b->GetMass();

b->SetAwake(true);

// ジョイント取得
mouseJoint = (b2MouseJoint *)world->CreateJoint(&md);
return mouseJoint;
}

}

return NULL;
}
////////////////////////////////////////////////////////////////////////////////
// マウスジョイント移動処理
void HelloWorld::setMouseTarget(CCPoint p) {
if (mouseJoint) {
mouseJoint->SetTarget(b2Vec2(p.x/PTM_RATIO, p.y/PTM_RATIO));
}
}
////////////////////////////////////////////////////////////////////////////////
// マウスジョイント破棄
void HelloWorld::setDestroyMouseJoint() {
if (mouseJoint) {
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;

}
}

スポンサーサイト

[LevelHelper講座]第11回:タッチイベントを実装する

LevelHelperにはスプライト毎にタッチ処理を割り当てる事が出来ます。
わざわざccTouchesBegan、ccTouchesMoved、ccTouchesEndedなどでダラダラとプログラムを書かなくても良いです。

具体的な実装方法は以下の通り


#include "cocos2d.h"
#include "Box2D.h"
#include "LevelHelperLoader.h"

class HelloWorld : public cocos2d::CCLayer {
public:
~HelloWorld();
HelloWorld();

// returns a Scene that contains the HelloWorld as the only child
static cocos2d::CCScene* scene();

void initPhysics();
virtual void draw();
virtual void ccTouchesEnded(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
void update(float dt);

private:
b2World* world;
LevelHelperLoader* lh;

// 各種タッチ処理を定義
void touchBeganOnSprite(LHTouchInfo* info);
void touchMovedOnSprite(LHTouchInfo* info);
void touchEndedOnSprite(LHTouchInfo* info);

};


void HelloWorld::initPhysics()
{

CCSize s = CCDirector::sharedDirector()->getWinSize();

b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
world = new b2World(gravity);

// Do we want to let bodies sleep?
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);

// LevelHelper初期化
lh = new LevelHelperLoader("GaomarTest.plhs");
LHSettings::sharedInstance()->setLhPtmRatio(PTM_RATIO);
lh->addObjectsToWorld(world, this);

if(lh->hasPhysicBoundaries())
lh->createPhysicBoundaries(world);

if(!lh->isGravityZero())
lh->createGravity(world);

this->setupCollisionHandling();

LHSprite* spr = lh->createSpriteWithName("0_0_0", "AnimalSheet", "Animal.pshs");
spr->setPosition(ccp(s.width/2, s.height/2));

// スプライトに対してタッチ処理を定義していきます
spr->registerTouchBeganObserver(this, callfuncO_selector(HelloWorld::touchBeganOnSprite));
spr->registerTouchMovedObserver(this, callfuncO_selector(HelloWorld::touchMovedOnSprite));
spr->registerTouchEndedObserver(this, callfuncO_selector(HelloWorld::touchEndedOnSprite));
}

////////////////////////////////////////////////////////////////
// タッチ開始
void HelloWorld::touchBeganOnSprite(LHTouchInfo *info)
{
if(info->sprite)
CCLog("Touch BEGIN on sprite %s", info->sprite->getUniqueName().c_str());
}
////////////////////////////////////////////////////////////////
// タッチ移動
void HelloWorld::touchMovedOnSprite(LHTouchInfo *info)
{
if(info->sprite)
CCLog("Touch MOVED on sprite %s", info->sprite->getUniqueName().c_str());

}
////////////////////////////////////////////////////////////////
// タッチ離す
void HelloWorld::touchEndedOnSprite(LHTouchInfo *info)
{
if(info->sprite)
CCLog("Touch ENDED on sprite %s", info->sprite->getUniqueName().c_str());


}


かなり簡単にタッチ処理が実装出来ちゃいます♪

[LevelHelper講座]第10回:カスタムクラスを使う

LevelHelperにはカスタムクラスという機能があります。
好きなクラスを作成出来て、対象スプライトに定義することができます。

■使用例
・キャラクターの名前
・キャラクターのヒットポイント
・保持しているスコア

こんな感じでデフォルトで何か値を保持したい時に便利です。
もちろんGetter/Setterなので値の取得/変更も可能です。


LevelHelperを開いてCustom Propertiesタグから
カスタムクラスを新規で作成していきましょう。
s_20130128_0.png

設定するスプライトにカスタムクラスをあてがいます。
s_20130128_1.png

カスタムクラスを追加したので、
ソースの出力を行なってください。
s_20130128_4.png


ソースの出力を行うと「CustomClasses」フォルダにカスタムクラスが生成されています
XCodeを開いて、作成されたカスタムクラスをADDしておきます。

s_20130128_2.png


今回のサンプルはANIMALタグで取得して、
画面をタッチした所にスプライトがあれば取得する。
その取得したスプライトのカスタムクラスを取得しようと思います。

void HelloWorld::ccTouchesEnded(CCSet* touches, CCEvent* event)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();

//Add a new body/atlas sprite at the touched location
CCSetIterator it;
CCTouch* touch;

for( it = touches->begin(); it != touches->end(); it++)
{
touch = (CCTouch*)(*it);

if(!touch)
break;

CCPoint location = touch->getLocationInView();
location = CCDirector::sharedDirector()->convertToGL(location);

// ANIMALタグを全て取得
CCArray* spritesWithTag = lh->spritesWithTag(ANIMAL);
CCObject* data = NULL;
CCARRAY_FOREACH(spritesWithTag, data) {
LHSprite* spr = (LHSprite*)data;

// タッチした所にスプライトがあるかどうか確認
if (spr->isTouchedAtPoint(location)) {

// カスタムクラスをuserInfoで取得する
LHAnimalClass* cl = (LHAnimalClass*) spr->userInfo();

if (cl != NULL) {
// 定義したプロパティ名で取得する事が出来ます
CCLog("name=%s", cl->getName().c_str());
CCLog("hp=%f", cl->getHp());

}

}

}
}
}



[LevelHelper講座]第9回:Box2dの衝突処理

Box2dにはオブジェクト同士の衝突を検知する事ができます。
LevelHelperにはそのオブジェクト同士の衝突を簡単に検知と処理できる機能があります。

まずは、SpriteHelperでDynamicなBodyを作りましょう
s_20130127_0.png

LevelHelperを開いて、
新しいタグ「GROUND」を追加しましょう
s_20130127_1.png

今回は地面に当たると消したいので、
Bottom Borderにタグを指定してください。
s_20130127_2.png

Xcodeでソースを編集しましょう。
ヘッダーファイルに衝突の初期化と地面衝突時のメソッドを書きます

#include "cocos2d.h"
#include "Box2D.h"
#include "LevelHelperLoader.h"

class HelloWorld : public cocos2d::CCLayer {
public:
~HelloWorld();
HelloWorld();

// returns a Scene that contains the HelloWorld as the only child
static cocos2d::CCScene* scene();

void initPhysics();
virtual void draw();
virtual void ccTouchesEnded(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
void update(float dt);

private:
b2World* world;
LevelHelperLoader* lh;

// 衝突処理初期化
void setupCollisionHandling();
// 地面と衝突した時の処理
void groundCollision(LHContactInfo* contact);

};



void HelloWorld::initPhysics()
{

b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
world = new b2World(gravity);

// Do we want to let bodies sleep?
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);

// LevelHelper初期化
lh = new LevelHelperLoader("GaomarTest.plhs");
LHSettings::sharedInstance()->setLhPtmRatio(PTM_RATIO);
lh->addObjectsToWorld(world, this);

if(lh->hasPhysicBoundaries())
lh->createPhysicBoundaries(world);

if(!lh->isGravityZero())
lh->createGravity(world);

// 衝突処理初期化
this->setupCollisionHandling();


}

////////////////////////////////////////////////////////////////
void HelloWorld::setupCollisionHandling()
{
// 初期化処理これは絶対に要るやつ
lh->useLevelHelperCollisionHandling();

// タグで指定して呼び出すメソッドを指定
lh->registerBeginOrEndCollisionCallbackBetweenTagA(
GROUND,
ANIMAL,
this,
callfuncO_selector(HelloWorld::groundCollision));
}
////////////////////////////////////////////////////////////////
void HelloWorld::groundCollision(LHContactInfo *contact) {
if(LH_BEGIN_CONTACT == contact->contactType ) {

// GROUNDはspriteA()、ANIMALはspriteB()になってるはず
if (contact->spriteB()->getTag() == ANIMAL) {

LHSprite* spr = contact->spriteB();

// 接触したらスプライトを消す
spr->removeSelf();

}
}
}



これで地面にライオンが当たれば、
画像が消えます。
s_20130127_3.png


衝撃度によって処理をかえる
ぶつかった時の衝撃度に応じた処理もあると思います。
Box2dでは計算で求めることも可能です。

先ほどのソースに一部修正を加えます。
衝撃度の計算はそのままコピペすれば使えると思います。

void HelloWorld::groundCollision(LHContactInfo *contact) {
if(LH_BEGIN_CONTACT == contact->contactType ) {
// GROUNDはspriteA()、ANIMALはspriteB()になってるはず
if (contact->spriteB()->getTag() == ANIMAL) {
b2Contact* boxContact = contact->contact;
b2WorldManifold worldManifold;
boxContact->GetWorldManifold(&worldManifold);
b2Vec2 point = worldManifold.points[0];

b2Body* body1 = contact->bodyA;
b2Body* body2 = contact->bodyB;
b2Vec2 vA = body1->GetLinearVelocityFromWorldPoint(point);
b2Vec2 vB = body2->GetLinearVelocityFromWorldPoint(point);
b2Vec2 now = vB-vA;
float32 force = abs(b2Dot(now, worldManifold.normal));

CCLog("force=%f", force);

// 指定した値以上の衝突の場合にだけ消す
if (force >= 10 ) {

LHSprite* spr = contact->spriteB();
spr->removeSelf();

}
}
}
}


衝撃度が取得出来ていると思います。
これを利用して様々な事が出来ますよね。
s_20130127_4.png

[LevelHelper講座]第8回:タグを使用する

LevelHelperにはスプライトをタグで簡単に管理出来ちゃう機能があります。
スプライトにタグを指定するやり方をお知らせしましょう。

LevelHelperを起動してください。
タグ名は必ず大文字になります。

s_20130126_0.png

タグを設定したいスプライトを選択して
タグを決めちゃいます。
s_20130126_1.png

cocos2d-x用にソースの出力を忘れずに!

s_20130126_2.png
s_20130126_3.png

Xcodeを起動してソースを編集します。

void HelloWorld::ccTouchesEnded(CCSet* touches, CCEvent* event)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();

//Add a new body/atlas sprite at the touched location
CCSetIterator it;
CCTouch* touch;

for( it = touches->begin(); it != touches->end(); it++)
{
touch = (CCTouch*)(*it);

if(!touch)
break;

CCPoint location = touch->getLocationInView();
location = CCDirector::sharedDirector()->convertToGL(location);

// spritesWithTag()メソッドで取得します
CCArray* spritesWithTag = lh->spritesWithTag(ANIMAL);
CCObject* data = NULL;
CCARRAY_FOREACH(spritesWithTag, data) {
LHSprite* spr = (LHSprite*)data;
spr->removeSelf();
}
}
}


これで画面をタップすると
「ANIMAL」タグのスプライトは全て削除されると思います。
s_20130126_4.png
次のページ

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。