经过摸索, 终于实现了angry birds中的橡皮筋.
首先要知道它用Weld Joint(焊接关节), Mouse Joint(鼠标关节)和Distance Joint(距离关节).
Weld Joint: 把小鸟固定在弹弓上.
Mouse Joint: 用来拖拽小鸟.这个好理解.
Distance Joint: 用来保持小鸟和弹弓之间的距离. 但是用的时候得有技巧.
步骤:
1. init:先用weldJoing把小鸟固定在弹弓中间位置. 可以是地面, 固定点在弹弓上, 这样Box2D可以少处理弹弓这个Body.
2. TouchBegan: 把weldJoint摧毁, 创建一个MouseJoint, 让小鸟跟随手指移动.
3. TouchMove: 如果小鸟被拖出弹弓一定距离, 创建一个Distance Joint, 让小鸟跟弹弓保持一定距离不变. 相当于给了一个弹弓最大拉伸度. 同时画两条线模仿皮筋.
4. TouchEnd: 手放开, 小鸟要飞了.这时要摧毁Distance Joint 和 Weld Joint, 让小鸟不再受限制, 同时给小鸟一个直线速度朝着弹弓中间飞出去.
上代码:
#import "cocos2d.h"
#import "box2d.h"
#import "PauseLayer.h"
#import "LevelScene.h"
#import "EKSprite.h"
USING_NS_CC;
class Level1Layer : public LevelScene {
public:
Level1Layer();
~Level1Layer();
CC_SYNTHESIZE(PauseLayer*, _pauseLayer, PauseLayer);
CREATE_FUNC(Level1Layer);
virtual void levelInit();
virtual void ccTouchesBegan(cocos2d::CCSet *touches, cocos2d::CCEvent *event);
virtual void ccTouchesMoved(cocos2d::CCSet *touches, cocos2d::CCEvent *event);
virtual void ccTouchesEnded(cocos2d::CCSet *touches, cocos2d::CCEvent *event);
virtual void ccTouchesCancelled(cocos2d::CCSet *touches, cocos2d::CCEvent *event);
virtual void update(float dt);
private:
EKSprite *_currBird;
CCSprite *_slingshot;
CCSpriteBatchNode *_parent;
b2World *_world;
b2Body *_groundBody;
b2MouseJoint *_mouseJoint;
b2DistanceJoint *_disJoint;
b2WeldJoint *_weldJoint;
bool _isPulling;
};
#import "Level1Layer.h"
#include "EKShapeCache.h"
#include "EKHelper.h"
#include "LineLayer.h"
#define FLOOR_HEIGHT 49.0f
#define FACTOR 20.0f
Level1Layer::Level1Layer() {
CCLOG("Level1Layer");
}
Level1Layer::~Level1Layer() {
CCLOG("~Level1Layer");
EKEngine::sharedInstance()->deleteAllObjects();
CCTextureCache::sharedTextureCache()->purgeSharedTextureCache();
}
void Level1Layer::levelInit() {
setTouchMode(kCCTouchesAllAtOnce);
setTouchEnabled(true);
_mouseJoint = NULL;
_disJoint = NULL;
_isPulling = false;
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("level1.plist");
EKShapeCache::sharedEKShapeCache()->addShapesWithFile("level1-shape.plist");
CCSize s = CCDirector::sharedDirector()->getWinSize();
_world = EKEngine::sharedInstance()->getworld();
_groundBody = EKEngine::sharedInstance()->setEdge(ccp(0, FLOOR_HEIGHT), 960, 480);
CCSprite *bg = CCSprite::createWithSpriteFrameName("bg-1.png");
bg->setScale(1.34);
bg->setAnchorPoint(CCPointZero);
bg->setPosition(CCPointZero);
addChild(bg);
_slingshot = CCSprite::createWithSpriteFrameName("slingshot.png");
float y = _slingshot->boundingBox().size.height / 2.0f;
_slingshot->setPosition(ccp(78.0f, FLOOR_HEIGHT + y));
addChild(_slingshot);
_currBird = EKSprite::createWithFrameName("bird-1.png", "bird", true);
_currBird->setPosition(ccp(78.0f, FLOOR_HEIGHT + _slingshot->getContentSize().height * 0.81f));
_currBird->getBody()->SetBullet(true);
addChild(_currBird);
b2WeldJointDef wjd;
wjd.Initialize(_groundBody, _currBird->getBody(), _currBird->getBody()->GetWorldCenter());
wjd.collideConnected = false;
CCLOG("create _weldJoint");
_weldJoint = (b2WeldJoint *) _world->CreateJoint(&wjd);
scheduleUpdate();
}
void Level1Layer::ccTouchesBegan(cocos2d::CCSet *touches, cocos2d::CCEvent *event) {
CCSize s = CCDirector::sharedDirector()->getWinSize();
if (touches->count() == 1) {
if (_mouseJoint != NULL) {
return;
}
CCTouch *touch = (CCTouch *) touches->anyObject();
CCPoint location = touch->getLocationInView();
location = CCDirector::sharedDirector()->convertToGL(location);
if (location.x > _currBird->getPositionX() - 50.0f && location.x < _currBird->getPositionX() + 50.0f && location.y > _currBird->getPositionY() - 50.0f && location.y < _currBird->getPositionY() + 50.0f) {
b2MouseJointDef mjd;
mjd.bodyA = _groundBody;
mjd.bodyB = _currBird->getBody();
mjd.target = b2Vec2FromCCPoint(location);
mjd.collideConnected = true; //
mjd.maxForce = 1000.0f * _currBird->getBody()->GetMass(); //给一个拖动的力
CCLOG("create _mouseJoint");
_mouseJoint = (b2MouseJoint *) _world->CreateJoint(&mjd);
_currBird->getBody()->SetFixedRotation(true);
if (_weldJoint != NULL) {
CCLOG("destroy _weldJoint");
_world->DestroyJoint(_weldJoint);
_weldJoint = NULL;
}
_isPulling = true;
}
}
stopActionByTag(1);
}
void Level1Layer::ccTouchesMoved(cocos2d::CCSet *touches, cocos2d::CCEvent *event) {
CCSize s = CCDirector::sharedDirector()->getWinSize();
CCTouch *touch = (CCTouch *) touches->anyObject();
CCPoint location = touch->getLocationInView();
location = CCDirector::sharedDirector()->convertToGL(location);
if (_isPulling) {
if (ccpDistance(ccp(_slingshot->getPosition().x, _slingshot->getContentSize().height * 0.81f + FLOOR_HEIGHT), location) > 80.0f) {
if (_disJoint == NULL) {
b2DistanceJointDef djd;
djd.Initialize(_groundBody, _currBird->getBody(), b2Vec2FromCCPoint(ccp(_slingshot->getPositionX(), FLOOR_HEIGHT + _slingshot->getPositionY() * 0.81f)), _currBird->getBody()->GetWorldCenter());
djd.collideConnected = true;
djd.frequencyHz = 4.0f;
djd.dampingRatio = 0.5f;
CCLOG("create _disJoint");
_disJoint = (b2DistanceJoint *) _world->CreateJoint(&djd);
_disJoint->SetLength(80.0f / 32.0f);
}
} else if (_disJoint != NULL) {
CCLOG("destroy _disJoint");
_world->DestroyJoint(_disJoint);
_disJoint = NULL;
}
visit();
removeChildByTag(77);
CCLayer *line = LineLayer::createWithPoints(_currBird->getPosition(), ccp(_slingshot->getPositionX() - 10, FLOOR_HEIGHT + _slingshot->getPositionY() * 0.81f), ccp(_slingshot->getPositionX() + 10, FLOOR_HEIGHT + _slingshot->getPositionY() * 0.81f));
addChild(line, 1000, 77);
_mouseJoint->SetTarget(b2Vec2FromCCPoint(location));
}
}
void Level1Layer::ccTouchesEnded(cocos2d::CCSet *touches, cocos2d::CCEvent *event) {
if (_isPulling && _mouseJoint != NULL ) {
CCLOG("destroy _mouseJoint");
EKEngine::sharedInstance()->getworld()->DestroyJoint(_mouseJoint);
_mouseJoint = NULL;
_isPulling = false;
if (_disJoint != NULL) {
CCLOG("destroy _disJoint");
EKEngine::sharedInstance()->getworld()->DestroyJoint(_disJoint);
_disJoint = NULL;
}
_currBird->getBody()->SetFixedRotation(false);
CCPoint point = ccpSub(ccp(_slingshot->getPositionX(), FLOOR_HEIGHT + _slingshot->getPositionY()), _currBird->getPosition());
b2Vec2 direct = b2Vec2FromCCPoint(point);
b2Vec2 dd(direct.x * FACTOR, direct.y * FACTOR);
CCLOG("%f, %f", point.x, point.y);
CCLOG("%f, %f", direct.x, direct.y);
_currBird->getBody()->ApplyLinearImpulse(dd, _currBird->getBody()->GetWorldCenter());
removeChildByTag(77);
}
}
void Level1Layer::ccTouchesCancelled(cocos2d::CCSet *touches, cocos2d::CCEvent *event) {
CCLOG("ccTouchesCancelled");
ccTouchesEnded(touches, event);
}
void Level1Layer::update(float dt) {
EKEngine::sharedInstance()->update(dt);
}
上图.