首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > Iphone >

应用Chipmunk物理引擎制作简单的iPhone游戏

2012-11-09 
使用Chipmunk物理引擎制作简单的iPhone游戏转载2.告诉chipmunk有哪些力施加在这些物体上,比如:重力,风力,

使用Chipmunk物理引擎制作简单的iPhone游戏
转载

2.告诉chipmunk有哪些力施加在这些物体上,比如:重力,风力,外星引力光束等。

应用Chipmunk物理引擎制作简单的iPhone游戏

3.不时让chipmunk更新物理模拟,比如:chipmunk可能会计算某个动物比刚才向下落了一点距离,一朵花在风中轻轻弯腰,等等。

应用Chipmunk物理引擎制作简单的iPhone游戏

4.根据物理模拟情况来更新“真实的”Cocos2d世界。比如:将动物,花朵或布偶的位置设置在chipmunk中虚拟物体所在的地方。

应用Chipmunk物理引擎制作简单的iPhone游戏

最最重要的是要记住,Chipmunk的世界和Cocos2D的世界是完全不同的。

物体和形状

在我们学习具体的编码之前,还有一个one more thing要注意的:Chipmunk物体和形状的概念。

一个Chipmunkbody代表了Chipmunk虚拟空间中的一个物体。它可能会包含一个或多个Chipmunk形状,从而定义了该物体的几何形状,如下图所示:

应用Chipmunk物理引擎制作简单的iPhone游戏

上图显示了我们将要用作猫咪睡床的Chipmunk body(物体)。它包含了三个Chipmunk形状,其中一个代表床的左侧,另一个代表床的右侧,而最后一个则代表了床的底部。

Chipmunk的body(物体)可以分为两类:

1.Dynamic bodies(动态物体):也即可以移动的物体,是我们在游戏开发中最经常用到的

2.Static bodies(静态物体):也即不能移动的物体,比如地面或其它不能移动的永久性夹具

对于每个Chipmunk物体,我们都可以指定其mass(质量)。一个形状的质量越大,则越难以移动。

当我们创建形状的时候,可以指定是boxes(盒子),circles(圆形),polygons(多边形),或segments(线段,也即带有厚度的直线)中的哪一种。对于每种形状,我们可以设置以下几个属性:

1.elasticity(弹性):代表了物体的弹性。如果设置为0,则完全没有弹性。如果设置为1,则属于完全弹性。如果设置为高于1的数值,则会比落下时弹的更高。

2.friction(摩擦力):代表一个物体表面的光滑程度。如果设置为0,则表示完全光滑。如果设置为1或更高,则物体表面非常粗糙。

Hello,Chipmunk!

看了这么多的理论知识,该实际操作一下了!

打开Xcode,点击File\New\New Project,选择iOS\cocos2d\cocos2d_chipmunk模板,点击Next。将项目命名为CatNap,并选择一个目录保存。

在继续之前,先让我们编译运行一下项目。此时会在模拟器中看到一个奇怪的光头哥:

应用Chipmunk物理引擎制作简单的iPhone游戏

光头哥到底是何方神圣,以及为何他的大名叫Grossini,我们不得而知。

这个小小的示例显示了Chipmunk的妙处。当我们触碰屏幕的时候,就会有更多的光头哥出现在屏幕中(是不是有点像黑客帝国的Smith探员?),他们之间会发生碰撞。

应用Chipmunk物理引擎制作简单的iPhone游戏

如果你是在设备上进行测试,那么这些光头哥们还会受到重力和加速计的影响。

如果感兴趣,大家可以自己好好研究下这个默认的示例代码。但这里我们打算从头开始。当然,不是因为我太变态,而是从零开始学会如何设置对今后的游戏开发会很有帮助。

但是,别担心,这些步骤没有那么复杂,现在放宽心,学着和下面的这哥们一样开心点:

应用Chipmunk物理引擎制作简单的iPhone游戏

好吧,哈哈大笑过后,让我们从头开始吧!

从零开始

