//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