使用cocos2d-iphone實現割繩子游戲
參考資料: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_RATIO32
// 繩子小段圖片的高度和寬度~
#define ROPE_ITEM_WIDTH16.0f
#define ROPE_ITEM_HEIGHT4.0f
// 繩子 bar 圖片的高度和寬度~
#define ROPE_BAR_DIAMETER8.0f
// 1.是否在繩子的兩個連接點之間啟用 距離限定關節~
#define B2_ROPE_JOINT_ENABLED NO
// 2.bar body到底是連接在繩子最外側小節的 body 上,還是連接在 comp 元素的 body 上~
#define CONNECTED_WITH_COMP_BODY0 // 緊隨comp
#define CONNECTED_WITH_ROPE_BODY1 // 緊隨繩子最外側小節(因為會隨著繩子抖動,所以效果感覺不怎么樣)~
#define ROPE_BAR_CONNECT_MODE0
/**
* 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];
[itemSpritesetPosition:ccp(midPoint.x*PTM_RATIO, midPoint.y*PTM_RATIO)];
[itemSpritesetRotation:-1 *CC_RADIANS_TO_DEGREES(stickAngle)];
[itemBatchaddChild: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];
[spriteAsetPosition:ccp(p1.x*PTM_RATIO, p1.y*PTM_RATIO)];
[barBatchaddChild:spriteA];
b1->SetUserData(spriteA);
CCSprite *spriteB = [CCSprite spriteWithTexture:tex2d rect:barRect];
[spriteBsetPosition:ccp(p2.x*PTM_RATIO, p2.y*PTM_RATIO)];
[barBatchaddChild:spriteB];
b2->SetUserData(spriteB);
}
}
return self;
}
-(void)dealloc{
[super dealloc];
}
@end