verlet-rope-latest 的扩展—— BYRope

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

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

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

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

废话不多说:

上效果图:


上相关代码:


//

//  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上解决~

posted on 2012-02-27 01:32  yang3wei  阅读(343)  评论(0编辑  收藏  举报