点击File\New\New File,选择iOS\Cocoa Touch\Objective-C class,点击Next。选择Subclass of CCLayer,点击Next,命名为ActionLayer.m,然后点击Finish。

然后把本教程中用到的资源文件拖到Resources中。

打开ActionLayer.h,使用下面的代码替代其中的内容:

#import"cocos2d.h"

@interfaceActionLayer : CCLayer {

}

+(id)scene;

@end

这段代码只是创建了一个标准的CCLayer。后续我们会加入一些Chipmunk代码,但现在暂时留白。

切换到ActionLayer.m,使用以下代码替代其中的内容:

#import"ActionLayer.h"

@implementationActionLayer

+(id)scene{

CCScene *scene = [CCScenenode];

ActionLayer *layer = [ActionLayernode];

[sceneaddChild:layer];

return scene;

}

-(id)init{

if((self = [superinit])){

CGSizewinSize = [CCDirectorsharedDirector].winSize;

CCLabelBMFont *label = [CCLabelBMFontlabelWithString:@"Hello,Chipmunk!"fntFile:@"Arial.fnt"];

label.position = ccp(winSize.width/2,winSize.height/2);

[selfaddChild:label];

}

returnself;

}

@end

以上代码完成了以下工作:

1.使用scene类方法来创建并初始化CCScene,并将ActionLayer层添加为场景的子节点

2.init方法只是在屏幕中间放了一个标签,上面写着”hello,chipmunk!”

需要注意的是,这里用到了CCLabelBMFont,会需要提供一个字体文件。而这个字体文件可以使用Glyph Designer或Hiero来创建。

一切就绪后,打开AppDelegate.m,做出以下修改:

//在文件顶部添加以下代码:

#import"ActionLayer.h"

//在applicationDidFinishLaunching方法中修改最后一行代码:

[[CCDirectorsharedDirector] runWithScene: [ActionLayerscene]];

此时编译运行程序,会看到下面的画面:

应用Chipmunk物理引擎制作简单的iPhone游戏

到目前为止,我们所作的一切和Chipmunk还没有任何关系。接下来就好玩了!

创建一个基本的Chipmunk 场景

为了创建chipmunk场景,通常会通过以下步骤进行:

1.初始化Chipmunk

第一步就是初始化Chipmunk类库。在整个游戏中只需要进行一次就好了,所以通常我们可以在AppDelegate.m中的applicationDidFinishLaunching方法中进行。

让我们试试看。

打开AppDelegate.m,对代码做以下修改:

//在文件顶部添加以下代码:

#import"chipmunk.h"

//在applicationDidFinishLaunching方法的最后一行前添加以下代码:

cpInitChipmunk();

这就搞定了!以上操作可能是学习Chipmunk中最简单的一步了。需要注意的是以上函数只能调用一次!否则会遇到内存泄露的问题。

2.创建Chipmunk空间

接下来就是创建一个虚拟的Chipmunk空间,以便在其中运行物理模拟。

代表Chipmunk空间的对象是cpSpace,我们只需创建并初始化它即可。

切换到ActionLayer.h文件,并对代码做以下调整:

//在文件顶部添加:

#import"chipmunk.h"

//在声明部分添加:

cpSpace *space;

以上代码只是导入了chipmunk的头文件,并声明了一个实例变量,用于记录Chipmunk空间,因为后续我们将频繁用到它!

然后切换到ActionLayer.m,并对代码做以下调整:

-(void)createSpace{

space = cpSpaceNew();

space->gravity = ccp(0,-750);

cpSpaceResizeStaticHash(space, 400, 200);

cpSpaceResizeActiveHash(space, 200, 200);

}

//在init方法中注释那些用于创建标签的代码,并添加以下代码:

[selfcreateSpace];

如你所想,以上代码中最重要的就是createSpace这个方法。该方法中,第一行调用cpSpaceNew()为Chipmunk虚拟空间创建了一个新的对象,并将其存储在space这个实例变量中。

第二行代码将chipmunk世界的重力设置为x轴是0,而y轴则向下。至于具体的数值是多少,可能就看你自己的感觉了。

