[Cocos3.0 Tutorial] Predict a trajectory with cocos2d-x and Box2d

Predict Trajectory with Cocos2dx 3 and Box2d

How to predict a trajectory with cocos2d-x and Box2d

GITHUB: GitHub - oldanidavide/trajectoryPredictionCocos2dx3: How to predict trajectory with cocos2dx and Box2D

Create a New Project

Create new cocos2d-x project with:

cocos new MyGame -p com.MyCompany.MyGame -l cpp -d ~/MyCompany

for more details check that page:

http://cocos2d-x.org/wiki/How_to_Start_A_New_Cocos2D-X_Game

NOTE: When you create a project with cocos-console, it make in your project dir, the folder cocos2d, i don’t have upload it because the size in MB it’s to big

Resources

Download the ball.png and dot.png and copy it in the Resources folder

Ball

Ball

Linux Version

open your project folder and in the file CMakeLists.txt add on line 161 ( on target link libraries ) the box2d

box2d

the result looks like that:

target_link_libraries(${APP_NAME}
	  ui
	  network
	  storage
	  spine
	  cocostudio
	  cocosbuilder
	  extensions
	  audio
	  cocos2d
	  box2d
  )

Windows Version

Open your proj.win32 .SLN file and add Box2D dependency

Right click on Solution not project name then click on add, add exist project, search on external resources the BOX2D project and add it

Now right click on the solution, click property, select Project Dependencies and flag the libBox2D to compile.

Now right click on your project (inside the solution), and select reference, on the bottom, click ADD NEW REFERENCE then flag libBox2D, and click OK.

Click OK to close the windows

IT’S TIME TO CODE:

In HelloWorldScene.h file, on line 4 under #include "cocos2d.h" add:

USING_NS_CC; 

and under public add:

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

On the INIT main function add mouse event listener (example: 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

In the main CPP file (HelloWorldScene.cpp) add the following function;

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

    return true;
}

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

   
}

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

} 

Inside INIT function add:

//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); 

in the header (HelloWorldScene.h) file add:

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

ADD A PHYSICS

Include in the header file the box2d library:

#include <Box2D/Box2D.h>

Add the b2ContactListener changing:

class HelloWorld : public cocos2d::Layer

to:

class HelloWorld : public cocos2d::Layer, public b2ContactListener

then add :

b2World *world;
float deltaTime;

Add in the INIT function the following line of code:

b2Vec2 gravity = b2Vec2(0.0f, -10.0f);
world = new b2World(gravity);    

Where the -10.0f indicate the gravity on the y axis.

Now, we need to add a SCALE_RATIO. then define it at the top of the source with:

#define SCALE_RATIO 32.0

SCALE_RATIO indicate the value to convert pixel in meter, because BOX2D work with METERS.

In main source code (HelloWorldScene.cpp) add:

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);
}

Call the function just after create a ball sprite, under the this->addChild(ball);

HelloWorld::defineBall();

Add an event to update the physics

In the header file add:

void update(float dt);

And in main cpp source add:

//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();        
}  	

Call the scheduleUpdate in the init function with:

scheduleUpdate();

If you try to start your application, you see the ball fall down

Now add a wall, to make our ball bouncing on it…

in the header file add:

void addWall(float w,float h,float px,float py);

in the main cpp file add:

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);

}		

and in the INIT main function add:

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

ADD POINT FOR TRAJECTORY

in the header file add:

Sprite *points[32];

and in the INIT main function add:

for (int i = 1 ; i <= 31; i++){
	points[i] =CCSprite::create("dot.png");
	this->addChild(points[i]); 
}

ADD CONTROL

Remove the HelloWorld::defineBall(); calling previously in INIT method

Now inside the method onTouchBegan add:

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));

Inside the method onTouchMoved add:

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));

Now we need to create the function simulateTrajectory function

Add in the header file:

void simulateTrajectory(b2Vec2 coord);

And in the main cpp source add:

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);
}

If you try the code, now we have a ball, in the “center” of the screen, and if you try drag the mouse, make a trajectory starting from ball.

THROW THE BALL

Now we need to add an onTouchEnded, go to the method and add:

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));	

ADD SIMPLE POWER

For add power at shooting just add a coefficient multiplier, add it on the header file:

float powerMultiplier;

set the power when create the ball adding:

powerMultiplier = 10;

then change the line:

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

To:

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

And the line:

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

To:

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


traj.jpg (27.8 KB)


ball.png (12.1 KB)


dot.png (2.9 KB)


HelloWorldScene.cpp.zip (2.0 KB)


HelloWorldScene.h.zip (0.8 KB)

great tutorials.

Thanks:)

@owen wrote:

great tutorials.

Thanks:)

Thank You Owen!!

Thanks for your tutorial!
I’m still at:
ADD A PHYSICS
Include in the header file the box2d library:
#include

The line is empty and I don’t know how to do.
I tried to put the Box2D.h into the include folder and with this in the header of HelloWorldScene.h : #include “Box2D.h” but don’t work…

Thanks again

@panor wrote:

Thanks for your tutorial!
I’m still at:
ADD A PHYSICS
Include in the header file the box2d library:
#include

The line is empty and I don’t know how to do.
I tried to put the Box2D.h into the include folder and with this in the header of HelloWorldScene.h : #include “Box2D.h” but don’t work…

Thanks again

Sorry you right, you need to add #include <Box2D/Box2D.h>
But in the edit mode i see #include <Box2D/Box2D.h>, i don’t know why i publish it, disappear :;qst

When i use < > the code inside disappear :frowning:

Thanks for the tutorial.

Why do we need to call defineBall() and world->DestroyBody(ballBody); repeatedly?

@devnoob wrote:

Thanks for the tutorial.

Why do we need to call defineBall() and world->DestroyBody(ballBody); repeatedly?

Because the throw is simulated, i really throw the ball, drawing a point, then delete a ball and physics property.

Thanks again @oldanidavide

Now I have another doubt, when you say:

“On the INIT main function add mouse event listener (example: HelloWorldScene.cpp)”

I supposed so as you are talking about the HelloWorld.h, my HelloWorld.h dont have INIT function. I did copy/paste from the INIT func of HelloWorld.cpp, to paste the event listener inside… It don’t work.

Could you put the whole code, for copy/paste only to do it work? :slight_smile:

Sorry for my questions, i’m still a rookie.

@panor wrote:

Thanks again @oldanidavide

Now I have another doubt, when you say:

“On the INIT main function add mouse event listener (example: HelloWorldScene.cpp)”

I supposed so as you are talking about the HelloWorld.h, my HelloWorld.h dont have INIT function. I did copy/paste from the INIT func of HelloWorld.cpp, to paste the event listener inside… It don’t work.

Could you put the whole code, for copy/paste only to do it work? :slight_smile:

Sorry for my questions, i’m still a rookie.

i’ve upload on my first post my example Classes ( before only in GitHub ), the INIT function, are in my HelloWorldScene.cpp on line 23.

view the attachment.

Davide

@oldanidavide
thanks for that explanation.

Gud tutorial… thanks for sharing…

Thanks! works fine in VS 2012 :slight_smile:

@panor wrote:

Thanks! works fine in VS 2012 :slight_smile:

Well!!! :slight_smile:

@oldanidavide

Hi… I tried yyour tutorial for windows-eclipse-android-cocos2d-x v3.0
I am using emulator.

I got every running with your code and able to give trajectory downwards but the ball is always falling downwards, and I am not able to give the trajectory to left or right or up.

@catch_up wrote:

@oldanidavide

Hi… I tried yyour tutorial for windows-eclipse-android-cocos2d-x v3.0
I am using emulator.

I got every running with your code and able to give trajectory downwards but the ball is always falling downwards, and I am not able to give the trajectory to left or right or up.

Hi @catch_up!

Can you attach your code?

@oldanidavide

Hello, I am attaching all the 3 files I modified. I exactly copied your code, so I guess there shouldn’t be any problem with the code.

However, possible problem I feel is with (in HelloWorldScene.h)
changing “class HelloWorld : public cocos2d::Layer”
to
“class HelloWorld : public cocos2d::Layer, public b2ContactListener”

because “b2ContactListener” is not present anywhere in my project directoy and not even present in cocos2d-x v3.0 folder. I guess, it must be present in Box2D folder, but it is not present anywhere.

Note: In your tutorial, where you told to add update function, it was a bit confusing, as you told this to add it in main cpp. So, one by one added it in both main.cpp in jni/hellocpp/main.cpp and in HelloWorldScene.cpp.
However, the results were coming same.

@catch_up wrote:

@oldanidavide

Hello, I am attaching all the 3 files I modified. I exactly copied your code, so I guess there shouldn’t be any problem with the code.

However, possible problem I feel is with (in HelloWorldScene.h)
changing “class HelloWorld : public cocos2d::Layer”
to
“class HelloWorld : public cocos2d::Layer, public b2ContactListener”

because “b2ContactListener” is not present anywhere in my project directoy and not even present in cocos2d-x v3.0 folder. I guess, it must be present in Box2D folder, but it is not present anywhere.

Note: In your tutorial, where you told to add update function, it was a bit confusing, as you told this to add it in main cpp. So, one by one added it in both main.cpp in jni/hellocpp/main.cpp and in HelloWorldScene.cpp.
However, the results were coming same.

Hi, with main cpp, i mean your main source file.

As described in the line: In the main CPP file (HelloWorldScene.cpp) add the following function;

In your case the problem it’s the value powerMultiplier, in the function

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

the value getted it 0, try to declare you create a ball, example:

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

@oldanidavide

Hi, I’ve set the value of powerMultiplier to 10.0 in 73 line, however it is after scheduleUpdate(), but initializing it after scheduleUpdate()shouldn’t be problem.

So, this cannot be the problem as marked by you in my attached file.

Again, I would like to mention is that there is nothing like b2ContactListener amywhere.
So. can it be the possible problem?

Thanks a lot for your reply

@catch_up

Increase the Sprite *points[31]; to Sprite *points[32];

I’ve also fix it in the tutorial!

the b2ContactListener it’s for manage a collision

@oldanidavide

Thanks for your kind reply.

Thats alright that b2ContactListener is for managing collision, but it should be present somewhere. Can you please tell me where it is located, because I cannot see it anywhere. By the way, I am trying to locate the file b2ContactListener.h or b2ContactListener.cpp
which is not present anywhere in project directoy or cocos2d-x v3.0 directory.

Thanks a lot for spending time for looking into my problem. :slight_smile: