Cocos2dx游戏教程(八):“见缝插针”,UserDefault数据存储与读取

前面已经介绍了前两个场景使用,中间涉及到了几个核心类,菜单,label标签等,同样我们也实现了两个场景的切换和关卡数字的显示。那么我们怎么记录游戏相关的信息呢?

下面我们将走进cocos2dx的数据存储,UserDefault。

一、UserDefault介绍

cocos2dx提供了一个数据存储类UserDefault,可以作为一个轻量级的数据库来使用。它支持五种基本数据bool、int、float、double、string的存储,3.x还增加了Date类型
新增类型如下,在此不做具体介绍。

void setDataForKey(const char* pKey, const Data& value);
Data getDataForKey(const char* pKey, const Data& defaultValue = Data::Null);

UserDefault和我们前面看到的导演类一样,是一个单例模式,采用XML存储技术,就是一般的键值对,这个类似C++中的map的映射(k,v)。一个关键字对应一个值。其实现的接口也比较简单实用,通过传统的set()、get()方法访问和修改值。使用方法如下:

1、设置数据

UserDefault::getInstance()->setStringForKey("string","value");  //根据键值写入std::string类型字符串
UserDefault::getInstance()->setIntegerForKey("int",1);
UserDefault::getInstance()->setFloatForKey("float",1.0f);
UserDefault::getInstance()->setDoubleForKey("double",1.0);
UserDefault::getInstance()->setBoolForKey("bool",false);

写入后不要忘了存储到硬盘上哦

UserDefault::getInstance()->flush();

2、读取数据

auto s = UserDefault::getInstance()->getStringForKey("string");    //根据键值取出std::string类型字符串
auto i = UserDefault::getInstance()->getIntegerForKey("int");
auto f = UserDefault::getInstance()->getFloatForKey("float");
auto d = UserDefault::getInstance()->getDoubleForKey("double");
auto b = UserDefault::getInstance()->getBoolForKey("bool");

是不是感觉数据存储很简单?
那么我们存储的数据在哪呢?
在这里插入图片描述
我们打开看看里面有什么

<?xml version="1.0" encoding="UTF-8"?>
<userDefaultRoot>
    <_isInitGameData>true</_isInitGameData>
    <current_max_level>1</current_max_level>
</userDefaultRoot>

里面记录了是否初始化了游戏数据,记录的当前最大的关卡数。
当我们希望对某些数据只初始化一次的时候,我们可以用如下方式处理:

if(!UserDefault::getInstance()->getBoolForKey("_isInitGameData")){
	//判断是否是第一次调用
	UserDefault::getInstance()->setBoolForKey("_isInitGameData",true);
}

UserDefault主要的用法到这就结束啦,当然cocos2dx支持的数据存储有很多种,Json,xml,plist,csv,sqlite3等,大家可以慢慢的尝试下,下面通过具体的事例应用来介绍一下。

二、新增类

前面主要是介绍了相关的基础知识,现在更进一步,新增数据存储类,我们看一下新增了哪些类呢
到这新增了类有
在这里插入图片描述

1、CSVParser
CSV数据格式解析可以参考
https://blog.csdn.net/slaron/article/details/55517440
本例游戏配置采用的就是CSV数据格式
在这里插入图片描述
这里面保存了关卡的id,默认小球的数量,玩家需要插得小球的数量,中心圆盘旋转方向,遮罩层的开始结束角(后期介绍)
2、GameDataManage
我们来看一下GameDataManage做了哪些事情,为了看的更清除,这里简单的使用单个方法来获取各个属性,小伙伴们可使用其他的方法获取,比如结构体对象保存属性。

头文件GameDataManage.h

#pragma once
#include "cocos2d.h"
#include "CSVParser.h"

USING_NS_CC;
using namespace CSVParser;

