【英文论坛获奖教程】用Cocos2d-x-3.0及Box2d预测物体弹道轨迹

用Cocos2d-x-3.0及Box2d预测物体弹道轨迹

原作者:
oldanidavide

英文论坛原帖:
http://discuss.cocos2d-x.org/t/cocos3-0-tutorial-predict-a-trajectory-with-cocos2d-x-and-box2d/13493

如何使用cocos2d-x和Box2d来预测物体弹道轨迹


GITHUB:
https://github.com/oldanidavide/trajectoryPredictionCocos2dx3

代码:
HelloWorldScene.cpp.zip (2 KB)
HelloWorldScene.h.zip (1 KB)

创建一个新项目

用这些代码创建一个新cocos2d-x项目:
cocos new MyGame -p com.MyCompany.MyGame -l cpp -d ~/MyCompany

关于创建新项目的细节请查阅下面的网页:
http://cocos2d-x.org/wiki/How_to_Start_A_New_Cocos2D-X_Game

注意:当你使用cocos-console新创建一个项目时,文件夹cocos2d会在你的项目文件夹里生成。但因为它太大了,所以我并没有将它上传。

素材资源
下载ball.png 和 dot.png 并复制进Resources文件夹。

Linux版本

打开你的项目文件夹并且在文件夹CMakeLists.txt第161行添加box2d(在on target link libraries那一部分)

box2d
结果看起来就像是:
target_link_libraries(${APP_NAME}
ui
network
storage
spine
cocostudio
cocosbuilder
extensions
audio
cocos2d
box2d
)
Windows版本

打开你的proj.win32 .SLN 文件并添加Box2D文件依赖

右键点击解决方案Solution而不是项目名称然后点击添加add—>现有项目add exist project,搜索外部资源“BOX2D“并添加

现在右键点击解决方案,点击属性,选择项目依赖项Project Dependencies并勾取libBox2D使其加入编译

现在右键点击你的项目名称(解决方案的里面),并选择引用reference,在底部点击添加新引用ADD NEW REFERENCE然后勾选libBox2D并点击确定OK。

点击确定OK关闭窗口。

是时候敲键盘写代码了:

在HelloWorldScene.h里,在第四行也就是#include "cocos2d.h"的下面添加:

USING_NS_CC; 
```


在public的下面添加:

bool onTouchBegan(Touch* touch, Event* event);
void onTouchMoved(Touch* touch, Event* event);
void onTouchEnded(Touch* touch, Event* event);
```


在INIT主要函数中添加鼠标监听事件(提示:是在HelloWorldScene.cpp)

//SET MOUSE LISTENER
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);

listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
//END MOUSE LISTENER
```


在CPP主文件里(HelloWorldScene.cpp)添加下列函数:

bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{

    return true;
}

void HelloWorld::onTouchMoved(Touch* touch, Event* event)
{


}

void HelloWorld::onTouchEnded(Touch* touch, Event* event)
{

} 
```


在INIT函数中添加:

//CREATE A BALL
dragOffsetStartX = 0;    
dragOffsetEndX = 0;    
dragOffsetStartY = 0;    
dragOffsetEndY = 0;    
existBall= false;    
ballX = 500;
ballY = 200;    
ball =Sprite::create("ball.png");
ball->setPosition(CCPoint(ballX,ballY));
this->addChild(ball); 
```


在头文件(HelloWorldScene.h)中添加:

Sprite *ball;
bool existBall;
float ballX;
float ballY;    
int dragOffsetStartX;
int dragOffsetEndX;
int dragOffsetStartY;
int dragOffsetEndY;    
b2Body *ballBody;    
b2CircleShape ballShape;
b2BodyDef ballBodyDef;  
void defineBall();
```


添加物理引擎

在头文件中添加box2d程序库:

#include 
```


添加b2ContactListener
更改:

class HelloWorld : public cocos2d::Layer
```

成:
class HelloWorld : public cocos2d::Layer, public b2ContactListener
```

然后添加:
b2World *world;
float deltaTime;
```

