手把手教你制作一款Box2D小游戏(一)

原创 2017年01月03日 21:32:50

这次我们利用Box2D物理引擎来制作一款类似于下楼梯的小游戏,关于Box2D物理引擎的介绍,可以参考我博客中的Box2D v2.3.0 用户指南进行学习。

我们先来看一下最终效果:


游戏的组成元素有:

滚动背景(左右两个宽度为15,与屏幕等高的部分),上升的台阶(或者说是楼梯),滚动的球,分数。

滚动背景的制作我们不做介绍了,制作方法可以参考FlaggyBird的滚动背景的制作方法(手把手教你制作那个风靡的flappy bird小游戏),背景大部分面积填充的是深蓝色(静止),用平铺的方法平铺两个小的纹理:

 

我们先来介绍使用Box2D来创建台阶:

首先创建StepBlock类,注意我们在创建类之后默认会创建出.h和.m文件,因为Box2D是基于C++的框架,所以需要我们手动将.m文件的扩展名更改为.mm。StepBlock类声明如下:

#import "CCNode.h"

#import "CCPhysicsSprite.h"

#import "Box2D.h"

#import "TagDefinitions.h"

 

@interface StepBlock : CCNode{

    float screenWidth;

    float screenHeight;

    CCPhysicsSprite* shape;

    b2Body* body;

    int tagSave;

}

 

- (id)initWithY:(float) y;

- (id)initWithPos:(CGPoint) pos andType:(StepBlockTypes)type;

- (void)resetWithY:(float) yPos;

- (BOOL)moveUp:(float) length lowestStepY:(float) lowestStepY;

- (void)resetWithPos:(CGPoint) pos andType:(StepBlockTypes) type;

 

@end

类中我们定义了screenHeight和screenWidth用来存放屏幕长宽,避免反复获取影响效率。同时我们定义了shape,来存储StepBlock(后面简称Block)的图形(CCPhysicsSprite和CCSprite用法相同,只不过可以指定b2Body作为其成员,这里我们也可以使用CCSprite,不影响效果)。另外又定义了一个body对象,用来存储StepBlock的刚体对象。tagSave我们后面会用到,这里先忽略这个成员。

另外我们定义了一系列的方法,initWithY用来初始化StepBlock,所以这个初始化方法只需要指定y值即可。initWithPos:andType:方法用一个确定的初始位置和Block类型来初始化,这里Block类型我们接下来会介绍,resetWithY方法重置Block的类型和位置,moveUp用来将Block上移,resetWithPos:andType:用确定的位置和Block类型来重置Block。我们看到头文件中的“TagDefinitions.h”,这个头文件中包含了下面的枚举定义和预编译指令:

#ifndef Steps_TagDefinitions_h

#define Steps_TagDefinitions_h

 

#define PTM_RATIO 100

 

typedef enum{

    StepBlockNormal = 0,

    StepBlockConcave,

    StepBlockConvex,

    StepBlockHighLow,

    StepBlockLowHigh,

    StepBlockRough,

    StepBlockSlideLeft,

    StepBlockSlideRight

} StepBlockTypes;

 

typedef enum {

    ForceNone,

    ForceLeft,

    ForceRight

} ForceType;

 

#endif

这里PTM_RATIO定义了Box2D中“米”和“像素”的转化比例,StepBlockTypes定义了Block类型的枚举,ForceType定义了对小球的施力方向枚举,我们后面会用到。

下面是用到的不同类型Block的贴图:

step_high_low.png


step_low_high.png


step_concave.png


step_normal.png


step_rough.png


step_slide_left.png


step_slide_right.png


step_convex.png


将这些图片导入到工程资源中。接着我们来看StepBlock.mm中的类定义。首先定义一个静态数组,用来存放Block的类型名:

static NSArray* stepBlockTypes = [NSArrayarrayWithObjects:@"step_normal", @"step_concave",@"step_convex", @"step_high_low",@"step_low_high", @"step_rough",@"step_slide_left", @"step_slide_right", nil];

接着定义下面的方法,用来设置Block的精灵对象:

- (void)initShape:(NSString*) shapeName{

    shape = [CCSprite spriteWithFile:shapeName];

 

    [self addChild:shape];

}

下面的方法用来初始化screenHeight和screenWidth属性:

- (void)initProps{

    //screen dimensions

    CGSize screenSize = [[CCDirector sharedDirector]winSize];

    screenHeight = screenSize.height;

    screenWidth = screenSize.width;

}

接着我们定义resetWithPos:andType:方法:

- (void)resetWithPos:(CGPoint) pos andType:(StepBlockTypes) type{

    float xPos = pos.x;

    xPos = max(65,xPos);                 // 15(wall width) + 50(half the step block width)

    xPos = min(xPos, screenWidth -65);    // 65 = 100(step block width) + 15(wall width) - 50(halfthe step block width)

   

    if (body) {

        [[GameLayer gameLayer]sharedWorld]->DestroyBody(body);

    }

    b2BodyDef bodyDef;

    bodyDef.type = b2_staticBody;

    bodyDef.userData = [NSNumbernumberWithInteger:tagSave];

    body = [[GameLayer gameLayer]sharedWorld]->CreateBody(&bodyDef);

   

    [self generateShapeAndFixture:ccp(xPos, pos.y)withType:type];

}

