注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

风的驿站

一徐清风,半指烛光,觥筹已净,只余茶香。残卷一章,妙趣非常,忽闻帘响,愿闻其详?

 
 
 

日志

 
 
关于我

喜欢写生 编程 音乐 设计 喜欢把自己的想法变成实实在在的东西 喜欢安静的做一些事情 CSDN博客:http://blog.csdn.net/qwertyupoiuytr

网易考拉推荐

【原创】手把手教你制作一款Box2D小游戏(二)  

2014-06-23 13:41:15|  分类: Box2D |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

我们继续来制作这款Box2D小游戏。

前一篇中我们介绍了StepBlock类的制作,本篇中我们首先来定义好游戏的场景(CCScene)和层(CCLayer)。

我们定义一个GameLayer类,继承自CCLayer,声明如下:

#import "CCLayer.h"

#import "cocos2d.h"

#import "Box2D.h"

#import "ParallaxBackground.h"

#import "Ball.h"

#import "TagDefinitions.h"

#import "ContactListener.h"

 

@interface GameLayer : CCLayer <CCTouchOneByOneDelegate>{

    float screenWidth;

    float screenHeight;

    b2World* world;

    ParallaxBackground* background;

    NSMutableArray* steps;

    Ball* ball;

    float lowestStepY;

    float stepDistance;

    BOOL gameBegin;

    ForceType appliedForceType;

    CCLabelAtlas* scoreLb;

    CCLabelAtlas* highScoreLb;

    CCSprite* scoreIcon;

    CCSprite* highScoreIcon;

    int score;

    int highScore;

    ContactListener* myListener;

    BOOL isTouchable;

}

 

@property int lastScoredStep;

 

+ (GameLayer*) gameLayer;

+ (id)scene;

- (b2World*) sharedWorld;

- (void)addScore;

 

@end

screenHeightscreenWidth为屏幕高宽,world为这款游戏的世界对象,background是游戏的滚动背景(需要自己制作),steps是游戏中需要用到的StepBlock的数组,主要是出于复用的考虑。ball是游戏中的小球对象,我们接下来会实现。lowestStepY前面介绍过了,是位置最低的Blocky值。stepDistance是相邻的上下两个StepBlock之间的间隔。gameBegin用来标记游戏是否已经开始,如果游戏没有开始,所有的物体都应静止不动。appliedForceType用来记录小球的受力情况(小球受点击屏幕控制)。下面的跟score相关的CCLabelAtlasCCSprite以及int值都是跟当前分数和最高分有关的,myListener是重载b2ContactListener的类的实例,用来侦听碰撞事件并做相应的处理,我们后面来介绍。isTouchable用来控制屏幕点击,后面会用到。

GameLayer.mm的定义:

首先定义属性的实现:

@synthesize lastScoredStep;

然后是一个静态变量:

static GameLayer* instance;

静态方法,用来返回静态成员instanceinstance在初始化的时候会被赋值为self):

+ (GameLayer*)gameLayer{

    return instance;

}

静态的scene方法,返回一个包含GameLayer的场景对象供AppDelegate引用:

+ (id)scene{

    CCScene* scene = [CCScene node];

    GameLayer* gameLayer = [GameLayer node];

    [scene addChild:gameLayer];

   

    return scene;

}

下面列出了需要用到的图片素材:

highscore.png

【原创】手把手教你制作一款Box2D小游戏(二) - 远行的风 - 风的驿站

numbers.png

【原创】手把手教你制作一款Box2D小游戏(二) - 远行的风 - 风的驿站

score.png

【原创】手把手教你制作一款Box2D小游戏(二) - 远行的风 - 风的驿站

接着我们在GameLayer中添加下面的方法,初始化ScoreHighScore(效果可以看上一篇最开始的那个截图):