最后两行代码分别创建了Chipmunk的静态和活跃哈希散列。在Chipmunk中用它来加速碰撞检测。

这里简单的将Chipmunk空间划分成网格。如果两个物体在不同的网格中,Chipmunk会立即知道物体没有发生碰撞,从而无需进行下一步的数学运算。

因此以上代码的作用就是,设置网格的大小(第二个参数),以及网格的数量(第三个参数)。建议将网格的大小设置得比一般的物体要大,而网格的数量则至少是游戏中物体数量的十倍。

对于简单的小游戏来说当然无所谓,但是考虑到游戏的性能,必须把这个放在心上。

3.(可选)添加“地面”

对很多游戏来说,有必要添加一些物体到Chipmunk空间中来代表关卡中的地面。

例如,在我们要制作的这个小游戏中,我们创建了一个直线段,从左下角到右下角。这样就相当于设置了地面物体。今后所创建的物体落下来会撞到地面上,而不是跌出屏幕。

为了添加地面物体,需要对ActionLayer.m做以下调整:

//添加一个方法用于创建地面物体

-(void)createGround{

//1

CGSizewinSize = [CCDirectorsharedDirector].winSize;

CGPointlowerLeft = ccp(0,0);

CGPointlowerRight = ccp(winSize.width,0);

//2

cpBody *groundBody = cpBodyNewStatic();

//3

float radius = 10.0;

cpShape *groundShape = cpSegmentShapeNew(groundBody, lowerLeft, lowerRight, radius);

//4

groundShape->e = 0.5;// elasticity

groundShape->u = 1.0; //friction

//5

cpSpaceAddShape(space, groundShape);

}

在init方法中,在调用createSpace的方法后添加以下代码:

[selfcreateGround];

到目前为止,这是我们首次看到如何向Chipmunk的场景中添加物体。因此有必要一步步解释:

1.因为我们将把地面物体设置为从屏幕左下角到右下角的直线段,因此需要获得相关的坐标信息,以便后续使用。

2.创建一个新的Chipmunk物体,将其设置为静态物体,因为我们不希望地面可以移动。通常情况下我们需要将物体添加到场景中,但是对静态物体就没这个必要。事实上,如果特意添加,那么动态物体会直接跌出地面!

3.创建一个直线段形状,并将其关联到所创建的物体上。

4.设置弹性系数和摩擦力系数

5.将该形状添加到Chipmunk的虚拟空间中。

4.(可选)添加物体/形状

接下来,我们将创建一个辅助方法,从而把动态(可移动)的Chipmunk物体添加到场景中。并调用它来创建几个盒子。

在ActionLayer.m中添加以下方法:

-(void)createBoxAtLocation:(CGPoint)location{

floatboxSize = 60.0;

float mass = 1.0;

cpBody *body = cpBodyNew(mass, cpMomentForBox(mass, boxSize, boxSize));

body->p = location;

cpSpaceAddBody(space, body);

cpShape *shape = cpBoxShapeNew(body, boxSize, boxSize);

shape->e = 1.0;

shape->u = 1.0;

cpSpaceAddShape(space, shape);

}

然后在init方法中,在createGround这行代码的后面添加以下代码:

[selfcreateBoxAtLocation:ccp(100,100)];

[selfcreateBoxAtLocation:ccp(200,200)];

以上代码和第三步中的代码有一些类似之处,但有两个主要的区别。

第一个区别是,我们不是使用cpBodyNewStatic来创建物体,而是使用cpBodyNew来创建动态(可移动的)物体。

当使用cpBodyNew方法来创建物体的时候,需要传入两个参数,分别是质量和”moment of inertia(转动惯量,又称惯性矩)”。我们都知道质量是什么,那么什么是转动惯量?它的作用是决定物体旋转的难易程度。为了计算出惯性矩的参数,我们调用了一个辅助方法cpMomentForBox,它会根据形状类型和质量等来计算惯性矩。

第二个区别是,这里我们创建了一个盒子形状,而不是线段,所以调用了cpBoxShapeNew方法,而不是cgSegmentShapeNew。

