Control Sphere Object with simple joystick in Box2D

I hope this thread is helpful to someone. Any comments are welcome.


//////////////////////////////////HSJoystick.h (I ported Obj-c code : http://www.cocos2d-iphone.org/forum/topic/1418 )


#pragma once
#include "cocos2d.h"

using namespace cocos2d;

class HSJoystick : public CCLayer
{

public:

    virtual bool init();  
    CCPoint getVelocity(){return velocity;}
    LAYER_NODE_FUNC(HSJoystick);

private:

    CCPoint kCenter;
    CCSprite *thumb;
    bool isPressed;

    CCPoint velocity;

    void updateVelocity(CCPoint point);
    void resetJoystick();
    bool handleLastTouch();
    void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
    void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);
    void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
    void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);

};









////////////////////////////////// HSJoystick.cpp


#include "HSJoystick.h"

#define JOYSTICK_OFFSET_X 5.0f
#define JOYSTICK_OFFSET_Y 5.0f

#define JOYSTICK_RADIUS 64.0f

#define THUMB_RADIUS 26.0f



static CCPoint convertCoordinate(CCPoint point){
    return CCDirector::sharedDirector()->convertToGL(point);
}

static bool isPointInCircle(CCPoint point, CCPoint center, float radius){
    float dx = (point.x - center.x);
    float dy = (point.y - center.y);
    return (radius >= sqrt((dx*dx)+(dy*dy)));
}

bool HSJoystick::init()
{
    bool bRet = false;
    do 
    {




        kCenter=CCPoint(JOYSTICK_RADIUS + JOYSTICK_OFFSET_X,
            JOYSTICK_RADIUS + JOYSTICK_OFFSET_Y);
        //////////////////////////////////////////////////////////////////////////
        // super init first
        //////////////////////////////////////////////////////////////////////////

        CC_BREAK_IF(!CCLayer::init());

        //////////////////////////////////////////////////////////////////////////
        // add your codes below...
        //////////////////////////////////////////////////////////////////////////

        this->setIsTouchEnabled(true);
        velocity = CCPointZero;

        CCSprite *bg = CCSprite::spriteWithFile("joystick_background.png");
        bg->setPosition(kCenter);
        this->addChild(bg,0);

        thumb = CCSprite::spriteWithFile("joystick_thumb.png");
        thumb->setPosition(kCenter);
        this->addChild(thumb,1);

        bRet=true;

    }while(0);

    return bRet;
}


void HSJoystick::updateVelocity(CCPoint point)
{
    // calculate Angle and length
    float dx = point.x - kCenter.x;
    float dy = point.y - kCenter.y;

    float distance = sqrt(dx*dx + dy*dy);
    float angle = atan2(dy,dx); // in radians

    if(distance > JOYSTICK_RADIUS){
        dx = cos(angle) * JOYSTICK_RADIUS;
        dy = sin(angle) * JOYSTICK_RADIUS;
    }

    velocity = CCPointMake(dx/JOYSTICK_RADIUS, dy/JOYSTICK_RADIUS);

    if(distance>THUMB_RADIUS)
    {
        point.x = kCenter.x + cos(angle) * THUMB_RADIUS;
        point.y = kCenter.y + sin(angle) * THUMB_RADIUS;
    }

    thumb->setPosition(point);
}

void HSJoystick::resetJoystick()
{
    this->updateVelocity(kCenter);
}

bool HSJoystick::handleLastTouch()
{
    bool wasPressed = isPressed;
    if(wasPressed){
        this->resetJoystick();
        isPressed = false;
    }
    return (wasPressed ? true : false);
}


void HSJoystick::ccTouchesBegan( CCSet *pTouches, CCEvent *pEvent )
{
    CCTouch *touch = (CCTouch*)pTouches->anyObject();
    CCPoint point = touch->locationInView(touch->view());
    point = convertCoordinate(point);

    if(isPointInCircle(point,kCenter,JOYSTICK_RADIUS)){
        isPressed = true;
        this->updateVelocity(point);
    }
}




void HSJoystick::ccTouchesMoved( CCSet *pTouches, CCEvent *pEvent )
{
    if(isPressed){
        CCTouch *touch = (CCTouch*)pTouches->anyObject();
        CCPoint point = touch->locationInView(touch->view());
        point = convertCoordinate(point);
        this->updateVelocity(point);
    }
}

void HSJoystick::ccTouchCancelled( CCTouch *pTouch, CCEvent *pEvent )
{
    this->handleLastTouch();
}

