跟着BOY学习开发cocos2d-x 游戏 实战篇(7)之 关卡系统的基本设计

时间:2023-02-08 09:11:25

           看到群里面的朋友期待了很久的关卡系统,我以种种理由没有写。在这里给大家说声对不起。从上一期到现在基本快10天了。让大家久等了。自从看了CJ 以后发现自己变懒了哈哈玩笑,主要上面的美女太多了。梦想一天自己写的游戏也能去哪里参展。呵呵群里的兄弟们努力啊希望有一天能看到你自己开发的游戏区哪里参展。呵呵废话不多说了。下面我简单的说了一下我的设计关卡系统的一个简单的思路。我感觉做这类的塔防游戏其实就是每一个关卡就是一个生存模式,当你过了以后就可以进入到下一关,当你没过的时候会让你一直停留在次关 一直打到你过为止。既然是这样的一个思路那么没提升一个关卡怪物的属性值和怪物的数量值总是需要提升,来增加游戏的难度。让玩家更好的玩下去。我的总体思路就是这样。 我画个简单的流程图

        跟着BOY学习开发cocos2d-x 游戏 实战篇(7)之 关卡系统的基本设计

大家看到这个图是不是发现其实挺简单的 但是做起啦并不是那么的简单 没过这都好说 难点在于过了以后 各项能力值的提升 boy我在做到这里的时候真心感觉需要一个数值策划。还有重要的一点就是我们怪物出场的时候排列的阵型这都需要你仔细的推敲一下

    在对已上一章的内容中这一章增加了不少的小的新东西  例如关卡的提示

    主要代码如下

   

#include "RatingSprite.h"
USING_NS_CC;
RatingSprite::RatingSprite(){}
RatingSprite::~RatingSprite(){}
// 此处的图片必须放在缓存中
RatingSprite* RatingSprite::createWidthPic(const char* picName){
RatingSprite *pobSprite = new RatingSprite();
if (pobSprite && pobSprite->initWithTexture(CCTextureCache::sharedTextureCache()->textureForKey(picName))&&pobSprite->setUpdateView())
{
pobSprite->autorelease();
return pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return NULL;
}

bool RatingSprite::setUpdateView(){
bool isRet=false;
do {
CCLabelAtlas* numbrer= CCLabelAtlas::create("0","game/number_ttf.png",54,50,'0');
CC_BREAK_IF(!numbrer);
numbrer->setAnchorPoint(ccp(0,0.4));
numbrer->setPosition(this->getContentSize().width/2,this->getContentSize().height/2);
this->addChild(numbrer,1,2);
this->setScaleY(0);
isRet=true;
} while (0);
return isRet;

}

void RatingSprite::runAnimation(){
this->stopAllActions();
CCActionInterval* cd= CCScaleTo::create(2,1,1);
CCActionInterval* cd2=CCScaleTo::create(2,1,0);
CCCallFuncN *onComplete = CCCallFuncN::create(this, callfuncN_selector(RatingSprite::runAnimationCallBack));
CCSequence* ce=CCSequence::create(cd,cd2,onComplete,NULL);
this->runAction(ce);
}


void RatingSprite::setLeverNum(int num){
CCLabelAtlas* numbrer =(CCLabelAtlas*) this->getChildByTag(2);
char* temp=new char[12];
sprintf(temp,"%d",num);
numbrer->setString(temp);
delete temp;
}
void RatingSprite::runAnimationCallBack(cocos2d::CCNode* psed){
this->setScaleY(0);
}

大家要是看到这代码真是没什么讲的要是说能说的就是

CCLabelAtlas* numbrer= CCLabelAtlas::create("0","game/number_ttf.png",54,50,'0');
对于这一行代码其实就是穿件一个自定义的数字标签。我们都知道在游戏中我们有很多字体系统的字体库是满足不了的这个时候就需要自定义的字体了。对于这个希望大家吧coco2d-x 中每种字体的方式都在网上查询一下。


当你实现上面的代码的以后就会看到如下效果跟着BOY学习开发cocos2d-x 游戏 实战篇(7)之 关卡系统的基本设计


看起来很帅吧  其实那个24 就是使用的自定义字体

细心的朋友会发现在游戏的窗口中多了 金币提示 关卡提示 和怪我波数提示 

这个我给封装了成了一个简单的小控件 主要代码如下

#include "GameTipsSprite.h"
USING_NS_CC;
bool GameTipsSprite::init(){
bool isRet=false;
do {
CC_BREAK_IF(!CCNode::init());
CC_BREAK_IF(!setUpdateView());
isRet=true;
} while (0);
return isRet;
}

bool GameTipsSprite::setUpdateView(){
bool isRet=false;
do {
//金币图标
CCSprite* gold=CCSprite::createWithTexture(CCTextureCache::sharedTextureCache()->textureForKey("game/coin.png"));
gold->setPosition(CCPointZero);
this->addChild(gold);
// 金币数目
CCLabelAtlas* goldnumbrer= CCLabelAtlas::create("0","game/numtips.png",25,31,'0');
CC_BREAK_IF(!goldnumbrer);
goldnumbrer->setAnchorPoint(ccp(0,0.5));
goldnumbrer->setPosition(ccp(gold->getContentSize().width/2,0));
goldnumbrer->setScale(0.5);
this->addChild(goldnumbrer,1,1);

//怪物波数
CCLabelAtlas* monstnumbrer= CCLabelAtlas::create("0","game/numtips.png",25,31,'0');
CC_BREAK_IF(!monstnumbrer);
monstnumbrer->setAnchorPoint(ccp(0,0.5));
monstnumbrer->setPosition(ccp(goldnumbrer->boundingBox().getMaxX()+120,0));
monstnumbrer->setScale(0.5);
this->addChild(monstnumbrer,1,2);

// 当前关卡
CCLabelTTF* monstlable=CCLabelTTF::create("/","Arial",24);
CC_BREAK_IF(!monstlable);
monstlable->setAnchorPoint(ccp(0,0.5));
monstlable->setPosition(ccp(monstnumbrer->boundingBox().getMaxX()+10,0));
monstlable->setColor(ccc3(255,255,255));
this->addChild(monstlable,1);

// 怪物总波数
CCLabelAtlas* monsttotalnumbrer= CCLabelAtlas::create("0","game/numtips.png",25,31,'0');
CC_BREAK_IF(!monsttotalnumbrer);
monsttotalnumbrer->setAnchorPoint(ccp(0,0.5));
monsttotalnumbrer->setScale(0.5);
monsttotalnumbrer->setPosition(ccp(monstlable->boundingBox().getMaxX(),0));
this->addChild(monsttotalnumbrer,1,3);

// 当前关卡
CCLabelTTF* stagelable=CCLabelTTF::create("Stage:","Arial",24);
CC_BREAK_IF(!stagelable);
stagelable->setAnchorPoint(ccp(0,0.5));
stagelable->setPosition(ccp(monsttotalnumbrer->boundingBox().getMaxX()+40,0));
stagelable->setColor(ccc3(255,255,255));
this->addChild(stagelable,1);

CCLabelAtlas* stagenumbrer= CCLabelAtlas::create("0","game/numtips.png",25,31,'0');
CC_BREAK_IF(!stagenumbrer);
stagenumbrer->setScale(0.5);
stagenumbrer->setAnchorPoint(ccp(0,0.5));
stagenumbrer->setPosition(ccp(stagelable->boundingBox().getMaxX(),0));
this->addChild(stagenumbrer,1,4);




isRet=true;
} while (0);
return isRet;
}

void GameTipsSprite::initialization(){

}

void GameTipsSprite::setgoldNum(int num){
CCLabelAtlas* goldnumbrer =(CCLabelAtlas*) this->getChildByTag(1);
char* temp=new char[12];
sprintf(temp,"%d",num);
goldnumbrer->setString(temp);
delete temp;
}
void GameTipsSprite::setMonstNum(int num){
CCLabelAtlas* monstnumbrer =(CCLabelAtlas*) this->getChildByTag(2);
char* temp=new char[12];
sprintf(temp,"%d",num);
monstnumbrer->setString(temp);
delete temp;
}
void GameTipsSprite::setMonstTotalNum(int num){
CCLabelAtlas* monsttotalnumbrer =(CCLabelAtlas*) this->getChildByTag(3);
char* temp=new char[12];
sprintf(temp,"%d",num);
monsttotalnumbrer->setString(temp);
delete temp;
}
void GameTipsSprite::setStageNum(int num){
CCLabelAtlas* stagenumbrer =(CCLabelAtlas*) this->getChildByTag(4);
char* temp=new char[12];
sprintf(temp,"%d",num);
stagenumbrer->setString(temp);
delete temp;
}


GameTipsSprite::GameTipsSprite(){

}
GameTipsSprite::~GameTipsSprite(){

}

看过之后是不是感觉挺简单的,其实这里是教会大家要学会自定义控件。虽然cocos2d-x 中提供很多有用的控件但是不可能满足我们所有的情况。我在这里封装成一个控件以后如果那个界面需要展示这个 我直接把这个控件添加到那个界面即可。

  上面上面的代码既可以得到这样的一个东西

   跟着BOY学习开发cocos2d-x 游戏 实战篇(7)之 关卡系统的基本设计  这里大量使用了自定义字体。对了在这里做说明一点关于自定义控件很多同学不知道怎么去写,其实你参照ccsprite 就可以了 。

既然是每个关卡是生成模式 那么总要有血量的判断吧,我这里是这么做的当怪物没执行完一个攻击动作的时候我就让主城的气血减少当气血为0的时候表示GAME OVER

这段代码我就不用上了吧其实就是修改下上一张的部分代码。
如果你实现那部分代码会得到这样的一个界面

跟着BOY学习开发cocos2d-x 游戏 实战篇(7)之 关卡系统的基本设计

   

这里我说明一下 你看到这个游戏结束界面其实是一个自定义的弹出框。哈哈我这么一说 是不是感觉原来你是这么设计。其实我喜欢吧游戏分块设计,尽量减少模块之间的耦合性。

如果过了这一关 那么就会展示出这样的界面  不过游戏胜利的界面不是一个弹出框了 而是一个场景了

跟着BOY学习开发cocos2d-x 游戏 实战篇(7)之 关卡系统的基本设计

下面的那一行字 是闪烁的 来提示玩家点击

当大家吧这些界面昨晚的时候 基本上 一个简单的关卡就做完了。是不是挺简单的。不过真正游戏肯定不会让玩家这么一直枯燥无味的玩下去。下面就牵涉到当关卡升级的时候 就需要需要提升个个怪物能力值 这里我只是简单的设计一个这样的类

大家可以粗略的看一下

   

#ifndef  __CHECKPOINT_SYSTEMS_H__
#define __CHECKPOINT_SYSTEMS_H__
//关卡系统
class CheckpointSystems{
public:
CheckpointSystems();
~CheckpointSystems();
//根据怪物类型和当前等级 获取怪物的伤害提升值
static int hurtHoist(int type); // 传入参数怪物类型
static int speedHoist(int type);//根据关卡和怪物类型获获取提升能力值
static int monsterBach();//根据当前关卡来提升怪物的波数 提高游戏难度
static int bloodHoist(int type);//气血提升
static int monsterCount(int count);//根据关卡来获取当前出怪的数量 // 这个其实是游戏的重点 我到现在还无法相处一个合理的函数来根据关卡计算出怪数
};
#endif
其实这里面的每个函数都需要一定的公式或者约定来计算出当前最合理的怪物的属性值。如果想看我的简单的实现的话 可以下载源码之后仔细观看。其实就是通过这样的一个小的设计让咱们的游戏更具有可玩性。

 这里我在说明一点 我在这里修改原来的怪物系统的出场 规则,不在只采用随机的方式 而是按照一定的阵型出现在关卡中,这样让咱们的怪物看起来更智能一点

下面贴上我我部分代码

void  monsterFormation(cocos2d::CCArray* monsterArrays);// 这里其实是一个简单的怪物阵型 的设计
void monsterFormationToOne(MonsterSprite* monsterSprite);// 这种方式是对一个 怪物的坐标进行随机
void monsterFormationOne(cocos2d::CCArray* monsterArrays);// 第一种方式 全部随机的形式
void monsterFormationTwo(cocos2d::CCArray* monsterArrays);// 第二种方式 排成一排 当然局限于当前做多能排多少个
void monsterFormationThree(cocos2d::CCArray* monsterArrays);// 第三种方式 排成2排 竖着两排
void monsterFormationFour(cocos2d::CCArray* monsterArrays);// 第四中种方式 排成2排 横着着两排 随机出现 第一排和 第二排的个数
void monsterFormationFive(cocos2d::CCArray* monsterArrays);// 第五中种方式 实现前后错开的这样的样式

呵呵当你看到这段代码的时候是不是发现 哈哈你懂得 至于怎么实现其实就让出场的怪物的坐标按照你规定的阵型出来。这里的面5种方你可以自己根据自己的喜好在进行扩展


哈哈上一张怪物的出场图跟着BOY学习开发cocos2d-x 游戏 实战篇(7)之 关卡系统的基本设计

看起来好像还不错,不过我写的比较粗糙并没有仔细的去测试。我回头会把这部分代码好好修改一下。不过思路就是这样。怪物的ai是一个游戏不可确守的部分。

                       

                              当游戏写到这里的时候已经基本接近尾声了,不过还有一个模块没写那就是 用金币让你的武器 攻击力 城墙气血 魔法伤害增加 等等。要不按照现在的模式当你打到一定的关卡的时候你发现你根本就打不死怪物了 哈哈。这个时候就需要加入这样的一个系统,要不我们游戏中设计的金币也没用是不是。关于武器的升级系统我尽量在周一的时候给弄出。看到很多群里的朋友不知道coco2d-x 中联网和多线程的使用。我下周将会增加这样的章节来说明游戏中如何加入联网系统。是不是很期待。哈哈,那就关注BOY 


                       

本节源码下载