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

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

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

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


本部分内容设定了隐藏,需要回复后才能看到

创建一个新项目

用这些代码创建一个新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文件夹。

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


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

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"的下面添加:


?
1
USING_NS_CC;


在public的下面添加:

?
1
2
3
bool onTouchBegan(Touch* touch, Event* event);
void onTouchMoved(Touch* touch, Event* event);
void onTouchEnded(Touch* touch, Event* event);


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

?
1
2
3
4
5
6
7
8
9
10
//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)添加下列函数:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{
 
    return true;
}
 
void HelloWorld::onTouchMoved(Touch* touch, Event* event)
{
 
 
}
 
void HelloWorld::onTouchEnded(Touch* touch, Event* event)
{
 
}


在INIT函数中添加:

?
1
2
3
4
5
6
7
8
9
10
11
//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)中添加:

?
1
2
3
4
5
6
7
8
9
10
11
12
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程序库:

?
1
#include <Box2D/Box2D.h>


添加b2ContactListener
更改:

?
1
class HelloWorld : public cocos2d::Layer

成:
?
1
class HelloWorld : public cocos2d::Layer, public b2ContactListener

然后添加:
?
1
2
b2World *world;
float deltaTime;

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

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


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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)后面引用函数:

?
1
HelloWorld::defineBall();



添加一个时间来更新physics

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


在主文件cpp中添加:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//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:

?
1
scheduleUpdate();


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

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

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


在主文件夹cpp中添加:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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文件主函数里添加:

?
1
2
3
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


为弹道轨迹添加指示点

在头文件里添加:
?
1
Sprite *points[32];

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


添加控制

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

现在在方法 onTouchBegan里面添加方法:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
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里面添加:

?
1
2
3
4
5
6
7
8
9
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。

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


在cpp文件中添加
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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<i>->setPosition(CCPoint(ballBody->GetPosition().x*SCALE_RATIO,ballBody->GetPosition().y*SCALE_RATIO));
    world->ClearForces();
 
    }
 
    world->DestroyBody(ballBody);
}</i>


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

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


?
1
2
3
4
5
6
7
8
9
10
11
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));  


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

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

?
1
powerMultiplier = 10;


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

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

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

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

相关文章: