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

news/2024/7/20 23:06:37 标签: IOS, box2d, 游戏


我们继续来制作这款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

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

GameLayer.mm的定义:

首先定义属性的实现:

@synthesize lastScoredStep;

然后是一个静态变量:

static GameLayer* instance;

静态方法,用来返回静态成员instance(instance在初始化的时候会被赋值为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


numbers.png


score.png


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

- (void)initLabels{

    int labelPos = 25;

    scoreIcon = [CCSpritespriteWithFile:@"score.png"];

    scoreIcon.anchorPoint = ccp(0, 1);

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

    [self addChild:scoreIcon];

    scoreLb = [CCLabelAtlaslabelWithString:@"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 = [CCSpritespriteWithFile:@"highscore.png"];

    highScoreIcon.anchorPoint = ccp(0, 1);

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

    [self addChild:highScoreIcon];

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

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

    highScoreLb.anchorPoint = ccp(0, 1);

    [self addChild:highScoreLb];

}

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

- (void)initScores{

    score = 0;

    [scoreLb setString:@"0"];

}

 

- (void)saveHighScore{

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

    highScore = [[[NSUserDefaultsstandardUserDefaults] objectForKey:@"highScore"] intValue];

}

 

- (void)addScore{

    score++;

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

    if (score > highScore) {

        highScore = score;

        [highScoreLbsetString:[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;

        [stepsaddObject:[[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([NSNumbernumberWithInteger: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

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

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

       

        //background

        background =[ParallaxBackground background];

        [selfaddChild: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 theworld.

      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;

        [[stepsobjectAtIndex:(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:lengthlowestStepY:(lowestStepY - stepDistance)] == NO) {

           lowestStepY -= stepDistance;

        }

    }

   

    int32 velocityIterations = 8;

    int32 positionIterations = 4;

   

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

}

update方法中用world的Step方法来执行物理计算,同时也更新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;

        [backgroundstartRolling];

        [self reset];

    }

   

    return YES;

}

 

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

    CGPoint touchLocation = [touchlocationInView:[[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就只做完成了。

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



http://www.niftyadmin.cn/n/878367.html

相关文章

利用微软接口制作的文字转语音神器Read Aloud

最近心血来潮又把抖音&#xff08;菊部创意&#xff09;用起来了&#xff0c;偶尔会传一些电脑/手机录屏&#xff0c;基本上也就是一些曾经分享过的好用的软件的实操&#xff0c;有兴趣的朋友可以关注一下&#xff0c;也帮忙提提意见。 看到别人总是用一些语音解说类的操作&am…

全平台可用且完全免费的国产思维导图知犀

作为国产的人&#xff0c;看到国产的任何东西都做得越来越好&#xff0c;不由得会产生一种分享和支持的心态&#xff0c;并希望感染身边的其他人。这不前阵子在做下半年计划&#xff0c;在使用思维导图的时候发现了一个挺好用的国产思维导图软件-知犀。 “知犀”的由来 “知”…

mysql datename_MySQL数据库之mysql实现merge into语法的(代码实例)

本文主要向大家介绍了MySQL数据库之mysql实现merge into语法的(代码实例) &#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习MySQL数据库有所帮助。mysql实现merge into语法的(代码实例)mysql并不没有oracle、mssql的merge into语法&#xff0c;但是有个on dupl…

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

&#xfeff;&#xfeff;接着我们先来重写ContactListener&#xff1a; 类声明&#xff1a; #import "Box2D.h" class ContactListener : public b2ContactListener { public: voidBeginContact(b2Contact* contact); voidPreSolve(b2Contact* contact, const b2Man…

mysql5.7.17x64位_mysql5.7.17在win2008R2的64位系统安装与配置实例

易采站长站已经给大家讲解过MYSQL其他版本在各种环境中的安装过程&#xff0c;大家可以参阅正文下面的相关文章&#xff0c;今天一起来学习下mysql5.7.17的实例安装教学&#xff0c;配置上稍微不同&#xff0c;希望能够帮助到你。安装MySql操作系统&#xff1a;Windows Server …

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

&#xfeff;&#xfeff;本篇教程需要读者对Box2D有了一些基本了解&#xff0c;如果过程中有什么概念不清楚&#xff0c;请参考Box2Dv2.3.0 用户指南中的相关章节进行学习。 我们知道&#xff0c;在利用Box2D引擎开发游戏的时候&#xff0c;对于规则的物体&#xff08;刚体&a…

wcf mysql_WCF+EF+mysql发布到IIS7上

1.创建一个 【WCF服务应用程序】。2.增加内部类库的引用&#xff0c;EntityFramework (Version 6.2)。MySql.Data, MySql.Data.Entity.EF6 (Version 6.9.9)3.配置Web.config1 <?xml version"1.0" encoding"utf-8"?>2 3 4 5 6 7 8 9 10 11 12 13…

Box2D v2.3.0 用户指南(第一章)

&#xfeff;&#xfeff;原著&#xff1a;2007-2013 Erin Catto 翻译&#xff1a;远行的风 关注我的博客&#xff1a;http://blog.csdn.net/qwertyupoiuytr 网上已经有很多版本的Box2D用户手册了&#xff0c;这里我们翻译的是最新的2.3.0这个版本的手册&#xff0c;也是为了…