GVKun编程网logo

火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二十五节: 3D项目优化方案

5

本篇文章给大家谈谈火云开发课堂-《使用Cocos2d-x开发3D游戏》系列第二十五节:3D项目优化方案,同时本文还将给你拓展Cocos2d-x3.2大富翁游戏项目开发-第二十九部分游戏配音、Cocos

本篇文章给大家谈谈火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二十五节: 3D项目优化方案,同时本文还将给你拓展Cocos2d-x 3.2 大富翁游戏项目开发-第二十九部分 游戏配音、Cocos2d-x 3.2 大富翁游戏项目开发-第二十五部分 大富翁股市、Cocos2d-x 3.2 大富翁游戏项目开发-第二十八部分 游戏保存和载入存档游戏、Cocos2d-x 3.2 大富翁游戏项目开发-第二十部分 螃蟹挡路(code)等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二十五节: 3D项目优化方案

火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二十五节: 3D项目优化方案

《使用Cocos2d-x 开发3D游戏》系列在线课程


第二十五节:3D项目优化方案

视频地址:http://edu.csdn.net/course/detail/1330/20825?auto_start=1

交流论坛:http://www.firestonegames.com/bbs/forum.php

Cocos2d-x 3.2 大富翁游戏项目开发-第二十九部分 游戏配音

Cocos2d-x 3.2 大富翁游戏项目开发-第二十九部分 游戏配音

我从大富翁里提取出来里面的wav音效文件,放到我们的游戏中以增加趣味性,仅供学习研究之用

1、修改AppDelegate.cpp文件

// This function will be called when the app is inactive. When comes a phone call,it's be invoked too
void AppDelegate::applicationDidEnterBackground() {
    Director::getInstance()->stopAnimation();

    // if you use SimpleAudioEngine,it must be pause
	//后台暂停
     SimpleAudioEngine::getInstance()->pauseBackgroundMusic(); 
}

// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground() {
    Director::getInstance()->startAnimation();

    // if you use SimpleAudioEngine,it must resume here
	//恢复播放
     SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}

2、在resources 目录下新建sound文件夹,存放游戏声音文件

角色的声音文件定义到ConstUtil.h文件中,大体如下:
#define P1_DEYIDEYITIAN  "sound/byelottery.wav"//拜拜 祝您中奖
#define P1_need1000  "sound/need1000.wav"//只要1000元
#define P1_select_lottery  "sound/select_lottery.wav"//请圈选你要购买的彩票
#define P1_meirendejiang "sound/meirendejiang.wav"//sorry 本月份没有人得奖
#define P1_xiwangshini  "sound/xiwangshini.wav"//希望下次得奖者就是您
#define P1_Speaking_00181  "sound/Speaking_00181.wav"//投资获利
#define P1_Speaking_00182  "sound/Speaking_00182.wav"//投资失败

角色相关声音大体依据如下内容进行分类定义:
//交过路费声音
//抢夺别人地块
//房屋被抢夺
//房屋被摧毁
//摧毁别人房屋
//螃蟹伤人
//看到别人住院
//收取过路费
//升级房子
//不交过路费
//买地
//捡到珍珠
//对方被罚收税


例如:角色1的文件定义
//交过路费声音
#define P1_Speaking_00435  "sound/Speaking_00435.wav"//oh 哈利路亚
#define P1_Speaking_00461  "sound/Speaking_00461.wav"//oh 我的血汗钱
#define P1_Speaking_00475  "sound/Speaking_00475.wav"//算了算了 老子有的是钱
#define P1_Speaking_01060  "sound/Speaking_01060.wav"//老本都快没了
#define P1_Speaking_001062  "sound/Speaking_001062.wav"//拿去了不用找了
//抢夺别人地块
#define P1_Speaking_00429  "sound/Speaking_00429.wav"//让我把他据为己有
//房屋被抢夺
#define P1_Speaking_00430  "sound/Speaking_00430.wav"//黄金地段 让给你
#define P1_Speaking_00464  "sound/Speaking_00464.wav"//太不给面子了
#define P1_Speaking_00469  "sound/Speaking_00469.wav"//你皮子痒啊
#define P1_Speaking_00470  "sound/Speaking_00470.wav"//竟敢在太岁头上动土
#define P1_Speaking_00476  "sound/Speaking_00476.wav"//算你狠
//房屋被摧毁
#define P1_Speaking_00462  "sound/Speaking_00462.wav"//好大的胆子
#define P1_Speaking_00463  "sound/Speaking_00463.wav"//谁敢动我的地
#define P1_Speaking_00466  "sound/Speaking_00466.wav"//竟敢破坏我的好事
#define P1_Speaking_00468  "sound/Speaking_00468.wav"//拆的还真干净
#define P1_Speaking_00474  "sound/Speaking_00474.wav"//你有没有搞错啊
#define P1_Speaking_001061  "sound/Speaking_001061.wav"//真没良心
//摧毁别人房屋
#define P1_Speaking_00433  "sound/Speaking_00433.wav"//不必谢我
#define P1_Speaking_00437  "sound/Speaking_00437.wav"//全部夷为平地
//螃蟹伤人
#define P1_Speaking_00449  "sound/Speaking_00449.wav"//快来帮我把
#define P1_Speaking_01054  "sound/Speaking_01054.wav"//我惨了
#define P1_Speaking_01055  "sound/Speaking_01055.wav"//哎呦喂啊
#define P1_Speaking_001071  "sound/Speaking_001071.wav"//我不要打针
//看到别人住院
#define P1_Speaking_001073  "sound/Speaking_001073.wav"//别闹了
//收取过路费
#define P1_Speaking_00453  "sound/Speaking_00453.wav"//小本经营 概不赊欠
#define P1_Speaking_01059  "sound/Speaking_01059.wav"//蝇头小利
#define P1_Speaking_01057  "sound/Speaking_01057.wav"//这是我应得的
//升级房子
#define P1_Speaking_01051  "sound/Speaking_01051.wav"//别嫉妒我
#define P1_Speaking_001066  "sound/Speaking_001066.wav"//我真佩服自己
//不交过路费
#define P1_Speaking_00446  "sound/Speaking_00446.wav"//有钱也不给你
#define P1_Speaking_00477  "sound/Speaking_00477.wav"//可别想占我便宜啊
//买地
#define P1_Speaking_00458  "sound/Speaking_00458.wav"//盖什么好呢
#define P1_Speaking_001067  "sound/Speaking_001067.wav"//我是个大地主
//捡到珍珠
#define P1_Speaking_01052  "sound/Speaking_01052.wav"//鸿运当头
#define P1_Speaking_001063  "sound/Speaking_001063.wav"//上帝保佑
//对方被罚收税
#define P1_Speaking_00452  "sound/Speaking_00452.wav"//别想偷漏税

3、
根据声音的分类把文件名称放入到Vector中,然后根据场景随机从Vector中取出声音进行播放。

在GameBaseScene.cpp的initAudioEffect方法中,据声音的分类把文件名称放入到Vector中
void GameBaseScene::initAudioEffect()
{
.........
	player2EffectVec_1.pushBack(String::create(P2_SPEAKING01));
	player2EffectVec_1.pushBack(String::create(P2_QISIWOLE));
	player2EffectVec_1.pushBack(String::create(P2_XINHAOKONGA));
	player2EffectVec_1.pushBack(String::create(P2_BUHUIBA));
	player2EffectVec_1.pushBack(String::create(P2_PAYHIGH));
	player2EffectVec_1.pushBack(String::create(P2_QIANGQIANA));
	player2EffectVec_1.pushBack(String::create(P2_HEBAOCHUXIE));
..........
}
在Util.cpp中定义声音播放方法,根据声音开关设置,进行声音的播放
void Util::playAudioEffect(const char* effectName,bool isLoop)
{
	bool music_on = UserDefault::getInstance()->getBoolForKey(MUSIC_ON_KEY,true);

	if(music_on)
	{
		CocosDenshion::SimpleAudioEngine::getInstance()->playEffect(effectName,isLoop);
	}
}

//随机从Vector中取出声音进行播放
void Util::playAudioEffectRandom(Vector<String*> effectVec,bool isLoop)
{
	playAudioEffect(effectVec.at(rand() % effectVec.size())->getCString(),isLoop);
}

void Util::stopAudioPlay()
{
	CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic();
	CocosDenshion::SimpleAudioEngine::getInstance()->stopAllEffects();
}

4、添加背景音乐,共3首背景音乐,随机播放
void GameBaseScene::initAudioEffect()
{
	bgMusicVector.push_back(BG01_MP3);
	bgMusicVector.push_back(BG02_MP3);
	bgMusicVector.push_back(BG03_MP3);

	for (int i = 0; i<bgMusicVector.size(); i++)
	{
		CocosDenshion::SimpleAudioEngine::getInstance()->preloadBackgroundMusic(bgMusicVector.at(i));
	}
	this->schedule(schedule_selector(GameBaseScene::playerBgMusic),5.0f);
	
.....
}	

5、角色对话相关的音效,就是根据具体场景,添加相应的音效播放就可以了


关于音乐音效的比较简单,可以参考 http://cn.cocos2d-x.org/tutorial/show?id=2448

这篇文章http://cn.cocos2d-x.org/tutorial/show?id=2352 ,里面的转盘界面效果做得挺好,稍微修改一下,拿到我们大富翁中来吧。

代码同下一节一并发布.

Cocos2d-x 3.2 大富翁游戏项目开发-第二十五部分 大富翁股市

Cocos2d-x 3.2 大富翁游戏项目开发-第二十五部分 大富翁股市

当角色走到股市图标时,进入股市界面。每走完一个回合,增加一条股票数据,
股市界面上半部分显示股票信息,包括代码,名称,当前价格,买入价格,涨跌百分比,角色持有的股票数量
下半部分显示股票价格走势,当点击一个股票时,显示相关股票的价格走势,共显示最新14条的价格走势。

每次点击购买,买入100股 。点击卖出,则卖出所持有的该股的所有股票。成交价格 等信息动态更新
点击返回,返回到游戏主界面,并更新角色资金值

1、首先添加股票类
包括代码,名称,买入价格,涨跌百分比,持仓数量等定义以及相关的get 、set方法

class Stock : public Sprite
{
................
private:
<span>	</span>int stockCode; //股票代码
	String* stockName;//股票名称
        int NowPrice;//当前价格
        int makedealprice;//成交价格
	float percent;//涨跌百分比
	int storeNumber;//持仓数量
};

2、VisibleRect类,主要是为了方便的获取屏幕的尺寸等信息
class VisibleRect
{
public:
    static cocos2d::Rect getVisibleRect();
...................
private:
    static void lazyInit();
    static cocos2d::Rect s_visibleRect;
};

3、股票记录中的单元格类StockCellCard,包括单元格背景和显示文字的label。同彩票类card大体相同,不再解释了


4、在RicherPlayer的init方法中,给角色创建股票持有信息,主要是股票代码和持仓数量和买入价格。其他信息无关紧要
bool RicherPlayer::init(char* name,int tag,bool enemy,int money,int strength)
{
...........
	stockMap.insert(0,Stock::create(800100,LanguageString::getInstance()->getLanguageString(RICH_TECHNOLOGY),10,100));
	stockMap.insert(1,Stock::create(800200,20,200));
	stockMap.insert(2,Stock::create(800300,70,800));
	stockMap.insert(3,Stock::create(800400,400));
	stockMap.insert(4,Stock::create(800500,0));
	..........
}

5、当角色走到股票图标时RicherGameController 控制器,发送进入股市的消息
	 if(passId == GameBaseScene::stock_tiledID)
	 {
		 String * str = String::createWithFormat("%d-%f-%f-%d-%d",MSG_STOCK_TAG,1.0f,_richerPlayer->getTag(),MOVEPASS);
		 NotificationCenter::getInstance()->postNotification(MSG_STOCK,str);
		 return;
	 }

6、GameBaseScene类定义了股票容器类,存放所以股票相关信息

vector<float> GameBaseScene::stock_pointvec1;
vector<float> GameBaseScene::stock_pointvec2;
vector<float> GameBaseScene::stock_pointvec3;
vector<float> GameBaseScene::stock_pointvec4;
vector<float> GameBaseScene::stock_pointvec5;

在更新回合计数时,调用updateStockVec()方法,添加股票一条新纪录
void GameBaseScene::refreshRounddisplay()
{
..........
updateStockVec()
.......

}

//最多纪录14条纪录,超过的会被覆盖掉
void GameBaseScene::updateStockVec()
{
	float valule1 = rand()%800+10;
	float valule2 = rand()%800+10;
	float valule3 = rand()%800+10;
	float valule4 = rand()%800+10;
	float valule5 = rand()%800+10;

	if(stock_pointvec1.size()>13)
	{
		for(int i=0;i<13;i++)
		{
			stock_pointvec1.at(i)=stock_pointvec1.at(i+1);
			stock_pointvec2.at(i)=stock_pointvec2.at(i+1);
			stock_pointvec3.at(i)=stock_pointvec3.at(i+1);
			stock_pointvec4.at(i)=stock_pointvec4.at(i+1);
			stock_pointvec5.at(i)=stock_pointvec5.at(i+1);
			
		}
		stock_pointvec1.at(13) =valule1;
		stock_pointvec2.at(13) =valule2;
		stock_pointvec3.at(13) =valule3;
		stock_pointvec4.at(13) =valule4;
		stock_pointvec5.at(13) =valule5;
	}else
	{
		stock_pointvec1.push_back(valule1);
		stock_pointvec2.push_back(valule2);
		stock_pointvec3.push_back(valule3);
		stock_pointvec4.push_back(valule4);
		stock_pointvec5.push_back(valule5);
	}
}	 

7、当GameBaseScene收到进入股市界面的消息时,添加股市layer,显示股市
	case MSG_STOCK_TAG:
		{
				auto lineView = LineChart::createChart(player1);   
				lineView->setPosition(Point(0,0));
				moveTag = messageVector.at(4)->intValue();
				lineView->moveTag = moveTag;				
				addChild(lineView);
				..............
			break;
		}	

8、LineChart类是股票界面类

(1)initChart方法

bool LineChart::initChart(RicherPlayer* player)
{
    if ( !LayerColor::initWithColor(Color4B(0,255))) //黑色背景
	{
		 return false; 
	}
	richerPlayer = player; //角色
	playerStockMap = player->stockMap;//角色持有的股票容器
	initStockVector(playerStockMap);
	drawNode = DrawNode::create();//创建DrawNode ,准备画图
	this->addChild(drawNode);
	tv = TableView::create(this,Size(650,160));//创建TableView对象
        tv->setAnchorPoint(Point(0,0));
        tv->setPosition(10,VisibleRect::getVisibleRect().size.height * 1 /2);	
        tv->setDelegate(this);
        addChild(tv);
	initMenu();//创建菜单,包括买入,卖出,返回Menu
	selectedTag =0;  
	float tableY =  VisibleRect::getVisibleRect().size.height * 1/2;
	//选择股票时,箭头移动到相关股票
	arrowSprite_left->setPosition(600+arrowSprite_left->getContentSize().width,tableY +selectedTag*32);
	arrowSprite_right->setPosition(10,tableY + selectedTag*32);
	setData(getsock_pointVec(selectedTag));//设置股票数据
	drawpic();//画走势图
    return true;
}

(2)initMenu方法创建菜单,包括买入,卖出,返回Menu,以及箭头提示
void LineChart::initMenu()
{
	Menu* menu = Menu::create();
	menu->setPosition(CCPointZero);
    setMenu(menu);
    MenuItemImage* buyMenuItemButton = MenuItemImage::create("images/buy_normal.png","images/buy_pressed.png",this,menu_selector(LineChart::buttonCallback));
 
    buyMenuItemButton->setPosition(ccp(700,VisibleRect::getVisibleRect().size.height-110));
	buyMenuItemButton->setAnchorPoint(ccp(0,0));
	buyMenuItemButton->setTag(buy_button);
	menu->addChild(buyMenuItemButton);
	............
	arrowSprite_left = Sprite::create("images/arrow_left.png");
	arrowSprite_left->setPosition(ccp(-500,-500));
	arrowSprite_left->setAnchorPoint(ccp(0,0));
	addChild(arrowSprite_left);

	arrowSprite_right = Sprite::create("images/arrow_right.png");
	arrowSprite_right->setPosition(ccp(-500,-500));
	arrowSprite_right->setAnchorPoint(ccp(0,0));
	addChild(arrowSprite_right);
}

(3)initStockVector()添加股票上半部分表格的标题,以及给股票容器添加各个股票信息
void LineChart::initStockVector(Map<int,Stock*> stockMap)
{
	float percent = 0;
	if(GameBaseScene::stock_pointvec1.size()>1)
	{
		percent = (GameBaseScene::stock_pointvec1.at(GameBaseScene::stock_pointvec1.size()-1) - GameBaseScene::stock_pointvec1.at(GameBaseScene::stock_pointvec1.size()-2))/GameBaseScene::stock_pointvec1.at(GameBaseScene::stock_pointvec1.size()-2)*100;
	}

	stockVector.pushBack(Stock::create(800100,GameBaseScene::stock_pointvec1.at(GameBaseScene::stock_pointvec1.size()-1),stockMap.at(0)->getMakedealprice(),percent,stockMap.at(0)->getStoreNumber()));
	
	..................
	
	Label* code = Label::createWithSystemFont(LanguageString::getInstance()->getLanguageString(STOCK_CODE)->getCString(),"",20);
	code->setPosition(Point(20,410 ));
	code->setAnchorPoint(ccp(0,0));
	addChild(code);

	Label* name = Label::createWithSystemFont(LanguageString::getInstance()->getLanguageString(STOCK_NAME)->getCString(),20);
	name->setPosition(Point(stockCellWidth+20,410 ));
	name->setAnchorPoint(ccp(0,0));
	addChild(name);

	Label* Nowprice = Label::createWithSystemFont(LanguageString::getInstance()->getLanguageString(STOCK_NowPRICE)->getCString(),20);
	Nowprice->setPosition(Point(stockCellWidth*2+20,410 ));
	Nowprice->setAnchorPoint(ccp(0,0));
	addChild(Nowprice);

	Label* dealprice = Label::createWithSystemFont(LanguageString::getInstance()->getLanguageString(STOCK_DEALPRICE)->getCString(),20);
	dealprice->setPosition(Point(stockCellWidth*3+20,410 ));
	dealprice->setAnchorPoint(ccp(0,0));
	addChild(dealprice);

	Label* percentLabel = Label::createWithSystemFont(LanguageString::getInstance()->getLanguageString(STOCK_PERCENT)->getCString(),20);
	percentLabel->setPosition(Point(stockCellWidth*4+20,410 ));
	percentLabel->setAnchorPoint(ccp(0,0));
	addChild(percentLabel);


	Label* store = Label::createWithSystemFont(LanguageString::getInstance()->getLanguageString(STOCK_STORE)->getCString(),20);
	store->setPosition(Point(540,410 ));
	store->setAnchorPoint(ccp(0,0));
	addChild(store);


	playerMoneyLabel = Label::createWithSystemFont(
		String::createWithFormat("%s %d",LanguageString::getInstance()->getLanguageString(PLAYER_MONEY)->getCString(),richerPlayer->getMoney())->getCString(),20);
	playerMoneyLabel->setPosition(Point(20,450 ));
	playerMoneyLabel->setAnchorPoint(ccp(0,0));
	addChild(playerMoneyLabel);

}

(4)buttonCallback(),点击买入 ,卖出,返回的回调方法
void LineChart::buttonCallback(CCObject* pSender)
{
.............

}

(5)TableView 相关实现方法

//当点击股票时,移动箭头,更新数据,重画走势图
void LineChart::tableCellTouched(cocos2d::extension::TableView *table,cocos2d::extension::TableViewCell *cell)
{
    
	for(int i=0;i<30;i++)
	{
		this->removeChildByTag(100+i);
	}

	int tag = cell->getTag();
	selectedTag =tag;
    log("******click id = %d",tag);
	float height = VisibleRect::getVisibleRect().size.height;
	float tableY =  VisibleRect::getVisibleRect().size.height * 1/2;
	arrowSprite_left->setPosition(600+arrowSprite_left->getContentSize().width,tableY +tag*32);
	arrowSprite_right->setPosition(10,tableY + tag*32);
	log("all height is %f",height);
	log("all cellY is %f",tableY);
	setData(getsock_pointVec(tag));
	drawpic();
	    
}

//创建tableview相关单元格,当股市上涨时,背景色为红色,下跌时为绿色
TableViewCell* LineChart::tableCellAtIndex(cocos2d::extension::TableView *table,ssize_t idx)
{
    TableViewCell *cell = table->dequeueCell();  
    LabelTTF *label;

    int colorTag = 0;
	if(stockVector.at(idx)->getPercent()>0)
	{
		colorTag = 1;
	}else
	{
		colorTag = -1;
	}

    if (cell==NULL) 
	{		
        cell = TableViewCell::create();
		cell->setTag(idx);  
		for(int i=0; i<6; i++)    
		{  
			switch(i)
			{
			case 0:
				{
					StockCellCard* card = StockCellCard::createCardSprite(String::createWithFormat("%d",stockVector.at(idx)->getCode()),stockCellWidth,stockCellHeight,stockCellWidth*i+10,colorTag);    
					cell->addChild(card);
					break;
				}
			case 1:
				{
					StockCellCard* card = StockCellCard::createCardSprite(stockVector.at(idx)->getStockName(),colorTag);    
					cell->addChild(card);
					break;
				}
....................................
			}
			

		} 

    }
    
    return cell;
}

//共5支股票
ssize_t LineChart::numberOfCellsInTableView(cocos2d::extension::TableView *table){
return 5;
}

(6)画走势图

void LineChart::drawpic()
{
	drawNode->clear();
	int maxValue = getMaxValue(pointvec);
	int maxValue2 = int((maxValue+100) / 100)* 100 ;
     maxValue1 = maxValue2  / 10;
    
    
     spaceRatio = 0.08f;  //y轴间距系数
     leftRatioX = 0.1f;   //x轴左侧间距系数
    
    
    int fontSize = 20;
    string fontName = StringUtils::format("Thonburi");
    
    Size layerSize = Size(VisibleRect::getVisibleRect().size.width,VisibleRect::getVisibleRect().size.height * 1 /2);
    
    
      layerHeight1 = 30;
    float layerHeight = layerHeight1;
    float layerWidth = layerSize.width;
    int count = layerSize.width /50;
    /***********************画xy轴标签*************************************/

    for (int i = 0; i < 11; i++) {
        Point bPoint = Point(layerWidth* leftRatioX,layerHeight );
        Point ePoint = Point(layerWidth* leftRatioX+(count-2) * 50,layerHeight );
        Label* label = Label::createWithSystemFont(StringUtils::format("%d",maxValue1* i).c_str(),fontName.c_str(),fontSize);       
        label->setPosition(Point(layerWidth* 0.05f,layerHeight ));
	label->setTag(100+i);
        addChild(label);      
	drawNode->drawSegment(bPoint,ePoint,0.5,Color4F(100,100,200,200));		
        layerHeight += layerSize.height * spaceRatio;         
    }


	float layer_wd = layerSize.width * leftRatioX;
        for (int i = 0; i < count; i++) {
     

		Point bPoint  = Point(layer_wd,layerHeight1);        
		Point ePoint  = Point(layer_wd,layerSize.height * spaceRatio*10+layerHeight1); 
		if(i%2 == 0)
		{
			drawNode->drawSegment(bPoint,200));
		}

		auto labelX = Label::createWithSystemFont(StringUtils::format("%d",i).c_str(),"Thonburi",20);
                labelX->setPosition(Point(ePoint.x,0));
		labelX->setAnchorPoint(ccp(0,0));
		labelX->setTag(100+11+i);
                this->addChild(labelX);
                layer_wd += 50;
     
    }
	

	drawLine(pointvec,Color4B(0,255,255),Color4B(255,255));
}

//画走势线条
void LineChart::drawLine(vector<Point> vec,Color4B lineColor,Color4B dotColor)
{
	
    Size layerSize = Size(VisibleRect::getVisibleRect().size.width,VisibleRect::getVisibleRect().size.height * 1 /2);
       
    float layerWidth = layerSize.width;
        
    float tempWidth = layerSize.height * spaceRatio;
    float tempWidth2 = 0;
    
    float tempHeight1 = maxValue1  ;   

    double  ratio = tempWidth/tempHeight1;
    
    
    /**********************开始画线**********************/
    std::vector<Point>::iterator beforePoint;
    std::vector<Point>::iterator currentPoint;
    
    beforePoint = vec.begin();
    
    for (currentPoint = vec.begin() + 1;currentPoint != vec.end() ; currentPoint++) {
        Point bPoint  = *beforePoint;
        bPoint = Point(bPoint.x + layerWidth* leftRatioX,bPoint.y * ratio + layerHeight1 +tempWidth2);
        
        Point ePoint  = *currentPoint;
        ePoint = Point(ePoint.x + layerWidth* leftRatioX,ePoint.y * ratio + layerHeight1 +tempWidth2);
        
		drawNode->drawSegment(bPoint,0.8,Color4F::RED);
        beforePoint = currentPoint;
        
    }

     /**********************结束画线**********************/   
   
    /********************开始画点**********************************************/
    beforePoint = vec.begin();
    DrawPrimitives::setDrawColor4B(dotColor.r,dotColor.g,dotColor.b,dotColor.a);
    Point bPoint  = *beforePoint;
    bPoint = Point(bPoint.x +layerWidth* leftRatioX,bPoint.y * ratio + layerHeight1 +tempWidth2);

	drawNode->drawDot(bPoint,5,Color4F::YELLOW); 

    int i = 2;
    for (currentPoint = vec.begin() + 1;currentPoint != vec.end() ; currentPoint++) {
        Point ePoint  = *currentPoint;    
        ePoint = Point(ePoint.x + layerWidth* leftRatioX,ePoint.y * ratio + layerHeight1 + tempWidth2);              
        drawNode->drawDot(ePoint,Color4F::YELLOW); 
        i++;
    }
     /********************结束画点*********************************************END**/
    
}

//设置股票数据
void LineChart::setData(vector<float> data)
{
	pointvec.clear();
    vector<float>::iterator it;
    int i = 0;
    
    for (it = data.begin();it != data.end();it++) {
        float f = *it;
        pointvec.push_back(Point(50 * (i+1),f));               
        i++;
        
    }
    
}

//获取最大值
double LineChart::getMaxValue(std::vector<Point> vec)
{
    
    double maxY =1;
    
    for (int i = 0; i < vec.size(); i++)
	{
        float num = vec.at(i).y;
        if (maxY < abs(num)) 
		{
            maxY = abs(num);
        }
    }
    return maxY;
}

这就是大体代码,详细内容,请参考代码,如下效果图




点击下载代码

未完待续...................

Cocos2d-x 3.2 大富翁游戏项目开发-第二十八部分 游戏保存和载入存档游戏

Cocos2d-x 3.2 大富翁游戏项目开发-第二十八部分 游戏保存和载入存档游戏

1、游戏保存

如图,在右下角增加保存图标,点击后进行游戏的保存。



游戏保存采用json格式,具体如下:
{
	"map_level":2,// 游戏关卡
	"gameRoundCount":66,// 游戏回合数
	"players":[  //角色信息
		{
		"playerTag":1,//角色1
		"playerPosition_x":320,//角色所处的x坐标
		"playerPosition_y":192,//角色所处的y坐标
		"restTimes":3,//角色休息回合数
		"stop_x":0,//角色停留的x坐标
		"stop_y":0,//角色停留的y坐标
		"money":509000,//角色资金
		"strength":100,//角色体力
		"comeFromeRow":4,//角色从上一个位置的行
		"comeFromCol":10,//角色从上一个位置的列
		"isMyTurn":false,//角色是否该行走
		"skill":"1-2-2",//角色技能等级
		"lottery":"",//角色购买的彩票号码
		"stocks":[				//角色持有的股票信息
			{"stockCode":800100,"makedealprice":10,"storeNumber":100},{"stockCode":800200,"makedealprice":0,"storeNumber":0},{"stockCode":800300,{"stockCode":800400,"storeNumber":400},{"stockCode":800500,"storeNumber":0}
			]
		},{
		"playerTag":2,//角色2的保存信息,同上
		"playerPosition_x":423.635,"playerPosition_y":96,"restTimes":0,"stop_x":10,"stop_y":13,"money":410300,"strength":80,"comeFromeRow":2,"comeFromCol":15,"isMyTurn":false,"skill":"3-2-1","lottery":"","stocks":[
			{"stockCode":800100,"makedealprice":20,"storeNumber":200},"storeNumber":0}
			]
		}
	],"landlayer":[ //地块的信息,在x行,y列 地块的gid值
		{"x":5,"y":7,"gid":12},{"x":5,"y":12,{"x":6,"y":9,"y":10,"gid":11},{"x":7,"gid":1},"y":13,{"x":8,"y":11,{"x":9,"y":8,{"x":10,"y":6,{"x":11,"gid":13},"gid":15},{"x":12,"gid":14},{"x":13,{"x":14,"gid":10},{"x":15,{"x":16,{"x":17,"gid":10}
		]
}

//在添加go按钮的方法中同时添加保存按钮
void  GameBaseScene::addGoButton()
{
......
    saveMenuItemButton = MenuItemImage::create("map/save_normal.png","map/save_pressed.png",this,menu_selector(GameBaseScene::goButtonCallback));
 
	saveMenuItemButton->setPosition(ccp(winSize.width,0));
	saveMenuItemButton->setAnchorPoint(ccp(1,0));
	saveMenuItemButton->setTag(saveButtonTag);
	menu->addChild(saveMenuItemButton);
}

void GameBaseScene::goButtonCallback(cocos2d::CCObject *pSender)
{
	.........
	//当点击后调用saveGame()方法保存游戏
	if(tag == saveButtonTag)
	{
		if(saveGame())
		{
			CocosToast::createtoast(this,LanguageString::getInstance()->getLanguageString(SAVE_SUCESS)->getCString(),TOAST_SHOW_TIME,winSize/2);
		}else
		{
			CocosToast::createtoast(this,LanguageString::getInstance()->getLanguageString(SAVE_FAIL)->getCString(),winSize/2);
		}

	}
}


saveGame()进行具体的游戏保存
cocos2dx使用了rapidjson库来替换原来的jsoncpp,我们就用这个cocos2dx自带的json库
//*** 生成 json 文件,存储在 getWritablePath 文件夹下 *** ,当前在proj.win32\Debug.win32目录下

bool GameBaseScene::saveGame()
{
	rapidjson::Document writedoc; //创建Document
	writedoc.Setobject();
	rapidjson::Document::AllocatorType& allocator = writedoc.GetAllocator();
	rapidjson::Value players(rapidjson::kArrayType);//创建players数组
	writedoc.AddMember("map_level",map_level,allocator); //添加map_level属性值
	writedoc.AddMember("gameRoundCount",gameRoundCount,allocator); //添加gameRoundCount回合数

	int playerNumber=1;
	
	rapidjson::Value player1_json(rapidjson::kObjectType); //创建player1的json对象
	rapidjson::Value player2_json(rapidjson::kObjectType); //创建player2的json对象
	rapidjson::Value player3_json(rapidjson::kObjectType); //创建player3的json对象

	//保存各个角色的信息
	for(auto it=players_vector.begin();it!=players_vector.end();it++)
	{
		RicherPlayer* richerPlayer = dynamic_cast<RicherPlayer*>(*it);
		switch(playerNumber)
		{
		case 1:
			{
				//在player1_json对象中添加角色各个属性值,json object 格式 “名称/值” 
				player1_json.AddMember("playerTag",richerPlayer->getTag(),allocator); 
				player1_json.AddMember("playerPosition_x",richerPlayer->getPosition().x,allocator);
				player1_json.AddMember("playerPosition_y",richerPlayer->getPosition().y,allocator);
				 player1_json.AddMember("restTimes",richerPlayer->restTimes,allocator);
				 player1_json.AddMember("stop_x",richerPlayer->stop_x,allocator);
				 player1_json.AddMember("stop_y",richerPlayer->stop_y,allocator);

				 player1_json.AddMember("money",richerPlayer->getMoney(),allocator);
				 player1_json.AddMember("strength",richerPlayer->getStrength(),allocator);

				 player1_json.AddMember("comeFromeRow",richerPlayer->getComeFromeRow(),allocator);
				 player1_json.AddMember("comeFromCol",richerPlayer->getComeFromCol(),allocator);
				 player1_json.AddMember("isMyTurn",richerPlayer->getIsMyTurn(),allocator);

				 player1_json.AddMember("skill",String::createWithFormat("%d-%d-%d",richerPlayer->skill_vector.at(0),richerPlayer->skill_vector.at(1),richerPlayer->skill_vector.at(2))->getCString(),allocator);
		
				 std::string tempstr;
				 tempstr = "";
				 for(auto i=0;i<richerPlayer->lottery_vector.size();i++)
				 {
					 tempstr.append(String::createWithFormat("%02d",richerPlayer->lottery_vector.at(i))->getCString()).append("_");
				  }
				 player1_json.AddMember("lottery",String::createWithFormat("%s",tempstr.c_str())->getCString(),allocator);

				 rapidjson::Value stocks(rapidjson::kArrayType);
				 for(auto i=0;i<richerPlayer->stockMap.size();i++)
				{
					 rapidjson::Value stock(rapidjson::kObjectType);
					 stock.AddMember("stockCode",richerPlayer->stockMap.at(i)->getCode(),allocator);
					 stock.AddMember("makedealprice",richerPlayer->stockMap.at(i)->getMakedealprice(),allocator);
					 stock.AddMember("storeNumber",richerPlayer->stockMap.at(i)->getStoreNumber(),allocator);
					 stocks.PushBack(stock,allocator);
				 }

				 player1_json.AddMember("stocks",stocks,allocator);
				// 将player1_json加入到players数组
				 players.PushBack(player1_json,allocator);
				 break;
			}
		case 2:
			{
				//添加角色2属性值
				 player2_json.AddMember("playerTag",allocator);
				 player2_json.AddMember("playerPosition_x",allocator);
				 player2_json.AddMember("playerPosition_y",allocator);
				 player2_json.AddMember("restTimes",allocator);
				 player2_json.AddMember("stop_x",allocator);
				 player2_json.AddMember("stop_y",allocator);

				 player2_json.AddMember("money",allocator);
				 player2_json.AddMember("strength",allocator);

				 player2_json.AddMember("comeFromeRow",allocator);
				 player2_json.AddMember("comeFromCol",allocator);
				 player2_json.AddMember("isMyTurn",allocator);

				 player2_json.AddMember("skill",allocator);
				
				 std::string tempstr;
				 tempstr = "";
				 for(auto i=0;i<richerPlayer->lottery_vector.size();i++)
				 {
					 tempstr.append(String::createWithFormat("%02d",richerPlayer->lottery_vector.at(i))->getCString()).append("_");
				  }
				 player2_json.AddMember("lottery",allocator);
				 }

				 player2_json.AddMember("stocks",allocator);
				 players.PushBack(player2_json,allocator);
				 break;
			}
		case 3:
			{
			//添加角色3属性值
				 player3_json.AddMember("playerTag",allocator);
				 player3_json.AddMember("playerPosition_x",allocator);
				 player3_json.AddMember("playerPosition_y",allocator);
				 player3_json.AddMember("restTimes",allocator);
				 player3_json.AddMember("stop_x",allocator);
				 player3_json.AddMember("stop_y",allocator);

				 player3_json.AddMember("money",allocator);
				 player3_json.AddMember("strength",allocator);

				 player3_json.AddMember("comeFromeRow",allocator);
				 player3_json.AddMember("comeFromCol",allocator);
				 player3_json.AddMember("isMyTurn",allocator);

				 player3_json.AddMember("skill",richerPlayer->lottery_vector.at(i))->getCString()).append("_");
				  }
				 player3_json.AddMember("lottery",allocator);
				 }

				 player3_json.AddMember("stocks",allocator);
				 players.PushBack(player3_json,allocator);
				 break;
			}


		}


		playerNumber++;
	}
	//将players数组写入到writedoc
	 writedoc.AddMember("players",players,allocator);

	// 保存地块json
	 rapidjson::Value landlayerjson(rapidjson::kArrayType);
	Size _landLayerSize = landLayer->getLayerSize(); 
	for (int j = 0;  j < _landLayerSize.width; j++) {  
		for (int i = 0;  i < _landLayerSize.height; i++) {  
			Sprite* _sp = landLayer->tileAt(Point(j,i));  
			if (_sp) 
			{  
				rapidjson::Value landJson(rapidjson::kObjectType);
				int gid = landLayer->getTileGIDAt(Point(j,i));
				landJson.AddMember("x",j,allocator);
				landJson.AddMember("y",i,allocator);
				landJson.AddMember("gid",gid,allocator);
				landlayerjson.PushBack(landJson,allocator);
			}  

		}  
	}

	writedoc.AddMember("landlayer",landlayerjson,allocator);


	//把json数据保存到path路径文件下,文件名称为saveJsonName ,saveJsonName在SeaScene.cpp中赋值
   StringBuffer buffer;
	  rapidjson::Writer<StringBuffer> writer(buffer);
	  writedoc.Accept(writer);

	  auto path = FileUtils::getInstance()->getWritablePath();
	  path.append(saveJsonName);
	  FILE* file = fopen(path.c_str(),"wb");
	  if(file)
	  {
		fputs(buffer.GetString(),file);
		fclose(file);
	  }
	cclOG("%s",buffer.GetString());
  return true;

}

2、载入存档

效果如图


//在addMenuSprites()增加载入游戏按钮
void MenuScene:: addMenuSprites()
{
.............
	LabelTTF* loadGameTTF = LabelTTF::create(LanguageString::getInstance()->getLanguageString(LOAD_GAME)->getCString(),FONT_MENU,Btn_FontSize);
	ControlButton* loadGameBtn = ControlButton::create(loadGameTTF,btnnormal4);
	loadGameBtn->setBackgroundSpriteForState(btnPress4,Control::State::SELECTED);

	loadGameBtn->setPosition(ccp(visibleSize.width/2,visibleSize.height-360));
	loadGameBtn->setPreferredSize(Size(Btn_Width,Btn_Height));
	loadGameBtn->addTargetWithActionForControlEvents(this,cccontrol_selector(MenuScene::menuTouchDown),Control::EventType::TOUCH_DOWN);
	loadGameBtn->setTag(Btn_Load_Game_TAG);
	addChild(loadGameBtn);
........
}

//点击后进入到popupLoadGameLayer(),弹出游戏存档的界面
void MenuScene::popupLoadGameLayer()
{
  
    PopupLayer* popDialog = PopupLayer::create(DIALOG_BG);
   
	popDialog->setContentSize(CCSizeMake(Quit_Dialog_Size_Width,winSize.height)); 
    popDialog->setTitle(LanguageString::getInstance()->getLanguageString(LOAD_GAME)->getCString());
    popDialog->setContentText(LanguageString::getInstance()->getLanguageString(DIALOG_CONTENT)->getCString(),20,60,250);
	popDialog->setPopType(LOADGAME);
    popDialog->setCallbackFunc(this,callfuncN_selector(MenuScene::quitButtonCallback));
   
    popDialog->addButton(BUTTON_BG2,BUTTON_BG3,LanguageString::getInstance()->getLanguageString(CANCEL)->getCString(),Btn_Quit_Cancel_TAG);
  
    this->addChild(popDialog);
}

PopupLayer会根据pop类型,调用setLoadGameContext(),创建载入游戏的对话框界面
void PopupLayer::onEnter()
{
......
	case LOADGAME:
		{

			setLoadGameContext(contentSize);
			break;
		}
......
}

//该方法主要是判断path路径下是否有存档文件。如果没有存档,显示无存档,有则显示关卡图片
void PopupLayer::setLoadGameContext(Size size)
{
	Menu* menu = Menu::create();
	menu->setPosition(CCPointZero);  
	//判断是否有夏日海滩的存档文件
	  auto beach_path = FileUtils::getInstance()->getWritablePath();
	  beach_path.append("beach_save.json");
	  FILE* beach_file = fopen(beach_path.c_str(),"r");
	  //有则显示海滩图片,否则显示无存档
	  if(beach_file)
	  {
		beachLoadGameMenuItem = MenuItemImage::create("map/beach_load_normal.png","map/beach_load_pressed.png",menu_selector(PopupLayer::loadGameButtonCallback)); 
		beachLoadGameMenuItem->setPosition(winSize/2+Size(0,120));
		beachLoadGameMenuItem->setTag(save_beach_tag);
		menu->addChild(beachLoadGameMenuItem);
		fclose(beach_file);
	  }else
	  {
		beachLoadGameMenuItem = MenuItemImage::create("map/blank.png","map/blank.png",menu_selector(PopupLayer::loadGameButtonCallback));
		beachLoadGameMenuItem->setPosition(winSize/2+Size(0,120));
		menu->addChild(beachLoadGameMenuItem);
	  }

	//判断是否有海底世界的存档文件
	  auto sea_path = FileUtils::getInstance()->getWritablePath();
	  sea_path.append("sea_save.json");
	  FILE* sea_file = fopen(sea_path.c_str(),"r");
	  if(sea_file)
	  {
		seaLoadGameMenuItem = MenuItemImage::create("map/sea_load_normal.png","map/sea_load_pressed.png",menu_selector(PopupLayer::loadGameButtonCallback)); 
		seaLoadGameMenuItem->setPosition(winSize/2+Size(0,20));
		seaLoadGameMenuItem->setTag(save_sea_tag);
		menu->addChild(seaLoadGameMenuItem);
		fclose(sea_file);
	  }else
	  {
		seaLoadGameMenuItem = MenuItemImage::create("map/blank.png",menu_selector(PopupLayer::loadGameButtonCallback));
		seaLoadGameMenuItem->setPosition(winSize/2+Size(0,20));
		menu->addChild(seaLoadGameMenuItem);
	  }
	//判断是否有空中花园的存档文件
	  auto garden_path = FileUtils::getInstance()->getWritablePath();
	  garden_path.append("garden_save.json");
	  FILE* garden_file = fopen(garden_path.c_str(),"r");
	  if(garden_file)
	  {
		gardenLoadGameMenuItem = MenuItemImage::create("map/garden_load_normal.png","map/garden_load_pressed.png",menu_selector(PopupLayer::loadGameButtonCallback)); 
		gardenLoadGameMenuItem->setPosition(winSize/2+Size(0,-80));
		gardenLoadGameMenuItem->setTag(save_garden_tag);
		menu->addChild(gardenLoadGameMenuItem);
		fclose(garden_file);
	  }else
	  {
		gardenLoadGameMenuItem = MenuItemImage::create("map/blank.png",menu_selector(PopupLayer::loadGameButtonCallback));
		gardenLoadGameMenuItem->setPosition(winSize/2+Size(0,-80));
		menu->addChild(gardenLoadGameMenuItem);
	  }

	addChild(menu);

}

//当点击各个存档文件图片后,载入相应的游戏存档,恢复游戏
void PopupLayer::loadGameButtonCallback(cocos2d::CCObject *pSender)
{
	
	int tag = ((Node*)pSender)->getTag();
	if(tag == save_beach_tag)
	{
		log("beach load");
	}
	if(tag == save_sea_tag)
	{
		//点击海底世界图片,首先进入海底世界关卡
		TransitionFadeBL* scene = 	TransitionFadeBL::create(1,SeaScene::createScene());
		Director::getInstance()->pushScene(scene);
		//然后发送载入游戏的消息
		String * str = String::createWithFormat("%d-%d",MSG_LOAD_GAME_TAG,tag);
		NotificationCenter::getInstance()->postNotification(MSG_LOAD_GAME,str);	

		log("sea load");
	}
	if(tag == save_garden_tag)
	{
		log("garden load");
	}
}

//GameBaseScene收到载入的消息后,调用reloadGame()方法,载入游戏存档
void GameBaseScene::receivednotificationOMsg(Object* data)
{
........
	case MSG_LOAD_GAME_TAG:
		{
			int map_level = messageVector.at(1)->intValue();
			reloadGame(map_level);			
			break;
		}
.......
}

//开始具体游戏的载入恢复
bool GameBaseScene::reloadGame(int map_level)
{
	//根据关卡,载入相应的存档文件
	auto path = FileUtils::getInstance()->getWritablePath();
	switch(map_level)
	{
	case 1:
		{
			path.append("beach_save.json");
			break;
		}
	case 2:
		{
			path.append("sea_save.json");
			break;
		}
	case 3:
		{
			path.append("garden_save.json");
			break;
		}

	}
	//*** 读取 json 文件 ***
	rapidjson::Document readdoc;
	bool bRet = false;
	ssize_t size = 0;
	std::string load_str;

	// getFileData 如果不指定,读取根目录是 Resource 文件夹
	unsigned char* titlech = FileUtils::getInstance()->getFileData(path,"r",&size);
	load_str = std::string((const char*)titlech,size);


	readdoc.Parse<0>(load_str.c_str());	
	if(readdoc.HasParseError())
	{
		cclOG("GetParseError%s\n",readdoc.GetParseError());
	}

	if(!readdoc.IsObject())
		return 0;

	//回合数载入
	rapidjson::Value& _gameRoundCount = readdoc["gameRoundCount"];
	gameRoundCount = _gameRoundCount.GetInt();
	refreshRounddisplay();

	//土地等级载人
	rapidjson::Value& _landlayer = readdoc["landlayer"];
	if(_landlayer.IsArray())
	{
		for(int i=0; i<_landlayer.Capacity(); i++)
		{
			rapidjson::Value& arraydoc = _landlayer[i];

			int x = arraydoc["x"].GetInt();
			cclOG("x:%d",x);
			int y = arraydoc["y"].GetInt();
			cclOG("y:%d",y);
			int gid = arraydoc["gid"].GetInt();
			cclOG("gid:%d",gid);

			landLayer->setTileGID(gid,ccp(x,y));

		}
	}

	//人物信息载人
	rapidjson::Value& _players = readdoc["players"];
	if(_players.IsArray())
	{
		for(int i=0; i<_players.Capacity(); i++)
		{
			rapidjson::Value& arraydoc = _players[i];

			RicherPlayer* _richerPY = players_vector.at(i);

			int _restTimes = arraydoc["restTimes"].GetInt();	
			float _playerPositionX = arraydoc["playerPosition_x"].GetDouble();	
			float _playerPositionY = arraydoc["playerPosition_y"].GetDouble();
			int _stop_x = arraydoc["stop_x"].GetInt();			
			int _stop_y = arraydoc["stop_y"].GetInt();			
			int _money = arraydoc["money"].GetInt();
			int _strength = arraydoc["strength"].GetInt();
			int _comeFromeRow = arraydoc["comeFromeRow"].GetInt();
			int _comeFromCol = arraydoc["comeFromCol"].GetInt();
			bool _isMyTurn = arraydoc["isMyTurn"].GetBool();
			const char* _skill = arraydoc["skill"].GetString();
			const char* _lottery = arraydoc["lottery"].GetString();

			Vector<String*> lotteryVector = Util::splitString(_lottery,"_");
			for(int i=0;i<lotteryVector.size();i++ )
			{
				_richerPY->lottery_vector.push_back(lotteryVector.at(i)->intValue());
			}

			_richerPY->restTimes = _restTimes;
			_richerPY->setPosition(ccp(_playerPositionX,_playerPositionY));
			_richerPY->setComeFromeRow(_comeFromeRow);
			_richerPY->setComeFromCol(_comeFromCol);
			_richerPY->setIsMyTurn(_isMyTurn);
			_richerPY->stop_x = _stop_x;
			_richerPY->stop_y = _stop_y;
			_richerPY->setMoney(_money);
			_richerPY->setStrength(_strength);

			Vector<String*> skillVs = Util::splitString(_skill,"-");
			_richerPY->skill_vector.at(0) = skillVs.at(0)->intValue();
			_richerPY->skill_vector.at(1) = skillVs.at(1)->intValue();
			_richerPY->skill_vector.at(2) = skillVs.at(2)->intValue();
			refreshMoneyLabel(_richerPY,0);
			refreshStrengthLabel(_richerPY,0);

		}
	}

	//股票载入

	for(int i=0; i<players_vector.size(); i++)
	{
		RicherPlayer* _richerPY = players_vector.at(i);

		rapidjson::Value& _stocks = _players[i]["stocks"];
		if(_stocks.IsArray())
		{
			for(int i=0; i<_stocks.Capacity(); i++)
			{
				rapidjson::Value& arraydoc = _stocks[i];
				int _stockCode = arraydoc["stockCode"].GetInt();	
				int _makedealprice = arraydoc["makedealprice"].GetInt();			
				int _storeNumber = arraydoc["storeNumber"].GetInt();	
				_richerPY->stockMap.at(i)->setMakedealprice(_makedealprice);
				_richerPY->stockMap.at(i)->setStoreNumber(_storeNumber);
			}
		}

	}

    return 0;
}


至此,游戏从保存文档中恢复完毕。

点击下载代码 未完待续................

Cocos2d-x 3.2 大富翁游戏项目开发-第二十部分 螃蟹挡路(code)

Cocos2d-x 3.2 大富翁游戏项目开发-第二十部分 螃蟹挡路(code)

该部分我们添加螃蟹伤人事件,道路位置随机添加螃蟹精灵,当角色行走完毕如果停留位置碰到了螃蟹,首先播放伤人动画,然后是播放救护车把角色带走动画。
如果轮流到该角色行走时,吐司提示住院还有几天,当住院天数到期,该角色才可以继续行走

新建了几个精灵类

Item_crab.cpp 螃蟹
Item_emergency.cpp 救护车
Item_fog.cpp 烟雾

Item_stretcher.cpp 担架


//该方法在场景中添加这几个精灵
void GameBaseScene::initItemSprite()
{
	item_crab = (Item_crab*)Item::create(ITEM_CRAB);
	addChild(item_crab);
	item_crab->runAction(RepeatForever::create(item_crab->getnormal_anmi()));
	item_crab->setVisible(false);


	emerg = Item_emergency::create();
    addChild(emerg);
	emerg->setVisible(false);

	fog = Item_fog::create();
	addChild(fog);
	fog->setVisible(false);

	strentcher = Item_stretcher::create();
	addChild(strentcher);
	strentcher->setVisible(false);
	strentcher->setAnchorPoint(ccp(0,0.9));
}

定义了定时器,每隔100秒调用该方法,更新螃蟹位置

void GameBaseScene::registerBlockWaySchedule()
{
	schedule(schedule_selector(GameBaseScene::updateBlockWaySprites),100.0f);
}

void GameBaseScene::updateBlockWaySprites(float dt)
{
	int _rand1 = rand()%(wayLayerPass_vector.size()); 
	Vec2 position = wayLayerPass_vector.at(_rand1);
	position.x -= 5;
	position.y +=tiledHeight; 
	item_crab->setVisible(true);
	item_crab->setPosition(position);
	item_crab->setAnchorPoint(ccp(0,0.6));
}

1、首先在RicherPlayer.cpp中,添加 restTimes 属性,记录受伤后,角色需要住院停留的回合数
2、修改void RicherGameController::endGo()方法
当角色行走完毕,发送MSG_BLOCK_WAY_EVENT消息,该消息用来处理行走完毕后,检查道路上是否存在螃蟹等
void RicherGameController::endGo()
{
	...........
	if(stepHasGone >= stepsCount)
	{

		String * str = String::createWithFormat("%d-%f-%f-%d",MSG_BLOCK_WAY_EVENT_TAG,0.0f,_richerPlayer->getTag());
		NotificationCenter::getInstance()->postNotification(MSG_BLOCK_WAY_EVENT,str);
		return;
	}
	.............
}

3、GameBaseScene.cpp注册MSG_BLOCK_WAY_EVENT消息观察者
void GameBaseScene::registerNotificationObserver()
{
	...........
		NotificationCenter::getInstance()->addobserver(
		this,callfuncO_selector(GameBaseScene::receivednotificationOMsg),MSG_BLOCK_WAY_EVENT,NULL);
	............

}

4、receivednotificationOMsg 具体处理该消息,根据角色调用doblockWayEvent 方法
void GameBaseScene::receivednotificationOMsg(Object* data)
{
...........
	case MSG_BLOCK_WAY_EVENT_TAG:
		{
			int playerTag = messageVector.at(3)->intValue();
			switch(playerTag)
			{
				case PLAYER_1_TAG:
				{
					doblockWayEvent(player1);
					break;
				}
				case PLAYER_2_TAG:
				{
					doblockWayEvent(player2);
					break;
				}				
			}
			
			break;
		}
.............

}

5、doblockWayEvent方法,就是根据角色判断是否碰到了螃蟹,如果碰到了,播放受伤等一系列动画,动画播放完毕后,设置角色不可见,并发送MSG_PICKONE_TOGO消息,让控制器取出下一个角色继续行走。
void GameBaseScene::doblockWayEvent(RicherPlayer* player)
{
	if(player->getBoundingBox().containsPoint(item_crab->getPosition()+ccp(item_crab->getContentSize().width/2,-item_crab->getContentSize().height/2)))
	{
			//碰到了螃蟹,设置螃蟹位置,让其不可见
			item_crab->setPosition(ccp(-200,-200));
			//设置角色随机停留回合数
			player->restTimes =  rand()%(5) + 1;;
			log("doblockWayEvent intersetcRect");
			//救护车变为可见
			emerg->setVisible(true);
			//救护车从右往左移动的距离
			int distance = tableStartPosition_x-player->getPosition().x;
			//救护车的位置
			emerg->setPosition(player->getPosition()+ccp(distance,0));
			//螃蟹和角色打架的烟雾动画
			fog->setVisible(true);
			fog->setPosition(player->getPosition());
			Repeat* repeate0 = Repeat::create(fog->getnormal_anmi(),2);
			fog->runAction(repeate0);
			//救护车的移动动画
			MoveBy* moveBy = MoveBy::create(1.0f,ccp(-distance,0));
			MoveBy* moveBy2 = MoveBy::create(0.5f,ccp(-60,0));
			Repeat* repeate = Repeat::create(emerg->getCar_go_anmi(),5);
			Repeat* repeate2 = Repeat::create(emerg->getCar_stop_anmi(),1);

			Sequence* spawnAction = Sequence::create(Spawn::create(moveBy,repeate,NULL),Spawn::create(moveBy2,repeate2,CallFunc::create(CC_CALLBACK_0(GameBaseScene::endCarGo,this)),NULL);
			spawnAction->retain();
			emerg->runAction(spawnAction);
			//根据动画播放的时间设置角色不可见
			if(player->getTag() == PLAYER_1_TAG)
			{
				scheduleOnce(schedule_selector(GameBaseScene::setPlayer1InVisible),2.5f);	
			}else if(player->getTag() == PLAYER_2_TAG)
			{
				scheduleOnce(schedule_selector(GameBaseScene::setPlayer2InVisible),2.5f);	
			}
			//Toast显示角色受伤住院天数信息
			CocosToast::createtoast(this,String::createWithFormat("%s %d %s",LanguageString::getInstance()->getLanguageString(PLAYER_HURT)->getCString(),player->restTimes,LanguageString::getInstance()->getLanguageString(RICH_DAY)->getCString())->getCString(),TOAST_SHOW_TIME,player->getPosition());
	}
	else
	{
		//如果没有碰到螃蟹,角色继续发送消息用于处理停留位置事件:是否有问号时间,然后是上下左右相邻位置房屋是否需要购买升级缴纳过路费等
		NotificationCenter::getInstance()->postNotification(MSG_HANDLE_PROP_EVENT,String::createWithFormat("%d",MSG_HANDLE_PROP_EVENT_TAG));
	}
}

//让角色和打架烟雾动画不可见
void GameBaseScene::setPlayer1InVisible(float dt)
{
	player1->setVisible(false);
	fog->setVisible(false);
}

void GameBaseScene::setPlayer2InVisible(float dt)
{
	player2->setVisible(false);
	fog->setVisible(false);
}
//救护车开来动画结束后调用该函数,播放小黄人抬担架动画
void GameBaseScene::endCarGo()
{
	strentcher->setVisible(true);
	strentcher->setPosition(emerg->getPosition());
	MoveBy* moveBy = MoveBy::create(0.5f,ccp(60,0));
	ScaleBy* scaleBy = ScaleBy::create(0.5f,0.8);
	Repeat* repeate = Repeat::create(strentcher->getStretcher_empty_anmi(),1);
	ScaleBy* scaleBy2 = ScaleBy::create(0.5f,1.25);
	MoveBy* moveBy2 = MoveBy::create(0.5f,0));
	Repeat* repeate2 = Repeat::create(strentcher->getStretcher_full_anmi(),1);
	Sequence* spawnAction = Sequence::create(Spawn::create(moveBy,scaleBy,scaleBy2,CallFunc::create(CC_CALLBACK_0(GameBaseScene::startCarGoAgain,NULL);
	spawnAction->retain();
	strentcher->runAction(spawnAction);
}

//小黄人抬担架动画播放完毕后,播放救护车开走动画
void GameBaseScene::startCarGoAgain()

{
	strentcher->setVisible(false);
	int distance = emerg->getPositionX();
	MoveBy* moveBy = MoveBy::create(1.0f,0));
	Repeat* repeate = Repeat::create(emerg->getCar_go_anmi(),5);
	Sequence* spawnAction = Sequence::create(Spawn::create(moveBy,CallFunc::create(CC_CALLBACK_0(GameBaseScene::endCarGoLast,NULL);
	spawnAction->retain();
	emerg->runAction(spawnAction);
}

//救护车开走后,调用该方法,让救护车不可见,并发送MSG_PICKONE_TOGO消息,让其他角色继续行走
void GameBaseScene::endCarGoLast()

{
	emerg->setVisible(false);
	NotificationCenter::getInstance()->postNotification(MSG_PICKONE_TOGO,MSG_PICKONE_TOGO_TAG));
}

6、查看控制器中pickOnePlayerToGo方法
void RicherGameController::pickOnePlayerToGo()
{
	for(auto it=GameBaseScene::players_vector.begin();it!=GameBaseScene::players_vector.end();it++)
		{
			RicherPlayer* richerPlayer = dynamic_cast<RicherPlayer*>(*it);
			//如果轮流到该角色行走,但是在受伤休息,则发送MSG_REST 休息消息
			if(richerPlayer->getIsMyTurn() && richerPlayer->restTimes > 0 )
			{
				String * str = String::createWithFormat("%d-%f-%f-%d",MSG_REST_TAG,richerPlayer->getTag());
				NotificationCenter::getInstance()->postNotification(MSG_REST,str);
				return;
			}
			//如果轮流到角色行走,并且不在休息
			if(richerPlayer->getIsMyTurn() && richerPlayer->restTimes == 0)
			{
				richerPlayer->setVisible(true);
				//角色1 发送MSG_GO消息,显示go按钮
				if(richerPlayer->getTag() == PLAYER_1_TAG )
				{
					NotificationCenter::getInstance()->postNotification(MSG_GO,MSG_GO_SHOW_TAG));
					return;
				}
				//其他角色 获取路径,自动行走
				int randNumber = rand()%6 + 1; 
				RouteNavigation::getInstance()->getPath(richerPlayer,randNumber,GameBaseScene::canPassGrid,GameBaseScene::tiledRowsCount,GameBaseScene::tiledColsCount);
				richerPlayer->startGo(RouteNavigation::getInstance()->getPathRow_vector(),RouteNavigation::getInstance()->getPathCols_vector());
				return;
			}

		}
	oneRoundDone = true;
	//走到这里,说明角色都行走完毕,调用resetPlayerGoTurn方法,重新设置角色IsMyTurn为true等
	resetPlayerGoTurn();
	
}

//每一个回合结束后调用该方法,让角色的restTimes 减1 
void RicherGameController::resetPlayerGoTurn()
{
	for(auto it=GameBaseScene::players_vector.begin();it!=GameBaseScene::players_vector.end();it++)
	{
		RicherPlayer* richerPlayer = dynamic_cast<RicherPlayer*>(*it);
		richerPlayer->setIsMyTurn(true);

		richerPlayer->restTimes--;
		if(richerPlayer->restTimes < 0)
		{
			richerPlayer->restTimes = 0;
		}

	}
	//发送MSG_ROUND_COUNT 更新回合数
	NotificationCenter::getInstance()->postNotification(MSG_ROUND_COUNT,MSG_ROUND_COUNT_TAG));
	//设置完毕,继续调用pickOnePlayerToGo ,进行下一轮行走
	pickOnePlayerToGo();
}

7、GameBaseScene.cpp注册MSG_REST消息观察者
void GameBaseScene::registerNotificationObserver()
{
...........
		NotificationCenter::getInstance()->addobserver(
		this,MSG_REST,NULL);
.............
}

8、处理MSG_REST消息
当收到休息的消息,首先设置IsMyTurn 为false,并吐司显示还需要休息几个回合,显示结束后调用GameBaseScene::sendMSGPickOnetoGO 方法,继续下一个角色行走
void GameBaseScene::receivednotificationOMsg(Object* data)
{
..........
	case MSG_REST_TAG:
		{
			buy_land_x = messageVector.at(1)->floatValue();
			buy_land_y = messageVector.at(2)->floatValue();
			int playerTag = messageVector.at(3)->intValue();
			switch(playerTag)
			{
				case PLAYER_1_TAG:
				{
					player1->setIsMyTurn(false);									
					CocosToast::createtoast(this,LanguageString::getInstance()->getLanguageString(IN_HOSPITAL_REMAIN)->getCString(),player1->restTimes,player1->getPosition(),(SEL_CallFun)&GameBaseScene::sendMSGPickOnetoGO);				
					break;
				}
				case PLAYER_2_TAG:
				{
					player2->setIsMyTurn(false);
					CocosToast::createtoast(this,player2->restTimes,player2->getPosition(),(SEL_CallFun)&GameBaseScene::sendMSGPickOnetoGO);
					break;
				}				
			}
			
			break;
		}
..........
}

9、CocosToast.cpp 文件,添加了SEL_CallFun method 函数指针,用来调用传进来的方法,如 GameBaseScene::sendMSGPickOnetoGO 发送行走消息。
typedef void (Layer::*SEL_CallFun)();
void CocosToast::createtoast(cocos2d::Node *node,const std::string &msg,const float &time,Vec2 point,SEL_CallFun method);

10、当角色行走完毕 停留位置的房屋所有者如果在住院,则免缴过路费。吐司提示,并调用GameBaseScene::sendMSGPickOnetoGO 发送行走消息

void GameBaseScene::payTolls(int payTag,float x,float y,int playerTag)
{
.............
	if(landOwner->restTimes > 0)
			{
				CocosToast::createtoast(this,LanguageString::getInstance()->getLanguageString(IN_HOSPITAL)->getCString(),(SEL_CallFun)&GameBaseScene::sendMSGPickOnetoGO);
				return;
			}
.............
}

部分效果图



点击下载代码


http://download.csdn.net/detail/lideguo1979/8369525


未完待续..................

今天关于火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二十五节: 3D项目优化方案的介绍到此结束,谢谢您的阅读,有关Cocos2d-x 3.2 大富翁游戏项目开发-第二十九部分 游戏配音、Cocos2d-x 3.2 大富翁游戏项目开发-第二十五部分 大富翁股市、Cocos2d-x 3.2 大富翁游戏项目开发-第二十八部分 游戏保存和载入存档游戏、Cocos2d-x 3.2 大富翁游戏项目开发-第二十部分 螃蟹挡路(code)等更多相关知识的信息可以在本站进行查询。

本文标签: