Cocos2d Box2d 物理引擎实现愤怒的小鸟
这里我们修改HelloWorldScene场景,在这里实现
我们的思路是首先初始化一些刚体,,然后实现点击屏幕产生小鸟,松开小鸟,小鸟飞出,撞击初始化好的那些刚体,产生碰撞检测效果。
1.在HelloWorld.h中
#include"cocos2d.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
{
public:
// there'sno 'id' in cpp,so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here'sa difference. Method 'init' in cocos2d-x returns bool,instead of returning'id' in cocos2d-iphone
virtual bool init();
// aselector callback
void menuCloseCallback(cocos2d::Ref* pSender);
//implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
b2World*world;//创建一个物理世界
virtual bool onTouchBegan(Touch *touch, Event *unused_event);
virtual void onTouchMoved(Touch *touch, Event *unused_event);
virtual void onTouchEnded(Touch *touch, Event *unused_event);
void addNewPigAtPoint(int x,int y);
int pigIndex;//记录猪的编号
void update(float t);//
void initpig();//初始化一些猪
int birdIndex;//鸟德编号
void pigSmile(Sprite*spig,float x,float y,floatang);//猪笑了
void pigCry(Sprite*spig,floatang);//猪哭了
void birdContact(Sprite*spbird,float ang);//鸟掉毛
};
#endif
2.在Helloworld.cpp中
#include"HelloWorldScene.h"
#include"BirdContact.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene'is an autorelease object
auto scene = Scene::create();
// 'layer'is an autorelease object
auto layer = HelloWorld::create();
// addlayer as a child to scene
scene->addChild(layer);
// returnthe scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1.super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
auto bg=Sprite::create("startbg.png");
this->addChild(bg);
bg->setPosition(Director::getInstance()->getWinSize().width/2,
Director::getInstance()->getWinSize().height/2);
//创建一个物理世界
b2Vec2 gravite;
gravite.Set(0.0f,-9.8f);//定义一个初始化的坐标--牛顿的重力
this->world=new b2World(gravite);//这个参数作为这个物理世界的特性
//定义物理世界的侦听----碰撞检测
BirdContact*listenerb=new BirdContact();//
world->SetContactListener(listenerb);//侦听和物理世界相互绑定
//创建一个静态的刚体--地板
b2BodyDef grounddef;
grounddef.position.Set(0.0f,120/32.0f);
b2Body*ground=world->CreateBody(&grounddef);
//创建家具定义刚体的形状
b2PolygonShape groundshape;//定义刚体的形状--为多边形
groundshape.SetAsBox(visibleSize.width/32,10/32);//定义一个多边型的盒子
ground->CreateFixture(&groundshape,0);//让地板的刚体绑定这个形状
//添加一个侦听
auto listener=EventListenerTouchOneByOne::create();
listener->onTouchBegan=CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
listener->onTouchMoved=CC_CALLBACK_2(HelloWorld::onTouchMoved,this);
listener->onTouchEnded=CC_CALLBACK_2(HelloWorld::onTouchEnded,this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraPHPriority(listener,this);
//记录猪的编号,初始化的时候=0
pigIndex=0;
//必须模拟物理世界
this->scheduleUpdate();
//添加一个弹弓
auto sprite1=Sprite::create("leftshot.png");
sprite1->setPosition(Vec2(200,170));
sprite1->setScale(3);
this->addChild(sprite1);
auto sprite2=Sprite::create("rightshot.png");
sprite2->setPosition(Vec2(200,170));
sprite2->setScale(3);
this->addChild(sprite2);
//画线
auto drawnode1=DrawNode::create();
drawnode1->setTag(10001);
this->addChild(drawnode1);
drawnode1->setVisible(false);//不显示
auto drawnode2=DrawNode::create();
drawnode2->setTag(10002);
this->addChild(drawnode2);
drawnode2->setVisible(false);//不显示
//初始化一些猪
initpig();
//初始化小鸟的值为0
birdIndex=0;
return true;
}
void HelloWorld::menuCloseCallback(Ref* pSender)
{
}
bool HelloWorld::onTouchBegan(Touch *touch, Event *unused_event)
{
//添加一个刚体
//addNewPigAtPoint(touch->getLocation().x,touch->getLocation().y);
//添加小鸟
auto spbird=Sprite::create("bird1.png");
spbird->setTag(200+birdIndex);
spbird->setScale(2);
this->addChild(spbird);
spbird->setPosition(touch->getLocation());
birdIndex++;
//显示两条线
this->getChildByTag(10001)->setVisible(true);
this->getChildByTag(10002)->setVisible(true);
DrawNode*n1=(DrawNode*)this->getChildByTag(10001);
DrawNode*n2=(DrawNode*)this->getChildByTag(10002);
n1->clear();
n2->clear();
n1->drawSegment(Vec2(200-20,230),spbird->getPosition(),4,Color4F(255,255,1));
n2->drawSegment(Vec2(200+20,1));
n1->drawSegment(Vec2(200-20,1));
return true;
}
void HelloWorld::onTouchMoved(Touch *touch, Event *unused_event)
{
//移动小鸟
auto bird=this->getChildByTag(200+birdIndex-1);
bird->setPosition(touch->getLocation());
DrawNode*n1=(DrawNode*)this->getChildByTag(10001);
DrawNode*n2=(DrawNode*)this->getChildByTag(10002);
n1->clear();
n2->clear();
n1->drawSegment(Vec2(200-20,bird->getPosition(),1));
}
void HelloWorld::onTouchEnded(Touch *touch, Event *unused_event)
{
//发射
auto bird=this->getChildByTag(200+birdIndex-1);
//将小鸟加刚体
b2BodyDef bodydef;
bodydef.type=b2_dynamicBody;
bodydef.position.Set(touch->getLocation().x/32,touch->getLocation().y/32);
b2Body*newbird=world->CreateBody(&bodydef);
//定义夹具
//b2CircleShape c;//圆形
//c.m_radius=50/32;//半径
b2PolygonShape birdshap;
birdshap.SetAsBox(50/32,50/32);
//定义盒子的形状
b2FixtureDef fixturebird;
fixturebird.shape=&birdshap;
//设置属性
fixturebird.density=1.0;
fixturebird.friction=0.3;
fixturebird.restitution=0.5;
newbird->CreateFixture(&fixturebird);
//最核心的--
newbird->SetUserData(bird);//物理世界在物理世界模拟的时候就会根据这个进行坐标的更新和角度的更新
//给刚体加力
b2Vec2 force=b2Vec2((200-touch->getLocation().x),
( 230-touch->getLocation().y));//力=弹弓的点到你拉动的点---/3经过多次加算出来的
newbird->ApplyLinearImpulse(force,newbird->GetPosition(),true);//应用一个线速度 true:是否恢复计算
//隐藏两条线
this->getChildByTag(10001)->setVisible(false);
this->getChildByTag(10002)->setVisible(false);
}
void HelloWorld::addNewPigAtPoint(int x,int y)
{
//产生一个动态的刚体
b2BodyDef pigdef;
pigdef.type=b2_dynamicBody;//动态的刚体
pigdef.position.Set(x/32,y/32);
b2Body*newpig=world->CreateBody(&pigdef);//创建一个刚体
//定义形状夹具--圆形
b2PolygonShape pigshape; //--方形
//b2CircleShape c;//圆形
//c.m_radius=50/32;//半径
pigshape.SetAsBox(50/32,50/32);//宽度,高度--这个盒子的大小
b2FixtureDef fixturedef;//定义盒子的形状
fixturedef.shape=&pigshape;
fixturedef.density=1.0f;//阻尼--密度
fixturedef.friction=0.3;//摩擦力
fixturedef.restitution=0.3f;//恢复系数
newpig->CreateFixture(&fixturedef);//动态刚体绑定这个形状
//刚体怎么表现?跟我们的cocos2d进行绑定
auto sprite=Sprite::create("pig_1.png");
sprite->setTag(100+pigIndex);//记录下猪的编号,我们创建了几个猪
pigIndex++;
this->addChild(sprite);
newpig->SetUserData(sprite);//刚体中包含了只指向当前精灵的指针
sprite->setPosition(x,y);
}
void HelloWorld::update(float t){
//下一个时间这些刚体都运动到哪
world->Step(t,8,3);//t-时间 8--模拟8次 3---迭代了多少次
for (b2Body*b=world->GetBodyList(); b!=NULL; b=b->GetNext()){
Sprite*sp=(Sprite*)b->GetUserData();
if (sp!=NULL) {
sp->setPosition(b->GetPosition().x*32,
b->GetPosition().y*32);
sp->setRotation(-CC_RADIANS_TO_DEGREES(b->GetAngle()));//弧度转化成角度
}
}
}
void HelloWorld::initpig()
{
//添加一个刚体
this->addNewPigAtPoint(700,128);
this->addNewPigAtPoint(700,160);
this->addNewPigAtPoint(700,192);
this->addNewPigAtPoint(700,224);
this->addNewPigAtPoint(700,256);
this->addNewPigAtPoint(700,288);
//
this->addNewPigAtPoint(750,128);
this->addNewPigAtPoint(750,160);
this->addNewPigAtPoint(750,192);
this->addNewPigAtPoint(750,224);
this->addNewPigAtPoint(750,256);
this->addNewPigAtPoint(750,288);
this->addNewPigAtPoint(800,128);
this->addNewPigAtPoint(800,160);
this->addNewPigAtPoint(800,192);
this->addNewPigAtPoint(800,224);
this->addNewPigAtPoint(800,256);
this->addNewPigAtPoint(800,288);
this->addNewPigAtPoint(850,128);
this->addNewPigAtPoint(850,160);
this->addNewPigAtPoint(850,192);
this->addNewPigAtPoint(850,224);
this->addNewPigAtPoint(850,256);
this->addNewPigAtPoint(850,288);
this->addNewPigAtPoint(900,128);
this->addNewPigAtPoint(900,160);
this->addNewPigAtPoint(900,192);
this->addNewPigAtPoint(900,224);
this->addNewPigAtPoint(900,256);
this->addNewPigAtPoint(900,288);
}
void HelloWorld::pigSmile(Sprite*spig,floatang){
Vector<SpriteFrame*> allf;
allf.pushBack(SpriteFrame::create("pig_1.png",Rect(0,100,100)));
allf.pushBack(SpriteFrame::create("pig_2.png",100)));
allf.pushBack(SpriteFrame::create("pig_3.png",100)));
auto animation=Animation::createWithSpriteFrames(allf);
animation->setDelayPerUnit(0.3);
auto animate=Animate::create(animation);
spig->runAction(animate);
}//猪笑了
void HelloWorld::pigCry(Sprite*spig,floatang){
Vector<SpriteFrame*> allf;
allf.pushBack(SpriteFrame::create("pig_1.png",100)));
allf.pushBack(SpriteFrame::create("pig_4.png",100)));
auto animation=Animation::createWithSpriteFrames(allf);
animation->setDelayPerUnit(0.3);
auto animate=Animate::create(animation);
spig->runAction(Sequence::create( animate,
CallFuncN::create([&](Node * obj){
((Sprite*)obj)->setTexture("pig_1.png");
}),
NULL));
}//猪哭了
void HelloWorld::birdContact(Sprite*spbird, float x,floatang)
{
for (int i=0; i<3; i++) {
auto sp=Sprite::create("yumao1.png");
//apaw与动作序列一样,但是是同时执行
auto act1=Spawn::create(RotateBy::create(3,i*60),//1.5秒旋转这么大得角度
FadeOut::create(1.5),
ScaleBy::create(3,3),
nullptr);
sp->setPosition(x+i*10,y+i*10);
sp->setAnchorPoint(Vec2(0.5,0.5));
this->addChild(sp);
sp->runAction(Sequence::create(act1,CallFuncN::create([](Node*obj)
{
obj->removeFromParentAndCleanup(true);
}),
NULL));
}
}//鸟掉毛
3.实现碰撞检测,加入鸟撞击到猪,就让小鸟掉毛,小猪哭。
我们在BirdContact.h中这个样定义,也就是说我们定义侦听
#include"cocos2d.h"
USING_NS_CC;
class BirdContact:public b2ContactListener{
public:
virtual void BeginContact(b2Contact* contact) ;
virtual void EndContact(b2Contact* contact);
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);
};
4.BirdContact.h中这个样实现,我们在BirdContact::PostSolve中实现撞击,得到冲量
#include"BirdContact.h"
#include"HelloWorldScene.h"
void BirdContact::BeginContact(b2Contact* contact)
{
}
void BirdContact::EndContact(b2Contact* contact)
{
}
void BirdContact::PreSolve(b2Contact* contact,const b2Manifold* oldManifold)
{
}
void BirdContact::PostSolve(b2Contact* contact,const b2ContactImpulse* impulse)
{
//得到的时横坐标的冲量
float force=impulse->normalImpulses[0];//水平方向
if (force>2) {//两牛
//得到第一个碰撞体
Sprite*sA=(Sprite*)contact->GetFixtureA()->GetBody()->GetUserData();
//得到第二个碰撞体
Sprite*sB=(Sprite*)contact->GetFixtureB()->GetBody()->GetUserData();
if (sA==NULL||sB==NULL)
{return;}
if (sA->getTag()>=100 && sA->getTag()<200)//猪
{
HelloWorld*nowLayer=(HelloWorld*)sA->getParent();
nowLayer->pigCry(sA,sA->getPositionX(),sA->getPositionY(),sA->getRotation());
}
if (sB->getTag()>=100 && sB->getTag()<200)//猪
{
HelloWorld*nowLayer=(HelloWorld*)sA->getParent();
nowLayer->pigCry(sA,sA->getRotation());
}
if (sA->getTag()>=200 && sA->getTag()<300)//鸟
{
HelloWorld*nowLayer=(HelloWorld*)sA->getParent();
nowLayer->birdContact(sA,sA->getRotation());
}
if (sB->getTag()>=200 && sB->getTag()<300)//鸟
{
HelloWorld*nowLayer=(HelloWorld*)sA->getParent();
nowLayer->birdContact(sA,sA->getRotation());
}
}
}
最后我们实现的效果如图: