verlet-rope-latest 的扩展—— BYRope

对 verlet-rope-latest 做了一下扩展:

1。可以添加绳子端点的sprite

2。以 b2RopeJoint 对绳联体做最大距离限制

3。可以在body的fixture上面任取一点作为连接点(原版仅仅支持连接到物体的中心点),这个也是推动我做修改的初衷~

废话不多说:

上效果图:

verlet-rope-latest 的扩展—— BYRope

上相关代码:


//

// BYRope.h

// HungryBear

//

// Created by Bruce Yang on 12-2-26.

// Copyright (c) 2012 EricGameStudio. All rights reserved.

//


#import <Foundation/Foundation.h>

#import "cocos2d.h"

#import "Box2D.h"

#import "VPoint.h"

#import "VStick.h"

#import "GCfg.h"

#import "GameConfig.h"



@interface BYRope : NSObject {

b2Body *_b1;

b2Body *_b2;

b2Vec2 _p1Original;

b2Vec2 _p2Original;

b2RopeJoint *_ropeJoint;

int _pointsCount;

float _antiSagHack;

NSMutableArray *_vPoints;

NSMutableArray *_vSticks;

CCSprite *_spriteBar1;

CCSprite *_spriteBar2;

NSMutableArray *_segmentSprites;

CCSpriteBatchNode *_batchBar;

CCSpriteBatchNode *_batchSegment;

/**

* 用于标识使用何种 update 方法( 即根据调用构造方法的不同,调用不同的 update 方法)~

* true 表示绳子连接的两个点为两 body 的中心点~

* false 表示不以两 body 的中心点为连接点~

*/

BOOL _isBodyCenter;

}



/** 1.简化调用的静态方法~ */


+(id) ropeWithBody1:(b2Body*)body1

body2:(b2Body*)body2

batchSegment:(CCSpriteBatchNode*)batchSegment

batchBar:(CCSpriteBatchNode*)batchBar;


+(id) ropeWithBody1:(b2Body*)body1

body2:(b2Body*)body2

posi1:(b2Vec2)posi1

posi2:(b2Vec2)posi2

batchSegment:(CCSpriteBatchNode*)batchSegment

batchBar:(CCSpriteBatchNode*)batchBar;


/** 2.初始化~ */

-(id) initWithBody1:(b2Body*)body1

body2:(b2Body*)body2

batchSegment:(CCSpriteBatchNode*)batchSegment

batchBar:(CCSpriteBatchNode*)batchBar;


-(id) initWithBody1:(b2Body*)body1

body2:(b2Body*)body2

posi1:(b2Vec2)posi1

posi2:(b2Vec2)posi2

batchSegment:(CCSpriteBatchNode*)batchSegment

batchBar:(CCSpriteBatchNode*)batchBar;


/** 3.重置~ */

-(void) reset;



/** 4.逻辑更新:施加重力并更新 points 的位置、紧缩小棍儿~ */

-(void) update:(float)dt;// --> GameScene.tick~ Essential!



/**

* 5.更新 sprites 的位置

* (因为绳子的受力逻辑并不受 box2d 物理引擎掌控,没有一个所依附的 body,因此 sprite 的位置需手动来调整)~

*/

-(void) updateSprites;// --> GameScene.draw~ Essential!



/** 6.调试~ */

-(void) debugDraw;



/** 7.清理,dealloc~ */

-(void) removeSprites;

-(void) dealloc;


@end


//

// BYRope.mm

// HungryBear

//

// Created by Bruce Yang on 12-2-26.

// Copyright (c) 2012 EricGameStudio. All rights reserved.

//


#import "BYRope.h"



@implementation BYRope


#pragma mark-

#pragma mark Quick Static Interface~

/**

* 1.简化调用的静态方法~

*/

+(id) ropeWithBody1:(b2Body*)body1

body2:(b2Body*)body2

batchSegment:(CCSpriteBatchNode*)batchSegment

batchBar:(CCSpriteBatchNode*)batchBar {

return [[[self alloc] initWithBody1:body1

body2:body2

batchSegment:batchSegment

batchBar:batchBar]autorelease];

}


+(id) ropeWithBody1:(b2Body*)body1

body2:(b2Body*)body2

posi1:(b2Vec2)posi1

posi2:(b2Vec2)posi2

batchSegment:(CCSpriteBatchNode*)batchSegment

batchBar:(CCSpriteBatchNode*)batchBar {

return [[[self alloc] initWithBody1:body1

body2:body2

posi1:posi1

posi2:posi2

batchSegment:batchSegment

batchBar:batchBar]autorelease];

}



#pragma mark-

#pragma mark Init~


/**

* 2.初始化~

*/

-(void) createRope:(CGPoint)pointA pointB:(CGPoint)pointB {

_vPoints = [[NSMutableArrayalloc]init];

_vSticks = [[NSMutableArrayalloc]init];

_segmentSprites = [[NSMutableArrayalloc]init];

float distance = ccpDistance(pointA, pointB);

// increase value to have less segments per rope, decrease to have more segments

int segmentFactor =16;// 最好是能和绳子小节图片的长度相一致~

_pointsCount = distance / segmentFactor;

CGPoint diffVector = ccpSub(pointB, pointA);

float multiplier = distance / (_pointsCount -1);

// HACK: scale down rope points to cheat sag. set to 0 to disable, max suggested value 0.1

_antiSagHack =0.01f;

for(int i =0; i <_pointsCount; ++ i) {

CGPoint tmpVector =ccpAdd(pointA,ccpMult(ccpNormalize(diffVector),multiplier*i*(1-_antiSagHack)));

VPoint *tmpPoint = [VPointvPoint];

[tmpPoint setPos:tmpVector.xy:tmpVector.y];

[_vPoints addObject:tmpPoint];

}

for(int i =0; i <_pointsCount - 1; ++ i) {

VStick *tmpStick = [VStickvStick:[_vPointsobjectAtIndex:i] pointb:[_vPointsobjectAtIndex:i +1]];

[_vSticks addObject:tmpStick];

}

if(_batchSegment ==nil ||_batchBar ==nil) {

// 多亏了这个,不然我又得找半天,真是瞎猫碰上死耗子了

printf("\nBYRope: _batchSegment == nil || _batchBar == nil\n");

}

float segmentHeight = [[[_batchSegmenttextureAtlas]texture] pixelsHigh]/[GCfggetInstance].factor;

for(int i = 0; i < _pointsCount - 1; ++ i) {

VPoint *point1 = [[_vSticksobjectAtIndex:i]getPointA];

VPoint *point2 = [[_vSticksobjectAtIndex:i]getPointB];

CGPoint stickVector = ccpSub(ccp(point1.x, point1.y),ccp(point2.x, point2.y));

float stickAngle = ccpToAngle(stickVector);

CGRect rect = CGRectMake(0,0, multiplier, segmentHeight);

CCSprite *tmpSprite = [CCSpritespriteWithBatchNode:_batchSegmentrect:rect];

ccTexParams params = {GL_LINEAR,GL_LINEAR, GL_REPEAT,GL_REPEAT };

[tmpSprite.texture setTexParameters:&params];

[tmpSprite setPosition:ccpMidpoint(ccp(point1.x, point1.y),ccp(point2.x, point2.y))];

[tmpSprite setRotation:-1 *CC_RADIANS_TO_DEGREES(stickAngle)];

[_batchSegment addChild:tmpSprite];

[_segmentSprites addObject:tmpSprite];

}

// 绳子端点的 sprite~

CGRect barRect = CGRectMake(0,0,8.0f, 8.0f);

_spriteBar1 = [CCSpritespriteWithBatchNode:_batchBarrect:barRect];

_spriteBar2 = [CCSpritespriteWithBatchNode:_batchBarrect:barRect];

VPoint *firstPoint = (VPoint*)[_vPointsobjectAtIndex:0];

VPoint *lastPoint = (VPoint*)[_vPointsobjectAtIndex:(_pointsCount -1)];

[_spriteBar1 setPosition:ccp(firstPoint.x, firstPoint.y)];

[_spriteBar2 setPosition:ccp(lastPoint.x, lastPoint.y)];

[_batchBar addChild:_spriteBar1];

[_batchBar addChild:_spriteBar2];

}


-(id) initWithBody1:(b2Body*)body1

body2:(b2Body*)body2

batchSegment:(CCSpriteBatchNode*)batchSegment

batchBar:(CCSpriteBatchNode*)batchBar {

if((self = [superinit])) {

_isBodyCenter = YES;

_b1 = body1;

_b2 = body2;

b2Vec2 p1 = body1->GetPosition();

b2Vec2 p2 = body2->GetPosition();

// 创建绳子关节限定两点间的最大距离~

b2RopeJointDef rjd;

rjd.bodyA = body1;

rjd.bodyB = body2;

rjd.localAnchorA =b2Vec2_zero;

rjd.localAnchorB =b2Vec2_zero;

rjd.maxLength = (p2 - p1).Length();

_ropeJoint = (b2RopeJoint*)body1->GetWorld()->CreateJoint(&rjd);

CGPoint pointA = ccp(p1.x * PTM_RATIO, p1.y *PTM_RATIO);

CGPoint pointB = ccp(p2.x * PTM_RATIO, p2.y*PTM_RATIO);

_batchSegment = batchSegment;

_batchBar = batchBar;

[self createRope:pointA pointB:pointB];

}

return self;

}


-(id) initWithBody1:(b2Body*)body1

body2:(b2Body*)body2

posi1:(b2Vec2)posi1

posi2:(b2Vec2)posi2

batchSegment:(CCSpriteBatchNode*)batchSegment

batchBar:(CCSpriteBatchNode*)batchBar {

if((self = [superinit])) {

_isBodyCenter = NO;

_b1 = body1;

_b2 = body2;

// 创建绳子关节限定两点间的最大距离~

b2RopeJointDef rjd;

rjd.bodyA = body1;

rjd.bodyB = body2;

rjd.localAnchorA = posi1 - body1->GetPosition();

rjd.localAnchorB = posi2 - body2->GetPosition();

rjd.maxLength = (posi2 - posi1).Length();

_ropeJoint = (b2RopeJoint*)body1->GetWorld()->CreateJoint(&rjd);

_p1Original = posi1 - body1->GetPosition();

_p2Original = posi2 - body2->GetPosition();

CGPoint pointA = ccp(posi1.x *PTM_RATIO, posi1.y *PTM_RATIO);

CGPoint pointB = ccp(posi2.x *PTM_RATIO, posi2.y *PTM_RATIO);

_batchSegment = batchSegment;

_batchBar = batchBar;

[self createRope:pointA pointB:pointB];

}

return self;

}



#pragma mark-

#pragma mark Reset~


/** 3.重置~ */

-(void) resetWithPoints:(CGPoint)pointA pointB:(CGPoint)pointB {

float distance = ccpDistance(pointA, pointB);

CGPoint diffVector = ccpSub(pointB, pointA);

float multiplier = distance / (_pointsCount -1);

for(int i =0; i <_pointsCount; ++ i) {

CGPoint tmpVector =ccpAdd(pointA,ccpMult(ccpNormalize(diffVector), multiplier*i*(1-_antiSagHack)));

VPoint *tmpPoint = [_vPointsobjectAtIndex:i];

[tmpPoint setPos:tmpVector.xy:tmpVector.y];

}

}

-(void) reset {

CGPoint pointA;

CGPoint pointB;

if(_isBodyCenter ==YES) {

pointA = ccp(_b1->GetPosition().x*PTM_RATIO,_b1->GetPosition().y*PTM_RATIO);

pointB = ccp(_b2->GetPosition().x*PTM_RATIO,_b2->GetPosition().y*PTM_RATIO);

} else {

b2Vec2 vec1 = _b1->GetWorldPoint(_p1Original);

b2Vec2 vec2 = _b2->GetWorldPoint(_p2Original);

pointA = ccp(vec1.x *PTM_RATIO, vec1.y *PTM_RATIO);

pointB = ccp(vec2.x *PTM_RATIO, vec2.y *PTM_RATIO);

}

[selfresetWithPoints:pointApointB:pointB];

}



#pragma mark-

#pragma mark Logic Update~


/**

* 4.逻辑更新:施加重力并更新 points 的位置、紧缩小棍儿~

*/

-(void) updateWithPoints:(CGPoint)pointA pointB:(CGPoint)pointB dt:(float)dt {

// manually set position for first and last point of rope

[[_vPoints objectAtIndex:0]setPos:pointA.xy:pointA.y];

[[_vPoints objectAtIndex:_pointsCount -1]setPos:pointB.xy:pointB.y];

// update points, apply gravity

for(int i =1; i <_pointsCount - 1; ++ i) {

[[_vPoints objectAtIndex:i] applyGravity:dt];

[[_vPointsobjectAtIndex:i]update];

}

// contract sticks

int iterations = 4;

for(int j =0; j < iterations; ++ j) {

for(int i =0; i <_pointsCount - 1; ++ i) {

[[_vSticksobjectAtIndex:i]contract];

}

}

}

-(void) update:(float)dt {

CGPoint pointA;

CGPoint pointB;

if(_isBodyCenter ==YES) {

pointA = ccp(_b1->GetPosition().x*PTM_RATIO,_b1->GetPosition().y*PTM_RATIO);

pointB = ccp(_b2->GetPosition().x*PTM_RATIO,_b2->GetPosition().y*PTM_RATIO);

} else {

b2Vec2 vec1 = _b1->GetWorldPoint(_p1Original);

b2Vec2 vec2 = _b2->GetWorldPoint(_p2Original);

pointA = ccp(vec1.x *PTM_RATIO, vec1.y *PTM_RATIO);

pointB = ccp(vec2.x *PTM_RATIO, vec2.y *PTM_RATIO);

}

[self updateWithPoints:pointApointB:pointBdt:dt];

}



#pragma mark-

#pragma mark Sprite Update~


/**

* 5.更新 sprites 的位置

* (因为绳子的受力逻辑并不受 box2d 物理引擎掌控,没有一个所依附的 body,因此 sprite 的位置需手动来调整)~

*/

-(void) updateSprites {

for(int i = 0; i < _pointsCount - 1; ++ i) {

VPoint *point1 = [[_vSticksobjectAtIndex:i]getPointA];

VPoint *point2 = [[_vSticksobjectAtIndex:i]getPointB];

CGPoint point1_ = ccp(point1.x, point1.y);

CGPoint point2_ = ccp(point2.x, point2.y);

float stickAngle = ccpToAngle(ccpSub(point1_, point2_));

CCSprite *tmpSprite = [_segmentSpritesobjectAtIndex:i];

[tmpSprite setPosition:ccpMidpoint(point1_, point2_)];

[tmpSprite setRotation: -CC_RADIANS_TO_DEGREES(stickAngle)];

}

/** 以下代码无法消除端点抖动的情况~ */

// VPoint *firstPoint = (VPoint*)[_vPoints objectAtIndex:0];

// VPoint *lastPoint = (VPoint*)[_vPoints objectAtIndex:(_pointsCount - 1)];

// [_spriteBar1 setPosition:ccp(firstPoint.x, firstPoint.y)];

// [_spriteBar2 setPosition:ccp(lastPoint.x, lastPoint.y)];

/** 消抖(将端点 sprite position b2RopeJoint 对象的两个 anchor 绑定),可能报错!舍弃~ */

// b2Vec2 vec1 = _ropeJoint->GetAnchorA();

// b2Vec2 vec2 = _ropeJoint->GetAnchorB();

/** 消抖2(采用和更新点一样的思路),最终方案~ */

b2Vec2 vec1 = _b1->GetWorldPoint(_p1Original);

b2Vec2 vec2 = _b2->GetWorldPoint(_p2Original);

[_spriteBar1 setPosition:ccp(vec1.x *PTM_RATIO, vec1.y *PTM_RATIO)];

[_spriteBar2 setPosition:ccp(vec2.x *PTM_RATIO, vec2.y *PTM_RATIO)];

}


#pragma mark-

#pragma mark Debug Draw~


/**

* 6.调试~

*/

-(void) debugDraw {

glColor4f(0.0f,0.0f,1.0f, 1.0f);

glLineWidth(5.0f);

for(int i =0; i <_pointsCount - 1; ++ i) {

VPoint *pointA = [[_vSticksobjectAtIndex:i]getPointA];

VPoint *pointB = [[_vSticksobjectAtIndex:i]getPointB];

ccDrawPoint(ccp(pointA.x, pointA.y));

ccDrawPoint(ccp(pointB.x, pointB.y));

ccDrawLine(ccp(pointA.x, pointA.y),ccp(pointB.x, pointB.y));

}

// restore to white and default thickness

glColor4f(1.0f,1.0f,1.0f, 1.0f);

glLineWidth(1);

}



#pragma mark-

#pragma mark Clear & Dealloc~

/**

* 7.清理,dealloc~

*/

-(void) removeSprites {

// 1. _batchSegment中将绳子小节 sprite移除~

for(int i=0;i<_pointsCount-1;i++) {

[(CCSprite*)[_segmentSpritesobjectAtIndex:i]removeFromParentAndCleanup:YES];

}

[_segmentSpritesremoveAllObjects];

// 2. _batchBar中将绳子端点 _spriteBar1, _spriteBar2移除~

[_spriteBar1removeFromParentAndCleanup:YES];

[_spriteBar2removeFromParentAndCleanup:YES];

}

-(void) dealloc {

[_segmentSprites release];

[_vPoints release];

[_vSticks release];

[superdealloc];

}


@end



上全部下载连接:

http://download.csdn.net/detail/yang3wei/4092594


PS:

(仅仅只是绳子的代码部分,不是完整的项目,留给各位自己来探究一下如何运用,

也可以参照我之前写过的关于绳子的文章,那里有 verlet-rope-latest 的原文出处,

而且,verlet-rope-latest VRope.mm 文件的注释中详细点明了绳子类的使用方法,

创建,在 Scene 的 tick 方法里面做逻辑更新,在 draw 方法里面做sprite 位置更新,清理等等,一应俱全)

还有可以更进一步的地方,绳子的端点有点儿抖动的现象可以通过将端点sprite附着到 b2RopeJoint的两个 anchor上解决~

你可能感兴趣的:(test)