Chipmunk:一個支持iPhone平臺游戲開發的2D物理引擎

jopen 12年前發布 | 30K 次閱讀 iPhone 游戲 游戲開發

Chipmunk是一個支持iPhone平臺游戲開發的2D物理引擎,提供2D physic和rigid body特性。Chipmunk特性是靈活和易用。
Chipmunk:一個支持iPhone平臺游戲開發的2D物理引擎
下面是關于Chipmunk的簡介和一個簡單的教程

  1. Introduction
  2. Setup
  3. Basic Concepts
  4. Initializing Chipmunk
  5. Defining the ball's body and shapes
  6. Tracking the ball's movements
  7. Defining the floor's body and shapes
  8. Evaluating the results & Conclusion
  9. Download the complete project
  10. </ol>

    chipmunk本是一個獨立的開源項目,用純c編寫.cocos2d同時整合了chipmunk和box2d兩個物理引擎.

    相比之下,chipmunk更輕量易用,但是相關的文檔很少.
    box2d還沒開始學習,暫時不敢妄言.
    chipmunk的官方地址:http://code.google.com/p/chipmunk-physics/ 
    之所以在對cocos2d還一知半解的時候就開始學chipmunk,是因為公司最近給的項目,需要用到物理引擎.被人趕著學習也是件好事,因為俺的確是 個懶人.

    [一些基本的概念]
    space:這是一個虛擬出物理空間,所有剛體(body),形狀(shape),連接(joint)都在這個空間里發生物理事件.
    body:剛體.在物理上,剛體是被抽象為一個物理例子實體單元去看待的,但是它不同與真正的粒子,因為剛體可以有形狀(shape)并且可以旋轉.另 外,剛體上附帶著最基本的物理屬性:
    shape:剛體的形狀.剛體間發生碰撞,是以剛體的形狀外延為碰撞臨界的.一個剛體可以附加多個shape形成復雜的剛體外形.同屬于一個剛體的形狀之 間不會發生碰撞.
    joint:連接,兩個以上的剛體連接在一起,比如一條鎖鏈,是由多個剛體的鐵環組成的.具體joint的使用,暫時還沒深入研究.

    [Demo HelloChipmunk]
    利用cocos2d的項目模板,可以方便的建立一個支持cocos2d和chipmunk的項目.
    與HelloCocos2d類似的,初始項目還是建立了一個AppDelegate和一個CCLayer的實現(HelloChipmunkScene, 這個模板命名我覺得有點問題,其實這個類是繼承自CCLayer的,可是卻以Scene冠名).
    AppDelegate與上一個項目HelloCocos2d的完全一樣,不再做重復分析了.
    直接看關鍵的HelloChipmunkScene.

    //HelloWorldScene.m

    import "HelloWorldScene.h"

    enum { //設置一個SpriteSheet(精靈表)的標記 //用于方便的從CCLayer中找到SpriteSheet kTagAtlasSpriteSheet = 1, };

    static void eachShape(void ptr, void unused) { //使用計算后的shape狀態來調整用于顯示的sprite的狀態. cpShape shape = (cpShape) ptr; CCSprite sprite = shape->data; if( sprite ) { cpBody body = shape->body; //提示: cocos2d 和 chipmunk使用相同的struct存儲position信息. //chipmunk使用cpVect,cocos2d使用CGPoint,但是事實上他們都是 CGPoint [sprite setPosition: body->p];
    [sprite setRotation: (float) CC_RADIANS_TO_DEGREES( -body->a )]; } }

    @implementation HelloWorld

    //返回初始化好的場景. +(id) scene { CCScene scene = [CCScene node]; HelloWorld layer = [HelloWorld node]; [scene addChild: layer]; return scene; }

    //這是相應屏幕點擊后添加精靈的代碼 -(void) addNewSpriteX: (float)x y:(float)y { int posx, posy; //從layer中找回CCSpriteSheet,它是一組精靈的集合 CCSpriteSheet sheet = (CCSpriteSheet) [self getChildByTag:kTagAtlasSpriteSheet];

    //計算精靈的在圖片中的初始位置,也就是從圖片中截取一格用于顯示
    posx = (CCRANDOM_0_1() * 200);
    posy = (CCRANDOM_0_1() * 200);
    
    posx = (posx % 4) * 85;
    posy = (posy % 3) * 121;
    //從精靈表中根據位置信息創建精靈
    CCSprite *sprite = [CCSprite spriteWithSpriteSheet:sheet rect:CGRectMake(posx, posy, 85, 121)];
    [sheet addChild: sprite];
    //設置精靈在屏幕的顯示的位置
    sprite.position = ccp(x,y);
    
    //設置物體在四個方向上的慣性力矩向量
    int num = 4;
    CGPoint verts[] = {
        ccp(-24,-54),
        ccp(-24, 54),
        ccp( 24, 54),
        ccp( 24,-54),
    };
    //cpMomentForPoly函數可以計算物體的慣性力矩
    //第一個參數m是物體的質量
    //第二個參數是慣性力矩向量的個數
    //第三個參數是慣性力矩向量數組
    //第四個參數是物體的重心
    cpBody *body = cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts, CGPointZero));
    
    //設置物體在物理空間的位置
    body->p = ccp(x, y);
    //將物體添加到空間內,這樣物體才能受到空間重力的影響
    cpSpaceAddBody(space, body);
    //給物體定義形狀
    cpShape* shape = cpPolyShapeNew(body, num, verts, CGPointZero);
    //e系數為碰撞回饋彈性系數,也就是物體碰撞到這個形狀的反彈力度
    //u系數為物體間碰撞時的摩擦系數.
    shape->e = 0.5f; shape->u = 0.5f;
    //將顯示用的精靈作為數據附加到shape上備用
    shape->data = sprite;
    //將形狀作為活動物體添加到空間里
    cpSpaceAddShape(space, shape);
    
    

    }

    -(id) init { if( (self=[super init])) {

        //設置layer是否支持觸摸,這個Demo中支持觸摸是必須的.
        self.isTouchEnabled = YES;
        //設置layer是否支持重力計感應,打開重力感應支持,會得到 accelerometer:didAccelerate:得回調
        self.isAccelerometerEnabled = YES;
    
        //獲得OpenGL View的尺寸,雖然這里是winSize,其實并不一定是整個windows的size.
        CGSize wins = [[CCDirector sharedDirector] winSize];
        //初始化chipmunk引擎,這是必須進行的工作.個人覺得,放在AppDelegate里做 這個動作比較好一些
        cpInitChipmunk();
        //設置一個靜態剛體,這個剛體不會被添加到物理空間里,因為添加入空間的剛體會受到重力的影響.
        //body對應的shape要添加入空間.作為其他剛體的活動限制空間.
        //簡單地說,就是讓這個物理空間里的物體可以與四壁發生碰撞,而不是飛出去.
        //第一個參數m是物體的質量
        //第二個參數i是物體的慣性力矩,指物體受到碰撞時是否容易圍繞中心軸轉動,慣性力矩越大越不容 易轉動
        cpBody *staticBody = cpBodyNew(INFINITY, INFINITY);
        //初始化物理空間.
        space = cpSpaceNew();
        //定義空間對形狀碰撞管理的哈希表屬性,這是一件復雜難懂的事,關于這東西,我會另外開一篇筆記 學習.
        cpSpaceResizeStaticHash(space, 400.0f, 40);
        cpSpaceResizeActiveHash(space, 100, 600);
    
        //設置空間的重力向量.以笛卡爾坐標系作為向量坐標系(Y軸與屏幕坐標系相反).
        //第一個參數為x軸值,正數趨向右側
        //第二個參數為y軸值,正數趨向向上
        space->gravity = ccp(0, -100);
    
        //設置空間內剛體間聯系的迭代計算器個數和彈性關系迭代計算器個數.
        //chipmunk使用迭代計算器計算出空間內物體的受力關系.
        //它建立一個大列表存放物體間所有的碰撞,連接等相互影響關系.根據實際情況傳遞某些相互作用.
        //傳遞相互作用的數取決于迭代器的個數,每次迭代都使計算結果更精確.
        //如果進行了過多的迭代,雖然物理影響效果會更好,但是這也會消耗過多的cpu處理時間.
        //如果進行的迭代太少,物理模擬效果會不精確,或者使本該靜止的物體沒能靜止下來.
        //使用迭代器的個數在于平衡CPU性能和物理模擬精確度之間權衡.
        space->elasticIterations = space->iterations;
    
        //定義靜態剛體的形狀,這個例子中,是定義可視范圍的四壁為形狀.使物體可以與四壁發生碰撞.
        cpShape *shape;
    
        //定義底部的形狀為一條橫線.
        shape = cpSegmentShapeNew(staticBody, ccp(0,0), ccp(wins.width,0), 0.0f);
        //定義形狀的相關系數
        //e系數為碰撞回饋彈性系數,也就是物體碰撞到這個形狀的反彈力度
        shape->e = 1.0f; 
        //u系數為物體間碰撞時的摩擦系數.
        shape->u = 1.0f;
        //將形狀作為靜態形狀添加到空間內.
        cpSpaceAddStaticShape(space, shape);
    
        // top
        shape = cpSegmentShapeNew(staticBody, ccp(0,wins.height), ccp(wins.width,wins.height), 0.0f);
        shape->e = 1.0f; shape->u = 1.0f;
        cpSpaceAddStaticShape(space, shape);
    
        // left
        shape = cpSegmentShapeNew(staticBody, ccp(0,0), ccp(0,wins.height), 0.0f);
        shape->e = 1.0f; shape->u = 1.0f;
        cpSpaceAddStaticShape(space, shape);
    
        // right
        shape = cpSegmentShapeNew(staticBody, ccp(wins.width,0), ccp(wins.width,wins.height), 0.0f);
        shape->e = 1.0f; shape->u = 1.0f;
        cpSpaceAddStaticShape(space, shape);
    
        //建立一個精靈表
        CCSpriteSheet *sheet = [CCSpriteSheet spriteSheetWithFile:@"grossini_dance_atlas.png"capacity:100];
        //把精靈表添加到layer里
        [self addChild:sheet z:0 tag:kTagAtlasSpriteSheet];
        //調用自定義的方法,添加第一個精靈
        [self addNewSpriteX: 200 y:200];
        //設置layer的播放時序為自定義的step方法
        [self schedule: @selector(step:)];
    }
    
    return self;
    

    } //錯誤處理,這方面,今后再做研究 -(void) onEnter { [super onEnter];
    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 60)]; }

    //cocos2d刷新顯示前的調度函數,更新物體的狀態用于顯示在屏幕上. -(void) step: (ccTime) delta { //為什么要設置steps=2并且執行兩次cpSpaceStep?不解... int steps = 2; CGFloat dt = delta/(CGFloat)steps;
    for(int i=0; i<steps; i++){ //進行物理空間的模擬計算 cpSpaceStep(space, dt); } //計算物體在空間內的狀態,回調eachShape函數調整顯示. cpSpaceHashEach(space->activeShapes, &eachShape, nil); cpSpaceHashEach(space->staticShapes, &eachShape, nil); }

    //接收layer上的touch ended事件

    • (void)ccTouchesEnded:(NSSet )touches withEvent:(UIEvent )event { for( UITouch *touch in touches ) {

        CGPoint location = [touch locationInView: [touch view]];
      
        location = [[CCDirector sharedDirector] convertToGL: location];
        //根據touch位置,添加精靈
        [self addNewSpriteX: location.x y:location.y];
      

      } }

    //處理重力計回饋的數值

    • (void)accelerometer:(UIAccelerometer)accelerometer didAccelerate:(UIAcceleration)acceleration {
      static float prevX=0, prevY=0;

    define kFilterFactor 0.05f

    float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
    float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
    
    prevX = accelX;
    prevY = accelY;
    
    CGPoint v = ccp( accelX, accelY);
    
    //轉換重力計得到的向量為space的重力向量,以手機的方向模擬重力影響.
    space->gravity = ccpMult(v, 400);
    

    } @end</pre>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!