在INIT函数里添加下列代码:
b2Vec2 gravity = b2Vec2(0.0f, -10.0f);
world = new b2World(gravity);    
```

参数-10.0f指出了在y轴上的重力加速度:
现在我们需要添加一个SCALE_RATIO。将其定义在文件顶端:
#define SCALE_RATIO 32.0
```


SCALE_RATIO指出了将单位:像素转换成单位:米的值,因为BOX2D使用的计量单位是米。

在主文件(HelloWorldScene.cpp)中添加:

void HelloWorld::defineBall() {
    ballShape.m_radius = 45 / SCALE_RATIO;
    b2FixtureDef ballFixture;
    ballFixture.density=10;
    ballFixture.friction=0.8;
    ballFixture.restitution=0.6;
    ballFixture.shape=&ballShape;

    ballBodyDef.type= b2_dynamicBody;
    ballBodyDef.userData=ball;

    ballBodyDef.position.Set(ball->getPosition().x/SCALE_RATIO,ball->getPosition().y/SCALE_RATIO);

    ballBody = world->CreateBody(&ballBodyDef);
    ballBody->CreateFixture(&ballFixture);
    ballBody->SetGravityScale(10);
}    
```


在添加球精灵this->addChild(ball)后面引用函数:

HelloWorld::defineBall();
```



添加一个时间来更新physics

在头文件里添加:
void update(float dt);
```


在主文件cpp中添加:
//Simulate Physics
void HelloWorld::update(float dt){
   int positionIterations = 10;  
   int velocityIterations = 10;

   deltaTime = dt;
   world->Step(dt, velocityIterations, positionIterations);  

   for (b2Body *body = world->GetBodyList(); body != NULL; body = body->GetNext())   
     if (body->GetUserData()) 
     {  
       CCSprite *sprite = (CCSprite *) body->GetUserData();  
       sprite->setPosition(ccp(body->GetPosition().x * SCALE_RATIO,body->GetPosition().y * SCALE_RATIO));  
       sprite->setRotation(-1 * CC_RADIANS_TO_DEGREES(body->GetAngle())); 



     }  
    world->ClearForces();
    world->DrawDebugData();        
}   
```


在init函数中引用scheduleUpdate:

scheduleUpdate();
```


如果这个时候你尝试着运行你的应用程序,你会看到掉了个球。

现在添加一堵墙,是我们的球能够反弹。

在头文件中添加:
void addWall(float w,float h,float px,float py);
```


在主文件夹cpp中添加:
void HelloWorld::addWall(float w,float h,float px,float py) {

    b2PolygonShape floorShape;

    floorShape.SetAsBox(w/ SCALE_RATIO,h/ SCALE_RATIO);
    b2FixtureDef floorFixture;
    floorFixture.density=0;
    floorFixture.friction=10;
    floorFixture.restitution=0.5;
    floorFixture.shape=&floorShape;
    b2BodyDef floorBodyDef;

    floorBodyDef.position.Set(px/ SCALE_RATIO,py/ SCALE_RATIO);
    b2Body *floorBody = world->CreateBody(&floorBodyDef);

    floorBody->CreateFixture(&floorFixture);

}       
```


并且在INIT文件主函数里添加:

addWall(visibleSize.width ,10,(visibleSize.width / 2) ,0); //CEIL
addWall(10 ,visibleSize.height ,0,(visibleSize.height / 2) ); //LEFT
addWall(10 ,visibleSize.height ,visibleSize.width,(visibleSize.height / 2) ); //RIGHT
```


为弹道轨迹添加指示点

在头文件里添加:
Sprite *points;
```

并且在INI主函数里添加:
for (int i = 1 ; i <= 31; i++){
    points* =CCSprite::create("dot.png");
    this->addChild(points*); 
}
```


添加控制

删除掉原先在INIT方法里的HelloWorld::defineBall();

现在在方法 onTouchBegan里面添加方法:
dragOffsetStartX = touch->getLocation().x;
dragOffsetStartY = touch->getLocation().y;

CCPoint touchLocation = touch->getLocation();

ballX = touchLocation.x;
ballY = touchLocation.y;

if (existBall){        
world->DestroyBody(ballBody);
}