class GameDataManage {
public:
	static GameDataManage* getInstance();
	static GameDataManage* singleInstance;
	GameDataManage();
	~GameDataManage();
	virtual bool init();
public:
	//载入游戏数据
	void loadGameData(int level);
	//获得出现的小球数
	int getObsBallNum(int level);
	//获得用户的小球数
	int getUserBallNum(int level);
	//获得旋转的速度
	int getRotateSpeed(int level);
	//获得旋转的方向
	int getTurnDir(int level);
	//获得遮罩圆开始角度
	int getStartAngle(int level);
	//获得遮罩圆结束角度
	int getEndAngle(int level);
public:
	//获得是否初始化
	bool getIsFirstInit();
	//存储是否初始化
	void setIsFirstInit(bool init);

	//获得当前已经完成的关卡数
	int getCurrentMaxLevel();
	//保存当前已经完成的关卡数
	void setCurrentMaxLevel(int level);
public:
	//初始出现的小球数
	int obsBallNum;
	//用户的小球数
	int userBallNum;
	//旋转的速度
	int rotateSpeed;
	//旋转的方向
	int turnDir;
	//总行数
	int totalRow;
	//遮罩圆开始角度
	int startAngle;
	//遮罩圆开始角度
	int endAngle;
private:
	Csv csv;
};

类文件GameDataManage.cpp

#include "GameDataManage.h"
#include "UserDefault.h"
#include "GBK2UTF.h"

GameDataManage* GameDataManage::singleInstance = NULL;

GameDataManage* GameDataManage::getInstance()
{
	if (NULL == singleInstance) {
		singleInstance = new GameDataManage();
	}
	return singleInstance;
}

GameDataManage::GameDataManage() 
	: csv("data/data.csv") 
{
	totalRow = csv.getRowCount() - 1;
}

GameDataManage::~GameDataManage() {
}

bool GameDataManage::init() {

	return true;
}

//载入游戏数据
void GameDataManage::loadGameData(int level) {
	//获得出现的小球数
	getObsBallNum(level);
	//获得用户的小球数
	getUserBallNum(level);
	//获得旋转的方向
	getTurnDir(level);
	//获得旋转的速度
	getRotateSpeed(level);
	//获得遮罩圆开始角度
	getStartAngle(level);
	//获得遮罩圆结束角度
	getEndAngle(level);
}

//获得出现的小球数
int GameDataManage::getObsBallNum(int level) {
	obsBallNum = std::atoi(csv[level]["ObsNum"].c_str());
	log("%s %d", GBK2UTF("出现的小球数:"), obsBallNum);
	return obsBallNum;
}

//获得用户的小球数
int GameDataManage::getUserBallNum(int level) {
	userBallNum = std::atoi(csv[level]["UserNum"].c_str());
	log("%s %d", GBK2UTF("用户的小球数:"), userBallNum);
	return userBallNum;
}

//获得旋转的方向
int GameDataManage::getTurnDir(int level) {
	turnDir = std::atoi(csv[level]["TurnDir"].c_str());
	if(turnDir == 1) {
		log("%s", GBK2UTF("旋转的方向:逆时针"));
	} else {
		log("%s", GBK2UTF("旋转的方向:顺时针"));
	}
	return turnDir;
}


//获得旋转的速度
int GameDataManage::getRotateSpeed(int level) {
	rotateSpeed = std::atoi(csv[level]["RotateSpeed"].c_str());
	if(turnDir == 1) {
		rotateSpeed = 0 - rotateSpeed;
	}
	log("%s %d", GBK2UTF("旋转的速度:"), rotateSpeed);
	return rotateSpeed;
}


//获得遮罩圆开始角度
int GameDataManage::getStartAngle(int level) {
	startAngle = std::atoi(csv[level]["StartAngle"].c_str());
	log("%s %d", GBK2UTF("遮罩圆开始角度:"), startAngle);
	return startAngle;
}

//获得遮罩圆结束角度
int GameDataManage::getEndAngle(int level) {
	endAngle = std::atoi(csv[level]["EndAngle"].c_str());
	log("%s %d", GBK2UTF("遮罩圆结束角度:"), endAngle);
	return endAngle;
}


