如图,在右下角增加保存图标,点击后进行游戏的保存。
游戏保存采用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; }
至此,游戏从保存文档中恢复完毕。
点击下载代码 未完待续................ 原文链接:https://www.f2er.com/cocos2dx/344256.html