void HSJoystick::ccTouchesEnded( CCSet *pTouches, CCEvent *pEvent )
{
    this->handleLastTouch();
}








////////////////////////////////// HSPlayer.h  // Ball which one is controlled by Joystick

#pragma once 


#include "cocos2d.h"
#include "Box2D/Box2D.h"
#include "HSJoystick.h"

#ifndef PTM_RATIO
#define PTM_RATIO 32
#endif

using namespace cocos2d;


class HSPlayer : public CCNode
{

public:
    HSPlayer();
    ~HSPlayer();

    static HSPlayer * node(void);

    virtual bool initWithWorld(b2World *world); 
    void setJoystick(HSJoystick *joystick);

private:

    HSJoystick *m_pJoystick;
    b2World *m_pWorld; // box2d world member Pointer
    b2Body *body;
    b2CircleShape m_ShapeDef;
    b2FixtureDef m_fixtureDef;

    CCSpeed *action;
    virtual void update(ccTime dt);


    void createBallandAnimation();

    cocos2d::CCSprite *pPlayer;
    cocos2d::CCSprite *pShadow;
    cocos2d::CCAnimate *stoneMove;

};
@







HSPlayer.cpp

#include "HSPlayer.h"
#include "HSGame.h"


HSPlayer::HSPlayer()
{

}

HSPlayer::~HSPlayer()
{   

}




bool HSPlayer::initWithWorld(b2World *world)
{
    bool bRet = false;
    do 
    {

        // initialize

        pPlayer = new CCSprite();
        CC_BREAK_IF(! pPlayer);

        CCSize size = CCDirector::sharedDirector()->getWinSize();


        // Define Body
        b2BodyDef bodyDef;
        bodyDef.type = b2_dynamicBody;
        bodyDef.position.Set(size.width/2/PTM_RATIO, size.height/2/PTM_RATIO);
        bodyDef.userData = pPlayer;
        body = world->CreateBody(&bodyDef);


        // Define another box shape for our dynamic body.

        m_ShapeDef.m_p.SetZero();
        m_ShapeDef.m_radius = 0.7f;



        // Define the dynamic body fixture.


        m_fixtureDef.shape = &m_ShapeDef;
        m_fixtureDef.density = 1.0f;
        m_fixtureDef.friction = 1.0f;
        m_fixtureDef.restitution = 0.3f;


        body->SetLinearDamping(0.8f);
        body->SetAngularDamping(1.0f);


        body->CreateFixture(&m_fixtureDef);
        body->SetBullet(true);



        createBallandAnimation();

        action = CCSpeed::actionWithAction(CCRepeatForever::actionWithAction(stoneMove),0.0f);
        pPlayer->runAction(action);
        this->addChild(pPlayer);

        scheduleUpdate();

        bRet=true;

    }while(0);

    return bRet;
}


void HSPlayer::createBallandAnimation()
{
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage("stone_roll.png");
    CCMutableArray *aniFrames =new CCMutableArray(10);

    CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("ball.plist");
    CCSize frameSize = CCSizeMake(50,50);

    // create Frame
    for(int i = 0; i < 10 ; i ++)
    {
        char str[25];
        sprintf(str,"ballh_%02d.png",i);
        CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(str);
        aniFrames->addObject(frame);
    }



    CCAnimation *animation = CCAnimation::animationWithFrames(aniFrames, 0.2f);
    CCAnimate *animate = CCAnimate::actionWithAnimation(animation, false);
    stoneMove = animate;

    aniFrames->release();
}



HSPlayer * HSPlayer::node( void )
{
    HSPlayer * pRet = new HSPlayer();
    pRet->autorelease();
    return pRet;
}

void HSPlayer::update( ccTime dt )
{
    float vx = m_pJoystick->getVelocity().x;
    float vy = m_pJoystick->getVelocity().y;

    // set ball velocity by Joystick
    body->SetLinearVelocity(b2Vec2(vx,vy)+body->GetLinearVelocity());

    // rotation ball by direction
    b2Vec2 vec = body->GetLinearVelocity();
    pPlayer->setRotation(-1*CC_RADIANS_TO_DEGREES(ccpToAngle(CCPointMake(vec.x, vec.y))));

    // animation speed by speed
    float vel;
    vel = body->GetLinearVelocity().Normalize();
    action->setSpeed(vel);
}

void HSPlayer::setJoystick( HSJoystick *joystick )
{
    m_pJoystick = joystick;
}












//////////////////////////////////HSGame.h


