cocos2d-x 使用box2d来做碰撞检测(且仅用来做碰撞检测)(二)
根据上一节的教程,我用C++更改了教程,我用的cocos2d-2.0-rc2-x-2.0.1版本,略微有些不同,都是小细节,直接贴上代码
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include < Box2D / Box2D.h >
#include " cocos2d.h "
#include < list >
using std::list;
class MyContact
{
public:
b2Fixture* fixtureA;
b2Fixture* fixtureB;
} ;
// Contact listener
class MyContactListener : public b2ContactListener
{
// Callbacks for derived classes.
virtual void BeginContact(b2Contact* contact)
{
if (contact)
{
MyContact mc;
mc.fixtureA = contact->GetFixtureA();
mc.fixtureB = contact->GetFixtureB();
contact_list.push_back(mc);
}
B2_NOT_USED(contact);
}
virtual void EndContact(b2Contact* contact)
{
contact_list.clear();
B2_NOT_USED(contact);
}
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
B2_NOT_USED(contact);
B2_NOT_USED(oldManifold);
}
virtual void PostSolve(const b2Contact* contact, const b2ContactImpulse* impulse)
{
B2_NOT_USED(contact);
B2_NOT_USED(impulse);
}
public:
std::list<MyContact> contact_list;
} ;
class HelloWorld : public cocos2d::CCLayer
{
public:
HelloWorld();
~HelloWorld();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp, so we recommand to return the exactly class pointer
static cocos2d::CCScene* scene();
// a selector callback
void menuCloseCallback(CCObject* pSender);
virtual void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
virtual void ccTouchesMoved(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
virtual void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
virtual void ccTouchesCancelled(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
cocos2d::CCTMXLayer *baseLayer;
// implement the "static node()" method manually
LAYER_CREATE_FUNC(HelloWorld);
private:
// Update per second
void secondUpdate(float dt)
{
spawnCat();
}
void tick(float dt);
// spawn a car
void spawnCar();
// spawn a car
void spawnCat();
// Sprite move over call back
void spriteDone(cocos2d::CCNode* sender);
// Add contact b2box for sprite
void addBoxBodyForSprite(cocos2d::CCSprite* sprite);
b2World* world;
MyContactListener* contactListener; // Contact event listener
} ;
#endif // __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include < Box2D / Box2D.h >
#include " cocos2d.h "
#include < list >
using std::list;
class MyContact
{
public:
b2Fixture* fixtureA;
b2Fixture* fixtureB;
} ;
// Contact listener
class MyContactListener : public b2ContactListener
{
// Callbacks for derived classes.
virtual void BeginContact(b2Contact* contact)
{
if (contact)
{
MyContact mc;
mc.fixtureA = contact->GetFixtureA();
mc.fixtureB = contact->GetFixtureB();
contact_list.push_back(mc);
}
B2_NOT_USED(contact);
}
virtual void EndContact(b2Contact* contact)
{
contact_list.clear();
B2_NOT_USED(contact);
}
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
B2_NOT_USED(contact);
B2_NOT_USED(oldManifold);
}
virtual void PostSolve(const b2Contact* contact, const b2ContactImpulse* impulse)
{
B2_NOT_USED(contact);
B2_NOT_USED(impulse);
}
public:
std::list<MyContact> contact_list;
} ;
class HelloWorld : public cocos2d::CCLayer
{
public:
HelloWorld();
~HelloWorld();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp, so we recommand to return the exactly class pointer
static cocos2d::CCScene* scene();
// a selector callback
void menuCloseCallback(CCObject* pSender);
virtual void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
virtual void ccTouchesMoved(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
virtual void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
virtual void ccTouchesCancelled(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
cocos2d::CCTMXLayer *baseLayer;
// implement the "static node()" method manually
LAYER_CREATE_FUNC(HelloWorld);
private:
// Update per second
void secondUpdate(float dt)
{
spawnCat();
}
void tick(float dt);
// spawn a car
void spawnCar();
// spawn a car
void spawnCat();
// Sprite move over call back
void spriteDone(cocos2d::CCNode* sender);
// Add contact b2box for sprite
void addBoxBodyForSprite(cocos2d::CCSprite* sprite);
b2World* world;
MyContactListener* contactListener; // Contact event listener
} ;
#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp
#include
"
HelloWorldScene.h
"
USING_NS_CC;
USING_NS_CC_EXT;
using namespace std;
#define TILE_SIZE 32
#define PT_RATIO 32 // 这个数一般定义为: 32.0,在box 世界中 是以 米 为单位的,这里是将坐标兑换为box世界中的米,即除以 PTM_RATIO
// --------------------------------------------
HelloWorld::HelloWorld()
{
}
HelloWorld:: ~ HelloWorld()
{
if(world)
delete world;
}
CCScene * HelloWorld::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::create();
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
enum
{
kTagTileMap = 1,
} ;
CCLabelTTF * pLabel;
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
/**///////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
pLabel = CCLabelTTF::create("Collsion", "Arial", 24);
CCSize size = CCDirector::sharedDirector()->getWinSize();
// position the label on the center of the screen
pLabel->setPosition( ccp(size.width / 2, size.height - 50) );
// add the label as a child to this layer
this->addChild(pLabel, 1, 0);
// create physic world
b2Vec2 gravity(0,0);
world = new b2World(gravity);
world->SetAllowSleeping(false);
contactListener = new MyContactListener();
world->SetContactListener(contactListener);
spawnCar();
schedule(schedule_selector(HelloWorld::tick));
schedule(schedule_selector(HelloWorld::secondUpdate), 1.f);
setTouchEnabled(true);
return true;
}
void HelloWorld::tick( float dt)
{
if (world)
world->Step(dt, 10, 10);
// 基于cocos2d的精灵位置来更新box2d的body位置
for(b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL)
{
CCSprite* sprite = (CCSprite*)b->GetUserData();
if (sprite)
{
b2Vec2 pt = b2Vec2((float)(sprite->getPosition().x / PT_RATIO), (float)(sprite->getPosition().y / PT_RATIO));
float angle = (CCFloat)CC_DEGREES_TO_RADIANS(sprite->getRotation());
b->SetTransform(pt, angle);
}
}
}
std::list<b2Body*> toDestroy_list;
for( std::list<MyContact>::iterator it = contactListener->contact_list.begin();
it != contactListener->contact_list.end();
++it)
{
MyContact& contact = *it;
b2Body* bodyA = contact.fixtureA->GetBody();
b2Body* bodyB = contact.fixtureB->GetBody();
CCSprite* sa = (CCSprite*)bodyA->GetUserData();
CCSprite* sb = (CCSprite*)bodyB->GetUserData();
if (sa && sb)
{
if (sa->getTag() == 1 && sb->getTag() == 2)
toDestroy_list.push_back(bodyB);
else if (sa->getTag() == 2 && sa->getTag() == 1)
toDestroy_list.push_back(bodyA);
}
}
// Destroy contact item.
std::list<b2Body*>::iterator it = toDestroy_list.begin();
while(it != toDestroy_list.end())
{
if ((*it)->GetUserData() != NULL)
{
CCSprite* sprite = (CCSprite*)((*it)->GetUserData());
if (sprite)
{
removeChild(sprite, true);
}
world->DestroyBody(*it);
}
++it;
}
toDestroy_list.clear();
}
void HelloWorld::spawnCar()
{
CCSprite* car = CCSprite::spriteWithFile("images/car.png");
car->setPosition(ccp(100,100));
car->runAction(CCRepeatForever::actionWithAction((CCActionInterval*)CCSequence::actions(
CCMoveTo::actionWithDuration(1.0f, ccp(300,100)),
CCMoveTo::actionWithDuration(1.0f, ccp(200,200)),
CCMoveTo::actionWithDuration(1.0f, ccp(100,100)),
NULL
)));
addBoxBodyForSprite(car);
this->addChild(car, 1, 1);
}
void HelloWorld::spawnCat()
{
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
CCSprite* cat = CCSprite::spriteWithFile("images/cat.png");
float minY = cat->getContentSize().height/2;
float maxY = winSize.height - cat->getContentSize().height/2;
float y = minY + rand() % (int)(maxY - minY);
float startX = winSize.width + cat->getContentSize().width/2;
float endX = -cat->getContentSize().width/2;
CCPoint startPos = ccp(startX, y);
CCPoint endPos = ccp(endX, y);
cat->setPosition(startPos);
cat->runAction(CCSequence::actions(CCMoveTo::actionWithDuration(10.f, endPos),
CCCallFuncN::actionWithTarget(this, callfuncN_selector(HelloWorld::spriteDone)), NULL));
addBoxBodyForSprite(cat);
addChild(cat, 1, 2);
}
void HelloWorld::addBoxBodyForSprite(cocos2d::CCSprite * sprite)
{
//PTM_RATIO ,这个数一般定义为: 32.0,在box 世界中 是以 米 为单位的,这里是将坐标兑换为box世界中的米,即除以 PTM_RATIO
// Create physic body for cat
b2PolygonShape polygon;
polygon.SetAsBox((float)sprite->getContentSize().width/PT_RATIO/2, (float)sprite->getContentSize().height/PT_RATIO/2);
b2FixtureDef spriteShapeDef;
spriteShapeDef.shape = &polygon;
spriteShapeDef.density = 10.f;
spriteShapeDef.isSensor = true; // 对象之间有碰撞检测但是又不想让它们有碰撞反应
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2((float)(sprite->getPosition().x / PT_RATIO),
(float)(sprite->getPosition().y /PT_RATIO));
bd.userData = sprite;
b2Body* spriteBody = world->CreateBody(&bd);
spriteBody->CreateFixture(&spriteShapeDef);
}
void HelloWorld::spriteDone(CCNode * sender)
{
// sprites被销毁的时候,我们需要销毁Box2d的body
CCSprite* sprite = dynamic_cast<CCSprite*>(sender);
if (sprite)
{
b2Body* spriteBody = NULL;
for(b2Body* b = world->GetBodyList(); b; b=b->GetNext())
{
if (b->GetUserData() != NULL)
{
CCSprite* curSprite = (CCSprite*)b->GetUserData();
if (curSprite == sprite)
{
spriteBody = b;
removeChild(sprite, true);
world->DestroyBody(spriteBody);
break;
}
}
//if (spriteBody)
//{
// world->DestroyBody(spriteBody);
//}
// removeChild(sprite, true);
}
}
}
void HelloWorld::ccTouchesBegan(CCSet * pTouches, CCEvent * pEvent)
{
CCSetIterator iter = pTouches->begin();
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint pos = pTouch->locationInView();
pos = CCDirector::sharedDirector()->convertToGL(pos);
}
}
void HelloWorld::ccTouchesMoved(CCSet * pTouches, CCEvent * pEvent)
{
CCSetIterator iter = pTouches->begin();
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint pos = pTouch->locationInView();
CCPoint touchLocation = pTouch->locationInView();
CCPoint prevLocation = pTouch->previousLocationInView();
/**//* touchLocation = CCDirector::sharedDirector()->convertToGL( touchLocation );
prevLocation = CCDirector::sharedDirector()->convertToGL( prevLocation );
CCPoint diff = ccpSub(touchLocation, prevLocation);
CCNode *node = getChildByTag(kTagTileMap);
CCPoint currentPos = node->getPosition();
node->setPosition( ccpAdd(currentPos, diff) )*/;
}
}
void HelloWorld::ccTouchesEnded(CCSet * pTouches, CCEvent * pEvent)
{
CCSetIterator iter = pTouches->begin();
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
}
}
void HelloWorld::ccTouchesCancelled(CCSet * pTouches, CCEvent * pEvent)
{
ccTouchesEnded(pTouches, pEvent);
}
USING_NS_CC;
USING_NS_CC_EXT;
using namespace std;
#define TILE_SIZE 32
#define PT_RATIO 32 // 这个数一般定义为: 32.0,在box 世界中 是以 米 为单位的,这里是将坐标兑换为box世界中的米,即除以 PTM_RATIO
// --------------------------------------------
HelloWorld::HelloWorld()
{
}
HelloWorld:: ~ HelloWorld()
{
if(world)
delete world;
}
CCScene * HelloWorld::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::create();
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
enum
{
kTagTileMap = 1,
} ;
CCLabelTTF * pLabel;
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
/**///////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
pLabel = CCLabelTTF::create("Collsion", "Arial", 24);
CCSize size = CCDirector::sharedDirector()->getWinSize();
// position the label on the center of the screen
pLabel->setPosition( ccp(size.width / 2, size.height - 50) );
// add the label as a child to this layer
this->addChild(pLabel, 1, 0);
// create physic world
b2Vec2 gravity(0,0);
world = new b2World(gravity);
world->SetAllowSleeping(false);
contactListener = new MyContactListener();
world->SetContactListener(contactListener);
spawnCar();
schedule(schedule_selector(HelloWorld::tick));
schedule(schedule_selector(HelloWorld::secondUpdate), 1.f);
setTouchEnabled(true);
return true;
}
void HelloWorld::tick( float dt)
{
if (world)
world->Step(dt, 10, 10);
// 基于cocos2d的精灵位置来更新box2d的body位置
for(b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL)
{
CCSprite* sprite = (CCSprite*)b->GetUserData();
if (sprite)
{
b2Vec2 pt = b2Vec2((float)(sprite->getPosition().x / PT_RATIO), (float)(sprite->getPosition().y / PT_RATIO));
float angle = (CCFloat)CC_DEGREES_TO_RADIANS(sprite->getRotation());
b->SetTransform(pt, angle);
}
}
}
std::list<b2Body*> toDestroy_list;
for( std::list<MyContact>::iterator it = contactListener->contact_list.begin();
it != contactListener->contact_list.end();
++it)
{
MyContact& contact = *it;
b2Body* bodyA = contact.fixtureA->GetBody();
b2Body* bodyB = contact.fixtureB->GetBody();
CCSprite* sa = (CCSprite*)bodyA->GetUserData();
CCSprite* sb = (CCSprite*)bodyB->GetUserData();
if (sa && sb)
{
if (sa->getTag() == 1 && sb->getTag() == 2)
toDestroy_list.push_back(bodyB);
else if (sa->getTag() == 2 && sa->getTag() == 1)
toDestroy_list.push_back(bodyA);
}
}
// Destroy contact item.
std::list<b2Body*>::iterator it = toDestroy_list.begin();
while(it != toDestroy_list.end())
{
if ((*it)->GetUserData() != NULL)
{
CCSprite* sprite = (CCSprite*)((*it)->GetUserData());
if (sprite)
{
removeChild(sprite, true);
}
world->DestroyBody(*it);
}
++it;
}
toDestroy_list.clear();
}
void HelloWorld::spawnCar()
{
CCSprite* car = CCSprite::spriteWithFile("images/car.png");
car->setPosition(ccp(100,100));
car->runAction(CCRepeatForever::actionWithAction((CCActionInterval*)CCSequence::actions(
CCMoveTo::actionWithDuration(1.0f, ccp(300,100)),
CCMoveTo::actionWithDuration(1.0f, ccp(200,200)),
CCMoveTo::actionWithDuration(1.0f, ccp(100,100)),
NULL
)));
addBoxBodyForSprite(car);
this->addChild(car, 1, 1);
}
void HelloWorld::spawnCat()
{
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
CCSprite* cat = CCSprite::spriteWithFile("images/cat.png");
float minY = cat->getContentSize().height/2;
float maxY = winSize.height - cat->getContentSize().height/2;
float y = minY + rand() % (int)(maxY - minY);
float startX = winSize.width + cat->getContentSize().width/2;
float endX = -cat->getContentSize().width/2;
CCPoint startPos = ccp(startX, y);
CCPoint endPos = ccp(endX, y);
cat->setPosition(startPos);
cat->runAction(CCSequence::actions(CCMoveTo::actionWithDuration(10.f, endPos),
CCCallFuncN::actionWithTarget(this, callfuncN_selector(HelloWorld::spriteDone)), NULL));
addBoxBodyForSprite(cat);
addChild(cat, 1, 2);
}
void HelloWorld::addBoxBodyForSprite(cocos2d::CCSprite * sprite)
{
//PTM_RATIO ,这个数一般定义为: 32.0,在box 世界中 是以 米 为单位的,这里是将坐标兑换为box世界中的米,即除以 PTM_RATIO
// Create physic body for cat
b2PolygonShape polygon;
polygon.SetAsBox((float)sprite->getContentSize().width/PT_RATIO/2, (float)sprite->getContentSize().height/PT_RATIO/2);
b2FixtureDef spriteShapeDef;
spriteShapeDef.shape = &polygon;
spriteShapeDef.density = 10.f;
spriteShapeDef.isSensor = true; // 对象之间有碰撞检测但是又不想让它们有碰撞反应
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2((float)(sprite->getPosition().x / PT_RATIO),
(float)(sprite->getPosition().y /PT_RATIO));
bd.userData = sprite;
b2Body* spriteBody = world->CreateBody(&bd);
spriteBody->CreateFixture(&spriteShapeDef);
}
void HelloWorld::spriteDone(CCNode * sender)
{
// sprites被销毁的时候,我们需要销毁Box2d的body
CCSprite* sprite = dynamic_cast<CCSprite*>(sender);
if (sprite)
{
b2Body* spriteBody = NULL;
for(b2Body* b = world->GetBodyList(); b; b=b->GetNext())
{
if (b->GetUserData() != NULL)
{
CCSprite* curSprite = (CCSprite*)b->GetUserData();
if (curSprite == sprite)
{
spriteBody = b;
removeChild(sprite, true);
world->DestroyBody(spriteBody);
break;
}
}
//if (spriteBody)
//{
// world->DestroyBody(spriteBody);
//}
// removeChild(sprite, true);
}
}
}
void HelloWorld::ccTouchesBegan(CCSet * pTouches, CCEvent * pEvent)
{
CCSetIterator iter = pTouches->begin();
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint pos = pTouch->locationInView();
pos = CCDirector::sharedDirector()->convertToGL(pos);
}
}
void HelloWorld::ccTouchesMoved(CCSet * pTouches, CCEvent * pEvent)
{
CCSetIterator iter = pTouches->begin();
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint pos = pTouch->locationInView();
CCPoint touchLocation = pTouch->locationInView();
CCPoint prevLocation = pTouch->previousLocationInView();
/**//* touchLocation = CCDirector::sharedDirector()->convertToGL( touchLocation );
prevLocation = CCDirector::sharedDirector()->convertToGL( prevLocation );
CCPoint diff = ccpSub(touchLocation, prevLocation);
CCNode *node = getChildByTag(kTagTileMap);
CCPoint currentPos = node->getPosition();
node->setPosition( ccpAdd(currentPos, diff) )*/;
}
}
void HelloWorld::ccTouchesEnded(CCSet * pTouches, CCEvent * pEvent)
{
CCSetIterator iter = pTouches->begin();
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
}
}
void HelloWorld::ccTouchesCancelled(CCSet * pTouches, CCEvent * pEvent)
{
ccTouchesEnded(pTouches, pEvent);
}
查看源代码