Verlet rope

Introduction

In this brief article we’ll be taking a look at an efficient way of creating ropes in our cocos2d games that use box2d, starting from a cocos2s+box2d template project.
We will be using box2d’s latest addition, the b2RopeJoint to constrict two bodies with a rope joint (a maximum distance joint, one might also call it), so as always big thanks to Erin Catto for all his hard work on box2d.
We will also be using a small set of classes I wrote, VRope, that use Verlet integration to calculate the points of the rope, using a CCSpriteBatchNode to visually draw the rope in our game.

Click for video

It’s worth mentioning that using Box2D isn’t strictly necessary, you could still use the VRope class to draw a rope between two given points in other scenarios (for example using Chipmunk, your own physics system, or simply updating the two points manually) and have it react under gravity. With that in mind, the class also has some helper methods to use it with CGPoints only. (somethingWithPoints methods)

Setting up the project

First things first, make sure you have the latest version of Cocos2D and create a new project with the “Cocos2d Box2d Application” template. You will then need to update Box2D to the latest committed version, which you can find at http://code.google.com/p/box2d/source/checkout
To update Box2D in your current project:

  • From within XCode, delete the Box2D folder group from the “cocos2d Sources” folder (Delete References)
  • From Finder, go to your project’s folder and delete the Box2D folder
  • Copy over the latest version of Box2D back into your project’s folder (make sure you only copy the most deep rooted “Box2D” folder, and delete CMakeLists.txt and Box2DConfig.cmake)
  • Back to XCode, Project -> Add to Project, select the Box2D folder, make sure create groups (not folder reference) is selected, Add

This should give you a working Cocos2D project, updated with the latest Box2D source that has the b2RopeJoint ready to use.
If you’re not running the latest version of cocos2d (0.99.5-rc0) but still want to use VRope, you’ll need to rename CCSpriteBatchNode back to CCSpriteSheet, the rest should work as is.

Using b2RopeJoint

Using the new b2RopeJoint is very easy, it’s similar to the b2DistanceJoint, but unlike other joints it doesn’t have an Initialize method.

//define rope joint, params: two b2bodies, two local anchor points, length of rope
b2RopeJointDef jd;
jd.bodyA=body1; //define bodies
jd.bodyB=body2;
jd.localAnchorA = b2Vec2(0,0); //define anchors
jd.localAnchorB = b2Vec2(0,0);
jd.maxLength= (body2->GetPosition() - body1->GetPosition()).Length(); //define max length of joint = current distance between bodies
world->CreateJoint(&jd); //create joint

Verlet Integration

Now that we know how to constrict two bodies with a rope joint, we need to find an elegant and efficient way of drawing the rope, drum roll, enter screen right: Verlet integration.
The implementation you’ll find below is based on a very informative tutorial by YoAmbulante.com (for Flash) that allowed even a simple minded person such as myself to grasp the basic concepts of Verlet integration, so big thanks to the author of that tutorial.

The “Verlet integration” consists in dots and links between these dots where each dot has remembered which was its previous position to determine its next step, the new position of x is equal to x + x – previous_x (same for y) and then each of these dots are associated (grouped) by pairs that try to keep same distance between each other as they were when the program started.

Based on this, I decided to design 3 classes (rather than use structs, each to his own)

  • VPoint – store a point’s current and previous position
  • VStick – connect two VPoints and keep them at constant distance
  • VRope – keeps arrays of VPoints and VSticks, update logic, CCSpriteBatchNode to render rope

Rather than go trough all the source code, we’ll be taking a look at how to implement it into our test project.
In the main header file (VRope.h) you can find instructions on usage, and the code is slightly commented and easy to understand, so more advanced users could easily adapt it further to their own needs.

Using VRope

Download the VRope class here, unzip, copy it over to your project’s folder and add it to your project from XCode.
First, you’ll want to import VRope.h into HelloWorldScene.h

#import "VRope.h"

Still in the HelloWorldScene.h file, add these lines in the @interface declaration:

b2Body* anchorBody; //reference to anchor body
CCSpriteBatchNode* ropeSpriteSheet; //sprite sheet for rope segment
NSMutableArray* vRopes; //array to hold rope references

Now we’ll add some code to our main class, HelloWorldScene.mm.
In the init method of HelloWorldScene, remove the creation of the groundBody and all it’s shapes and in it’s place add:

// +++ Add anchor body
b2BodyDef anchorBodyDef;
anchorBodyDef.position.Set(screenSize.width/PTM_RATIO/2,screenSize.height/PTM_RATIO*0.7f); //center body on screen
anchorBody = world->CreateBody(&anchorBodyDef);
// +++ Add rope spritesheet to layer
ropeSpriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"rope.png" ];
[self addChild:ropeSpriteSheet];
// +++ Init array that will hold references to all our ropes
vRopes = [[NSMutableArray alloc] init];

In the addNewSpriteWithCoords method, add these lines at the bottom:

// +++ Create box2d joint
b2RopeJointDef jd;
jd.bodyA=anchorBody; //define bodies
jd.bodyB=body;
jd.localAnchorA = b2Vec2(0,0); //define anchors
jd.localAnchorB = b2Vec2(0,0);
jd.maxLength= (body->GetPosition() - anchorBody->GetPosition()).Length(); //define max length of joint = current distance between bodies
world->CreateJoint(&jd); //create joint
// +++ Create VRope
VRope *newRope = [[VRope alloc] init:anchorBody body2:body spriteSheet:ropeSpriteSheet];
[vRopes addObject:newRope];

In the draw method, add these lines at the bottom:

// +++ Update rope sprites
for(uint i=0;i<[vRopes count];i++) {
    [[vRopes objectAtIndex:i] updateSprites];
}

In the tick method, add these lines at the bottom:

// +++ Update rope physics
for(uint i=0;i<[vRopes count];i++) {
    [[vRopes objectAtIndex:i] update:dt];
}

Copy rope.png to your Resources folder and add it to your project (rope.png can be found in the archive of the test project below)
Build & Run! If all went well, you should have the same result as the video at the beginning of the article.

As I mentioned at the beginning, the code is very simple and fast (no thanks to me), so adapting it to other uses should be pretty simple.

Downloads

VRope class
Cocos2D VRope Test Project

About the author

I’ve been using cocos2d for over a year and a half and I’m a regular on the forums, so feel free to ask questions in the thread about VRope.
The VRope class was originally created to be used in Paper Bridge, a physics based bridge construction puzzle game developed by Clever Hamster Games (aka: me).

This entry was posted in box2d, cocos2d. Bookmark the permalink.

48 Responses to "Verlet rope"

Leave a reply


× 4 = twenty