用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); } ``` 如果你试着运行代码,你会得到个球,在屏幕中心的球,如果你试着拖拽鼠标,你会看到从球里伸展出弹道轨迹。 投球 现在我们需要添加onTouchEndedexistBall = 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)); ``` ***





