# 如何创造一款类似《割绳子》的游戏（二）

VRope是由一系列VPoint对象（使用VStick对象进行连接）所组成的。所以如果你想要割断VRope，最佳方法便是明确用户手指移动所形成的线是否会与场景中的任何VRope的VStick对象产生交叉。

-(BOOL)checkLineIntersection:(CGPoint)p1 CGPoint)p2 CGPoint)p3 CGPoint)p4
{
// http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
CGFloat denominator = (p4.y – p3.y) * (p2.x – p1.x) – (p4.x – p3.x) * (p2.y – p1.y);

// In this case the lines are parallel so you assume they don’t intersect
if (denominator == 0.0f)
return NO;
CGFloat ua = ((p4.x – p3.x) * (p1.y – p3.y) – (p4.y – p3.y) * (p1.x – p3.x)) / denominator;
CGFloat ub = ((p2.x – p1.x) * (p1.y – p3.y) – (p2.y – p1.y) * (p1.x – p3.x)) / denominator;

if (ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0)
{
return YES;
}

return NO;
}

@synthesize sticks = vSticks;

-(CGPoint)point;

-(CGPoint)point {
return CGPointMake(x, y);
}

-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
static CGSize s = [[CCDirector sharedDirector] winSize];

UITouch *touch = [touches anyObject];
CGPoint pt0 = [touch previousLocationInView:[touch view]];
CGPoint pt1 = [touch locationInView:[touch view]];

// Correct Y axis coordinates to cocos2d coordinates
pt0.y = s.height – pt0.y;
pt1.y = s.height – pt1.y;

for (VRope *rope in ropes)
{
for (VStick *stick in rope.sticks)
{
CGPoint pa = [[stick getPointA] point];
CGPoint pb = [[stick getPointB] point];

if ([self checkLineIntersection:pt0 :pt1 :pa :pb])
{
// Cut the rope here
return;
}
}
}
}

stick（from raywenderlich）

-(VRope *)cutRopeInStick:(VStick *)stick newBodyA:(b2Body*)newBodyA newBodyB:(b2Body*)newBodyB;

-(VRope *)cutRopeInStick:(VStick *)stick newBodyA:(b2Body*)newBodyA newBodyB:(b2Body*)newBodyB {

// 1-First, find out where in your array the rope will be cut
int nPoint = [vSticks indexOfObject:stick];

// Instead of making everything again you’ll just use the arrays of
// sticks, points and sprites you already have and split them

// 2-This is the range that defines the new rope
NSRange newRopeRange = (NSRange){nPoint, numPoints-nPoint-1};

// 3-Keep the sticks in a new array
NSArray *newRopeSticks = [vSticks subarrayWithRange:newRopeRange];

// 4-and remove from this object’s array
[vSticks removeObjectsInRange:newRopeRange];

// 5-Same for the sprites
NSArray *newRopeSprites = [ropeSprites subarrayWithRange:newRopeRange];
[ropeSprites removeObjectsInRange:newRopeRange];

// 6-Number of points is always the number of sticks + 1
newRopeRange.length += 1;
NSArray *newRopePoints = [vPoints subarrayWithRange:newRopeRange];
[vPoints removeObjectsInRange:newRopeRange];

// 7-The removeObjectsInRange above removed the last point of
// this rope that now belongs to the new rope. You need to clone
// that VPoint and add it to this rope, otherwise you’ll have a
// wrong number of points in this rope
VPoint *pointOfBreak = [newRopePoints objectAtIndex:0];
VPoint *newPoint = [[VPoint alloc] init];
[newPoint setPos:pointOfBreak.x y:pointOfBreak.y];

// 7-And last: fix the last VStick of this rope to point to this new point
// instead of the old point that now belongs to the new rope
VStick *lastStick = [vSticks lastObject];
[lastStick setPointB:newPoint];
[newPoint release];

// 8-This will determine how long the rope is now and how long the new rope will be
float32 cutRatio = (float32)nPoint / (numPoints – 1);

// 9-Fix my number of points
numPoints = nPoint + 1;

// Position in Box2d world where the new bodies will initially be
b2Vec2 newBodiesPosition = b2Vec2(pointOfBreak.x / PTM_RATIO, pointOfBreak.y / PTM_RATIO);

// Get a reference to the world to create the new joint
b2World *world = newBodyA->GetWorld();

// 10-Re-create the joint used in this VRope since bRopeJoint does not allow
// to re-define the attached bodies
b2RopeJointDef jd;
jd.bodyA = joint->GetBodyA();
jd.bodyB = newBodyB;
jd.localAnchorA = joint->GetLocalAnchorA();
jd.localAnchorB = b2Vec2(0, 0);
jd.maxLength = joint->GetMaxLength() * cutRatio;
newBodyB->SetTransform(newBodiesPosition, 0.0);

b2RopeJoint *newJoint1 = (b2RopeJoint *)world->CreateJoint(&jd); //create joint

// 11-Create the new rope joint
jd.bodyA = newBodyA;
jd.bodyB = joint->GetBodyB();
jd.localAnchorA = b2Vec2(0, 0);
jd.localAnchorB = joint->GetLocalAnchorB();
jd.maxLength = joint->GetMaxLength() * (1 – cutRatio);
newBodyA->SetTransform(newBodiesPosition, 0.0);

b2RopeJoint *newJoint2 = (b2RopeJoint *)world->CreateJoint(&jd); //create joint

// 12-Destroy the old joint and update to the new one
world->DestroyJoint(joint);
joint = newJoint1;

// 13-Finally, create the new VRope
VRope *newRope = [[VRope alloc] initWithRopeJoint:newJoint2
spriteSheet:spriteSheet
points:newRopePoints
sticks:newRopeSticks
sprites:newRopeSprites];
return [newRope autorelease];
}

illustration（from raywenderlich）

1.首先决定割断会出现的点。通过的树枝对象将是属于新绳索的第一个树枝，所以它的指标便是割断发生的点的指标。在上图中便是3。

2.定义一个范围结构去指出你想要vStick数组在哪里分离。在图解中，这一范围将伴随着位置=3，长度=3。这表明了新的绳索在树枝上的范围是从3到5。

3.创造一个伴随着树枝（将转移到新的绳索上）的新数组。

4.将这些树枝从当前绳索的数组中删除。

5.在用于绘制树枝的精灵身上做同样的设置。

6.将长度范围延长1，因为点数总是树枝数+1。基于会出现在新绳索（图解中的点3和点6）的点创造一个新的数组，并将其从这一对象的数组中删除。

7、你可能会注意到在图解中绳索的断裂点被从对象数组中删除了，并转移到了新的绳索。为了改正这点你需要“复制”这一点并将其添加到对象的点数组中。这一对象的最后树枝同样也需要做出更新。

8.割断比例将用于判断新绳索的长度。在图解中，这一比例将为0.5。

9.修改这一对象的点数。在图解中，它变成了4。

10.重新创造节点，让它能够与这一对象联系在一起。附加bodyA和newBodyB到这一绳索的节点上，使用旧的长度和割断比例去决定新长度。同样，将newBodyB放置在绳索被割断的位置。

11.创造一个新的绳索节点并附加newBodyA和bodyB到它上面，同样在割断位置放置newBodyA。

12.然后销毁旧的节点并更新实例变量为新节点。

13.最后，使用你在之前所收集的数组创造新的VRope对象和新的节点。你仍然需要执行这一初始方法，但这却非常简单，紧接着便是这一代码。

-(id)initWithRopeJoint:(b2RopeJoint*)aJoint
spriteSheet:(CCSpriteBatchNode*)spriteSheetArg
points:(NSArray*)points
sticks:(NSArray*)sticks
sprites:(NSArray*)sprites {
if((self = [super init])) {
joint = aJoint;
spriteSheet = spriteSheetArg;
vPoints = [[NSMutableArray alloc] initWithArray:points];
vSticks = [[NSMutableArray alloc] initWithArray:sticks];
ropeSprites = [[NSMutableArray alloc] initWithArray:sprites];
numPoints = vPoints.count;
}
return self;
}

-(void)setPointB:(VPoint *)point;

-(void)setPointB:(VPoint *)point {
pointB = point;
}

-(b2Body *) createRopeTipBody
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.linearDamping = 0.5f;
b2Body *body = world->CreateBody(&bodyDef);

b2FixtureDef circleDef;
b2CircleShape circle;
circleDef.shape = &circle;
circleDef.density = 10.0f;

// Since these tips don’t have to collide with anything
// set the mask bits to zero
body->CreateFixture(&circleDef);

return body;
}

b2Body *newBodyA = [self createRopeTipBody];
b2Body *newBodyB = [self createRopeTipBody];

VRope *newRope = [rope cutRopeInStick:stick newBodyA:newBodyA newBodyB:newBodyB];

cut the rope（from raywenderlich）

b2Body *crocMouth_;          // weak ref
b2Fixture *crocMouthBottom_;    // weak ref

// Define the croc’s “mouth”.
b2BodyDef crocBodyDef;
crocBodyDef.position.Set((s.width – croc_.textureRect.size.width)/PTM_RATIO, (croc_.position.y)/PTM_RATIO);

crocMouth_ = world->CreateBody(&crocBodyDef);

// Define the croc’s box shape.
b2EdgeShape crocBox;

// bottom
crocBox.Set(b2Vec2(5.0/PTM_RATIO,15.0/PTM_RATIO), b2Vec2(45.0/PTM_RATIO,15.0/PTM_RATIO));
crocMouthBottom_ = crocMouth_->CreateFixture(&crocBox,0);

crocMouth_->SetActive(NO);

croc（from raywenderlich）

BOOL crocMouthOpened;
NSTimer *crocAttitudeTimer

-(void)changeCrocAttitude
{
crocMouthOpened = !crocMouthOpened;
NSString *spriteName = crocMouthOpened ? @”croc_front_mouthopen.png” : @”croc_front_mouthclosed.png”;
[croc_ setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteName]];
[croc_ setZOrder:crocMouthOpened ? 1 : -1];

crocMouth_->SetActive(crocMouthOpened);

[crocAttitudeTimer invalidate];
[crocAttitudeTimer release];
crocAttitudeTimer = [[NSTimer scheduledTimerWithTimeInterval:3.0 + 2.0 * CCRANDOM_0_1()
target:self
selector:@selector(changeCrocAttitude)
userInfo:nil
repeats:NO] retain];
}

[crocAttitudeTimer invalidate];
[crocAttitudeTimer release];

crocMouthOpened = YES;
[self changeCrocAttitude];

eat（from raywenderlich）

copy（from raywenderlich）

#import “MyContactListener.h”

MyContactListener *contactListener;

// Create contact listener
contactListener = new MyContactListener();
world->SetContactListener(contactListener);

delete contactListener;
contactListener = NULL;

// Check for collisions
bool shouldCloseCrocMouth = NO;
std::vector<b2Body *>toDestroy;
std::vector<MyContact>::iterator pos;
for(pos = contactListener->_contacts.begin(); pos != contactListener->_contacts.end(); ++pos)
{
MyContact contact = *pos;

bool hitTheFloor = NO;
b2Body *potentialCandy = nil;

// The candy can hit the floor or the croc’s mouth. Let’s check
// what it’s touching.
if (contact.fixtureA == crocMouthBottom_)
{
potentialCandy = contact.fixtureB->GetBody();
}
else if (contact.fixtureB == crocMouthBottom_)
{
potentialCandy = contact.fixtureA->GetBody();
}
else if (contact.fixtureA->GetBody() == groundBody)
{
potentialCandy = contact.fixtureB->GetBody();
hitTheFloor = YES;
}
else if (contact.fixtureB->GetBody() == groundBody)
{
potentialCandy = contact.fixtureA->GetBody();
hitTheFloor = YES;
}

// Check if the body was indeed one of the candies
if (potentialCandy && [candies indexOfObject:[NSValue valueWithPointer:potentialCandy]] != NSNotFound)
{
// Set it to be destroyed
toDestroy.push_back(potentialCandy);
if (hitTheFloor)
{
// If it hits the floor you’ll remove all the physics of it and just simulate the pineapple sinking
CCSprite *sinkingCandy = (CCSprite*)potentialCandy->GetUserData();

// Sink the pineapple
CCFiniteTimeAction *sink = [CCMoveBy actionWithDuration:3.0 position:CGPointMake(0, -sinkingCandy.textureRect.size.height)];

// Remove the sprite and check if should finish the level.
CCFiniteTimeAction *finish = [CCCallBlockN actionWithBlock:^(CCNode *node)
{
[self removeChild:node cleanup:YES];
[self checkLevelFinish:YES];
}];

// Run the actions sequentially.
[sinkingCandy runAction:[CCSequence actions:
sink,
finish,
nil]];

// All the physics will be destroyed below, but you don’t want the
// sprite do be removed, so you set it to null here.
potentialCandy->SetUserData(NULL);
}
else
{
shouldCloseCrocMouth = YES;
}
}
}

std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2)
{
b2Body *body = *pos2;
if (body->GetUserData() != NULL)
{
// Remove the sprite
CCSprite *sprite = (CCSprite *) body->GetUserData();
[self removeChild:sprite cleanup:YES];
body->SetUserData(NULL);
}

// Iterate though the joins and check if any are a rope
b2JointEdge* joints = body->GetJointList();
while (joints)
{
b2Joint *joint = joints->joint;

// Look in all the ropes
for (VRope *rope in ropes)
{
if (rope.joint == joint)
{
// This “destroys” the rope
[rope removeSprites];
[ropes removeObject:rope];
break;
}
}

joints = joints->next;
world->DestroyJoint(joint);
}

// Destroy the physics body
world->DestroyBody(body);

// Removes from the candies array
[candies removeObject:[NSValue valueWithPointer:body]];
}

if (shouldCloseCrocMouth)
{
// If the pineapple went into the croc’s mouth, immediately closes it.
[self changeCrocAttitude];

// Check if the level should finish
[self checkLevelFinish:NO];
}

#import <set>

-(void)checkLevelFinish:(BOOL)forceFinish
{
if ([candies count] == 0 || forceFinish)
{
// Destroy everything
[self finishedLevel];

// Schedule a level restart 2 seconds from now
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self initLevel];
});
}
}

-(void) finishedLevel
{
std::set<b2Body *>toDestroy;

// Destroy every rope and add the objects that should be destroyed
for (VRope *rope in ropes)
{
[rope removeSprites];

// Don’t destroy the ground body…
if (rope.joint->GetBodyA() != groundBody)
toDestroy.insert(rope.joint->GetBodyA());
if (rope.joint->GetBodyB() != groundBody)
toDestroy.insert(rope.joint->GetBodyB());

world->DestroyJoint(rope.joint);
}
[ropes removeAllObjects];

// Destroy all the objects
std::set<b2Body *>::iterator pos;
for(pos = toDestroy.begin(); pos != toDestroy.end(); ++pos)
{
b2Body *body = *pos;
if (body->GetUserData() != NULL)
{
// Remove the sprite
CCSprite *sprite = (CCSprite *) body->GetUserData();
[self removeChild:sprite cleanup:YES];
body->SetUserData(NULL);
}
world->DestroyBody(body);
}

[candies removeAllObjects];
}

checkLevelFinish非常简单。finishedLevel也非常简单，你所面对的唯一复杂元素便是在一开始销毁所有的VRope对象，然后所有对象都与之联系在一起，除了地面实体。这便是你在割断绳子并留下糖果时所创造的所有对象。

@synthesize joint = joint;

b2Body *body2 = [self createCandyAt:CGPointMake(s.width * 0.5, s.height)];

// Change the linear dumping so it swings more
body2->SetLinearDamping(0.01);

// Add a bunch of ropes
[self createRopeWithBodyA:groundBody anchorA:cc_to_b2Vec(s.width * 0.65, s.height + 5)
bodyB:body2 anchorB:body2->GetLocalCenter()
sag:1.0];

int segmentFactor = 12; //increase value to have less segments per rope, decrease to have more segments

-(void)applyGravity:(float)dt {
y -= 10.0f*dt; //gravity magic number
}

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

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

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

groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0));

groundBox.Set(b2Vec2(-s.width/PTM_RATIO,0), b2Vec2(2*s.width/PTM_RATIO,0));

for(int i=0;i&lt;numPoints;i++) {
VPoint *tmpPoint = [[VPoint alloc] init];
[tmpPoint setPos:tmpVector.x y:tmpVector.y];

[tmpPoint release];
}
for(int i=0;i&lt;numPoints-1;i++) {
VStick *tmpStick = [[VStick alloc] initWith:[vPoints objectAtIndex:i] pointb:[vPoints objectAtIndex:i+1]];

[tmpStick release];
}

for(int i=0; i < numPoints; i++) {
[[vPoints objectAtIndex:i] release];
if(i!=numPoints-1)
[[vSticks objectAtIndex:i] release];
}

sounds（from raywenderlich）

copy（from raywenderlich）

#import “SimpleAudioEngine.h”

#define kCuttingSound       @”cut.caf”
#define kBiteSound          @”bite.caf”
#define kSplashSound        @”splash.caf”
#define kBackgroundMusic    @”CheeZeeJungle.caf”

[[SimpleAudioEngine sharedEngine] playBackgroundMusic:kBackgroundMusic loop:YES];
[SimpleAudioEngine sharedEngine].backgroundMusicVolume = 0.4;

[[SimpleAudioEngine sharedEngine] playEffect:kCuttingSound];

// Play sound effect
[[SimpleAudioEngine sharedEngine] playEffect:kBiteSound];

[[SimpleAudioEngine sharedEngine] playEffect:kSplashSound];

crocdile（from raywenderlich）

CGSize s = [[CCDirector sharedDirector] winSize];
CCLabelTTF *loseLabel = [[CCLabelTTF alloc] initWithString:@”Try Again!”
dimensions:s
hAlignment:kCCTextAlignmentCenter
vAlignment:kCCVerticalTextAlignmentCenter
fontName:@”Verdana-Bold”
fontSize:60.0];
loseLabel.color = ccc3(255, 0, 0);
loseLabel.anchorPoint = CGPointZero;

// Remove the sprite and check if should finish the level.
CCFiniteTimeAction *finish = [CCCallBlockN actionWithBlock:^(CCNode *node)
{
[self removeChild:node cleanup:YES];
[self checkLevelFinish:YES];
[self removeChild:loseLabel cleanup:YES];
}];

if ([candies count] == 0 && !forceFinish)
{
CGSize s = [[CCDirector sharedDirector] winSize];
CCLabelTTF *winLabel = [[CCLabelTTF alloc] initWithString:@”Level Finished!”
dimensions:s
hAlignment:kCCTextAlignmentCenter
vAlignment:kCCVerticalTextAlignmentCenter
fontName:@”Verdana-Bold”
fontSize:60.0];
winLabel.color = ccc3(255, 0, 0);
winLabel.anchorPoint = CGPointZero;
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self removeChild:winLabel cleanup:YES];
});
}

How to Make a Game Like Cut the Rope – Part 2

Welcome back to our How to Make a Game Like Cut the Rope tutorial series!

In the first part of the series, you created the game’s basic scene and used Box2D’s b2RopeJoint and PatrickC’s VRope class to build a rope.

In this second and final part of the series, you’ll get to the best part – actually cuting that rope! :]

Getting Started: Cut the Verlet!
To cut the rope, you need to keep track of the user’s touches on screen and check if a touch intersects with a rope.

When the user drags his or her finger on the screen, the ccTouchesMoved:withEvent: method is called by Cocos2D, and you can use it to check the finger’s current position and last tracked position. Then you can check if the line formed by these two points intersects with any of the ropes on your scene.

To know which rope to cut and where, it helps to understand a bit more about how VRope works.

A VRope is composed of a series of VPoint objects that are connected using VStick objects. So, if you want to cut a VRope, the best way is to figure out if the line formed by the user’s finger movement intersects any of the VStick objects of any of the VRopes in your scene.

The first thing to do is to find a good algorithm to check if two lines intersect. The best one I found is explained very well on this page. You’ll implement this algorithm as a method in your HelloWorldLayer class.

This method is an implementation of the equations from the above site, simplified for this project. Since you don’t care much about the intersection point, only whether or not the lines intersect, you don’t need to calculate x and y. You only need to check if ua and ub are within 0 and 1.

You’re almost ready to implement the ccTouchesMoved:withEvent: method. Before you do, expose a few instance variables of VRope and VPoint that you’ll need.

First you need to expose the sticks array of VRope. Open VRope.h and add this inside the interface block:

Then synthesize the property in VRope.mm inside the implementation block:

Now you need to expose the coordinates of the VPoint class. Add this to VPoint.h:

And this to the end of VPoint.m:

You’re almost ready now. Add the skeleton of ccTouchesMoved:withEvent: to the end of HelloWorldLayer.mm:

This is pretty simple. For every rope, you check each of the sticks used to create the rope. As soon as you find a stick that intersects the line dragged by the user, you cut the rope and stop the loop.

OK, so not that simple, as you still need to figure out how to cut the rope!

Let’s think about this for a few moments before you rush off to VRope again to add this functionality. A VRope is backed by a b2RopeJoint, and this rope joint in turn needs a body at each end. When you cut a VRope, you want to split it into two VRope objects (or create a new one), and for this you need another b2RopeJoint and two new bodies.

What? Two new bodies? Thinking in real world terms, this seems weird. When you cut a rope, two bodies do not suddenly come into existence.

That may be true, but in your little Box2D world, your rope is not really a rope! It’s just a logical binding between two bodies. So when you cut the rope in your world, to keep the illusion alive you’ll attach a very small, invisible object to each end of the cut, so the ropes maintain their weight and rope-like behavior.

Here’s a rough illustration of what you’re going to implement:

OK, it’s not a masterpiece of art, but you get the idea. :] The code and explanation below will make this even clearer.

On to the implementation then!

Go to VRope.h and add this method declaration right after -(void)reset:

You’ll feed this method two new bodies and the VStick where you want to cut the rope. Now let’s see how it’s done. Open VRope.mm and add this method right after reset:

Wow, that’s a big one! I’ll try to explain everything done by the above code with the help of another masterpiece of illustration:

The illustration shows the VPoints and VSticks that make up the VRope. As you can see, the VRope has seven points and, therefore, six sticks. The indices of the points are indicated above them and the indices of the sticks under them.

In this example, the rope will be cut in the middle (nPoint = 3). The idea is to use the original VRope’s data so that the cut appears natural.

Let’s go though the code above, following the numbered comments:

1.First determine the point at which the cut will be made. The passed stick object will be the first stick belonging to the new rope, so its index is the index of the point where the cut will happen. In the illustration above, this is 3.

2.Define a range struct to indicate where you want your vSticks array to split. In the illustration, this range will have location=3 and length=3. This indicates the new rope will get sticks from 3 to 5.

3.Create a new array with sticks that will go to the new rope.

4.Remove these sticks from the current rope’s array.

5.Do the same for the sprites that are used to draw the sticks.

6.Extend the length of the range by one, because the number of points is always the number of sticks + 1. Create a new array with the points that will be in the new rope (points 3 to 6 in the illustration) and remove them from this object’s array.

7.You probably noticed in the illustration that the breaking point of the rope was removed from this object’s array and will go to the new rope. To rectify this you need to “clone” this point and add it to this object’s points array. The last stick of this object also needs to be updated to point to the cloned end point.

8.The cut ratio will be used to determine the length of the new ropes. In the illustration, this ratio will be 0.5.

9.Fix the number of points of this object. In the illustration, it becomes 4.

10.Recreate the joint that will be associated with this object. Attach bodyA and the newBodyB to this rope joint and use the old length and cut ratio to determine the new length. Also, place the newBodyB where the rope was cut.

11.Create the new rope joint and attach the newBodyA and bodyB to it, also placing the newBodyA in the position of the cut.

12.Then destroy the old joint and update the instance variable to the new joint.

13.And finally, create the new VRope object using the arrays you collected before and the new joint. You still need to implement this init method, but it’s very simple and the code’s coming up next.

As promised, here’s the init method. Add this below the code you just added:

Yes, it’s pretty simple. It just keeps track of the arrays you passed in above and updates the numPoints instance variable.

You’ve probably noticed that you have a warning in your code about a method called setPointB not being found. Yes, I slipped it by you! I was hoping not to complicate things further while explaining the massive rope-cutting method. But this is very easy to fix.

Open VStick.h and add this declaration:

Then open VStick.m and add this at the end:

As you can see, just a simple setter method. Now your program should compile without any issues, and you can finally cut the rope!

Except… before you cut the rope, you need two new bodies. In HelloWorldLayer.mm, add the following new method to create these “tip” bodies, as I call them:

The only thing worth pointing out here is the maskBits property. It’s set to zero so that it won’t collide with anything else in your world, and that’s how it should be since this is just a body you’re using to simulate the rope’s weight.

Now, go back to ccTouchesMove:withEvent: and add these lines before the return statement (where it says “Cut the rope here”):

This applies everything you created above and cuts the selected rope at the correct point.

That’s enough code for now. Build and run, and you should be able to cut a rope with your finger (or mouse if in the simulator…):

If you feel as if you need a break, don’t worry, the fun and games are just beginning (pun intended). :]

The repository tag for this point in the tutorial is RopeCutting.

A Pineapple-eating Crocodile? Why Not?
Now that the hard part is over, let’s add some gameplay and feed that pineapple to the crocodile.

You already have another sprite of the croc with its mouth open in your sprite sheet. What you’re going to do is make the croc open and close it’s mouth from time to time. The player will have to time the rope cuts so as to drop the pineapple over the croc’s mouth when it’s open. The croc decides when he’s hungry!

You need a way to check if the pineapple, as it drops, makes contact with the croc’s mouth. To do this, you’ll use Box2D’s contact listeners. You should be familiar with contact listeners from previous tutorials. If not, take a look at How To Create A Breakout Game with Box2D and Cocos2D – Part 2.

The first step is to add a body that will simulate the croc’s mouth, so the contact listener will have something to check for contact with the pineapple. Open HelloWorldLayer.h and add these lines:

Then go to initPhysics in HelloWorldLayer.mm and add this at the end:

You first create a static body with the origin on the bottom left corner of your sprite. You then create a fixture to simulate the croc’s mouth bottom (the one you’ll use in your collision detection). If you run the project, you can see the fixture in the right position:

