Verlet Rope (C++)

My only issue now is creating a verlet rope!

Anyone know how to do this in C++?

Thanks!

Here you go: https://github.com/teemukorh/VRope-x

(it is box2d though, so you might need to rework it if you’re using physicsbodies in cocos2d-x v3)

Nikcain,

I am using this same code to create the Verlet Rope. (Vpoint, Vstick and Vrope classes)

Although I dont know how to make the rope show up! (his example of implementing the rope is pretty vague).

Also I have enabled Box2d and Im using it in Cocos2d-x 3.8!

I have tried:

// Create joint
b2RopeJoint* bodyAbodyBJoint = (b2RopeJoint*) world->CreateJoint(&jd);
// Create batchnode and vrope for joint (can also be made between two bodies)
CCSpriteBatchNode* batch = dynamic_cast<CCSpriteBatchNode*> (getChildByTag(KTagRopeBatchNode));
VRope* verlet = new VRope(bodyAbodyBJoint, batch);

// Update the verlets in the game loop
verlet->update(dt);
verlet->updateSprites();

Although when I run this, nothing comes up. Not sure what Im doing wrong.
Note: I am 100% sure my Box2d is working as I have created other projects using it.

Can you get yours to work and show me what you did?

That looks right to me, and is the same as I’m doing. I didn’t have to change their code at all in my implementation.

The sprite batch was created as

ropeBatch = CCSpriteBatchNode::create("rope.png", 100);
addChild(ropeBatch, 1, myRopeBatchTag);

although the capacity automatically increases, so shouldn’t matter what number you put there.

What is your jointdef like? (with the bodies too?). I think I struggled creating joints more than I struggled with the rope class.

There’s also a debugdraw method that might help determine whether it’s a box2d issue or a sprite issue (i.e. the rope’s there, but the sprites aren’t in the right place). Also try logging the getPosition x & y on both the sprites and boidies after the update.

This is my code to implement the rope.

My Box2d is pretty bad, lol. Screenshot yours?

Here you go, this is a modified hello world project (using v2.2 - I’ve not graduated to v3 yet!).

I’ve also attached all the files Classes.zip (14.1 KB)

The body and joint definitions are a bit messy looking as I’ve exported them from the R.U.B.E editor (which is very useful). I don’t normally use RUBE’s exported code but instead parse their JSON files and construct them from that.

I couldn’t see anything particularly wrong with what you’d done (it’s odd to dereference your b2RopeJointDef like that, but it should still work). You’re not updating your world, but I assume that’s in a different bit of code.

The most likely cause is it’s just outside the screen view - try sorting out the ptmratio value.

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }
    
    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
                                        "CloseNormal.png",
                                        "CloseSelected.png",
                                        this,
                                        menu_selector(HelloWorld::menuCloseCallback));
    
    pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 ,
                                origin.y + pCloseItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
    pMenu->setPosition(CCPointZero);
    this->addChild(pMenu, 1);

    ropeBatch = CCSpriteBatchNode::create("rope.png", 100);
    addChild(ropeBatch, 1, 555);
    CCSpriteBatchNode* batch = dynamic_cast<CCSpriteBatchNode*> (getChildByTag(555));

    m_world = new b2World(b2Vec2(0.0, -10));

    b2Body* ceiling;
    b2Body* swing;
    b2Joint* rope1;
    b2Joint* rope2;

    // Ceiling
    {
      b2BodyDef bd;
      bd.type = b2BodyType(0);
      bd.position.Set(-1.614868640899658e-003f, 5.331412315368652e+000f);
      bd.angle = 0.0f;
      bd.linearVelocity.Set(0.0f, 0.0f);
      bd.angularVelocity = 0.0f;
      bd.linearDamping = 0.0f;
      bd.angularDamping = 0.0f;
      bd.allowSleep = bool(4);
      bd.awake = bool(2);
      bd.fixedRotation = bool(0);
      bd.bullet = bool(0);
      bd.active = bool(32);
      bd.gravityScale = 1.0f;
      ceiling = m_world->CreateBody(&bd);

      {
        b2FixtureDef fd;
        fd.friction = 2.000000029802322e-001f;
        fd.restitution = 0.0f;
        fd.density = 1.0f;
        fd.isSensor = bool(0);
        fd.filter.categoryBits = uint16(1);
        fd.filter.maskBits = uint16(65535);
        fd.filter.groupIndex = int16(0);
        b2PolygonShape shape;
        b2Vec2 vs[8];
        vs[0].Set(1.000161457061768e+001f, -3.314123153686523e-001f);
        vs[1].Set(1.000161457061768e+001f, 2.668587684631348e+000f);
        vs[2].Set(-1.998385190963745e+000f, 2.668587684631348e+000f);
        vs[3].Set(-1.998385190963745e+000f, -3.314123153686523e-001f);
        shape.Set(vs, 4);

        fd.shape = &shape;

        ceiling->CreateFixture(&fd);
      }
    }

    // Swing
    {
      b2BodyDef bd;
      bd.type = b2BodyType(2);
      bd.position.Set(2.391709089279175e+000f, 1.420945525169373e+000f);
      bd.angle = 0.0f;
      bd.linearVelocity.Set(0.0f, 0.0f);
      bd.angularVelocity = 0.0f;
      bd.linearDamping = 0.0f;
      bd.angularDamping = 0.0f;
      bd.allowSleep = bool(4);
      bd.awake = bool(2);
      bd.fixedRotation = bool(0);
      bd.bullet = bool(0);
      bd.active = bool(32);
      bd.gravityScale = 1.0f;
      swing = m_world->CreateBody(&bd);

      {
        b2FixtureDef fd;
        fd.friction = 5.000000000000000e-001f;
        fd.restitution = 0.0f;
        fd.density = 1.0f;
        fd.isSensor = bool(0);
        fd.filter.categoryBits = uint16(1);
        fd.filter.maskBits = uint16(65535);
        fd.filter.groupIndex = int16(0);
        b2PolygonShape shape;
        b2Vec2 vs[8];
        vs[0].Set(1.253884911537170e+000f, -2.436261177062988e-001f);
        vs[1].Set(1.253884673118591e+000f, 8.604699373245239e-002f);
        vs[2].Set(-6.699655055999756e-001f, 8.604681491851807e-002f);
        vs[3].Set(-6.699658036231995e-001f, -2.436261773109436e-001f);
        shape.Set(vs, 4);

        fd.shape = &shape;

        swing->CreateFixture(&fd);
      }
    }
    
    // rope joints
    {
      b2RopeJointDef jd;
      jd.bodyA = swing;
      jd.bodyB = ceiling;
      jd.collideConnected = bool(0);
      jd.localAnchorA.Set(1.166776180267334e+000f, 1.977586746215820e-002f);
      jd.localAnchorB.Set(5.222444057464600e+000f, -3.281159400939941e-001f);
      jd.maxLength = 4.0f;
      rope1 = m_world->CreateJoint(&jd);
      verlet1 = new VRope(rope1, batch); 
    }
    {
      b2RopeJointDef jd;
      jd.bodyA = swing;
      jd.bodyB = ceiling;
      jd.collideConnected = bool(0);
      jd.localAnchorA.Set(-5.537242889404297e-001f, 3.955173492431641e-002f);
      jd.localAnchorB.Set(3.620598316192627e+000f, -3.281159400939941e-001f);
      jd.maxLength = 4.0f;
      rope2 = m_world->CreateJoint(&jd);
      verlet2 = new VRope(rope2, batch); 
    }

    
    scheduleUpdate();
    return true;
}

void HelloWorld::update(float dt)
{
    m_world->Step(dt, 8, 1);
    verlet1->update(dt);
    verlet1->updateSprites();    
    verlet2->update(dt);
    verlet2->updateSprites();
}
1 Like

Finally got it to work, thanks alot!!