ball->setPosition(ccp(ballX ,ballY));
```


在onTouchMoved里面添加:

CCPoint touchLocation = touch->getLocation();

dragOffsetEndX = touchLocation.x;
dragOffsetEndY = touchLocation.y;

float dragDistanceX = dragOffsetStartX - dragOffsetEndX;
float dragDistanceY = dragOffsetStartY - dragOffsetEndY;

HelloWorld::simulateTrajectory(b2Vec2((dragDistanceX )/SCALE_RATIO,(dragDistanceY )/SCALE_RATIO));
```



现在我们需要创建函数simulateTrajectory。

在头文件中添加:
void simulateTrajectory(b2Vec2 coord);
```


在cpp文件中添加
void HelloWorld::simulateTrajectory(b2Vec2 coord){

    //define ball physicis
    HelloWorld::defineBall();

    ballBody->SetLinearVelocity(b2Vec2(coord.x,coord.y));
    for (int i = 1; i <= 31; i++){
    world->Step(deltaTime,10,10);
    points*->setPosition(CCPoint(ballBody->GetPosition().x*SCALE_RATIO,ballBody->GetPosition().y*SCALE_RATIO));
    world->ClearForces();

    }

    world->DestroyBody(ballBody);
}
```


如果你试着运行代码,你会得到个球,在屏幕中心的球,如果你试着拖拽鼠标,你会看到从球里伸展出弹道轨迹。

投球
现在我们需要添加onTouchEnded
    existBall = true;
```


HelloWorld::defineBall();

CCPoint touchLocation = touch->getLocation();

dragOffsetEndX = touchLocation.x;
dragOffsetEndY = touchLocation.y;

float dragDistanceX = dragOffsetStartX - dragOffsetEndX;
float dragDistanceY = dragOffsetStartY - dragOffsetEndY;

ballBody->SetLinearVelocity(b2Vec2((dragDistanceX)/SCALE_RATIO,(dragDistanceY)/SCALE_RATIO));  
```


添加简单的投掷力量
为了添加投掷力量仅仅只需添加一个乘法系数,将其添加进头文件里:
float powerMultiplier;
```

设置创建球时的力量大小:

powerMultiplier = 10;
```


然后把下列行:
HelloWorld::simulateTrajectory(b2Vec2((dragDistanceX )/SCALE_RATIO,(dragDistanceY )/SCALE_RATIO));
```

改为:
HelloWorld::simulateTrajectory(b2Vec2((dragDistanceX * powerMultiplier)/SCALE_RATIO,(dragDistanceY * powerMultiplier)/SCALE_RATIO));
```

还有行:
ballBody->SetLinearVelocity(b2Vec2((dragDistanceX)/SCALE_RATIO,(dragDistanceY)/SCALE_RATIO));
```

改为:
ballBody->SetLinearVelocity(b2Vec2((dragDistanceX * powerMultiplier)/SCALE_RATIO,(dragDistanceY * powerMultiplier)/SCALE_RATIO));
```

***

好贴必须顶~:2:

mark ~

mark 转到中文站首页啦~~楼主多翻译好文哦!

明白了
在simulateTrajectory中用Box2d预演一遍 记录下若干个步进点处刚体(球)的坐标
然后删掉预演用的刚体 建立真正的刚体 同样的起始点并施以同样的速度 所以结果一致

这招在只有一个刚体的时候好用 如果同时存在多个刚体 则world->Step之后是不可逆的
也许可以建立2个world (一个专门用来模拟)

回复查看,十个字啊十个字

牛B :2: ,应该有应用场景!!!如果在某个游戏中用到,那是相当不错的体验啊:2:

:2::2::2::2::2::2::2::2::2:

顶楼主!:2::2::2::2::2:

刚开始学习,努力努力:7:

我使用的环境是win7 vs2012,请问下“windows版本 打开你的proj.win32 .SLN 文件并添加Box2D文件依赖”,这个是什么意思?不懂,求教!

整了半天是ios的,:9::9::9::9::9::9::9::9::9::9::9::9::8::8:

:9::9::9::9::9::9::9::9::9::9::9:

mark aaaaa

:2::2::2::2::2::2::2::2::2::2::2::2::2:

学习了……………………

刚开始学习,努力努

找到错误原因了。

不错啊 要是有源码就就好了

:7::7:学习学习