You’ll be able to see it better once you open the croc’s mouth. You might wonder why the body (the physics body for the croc’s mouth, not it’s actual body :]) is set to be inactive (last line). This is because the croc’s mouth is initially closed. In that state, you don’t want the pineapple to interact with it.

Time to make the croc open its mouth. Add these two variables to HelloWorldLayer.h:

Now add this method to HelloWorldLayer.mm:

This method changes the boolean that indicates if the croc has its mouth open or not, changes the sprite to reflect this, sets the body to active if the mouth is open, and finally, starts a timer that will repeat this after a random amount of time between 3 and 5 seconds.

Notice that you’re also changing the croc’s sprite zOrder. This achieves the following behavior: When the croc’s mouth is open, if the pineapple drops, it will fall behind the croc (because the pineapple now has a zOrder of 0 and the croc a zOrder of 1), giving the illusion of having been eaten. If the croc’s mouth is closed, then the opposite happens and the pineapple falls in front of the croc. You’ll see this in action in a moment.

Before you forget, add this to dealloc in HelloWorldLayer.mm:

And finally, make the initial call to changeCrocAttitude at the end of initLevel:

After the above call, changeCrocAttitude will itself take care of calling the method again at random intervals.

Great! Build and run the project and you should see the croc’s mouth open and close from time to time:

If you drop the pineapple in the croc’s mouth when it’s open, it will lay there for a while, but when the croc’s mouth closes, the pineapple drops into view. Don’t worry, you’ll fix this in a moment with a contact listener.

The repository tag for this point in the tutorial is CrocAttitude.

You Have Contact!

For the contact listener, you’ll mostly use code from the breakout game tutorial mentioned above. So begin by downloading the contact listener developed in that tutorial. Here are the include and implementation files.

Drag these files to your project tree. Be sure to check the “Copy items into destination group’s folder” to copy these files to your project folder.

Now, open HelloWorldLayer.h and add this include:

And this to the class variables:

Open HelloWorldLayer.mm and add these lines at the end of initPhysics:

Add the following cleanup code to dealloc:

Now add this code to the end of update (don’t worry about the two warnings and an error that will appear, as you’ll fix those real quick):

This looks complicated, but it’s not, it’s just long. It’s a variation of the code used in the breakout game tutorial.

First you iterate through all the contacts. If any contact involves either the floor or the croc’s mouth, you keep a reference to the other body, as it might be a candy (in this project, it can’t be any other body, as the rope does not interact with either the body or the ground, but it’s better to include this check now, in case you add other elements to the game play later on). You then check the candies array, and if the potential candy body is there you can be sure it’s a candy.

If it is indeed a candy, you add it to the destruction vector. The difference is that, if it hit the croc’s mouth, you set a bool to true to make the croc’s mouth close. But if it hit the floor, then the croc’s mouth doesn’t need to close.

In the case of the candy hitting the floor, you’re going to add an animation to the sprite to simulate the candy sinking. At the end of the animation, you’ll force the level to end, since in this game you can only pass the level if the croc eats all the candy. (You’ll add this method shortly.)

After this first loop checks all the contacts, you go through the toDestroy vector to remove the physics bodies that need to be destroyed. One complication in your game is the removal of the VRope objects that might be associated with the destroyed object. So, for every destroyed object, you go though its joints and check if they belong to any VRope object. If they do, you remove the sprites from the scene and the VRope object from your ropes array.

Finally, you close the croc’s mouth if you had any mouth hits, and call a method (not yet written) to check if the level should end.

Now you should have two warnings about the checkLevelFinish method, and one error because the VRope object is not exposing the joint instance variable. Fix these problems now.

You’ll use another standard C++ class, so add this to the includes of HelloWorldLayer.mm:

I slipped the implementation for finishedLevel in there too. :]

checkLevelFinish is simple enough. finishedLevel is also pretty simple, the only complication being that you have to destroy all the VRope objects first, and then all the objects that were associated with them, except for the ground body. These are all the “tip” objects you created when cutting the ropes and the remaining candies that might still exist.

You’re using a set instead of a vector because, when looping through all the ropes, a candy can be attached to more than one rope. In this case, if you used a vector you would add the same body twice and remove it twice in the destroy loop. A set solves this by not adding the same object twice.

In the destroy loop, you also have to destroy the body’s associated sprite, again, in case it’s a candy.

Now you only need to expose the joint instance variable in VRope, and you’re done!

Open VRope.h and add this after the declaration of cutRopeInStick:newBodyA:newBodyB::

Then add this to VRope.mm right after the #ifdef BOX2D_H line:

Cool! Run the project and play!

The repository tag for this point in the tutorial is GameOver.

This has been fun, hasn’t it? You’ve come a long way in this tutorial! You can add some final touches to make it even better.

Go to initLevel in HelloWorldLayer.mm and add this block right before the block that moves the world forward a bit:

Build and run, and you’ll see that this adds one more candy swinging from the top.

Another thing that can be easily improved is the quality of the rope. You may have noticed that sometimes the rope is a little unnatural and you can see the sticks. To improve this, you need to change one of the parameters of VRope.

Open VRope.mm and go to createRope:pointB:distance: and find this line:

If you decrease this number to, say, 6, your rope will have more VStick segments and will behave more naturally. Of course, the more you decrease it the longer it takes to perform the rope simulation, so there’s a performance tradeoff – test to see what works best for your app.

Another weird thing that happens is that the rope swings a lot and is a little unstable, swinging even after it’s been static for a while. You can tweak this too. Open VPoint.m and find applyGravity::

If you decrease this number (10.0), the rope will become “lighter” and move more naturally. Try changing it to 1.0.

One thing that might still happen is a contact between the pineapple and the top edge of the world, which looks kinda weird – the pineapple colliding with the sky! So remove the top edge, along with the left and right edges, and increase the bottom edge a bit to widen your world. :]

Open HelloWorldLayer.mm and go to initPhysics. Remove these lines:

And change this line:

To:

Finally, some memory management fixes to VRope. If you use Xcode’s Analyze on the project, you’ll see some potential problems with memory management: unusual ways of doing things that could become a problem. In fact, they actually have become a problem, since you’re releasing the VPoint created in cutRopeInStick:bodyA:bodyB:, and it releases it again in the dealloc method.

To make the class follow best practices, open VRope.mm, find createRope:pointB:distance: and change it to add the two release statements (indicated via comments) below:

Now go to dealloc and remove these lines:

And that’s it, memory problems solved and analyzer happy!

The repository tag for this point in the tutorial is MinorTweaks.

Gratuitous Music and Sound Effects
The game is pretty cool as it is, but it’s a little quiet for my tastes. Add some sounds and background music to liven it up. :]

I’ve selected a nice jungle song from incompetech.com and some sound effects from freesound.org. You can select your own, or get the ones I picked out here.

Extract the archive and drag it to the Resources folder of your project:

Be sure to check the “Copy items into destination group’s folder” to copy these files to your project folder.

Then, add the following import and constants to the top of your HelloWorldLayer.mm:

Now, add this code to init, just before the scheduleUpdate call:

This preloads all your effect audio files. It also starts playing the background music and sets it to automatically loop when it ends. The last line lowers the volume of the background music a bit, so that it doesn’t drown out the other sounds.

Now you just need to add the effects at some key places.

The cutting sound should play when the user cuts the rope. As you remember, this happens in ccTouchesMoved: in an “if” condition inside two for loops. Find ccTouchedMoved:withEvent: and add this right before the return statement, inside the “if” condition:

The bite sound should go at the end of update, inside the last if condition where you force the croc’s mouth to close. Add this inside the if, right after the call to changeCrocAttitude:

Last but not least is the splash sound for the pineapple falling into the water. This also happens inside update. Remember when you added the code to make the pineapple sink? The sound effect should be triggered there. Find if (hitTheFloor) inside update and add this inside the if condition (at the top):

And that’s it! Run and test it out. It’s amazing how a few lines of code and some sound effects can change a game!

Knowing When Its Over

It’s over when it’s over, but you should display some text to let the user know if they’ve fed the crocodile and won, or disappointed the crocodile and lost.

First, there’s the case of when the user lost because a piece of candy fell in the water. Add the code in the same location that you added the splash sound, in update, right after the splash sound call:

This adds a label to the center of the screen, in red.

To make the message go away, add a call to remove it just a few lines below, inside the finish block (just add the line indicated with a comment, not the whole block):

If the user won by feeding all the candy to the crocodile, let them know by adding this at the end of the “if” condition inside checkLevelFinish (after the other existing code in the condition):

That’s it! Run and try to make the croc eat the two candies! That candy at the top is tricky, isn’t it? The croc really makes me mad sometimes….

The repository tag for this point in the tutorial is FinalProject.

Where to Go From Here?

Well, that was fun! Hard, but fun! Now go and make this game even better. I’m sure that are lots of ways to improve it. So, fork the project on github, make it better and add a pull request. I’d love to see what you’re capable of!(source:raywenderlich)