在定义完这个辅助方法后,我们就可以在init方法中两次调用该方法,来创建两个不同的物体,并添加到屏幕中。

5.在update循环中设置步进同步

在创建完基本的Chipmunk世界后,我们需要每帧运行一次仿真。这里先说一种最简单的方法。

在init方法中,在createBoxAtLocation代码之后添加以下代码:

[selfscheduleUpdate];

然后再添加一个新的方法:

-(void)update:(ccTime)dt{

cpSpaceStep(space, dt);

}

以上方法只是简单的调用cpSpaceStep,从而让chipmunk每帧都运行一次物理仿真。

6.(可选)启用debug draw

完成了以上的步骤之中,虚拟世界实际上已经在运行了,但我们不会在屏幕上看到任何东西。

为了让chipmunk虚拟空间中的物体视觉化呈现出来,我们需要使用一些类将Chipmunk的物体绘制到屏幕之中。它会遍历每个Chipmunk物体,并调用OpenGL指令在屏幕中绘制该物体的形象。

让我们试一下吧。在为本教程所提供的资源中,有一个子目录”Helper Code”,其中有drawSpace.c和drawSpace.h两个文件,将这两个文件拖到项目中,确保选中”copy items into….”,然后点击Finish。

好吧,如果你这时手痒编译一下,会发现提示有200个错误,红色的error让人心惊胆战。

在Xcode4中,选中drawSpace.c,在工具栏View部分点击第三个tab选项卡,从而可以看到Utilities窗口。在该面板下面选择”Show the File Inspector”,然后在Identity and Type下面,会看到File Name和File Type。然后将File Type 中的Default-C Source 更改为Objective-C source,如下图所示。

应用Chipmunk物理引擎制作简单的iPhone游戏

然后切换到ActionLayer.m,在文件顶部添加以下代码:

#import"drawSpace.h"

然后在ActionLayer.m中添加以下方法

-(void)draw{

drawSpaceOptions options ={

0, //drawHas

0,//drawBBs,

1,//drawShapes

4.0,//collisionPointSize

4.0,//bodyPointSize,

2.0//lineThickness

};

drawSpace(space, &options);

}

那么首先就是告诉这个函数我们需要绘制什么。这里我们要求不要绘制chipmunk hash,不要绘制bounding boxes,但是要绘制形状本身。同时我们还设置了point size和限度厚度等。

编译运行项目,如果没有出现低级错误,应该会看到屏幕上有几个物体跌落到地面上,稍微弹起,最后停下来:

应用Chipmunk物理引擎制作简单的iPhone游戏

很不幸的是,一旦这些盒子落下来,就基本上停在那里不动了。如果我们可以用手拖动这些物体该多好!说干就干!

7.(可选)添加鼠标关节

在本教程提供的资源文件夹中找到Helper Code这个文件夹,然后把cpMouse.c和cpMouse.h拖到项目中,确保选中”copy items into….”,然后点击Finish。

然后和刚才一样把file type更改为Objective-C source。

接下来打开ActionLayer.h,并对代码做出以下修改:

在文件的顶部添加以下代码:

#import"cpMouse.h"

然后在类声明中添加以下代码:

cpMouse *mouse;

以上代码导入了头文件,并定义了一个实例变量用于保存鼠标节点。

接下来切换到ActionLayer.m,并对代码做以下修改:

在init方法中,在调用scheduleUpdate方法后添加以下代码:

mouse = cpMouseNew(space);

self.isTouchEnabled = YES;

接下来添加以下几个方法:

-(void)registerWithTouchDispatcher{

[[CCTouchDispatchersharedDispatcher] addTargetedDelegate:selfpriority:0swallowsTouches:YES];

}

-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{

CGPointtouchLocation = [selfconvertTouchToNodeSpace:touch];

cpMouseGrab(mouse, touchLocation, false);

returnYES;

}

-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{

CGPointtouchLocation = [selfconvertTouchToNodeSpace:touch];

cpMouseMove(mouse, touchLocation);

}

-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{

cpMouseRelease(mouse);

}

-(void)dealloc{

cpMouseFree(mouse);

cpSpaceFree(space);

[superdealloc];

}

以上代码应该不难看懂,首先我们启用了对触摸事件的处理机制,然后调用了几个方法来初始化鼠标节点,当触摸开始时抓住东西,当触摸点移动时也移动这个东西,当触摸结束或取消时释放鼠标节点。

编译运行代码,此时就可以移动屏幕中的物体了。

使用精灵图片来装饰物体

现在我们有了个基本的Chipmunk场景,可以在其中加入一些艺术的成分了。

首先把项目资源中的SpriteSheets文件夹拖到Xcode项目中,确保选中”copy items into …”,点击Finish。

Sprite sheet中的图片如下图所示:

应用Chipmunk物理引擎制作简单的iPhone游戏

首先我们需要创建几个Chipmunk物体和盒子形状来代表猫咪,橙色砖块和黑色砖块。

那么我们如何将Chipmunk物体和Cocos2d精灵绑定在一起呢?我们只需要在游戏每一帧将精灵的坐标/角度和Chipmunk物体的坐标/角度保持一致即可。

那么我们如何知道精灵对应的是哪个chipmunk物体呢?有几种方法可供使用,我们采取的是下面这种:

1.创建一个CCSprite的子类(CPSprite),代表Chipmunk精灵子类

2.让这个子类来保存相关的信息,用于判断精灵所对应的Chipmunk物体

3.在CPSprite类中创建一个update方法,并将其位置/角度设置为和Chipmunk物体的位置/角度相同。

4.对于层中的每个CPSprite,逐帧调用update方法。

理论知识具备了,让我们动手试试吧。

点击File\New File,选择iOS\Cocoa Touch\Objective-C class,点击Next。选择Subclass of CCSprite,点击Next,将其命名为CPSprite.m,然后点击Save。

打开CPSprite.h,使用以下的代码替代其中的内容:

#import"cocos2d.h"

#import"chipmunk.h"

@interfaceCPSprite:CCSprite{

cpBody *body;

cpShape *shape;

cpSpace *space;

BOOLcanBeDestroyed;

}

@property(assign)cpBody *body;

-(id)initWithSpace:(cpSpace *)theSpace location:(CGPoint)location spriteFrameName:(NSString *)spriteFrameName;

-(void)update;

-(void)createBodyAtLocation:(CGPoint)location;

-(void)destroy;

@end

在以上的代码中,首先我们导入了cocos2d和chipmunk的头文件。然后我们创建了几个实例变量,用于记录精灵所对应的chipmunk物体,以及chipmunk空间和形状。同时我们定义了一个BOOL变量,用于记录当玩家触摸该物体时能否将其销毁的状态。

接下来定义了该对象的初始化方法,并预先定义了一个方法,根据精灵所对应的Chipmunk物体来更新精灵的位置/旋转等信息。我们还定义了一个方法,可以使用精灵的大小来创建Chipmunk物体/形状。最后定义了一个方法用于销毁chipmunk物体/形状及其精灵。

完成了以上的定义,现在让我们来实现这些方法。

切换到CPSprite.m文件,并使用以下的代码来替代其中的内容;

#import"CPSprite.h"

@implementationCPSprite

@synthesize body;

-(void)update{

self.position = body->p;

self.rotation = CC_RADIANS_TO_DEGREES(-1* body->a);

}

-(void)createBodyAtLocation:(CGPoint)location{

float mass = 1.0;

body = cpBodyNew(mass, cpMomentForBox(mass, self.contentSize.width,self.contentSize.height));

body->p = location;

body->data = self;

cpSpaceAddBody(space, body);

shape = cpBoxShapeNew(body, self.contentSize.width, self.contentSize.height);

shape->e = 0.3;

shape->u = 1.0;

shape->data = self;

cpSpaceAddShape(space, shape);

}

-(id)initWithSpace:(cpSpace *)theSpace location:(CGPoint)location spriteFrameName:(NSString *)spriteFrameName{

if((self = [superinitWithSpriteFrameName:spriteFrameName])){

space = theSpace;

[selfcreateBodyAtLocation:location];

canBeDestroyed =YES;

}

returnself;

}

-(void)destroy{

if(!canBeDestroyed) return;

cpSpaceRemoveBody(space, body);

cpSpaceRemoveShape(space, shape);

[selfremoveFromParentAndCleanup:YES];

}

@end

在以上代码中,update方法最简单,只是将精灵的位置设置为和chipmunk物体的位置相同。然后把旋转角度也设为相同,但经过了一个小小的转换(因为cocos2d使用角度而不是弧度,而且方向上使用顺时针而非逆时针)。

createBodyAtLocation方法和之前实现的createBoxAtLocation方法非常类似。首先我们创建了一个物体,然后创建了物体上的盒子形状。唯一的区别在于,这里将盒子的大小设置为与精灵的大小相同(使用contentSize)。

此外,还有一个区别是,将物体的数据指针和形状都设置为和自身(精灵)相同。这样以来,当我们使用chipmunk物体或形状的时候,就可以通过数据指针来获取所对应的精灵。

initWithSpace方法首先调用了其父类(CCSprite)的initWithSpriteFrameName方法,然后设置了实例变量,并调用创建物体的方法。

Destroy方法也比较简单了,调用了cpSpaceRemoveBody和cpSpaceRemoveShape方法来销毁物体和形状,同时调用removeFromParentAndCleanup方法来销毁所对应的精灵。

现在我们的类已经准备好了,接下来就可以使用它将猫咪和砖块精灵添加到场景中。但为了让代码今后具有可扩展性,我们可以创建以下的子类。

Sprites类的子类

创建子类很简单,让我们快速过一遍。打开File\New\New File,选择iOS\Cocoa Touch\Objective-C class,点击Next。在Subclass of的后面输入CPSprite ,点击Next,命名为CatSprite.m,点击Save。

打开CatSprite.h,使用以下代码替代其中的内容:

#import"CPSprite.h"

@interfaceCatSprite : CPSprite{

}

-(id)initWithSpace:(cpSpace *)theSpace location:(CGPoint)location;

@end

然后打开CatSprite.m,使用以下代码替代其中的内容:

#import"CatSprite.h"

@implementationCatSprite

-(id)initWithSpace:(cpSpace *)theSpace location:(CGPoint)location{

if((self = [superinitWithSpace:theSpacelocation:locationspriteFrameName:@"cat_sleepy.png"])){

canBeDestroyed = NO;

}

returnself;

}

@end

以上代码没什么特别的,我们使用一个精灵图片来初始化了猫咪精灵,并设置canBeDestroyed属性为NO.

然后再次点击File\New\New File,选择iOS\Cocoa Touch\Objective-C class,点击Next。在Subclass of 之后输入CPSprite,点击Next,命名为SmallBlockSprite.m,并点击Save。

打开SmallBlockSprite.h文件,并使用以下代码替代其中的内容:

#import"CPSprite.h"

@interfaceSmallBlockSprite : CPSprite{

}

-(id)initWithSpace:(cpSpace *)theSpace location:(CGPoint)location;

@end

然后切换到SmallBlockSprite.m文件,使用以下代码替代其中的内容:

#import"SmallBlockSprite.h"

@implementationSmallBlockSprite

-(id)initWithSpace:(cpSpace *)theSpace location:(CGPoint)location{

if((self = [superinitWithSpace:theSpacelocation:locationspriteFrameName:@"block_1.png"])){

}

returnself;

}

@end

上面的代码就不解释了,几乎完全相同,只是替换了精灵图片。

最后还需要一个文件。再次点击File\New\New File,选择iOS\Cocoa Touch\Objective-C class,点击Next。在Subclass of部分输入CPSprite,点击Next,命名为LargeBlockSprite.m,然后点击Save。

使用以下代码替代LargeBlockSprite.h中的内容:

