参考资料:http://www.cocos2d-iphone.org/forum/topic/10411
这是我改写的一则绳子,绳子以body小节相连接
不过我还是觉得 verlet rope那种原型方式更加漂亮!
原型的缺点就是绳子一旦被切断,就要即刻清除所有相关内容,
一根绳子被切断后就这么凭空消失了,这总让人觉得有点儿说不过去吧?
于是我便想以body的形式改写一则更有质感的绳子,
这样的话,即使在被切断了之后,两段“绳子尸体”依然还能飘来荡去~
学到了不少新的东西,例如CCSpriteBatchNode的正确使用方法~
以前我也用 CCSpriteBatchNode,但是用的方法不正确
我现在是这么认识的,CCSpriteBatchNode加载一份图片文件,
其他的CCSprite都可以来使用这份图片,直接从内存里面取的,迅速快捷~
如果一份图片反复要在游戏里面用到,但是每次都通过 [CCSprite spriteWithFile:@"123.png"]的方式来取的话
就会存在每次都要从磁盘介质里面拿取这份图片,而读磁盘是很耗时间的,不管怎么说,一定比从内存读取慢!
其实很多时候都要权衡利弊,从内存里面读取资源文件虽快,但是ios设备内存吃紧
将整个游戏几十兆的资源文件一次性全部加载到内存,程序很有可能会被强干掉
上次我查了一番加密资源文件的手法,有一种方式是将资源文件“打碎成”字节数据放在头文件里面
后来我一想,游戏一运行,那么装载头文件里面的资源文件肯定在第一时间就会被全部加载到内存
快是够快了,内存够不够用却又成问题了~
后来我留言给作者,作者的回复也是如同我所想,此种加密方式仅限于一些必须要保密的资源,
如防止别人篡改公司logo,就可能将logo图片置于头文件中~
last,绳子其实还很有待挖掘,今后有时间的话,一定会力争写出更真实,更高效的绳子来~
//
// BYRope.h
// GoldMine0.6
//
// Created by Bruce Yang on 12-1-13.
// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
#import <vector>
#define PTM_RATIO 32
// 绳子小段图片的高度和宽度~
#define ROPE_ITEM_WIDTH 16.0f
#define ROPE_ITEM_HEIGHT 4.0f
// 绳子 bar 图片的高度和宽度~
#define ROPE_BAR_DIAMETER 8.0f
// 1.是否在绳子的两个连接点之间启用 距离限定关节~
#define B2_ROPE_JOINT_ENABLED NO
// 2.bar body 到底是连接在绳子最外侧小节的 body 上,还是连接在 comp 元素的 body 上~
#define CONNECTED_WITH_COMP_BODY 0 // 紧随comp
#define CONNECTED_WITH_ROPE_BODY 1 // 紧随绳子最外侧小节(因为会随着绳子抖动,所以效果感觉不怎么样)~
#define ROPE_BAR_CONNECT_MODE 0
/**
* added by BruceYang on 2012.01.15.06.22
* 对 Box2D b2Math.h 做功能扩充(c语言 运算符重载的用法)~
* 之所以放在该类里面而不直接去 box2d 源码里面修改,主要还是为了升级 box2d 库的时候不出现方法丢失的问题~
* 可想而知,如果这些代码是插进 box2d 源码里面的话,升级的时候一个替换,这些我自己新添加的东西肯定就要丢失了~
*/
inline b2Vec2 operator * (const b2Vec2& a, float32 s) {
return b2Vec2(s * a.x, s * a.y);
}
inline b2Vec2 operator / (const b2Vec2& a, float32 s) {
return b2Vec2(a.x / s, a.y / s);
}
inline float32 b2Vec2ToAngle(const b2Vec2& v) {
return atan2f(v.y, v.x);
}
@interface BYRope : NSObject {
// ~
}
/**
* p1 为在 body1 fxitures 内部的一个世界坐标点~
* p2 为在 body2 fxitures 内部的一个世界坐标点~
* itemBatch 装载绳子小节图片
* barBatch 装载绳子端点圆形小图片(若传入参数nil,则绳子两端不包含圆形小图片)~
*/
-(id) init:(b2Body*)body1
point1:(b2Vec2)p1
body2:(b2Body*)body2
point2:(b2Vec2)p2
itemBatch:(CCSpriteBatchNode*)itemBatch
barBatch:(CCSpriteBatchNode*)barBatch;
-(void) dealloc;
@end
//
// BYRope.mm
// GoldMine0.6
//
// Created by Bruce Yang on 12-1-13.
// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
#import "BYRope.h"
@implementation BYRope
-(id) init:(b2Body*)body1
point1:(b2Vec2)p1
body2:(b2Body*)body2
point2:(b2Vec2)p2
itemBatch:(CCSpriteBatchNode*)itemBatch
barBatch:(CCSpriteBatchNode*)barBatch
{
if((self = [super init])) {
// world 完全没必要用一个参数传进来,直接从 body 里面取出来就是了~
b2World *world = body1->GetWorld();
vector<b2Vec2> vPoints;
// 计算两点之间的距离~
float distance = b2Distance(p1, p2);
// 定义绳子的 “每段平均长度”:长度越短,段数越多;长度越长,段数越少~
float segmentLength = ROPE_ITEM_WIDTH / PTM_RATIO;
int pointsCount = distance / segmentLength + 1;
// 两点间的矢量差~
b2Vec2 diffVector = p2 - p1;
// 求出单位向量~
diffVector.Normalize();
float multiplier = distance / (pointsCount - 1);
for(int i = 0; i < pointsCount; ++ i) {
b2Vec2 vector = p1 + multiplier * i * diffVector;
vPoints.push_back(vector);
}
b2Body *prevBody = body1;
b2BodyDef bodyDef;
b2Body* body;
b2Fixture* fixture;
b2RevoluteJointDef jd;
b2Body *firstBd, *lastBd;
for(int i = 0; i < pointsCount-1; ++ i) {
b2Vec2 p1_ = vPoints.at(i);
b2Vec2 p2_ = vPoints.at(i+1);
b2Vec2 stickVector = p1_ - p2_;
float stickAngle = b2Vec2ToAngle(stickVector);
b2Vec2 midPoint = (p1_ + p2_) / 2.0f;
bodyDef.type = b2_dynamicBody;
body = world->CreateBody(&bodyDef);
// 如果是绳子的第一个或最后一个关节,则不可以被切割~
if(i == 0) {
firstBd = body;
body->m_isCuttable = false;
}
b2PolygonShape boxShape;
boxShape.SetAsBox(multiplier/2.0f, ROPE_ITEM_HEIGHT/PTM_RATIO/2.0f);
fixture = body->CreateFixture(&boxShape, 1.0f);
// 设置绳子单元不响应碰撞~
fixture->SetSensor(true);
body->SetTransform(b2Vec2(midPoint.x, midPoint.y), stickAngle);
/**
* 如果将下面这块代码放到上面那块代码的前面,会出现很奇葩的现象:
* 所预期的绳子关节不会建立起来,绳子小段会到处乱飞,sprite图片也会一个个从场景中消失不见~
* 猜测原因可能是因为 body 创建出来的时候 position 还没有确定下来!!
*/
jd.collideConnected = false;
b2Vec2 anchor(p1_.x, p1_.y);
jd.Initialize(prevBody, body, anchor);
world->CreateJoint(&jd);
prevBody = body;
if(i == pointsCount-2) {
lastBd = body;
body->m_isCuttable = false;
jd.collideConnected = false;
b2Vec2 lastAnchor(p2_.x, p2_.y);
jd.Initialize(body, body2, lastAnchor);
world->CreateJoint(&jd);
}
CGRect spriteRect = CGRectMake(0, 0, multiplier*PTM_RATIO, ROPE_ITEM_HEIGHT);
CCSprite *itemSprite = [CCSprite spriteWithBatchNode:itemBatch rect:spriteRect];
ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT };
[itemSprite.texture setTexParameters:¶ms];
[itemSprite setPosition:ccp(midPoint.x*PTM_RATIO, midPoint.y*PTM_RATIO)];
[itemSprite setRotation:-1 * CC_RADIANS_TO_DEGREES(stickAngle)];
[itemBatch addChild:itemSprite];
body->SetUserData(itemSprite);
}
// 创建绳子关节,限定两物体之间的最大距离(解决了绳子与被连接物体碰撞的问题,这个绳子关节便没必要创建了)~
if(B2_ROPE_JOINT_ENABLED == YES) {
b2RopeJointDef rjd;
rjd.bodyA = body1;
rjd.bodyB = body2;
rjd.localAnchorA = p1-body1->GetPosition();
rjd.localAnchorB = p2-body2->GetPosition();
rjd.maxLength = (p2 - p1).Length();
world->CreateJoint(&rjd);
}
// 添加绳子端点的图片~
if(barBatch != nil) {
// 1.创建绳子端点上的body(给绳子 bar 依附用)~
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(p1.x, p1.y);
b2Body *b1 = world->CreateBody(&bd); // p1 点的 body~
bd.position.Set(p2.x, p2.y);
b2Body *b2 = world->CreateBody(&bd); // p2 点的 body~
// 2.将端点 body 连接到绳子最外侧的两个绳子小节~
b2RevoluteJointDef jd;
jd.collideConnected = false;
if(ROPE_BAR_CONNECT_MODE == CONNECTED_WITH_COMP_BODY) {
jd.Initialize(b1, body1, p1);
world->CreateJoint(&jd);
jd.Initialize(b2, body2, p2);
} else if(ROPE_BAR_CONNECT_MODE == CONNECTED_WITH_ROPE_BODY) {
jd.Initialize(b1, firstBd, p1);
world->CreateJoint(&jd);
jd.Initialize(b2, lastBd, p2);
}
world->CreateJoint(&jd);
// 3.创建绳子 bar sprite~
CCTexture2D *tex2d = barBatch.textureAtlas.texture;
CGRect barRect = CGRectMake(0, 0, ROPE_BAR_DIAMETER, ROPE_BAR_DIAMETER);
CCSprite *spriteA = [CCSprite spriteWithTexture:tex2d rect:barRect];
[spriteA setPosition:ccp(p1.x*PTM_RATIO, p1.y*PTM_RATIO)];
[barBatch addChild:spriteA];
b1->SetUserData(spriteA);
CCSprite *spriteB = [CCSprite spriteWithTexture:tex2d rect:barRect];
[spriteB setPosition:ccp(p2.x*PTM_RATIO, p2.y*PTM_RATIO)];
[barBatch addChild:spriteB];
b2->SetUserData(spriteB);
}
}
return self;
}
-(void)dealloc{
[super dealloc];
}
@end