物理引擎chipmunk

Chipmunk是一个支持iPhone平台游戏开发的2D物理引擎,提供2D physic和rigid body特性。Chipmunk特性是灵活和易用。
下面是关于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

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//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

posted on 2012-05-27 15:47  Hibernate4  阅读(489)  评论(0编辑  收藏  举报

导航