- (void)initLabels{

    int labelPos = 25;

    scoreIcon = [CCSprite spriteWithFile:@"score.png"];

    scoreIcon.anchorPoint = ccp(0, 1);

    scoreIcon.position = ccp(17, screenHeight - labelPos);

    [self addChild:scoreIcon];

    scoreLb = [CCLabelAtlas labelWithString:@"0" charMapFile:@"numbers.png" itemWidth:16 itemHeight:20 startCharMap:'0'];

    scoreLb.position = ccp(100, screenHeight - labelPos);

    scoreLb.anchorPoint = ccp(0, 1);

    [self addChild:scoreLb];

   

    highScoreIcon = [CCSprite spriteWithFile:@"highscore.png"];

    highScoreIcon.anchorPoint = ccp(0, 1);

    highScoreIcon.position = ccp(screenWidth * 0.5 + 25, screenHeight - labelPos);

    [self addChild:highScoreIcon];

    highScoreLb = [CCLabelAtlas labelWithString:[NSString stringWithFormat:@"%d", highScore] charMapFile:@"numbers.png" itemWidth:16 itemHeight:20 startCharMap:'0'];

    highScoreLb.position = ccp(screenWidth * 0.5 + 59, screenHeight - labelPos);

    highScoreLb.anchorPoint = ccp(0, 1);

    [self addChild:highScoreLb];

}

然后添加下面的方法,都是跟scorehighscore相关的方法,比较容易理解,不多做解释:

- (void)initScores{

    score = 0;

    [scoreLb setString:@"0"];

}

 

- (void)saveHighScore{

    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:highScore] forKey:@"highScore"];

    highScore = [[[NSUserDefaults standardUserDefaults] objectForKey:@"highScore"] intValue];

}

 

- (void)addScore{

    score++;

    [scoreLb setString:[NSString stringWithFormat:@"%d", score]];

    if (score > highScore) {

        highScore = score;

        [highScoreLb setString:[NSString stringWithFormat:@"%d", highScore]];

    }

}

接下来我们添加初始化StepBlock的方法:

- (void)initSteps{

    steps = [[NSMutableArray array] retain];

   

    lowestStepY = screenHeight * 0.5f;

    [steps addObject:[[StepBlock alloc] initWithPos:ccp(screenWidth * 0.5f, lowestStepY) andType:StepBlockNormal]];

    lowestStepY -= stepDistance;

    [steps addObject:[[StepBlock alloc] initWithPos:ccp(0, lowestStepY) andType:StepBlockNormal]];

    lowestStepY -= stepDistance;

    [steps addObject:[[StepBlock alloc] initWithPos:ccp(1000, lowestStepY) andType:StepBlockNormal]];

   

    for (int i = 0; i < 6; i++) {

        lowestStepY -= stepDistance;

        [steps addObject:[[StepBlock alloc] initWithY:lowestStepY]];

    }

   

    int tagBase = 1;

    for (StepBlock* step in steps) {

        step.tag = tagBase++;

        [self addChild:step];

    }

}

需要在StepBlock类中重载下面的方法:

- (void)setTag:(NSInteger)tag{

    body->SetUserData([NSNumber numberWithInteger:tag]);

    if (tagSave < 1) {

        tagSave = tag;

    }

}

tagSave用来区分每一个Block

接着我们定义GameLayer的初始化方法:

- (id)init{

    if (self = [super init]) {

        instance = self;

       

        //screen dimensions

        CGSize screenSize = [[CCDirector sharedDirector] winSize];

        screenHeight = screenSize.height;

        screenWidth = screenSize.width;

       

        //init cache

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

        highScore = [[[NSUserDefaults standardUserDefaults] objectForKey:@"highScore"] intValue];

       

        //background

        background = [ParallaxBackground background];

        [self addChild:background];

       

        //init physics

        [self initPhysics];

       

        //init random seed

        srand(time(nil));

 

        stepDistance = 90;

        gameBegin = false;

        isTouchable = true;

        lastScoredStep = -1;

        appliedForceType = ForceNone;

        [self initSteps];

        [self initLabels];

        [self initScores];

    }

   

    return self;

}

初始化方法中的initPhysics用来初始化Box2D相关的对象:

- (void)initPhysics{

    b2Vec2 gravity;

    gravity.Set(0.0f, -8.0f);

    world = new b2World(gravity);

    world->SetAllowSleeping(true);

    world->SetContinuousPhysics(false);

   

    myListener = new ContactListener();

    world->SetContactListener(myListener);

   

    ball = [Ball ball];

    [self addChild:ball];

   

    // Define the ground body.

      b2BodyDef groundBodyDef;

      groundBodyDef.position.Set(0, 0); // bottom-left corner

     

      // The body is also added to the world.

      b2Body* groundBody = world->CreateBody(&groundBodyDef);

      b2EdgeShape groundBox;

       

    b2Filter filter;

    filter.maskBits = 3;

    filter.categoryBits = 2;

     

    float leftX = 15.0/PTM_RATIO;

    float rightX = (screenWidth-15)/PTM_RATIO;

    float Y = 2*screenHeight/PTM_RATIO;

     

      // left

      groundBox.Set(b2Vec2(leftX, Y), b2Vec2(leftX,0));

      b2Fixture* fixture2 = groundBody->CreateFixture(&groundBox,0);

    fixture2->SetFilterData(filter);

    fixture2->SetFriction(0);

     

      // right

      groundBox.Set(b2Vec2(rightX, Y), b2Vec2(rightX, 0));

      b2Fixture* fixture3 = groundBody->CreateFixture(&groundBox,0);

    fixture3->SetFilterData(filter);

    fixture3->SetFriction(0);

}