在计算xPos的时候我们进行了一些判断,防止Block的位置超出左右的墙壁。并且由于我们在reset的时候会随机更换Block的类型,所以会先将body清除掉(destroy),但是第一次初始化的时候body并没有定义,所以添加了if语句进行了判断。这里用到的GameLayer类是游戏的主场景(CCScene)的层(CCLayer),我们下面会介绍,sharedWorld是我们这个游戏里的世界对象。在清除了body对象后,我们用b2BodyDef来定义body,定义的类型是静态物体(staticBody),因为我们不需要Block像小球一样受力运动,他们只需要匀速上升即可。接着我们定义了body的userData,将tagSave存储进去,这个值在我们后面记分(score)的时候会用到。接着使用CreateBody来创建刚体,然后调用generateShapeAndFixture:withType:方法。方法定义如下:

- (void)generateShapeAndFixture:(CGPoint) pos withType:(int) type{

    NSString *shapeName = [stepBlockTypesobjectAtIndex:type];

    shape.texture = [[CCTextureCachesharedTextureCache] addImage:[NSString stringWithFormat:@"%@.png",shapeName]];

    shape.position = pos;

    [[GB2ShapeCache sharedShapeCache]addFixturesToBody:body forShapeName:shapeName];

    shape.anchorPoint = ccp(0.5f, 0.5f);

    b2Vec2 newPos;

    newPos.Set(pos.x/PTM_RATIO, pos.y/PTM_RATIO);

    body->SetTransform(newPos, 0);

}

这个方法刷新了形状的纹理(texture),然后使用GB2ShapeCache类的addFixturesToBody:forShapeName:来初始化物体的fixture。GB2ShapeCache类可以在PhysicsEditor(简称PE)的dmg文件中的“\Loaders\generic-box2d-plist”路径下找到,关于PE的用法,请参考手把手教你使用PhysicsEditor来辅助制作Box2D刚体。将PE中创建导出的plist文件导入到Box2D工程中,使用下面的语句导入:

[[GB2ShapeCache sharedShapeCache]addShapesWithFile:@"steps-elements.plist"];

其中“steps-elements.plist”为PE生成的文件。

SetTransform方法用来重置物体的位置和角度,第一个参数是位置,第二个是角度,这里Block为水平放置,所以角度为0。

定义好之后,我们继续添加下面两个reset方法,只是参数上有所不同,复用上面的方法:

- (void)resetWithY:(float) yPos{

    float xPos = CCRANDOM_0_1() * (screenWidth -100);  // 100 is the width of the step block

    [self resetWithPos:ccp(xPos, yPos)];

}

 

- (void)resetWithPos:(CGPoint) pos{

    int type = rand() % 8;

    [self resetWithPos:posandType:(StepBlockTypes)type];

}

最后,我们定义好Block的初始化方法:

- (id)initWithY:(float) y{

    if (self = [super init]) {

        [self initProps];

        [selfinitShape:@"step_normal.png"];

        [self resetWithY:y];

    }

   

    return self;

}

 

- (id)initWithPos:(CGPoint)pos andType:(StepBlockTypes)type{

    if (self = [super init]) {

        [self initProps];

        NSString *shapeName =[NSString stringWithFormat:@"%@.png", [stepBlockTypesobjectAtIndex:(int)type]];

        [selfinitShape:shapeName];

        [self resetWithPos:posandType:type];

    }

   

    return self;

}

这里我们还差一个moveUp的方法,定义如下:

- (BOOL)moveUp:(float) length lowestStepY:(float) lowestStepY{

    float y = shape.position.y + length;

    float x = shape.position.x;

    BOOL isReallyMovedUp = true;

    if (y > screenHeight + 15) {

        y = lowestStepY;

        isReallyMovedUp = false;

        [self resetWithY:y];

    }

    shape.position = ccp(x, y);

    b2Vec2 newPos;

    newPos.Set(x/PTM_RATIO, y/PTM_RATIO);

    body->SetTransform(newPos, 0);

   

    return isReallyMovedUp;

}

使用SetTransform方法来移动刚体的位置,注意的一点是,当Block移动到屏幕外时,并不清理掉这个Block,而是重置Block并将其移动到最下方的Block的下面去,lowestStepY即是最下方的Block的y坐标。返回值isReallyMovedUp用来标记Block是否被重置到了最下方。

到这里StepBlock类就完成了定义。

 

下一篇我们继续来制作这个Box2D小游戏。


版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

手把手教你制作一款Box2D小游戏(一)

利用Box2D物理引擎来制作一款类似于下楼梯的小游戏

手把手教你制作一款Box2D小游戏(二)

利用Box2D物理引擎来制作一款类似于下楼梯的小游戏

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

手把手教你制作一款Box2D小游戏(三)

利用Box2D物理引擎来制作一款类似于下楼梯的小游戏

手把手教你制作那个风靡的flappy bird小游戏(一)

flappy bird小游戏

手把手教你开发一款IOS飞行射击游戏(一)

开发一款IOS飞行射击游戏

手把手教你使用PhysicsEditor来辅助制作Box2D刚体

Box2D中使用PhysicsEditor来辅助制作不规则形状的刚体

手把手教你开发一款IOS飞行射击游戏(六)

开发一款IOS飞行射击游戏

手把手教你开发一款IOS飞行射击游戏(完)

开发一款IOS飞行射击游戏

手把手教你开发一款IOS飞行射击游戏(五)

开发一款IOS飞行射击游戏

手把手教你开发一款IOS飞行射击游戏(四)

开发一款IOS飞行射击游戏
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)