...........
private:
    HSJoystick *joystick;
    b2World *world;
    HSPlayer *m_pPlayer;

    GLESDebugDraw *m_debugDraw;
    bool isControllPressed;


    void update(ccTime dt);
......


//////////////////////////////////HSGame.cpp
....
init()
{
.....

        joystick =  HSJoystick::node();
        this->addChild(joystick,2);

        b2Vec2 gravity;
        gravity.Set(0.0f, 0.0f);

    // Do we want to let bodies sleep?
    bool doSleep = true;

    // Construct a world object, which will hold and simulate the rigid bodies.
    world = new b2World(gravity, doSleep);
    world->SetContinuousPhysics(true);


    m_debugDraw = new GLESDebugDraw( PTM_RATIO );
    world->SetDebugDraw(m_debugDraw);

    uint32 flags = 0;
    flags += b2DebugDraw::e_shapeBit;
    flags += b2DebugDraw::e_jointBit;
    flags += b2DebugDraw::e_aabbBit;
    flags += b2DebugDraw::e_pairBit;
    flags += b2DebugDraw::e_centerOfMassBit;
    m_debugDraw->SetFlags(flags);       


    //m_debugDraw->DrawCircle(b2Vec2(5.0f,5.0f),100.0f,b2Color(200.0f,100.0f,100.0f));

    // Define the ground body.
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0, 0); // bottom-left corner

    // Call the body factory which allocates memory for the ground body
    // from a pool and creates the ground box shape (also from a pool).
    // The body is also added to the world.
    b2Body* groundBody = world->CreateBody(&groundBodyDef);

    // Define the ground box shape.
    b2PolygonShape groundBox;       

    // bottom
    groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(size.width/PTM_RATIO,0));
    groundBody->CreateFixture(&groundBox, 0);

    // top
    groundBox.SetAsEdge(b2Vec2(0,size.height/PTM_RATIO), b2Vec2(size.width/PTM_RATIO,size.height/PTM_RATIO));
    groundBody->CreateFixture(&groundBox, 0);

    // left
    groundBox.SetAsEdge(b2Vec2(0,size.height/PTM_RATIO), b2Vec2(0,0));
    groundBody->CreateFixture(&groundBox, 0);

    // right
    groundBox.SetAsEdge(b2Vec2(size.width/PTM_RATIO,size.height/PTM_RATIO), b2Vec2(size.width/PTM_RATIO,0));
    groundBody->CreateFixture(&groundBox, 0);



    m_pPlayer = HSPlayer::node();
    m_pPlayer->initWithWorld(world);
    m_pPlayer->setJoystick(joystick);

    this->addChild(m_pPlayer);
    scheduleUpdate();
    bRet = true;

......
}




void HSGame::update(ccTime dt)
{
    //It is recommended that a fixed time step is used with Box2D for stability
    //of the simulation, however, we are using a variable time step here.
    //You need to make an informed choice, the following URL is useful
    //http://gafferongames.com/game-physics/fix-your-timestep/

    int velocityIterations = 8;
    int positionIterations = 1;

    // Instruct the world to perform a single step of simulation. It is
    // generally best to keep the time step and iterations fixed.
    world->Step(dt, velocityIterations, positionIterations);

    //const float scale = .0f;


    //Iterate over the bodies in the physics world
    for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
    {


        if (b->GetUserData() != NULL) {
            //Synchronize the AtlasSprites position and rotation with the corresponding body
            CCSprite* myActor = (CCSprite*)b->GetUserData();
            myActor->setPosition( CCPointMake( b->GetPosition(). x * PTM_RATIO, b->GetPosition().y * PTM_RATIO));
        }   
    }
}

void HSGame::draw()
{

    glDisable(GL_TEXTURE_2D);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    world->DrawDebugData();

    // restore default GL states
    glEnable(GL_TEXTURE_2D);
    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);    
}
1 Like

Thanks for your contribution! I add the link to FAQ
And you can push the code to github.com/cocos2d/cocos2d-x-extensions/.

This is great, I just created my own assets and everthing works perfectly.

Thanks for the contribution.

Francis

GLESDebugDraw is not working in cocos2d-x…any alternative of it in cocos2d-x?

It is not working on device for me……works on awesomely on Simulator……

It is giving me touch point when I logged in ccTouchesBegan but thumb is not rotating

Is anyone faced this issue??? Please help me….

I am using cocos2d-x 2.0…

Thanks in advance
Mangesh

@walzer,

I have updated this code for cocos2d-x v3.15. How can I upload it to github.com/cocos2d/cocos2d-x-extensions/?

rasika