我们在这个方法中初始化了世界对象,listener对象,ball对象以及左右的墙体,注意一下我们定义的墙体碰撞过滤标记。

接着我们定义reset方法,用来在一局游戏结束(小球掉下去了或者升到屏幕外边去了)的时候重置游戏:

- (void)reset{

    [self initScores];

    [ball reset];

    lastScoredStep = -1;

    lowestStepY = screenHeight * 0.5f;

    [[steps objectAtIndex:0] resetWithPos:ccp(screenWidth * 0.5f, lowestStepY) andType:StepBlockNormal];

    lowestStepY -= stepDistance;

    [[steps objectAtIndex:1] resetWithPos:ccp(0, lowestStepY) andType:StepBlockNormal];

    lowestStepY -= stepDistance;

    [[steps objectAtIndex:2] resetWithPos:ccp(1000, lowestStepY) andType:StepBlockNormal];

    for (int i = 0; i < 6; i++) {

        lowestStepY -= stepDistance;

        [[steps objectAtIndex:(3+i)] resetWithY:lowestStepY];

    }

    [self scheduleUpdate];

}

最后定义update方法:

- (void)update:(ccTime)delta{

    int speed = min(80 + score/10, 150);

    if (ball.position.y < -20 || ball.position.y > (screenHeight + 20)) {

        [self showGameOver];

        return;

    }

   

    if (appliedForceType == ForceLeft) {

        [ball pushLeft];

    } else if (appliedForceType == ForceRight) {

        [ball pushRight];

    }

    float length = delta * speed;

    lowestStepY += length;

    for (StepBlock* step in steps) {

        if ([step moveUp:length lowestStepY:(lowestStepY - stepDistance)] == NO) {

            lowestStepY -= stepDistance;

        }

    }

   

    int32 velocityIterations = 8;

    int32 positionIterations = 4;

   

    world->Step(delta, velocityIterations, positionIterations);

}

update方法中用worldStep方法来执行物理计算,同时也更新StepBlock的位置,并且根据小球的受力来使其移动,根据小球的y坐标来判断是否GameOver

showGameOver方法定义如下:

- (void)showGameOver{

    gameBegin = false;

    [self unscheduleUpdate];

    [background stopRolling];

    [self saveHighScore];

    isTouchable = false;

    [self schedule:@selector(enableTouch) interval:1.0f repeat:1 delay:1.0f];

}

enableTouch方法:

- (void)enableTouch{

    isTouchable = true;

    [self unschedule:_cmd];

}

我们需要通过点击屏幕左右来控制小球的受力(移动),因此让GameLayer实现CCTouchOneByOneDelegate委托,重载下面的三个方法:

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

    if (!isTouchable) {

        return NO;

    }

    if (gameBegin) {

        CGPoint touchLocation = [touch locationInView:[[CCDirector sharedDirector] view]];

        touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];

        if (touchLocation.x < 0.5 * screenWidth) {

            appliedForceType = ForceLeft;

        } else {

            appliedForceType = ForceRight;

        }

    } else {

        gameBegin = true;

        [background startRolling];

        [self reset];

    }

   

    return YES;

}

 

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

    CGPoint touchLocation = [touch locationInView:[[CCDirector sharedDirector] view]];

    touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];

    if (touchLocation.x < 0.5 * screenWidth) {

        appliedForceType = ForceLeft;

    } else {

        appliedForceType = ForceRight;

    }

}

 

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

    appliedForceType = ForceNone;

}

别忘了注册代理:

- (void)onEnterTransitionDidFinish{

    [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:1 swallowsTouches:YES];

}

 

- (void)onExit{

    [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];

}

这样GameLayer就只做完成了。

由于篇幅限制,我们在下一篇中完成ContactListener和Ball类的编写。

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

  评论这张
 
阅读(167)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017