#import"CPSprite.h"

@interfaceLargeBlockSprite : CPSprite{

}

-(id)initWithSpace:(cpSpace *)theSpace location:(CGPoint)location;

@end

使用以下代码替代LargeBlockSprite.m中的内容:

#import"LargeBlockSprite.h"

@implementationLargeBlockSprite

-(id)initWithSpace:(cpSpace *)theSpace location:(CGPoint)location{

if((self = [superinitWithSpace:theSpacelocation:locationspriteFrameName:@"block_2.png"])){

}

returnself;

}

@end

好了,三个精灵类都有了,现在可以使用它们来创建几个实例,并将其添加到层中了。

切换到ActionLayer.h文件,并对代码做出以下修改:

//在文件的顶部添加:

#import"CatSprite.h"

#import"SmallBlockSprite.h"

#import"LargeBlockSprite.h"

在类的声明部分添加:

CCSpriteBatchNode *batchNode;

CatSprite *cat;

以上代码导入了要使用的精灵类头文件,并添加了一个实例变量用于保存精灵表单,同时定义了一个猫咪精灵对象。

接下来让我们切换到ActionLayer.m,并在init方法的最后添加以下代码(在isTouchEnabled = YES之后)

[[CCSpriteFrameCachesharedSpriteFrameCache]addSpriteFramesWithFile:@"catnap.plist"];

batchNode = [CCSpriteBatchNodebatchNodeWithFile:@"catnap.png"];

[selfaddChild:batchNode];

cat = [[[CatSpritealloc]initWithSpace:spacelocation:ccp(245,217)] autorelease];

[batchNodeaddChild:cat];

SmallBlockSprite *block1a = [[[SmallBlockSpritealloc]initWithSpace:spacelocation:ccp(213,47)]autorelease];

[batchNodeaddChild:block1a];

SmallBlockSprite *block1b = [[[SmallBlockSpritealloc]initWithSpace:spacelocation:ccp(272,59)]autorelease];

[batchNodeaddChild:block1b];

SmallBlockSprite *block1c = [[[SmallBlockSpritealloc]initWithSpace:spacelocation:ccp(267,158)] autorelease];

[batchNodeaddChild:block1c];

LargeBlockSprite *block2a = [[[LargeBlockSpritealloc]initWithSpace:spacelocation:ccp(270,102)]autorelease];

[batchNodeaddChild:block2a];

LargeBlockSprite *block2b = [[[LargeBlockSpritealloc]initWithSpace:spacelocation:ccp(223,139)]autorelease];

[batchNodeaddChild:block2b];

LargeBlockSprite *block2c = [[[LargeBlockSpritealloc]initWithSpace:spacelocation:ccp(214,85)]autorelease];

[batchNodeaddChild:block2c];

以上代码创建了一个精灵表单,并将精灵帧加载入缓存之中。然后我们使用刚才定义的子类来创建了几个精灵。

当然,为了让精灵和Chipmunk物体的状态符合,我们需要在每帧手动调用update方法。在update方法的最后添加以下代码:

for(CPSprite *sprite inbatchNode.children){

[spriteupdate];

}

编译运行项目,可以看到你那可爱的猫咪坐在几个砖头上!


音效和破坏!

到现在为止,除了使用鼠标关节来移动这些砖头,我们还没法通过触摸来摧毁它们。此外,既然是游戏,不来点音效也说不过去!当然,我们还得需要一个背景。

将Sounds文件夹拖到Xcode的Resources中,确保选中”copy items into….”,点击Finish。

这个确保选中已经重复了一万遍,但还得重复,直到彻底形成了条件反射为止。

接下来打开ActionLayer.m文件,并注释ccTouchBegan/Moved/Ended/Cancelled等方法中和cpMouseGrap,cpMouseMove,cpMouseRelease这些鼠标关节相关的代码,因为后续将不再使用鼠标关节。

完成注释之后,对ActionLayer.m的代码做出以下调整:

//在文件的顶部添加:

#import"SimpleAudioEngine.h"

然后在ccTouchBegan方法中,在刚才注释的cpMouseGrab的代码之后添加以下代码:

cpShape *shape = cpSpacePointQueryFirst(space, touchLocation, GRABABLE_MASK_BIT, 0);

if(shape){

CPSprite *sprite = (CPSprite *)shape->data;

[spritedestroy];

[[SimpleAudioEnginesharedEngine] playEffect:@"poof.wav"];

}

以上代码的作用是,使用chipmunk内置的cpSpacePointQueryFirst这个辅助方法来检查是否形状位于触摸点。然后使用数据指针来获取和该形状相关的CPSprite精灵对象。

同时我们还加了一行代码用来播放音效。

接着让我们再来添加背景音乐和背景。

在init方法的底部添加以下代码:

CCSprite *background = [CCSpritespriteWithFile:@"catnap_bg.png"];

background.anchorPoint = CGPointZero;

[selfaddChild:backgroundz:-1];

[[SimpleAudioEnginesharedEngine] playBackgroundMusic:@"TeaRoots.mp3"loop:YES];

[[SimpleAudioEnginesharedEngine] preloadEffect:@"sleep.wav"];

  [[SimpleAudioEnginesharedEngine] preloadEffect:@"wake.wav"];

[[SimpleAudioEnginesharedEngine] preloadEffect:@"poof.wav"];

编译运行游戏,一个简单的小游戏就成型了。

应用Chipmunk物理引擎制作简单的iPhone游戏

多边形

Cat Nap小游戏看起来还不错,但一个很大的问题就是猫咪和它的形状有点不太匹配。我们使用了最简单的盒子来制作其形状,但这样就留下了大量的空白区域,显得很不协调。

在chipmunk中,除了使用最简单的盒子,圆形或线段,还可以使用多边形。我们只需告诉chipmunk多边形中每个顶点的位置即可!

但是如何获取这些顶点的位置呢?比较傻的方法是在图片编辑器中手动获取,但我们也可以使用第三方的工具,比如Johannes Fahrenkrug开发的Vertex Helper(https://github.com/jfahrenkrug/VertexHelper)。我们既可以直接通过github页面下载,也可以在mac app store中购买。

要注意的是,通过github下载的是项目文档,因此需要编译运行,画面如下;

应用Chipmunk物理引擎制作简单的iPhone游戏

将cat_sleepy.png拖到Vertex Helper中,然后做出以下设置:

1.将Rows/Cols设置为1/1

2.将Type设置为chipmunk

3.将style设置为Initialization

4.在顶部工具栏中点击Edit Mode。

然后点击cat四周来定义所需的顶点。有几点需要特别注意的事项:

1.必须顺时针点选顶点。

2.顶点数目尽量少(这里只定义了4个)

3.无需手动连接起点和终点,vertex helper会自动帮你完成这一工作。(并使用一个灰线标示)

4.多边形必须是凸形的。也就是说不能有大于180度的内角,如下图所示:

应用Chipmunk物理引擎制作简单的iPhone游戏

下图是我给猫咪定义的顶点(可能你所设置的顶点会有所区别)

应用Chipmunk物理引擎制作简单的iPhone游戏

完成之后,将上面的代码拷贝,并放置到CatSprite.m的一个新方法中。

仔细观察会发现,创造多边形和盒子形状差不多,除了我们使用cpMomentForPoly和cpPolyShapeNew这两个新方法。

编译运行游戏,似乎像那么回事了,但是形状还是太大了!

应用Chipmunk物理引擎制作简单的iPhone游戏

这是因为我们使用的是retina尺寸的图片,因此在像素值上是所需的两倍。所以只需在刚才的顶点坐标中除以2即可,如下:

intnum =4;

CGPointverts[] = {

cpv(-31.5f/2, 69.5f/2),

cpv(41.5f/2,66.5f/2),

cpv(40.5f/2,-69.5f/2),

cpv(-55.5f/2,-70.5f/2)

};

编译运行游戏,现在一切正常了!

应用Chipmunk物理引擎制作简单的iPhone游戏