/************************************************************************************************
*	用户数据
************************************************************************************************/
//获得是否初始化
bool GameDataManage::getIsFirstInit() {
	auto isInit = getBoolFromXML("_isInitGameData");
	return isInit;
}
//存储是否初始化
void GameDataManage::setIsFirstInit(bool init) {
	setBoolToXML("_isInitGameData", init);
}

//获得当前已经完成的关卡数
int GameDataManage::getCurrentMaxLevel() {
	auto level = getIntegerFromXML("current_max_level");
	return level;
}
//保存当前已经完成的关卡数
void GameDataManage::setCurrentMaxLevel(int level) {
	if(level >= getIntegerFromXML("current_max_level")) {
		setIntegerToXML("current_max_level", level);
	}	
}

这里面出现CSV和GBK2UTF的类,GBK2UTF只做编码转换,大家可以把创建的文件已UTF创建,就不需要转换啦,所以在此不做多样赘述

3、UserDefault
UserDefault类只是简单的对原UserDefault进行了封装,大家可以不用关注,可以吧后面代码中是数据获取都改为原生方式或者采用自己定义的数据结构,单纯的修改,很单纯~~

#include "UserDefault.h"
#include "cocos2d.h"
#include "cocos-ext.h"

USING_NS_CC;
USING_NS_CC_EXT;

using namespace std;
//保存数据到XML
void setStringToXML(const char* key, std::string value){
	userDefault->setStringForKey(key,value);
	userDefault->flush();
}

void setIntegerToXML(const char* key,int value){
	userDefault->setIntegerForKey(key,value);
	userDefault->flush();
}

void setBoolToXML(const char* key,bool value){
	userDefault->setBoolForKey(key,value);
	userDefault->flush();
}

void setDoubleToXML(const char* key,double value){
	userDefault->setDoubleForKey(key,value);
	userDefault->flush();
}

void setFloatToXML(const char* key,float value){
	userDefault->setFloatForKey(key,value);
	userDefault->flush();
}

//从XML读取数据
std::string getStringFromXML(const char* key){
	return userDefault->getStringForKey(key);   
}

int getIntegerFromXML(const char* key){
	return userDefault->getIntegerForKey(key);   
}

bool getBoolFromXML(const char* key){
	return userDefault->getBoolForKey(key);   
}

double getDoubleToXML(const char* key){
	return userDefault->getDoubleForKey(key);   
}

float getFloatToXML(const char* key){
	return userDefault->getFloatForKey(key);   
}

那么整个数据存储都已经完成了,我们在WelcomeScene进行初次的数据获取吧
在WelcomeScene.h中加上初始化方法

//初始化第一次登录数据
void initFirstLoginData();

具体实现

//初始化第一次登录数据
void WelcomeScene::initFirstLoginData() {
	if (!GameDataManage::getInstance()->getIsFirstInit()) {
		//设置已经初始化
		GameDataManage::getInstance()->setIsFirstInit(true);
		//初始化当前最大关卡
		GameDataManage::getInstance()->setCurrentMaxLevel(1);
	}
}

别忘了在init中调用啊
到这里我们起两个场景包括数据加载已经实现了哦,我们看看关卡加载的效果吧
我们在GameMenuScene init方法中添加如下代码

//获得最大关卡数据
auto maxLevel = GameDataManage::getInstance()->getCurrentMaxLevel();
GameDataManage::getInstance()->loadGameData(maxLevel);

我们在运行一下看看,运行后在控制台我们可以看到,CSV格式的数据是不是被打印出来啦
在这里插入图片描述
我们会建立场景,会加载数据,离我们游戏的完成是不是又进了一步呢。
别着急,我们前面介绍了replaceScene,是不是感觉场景切换太过单调呢?下一节将会给大家介绍场景的过渡和相关效果哦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章