Cocos2dx game tutorial: "see the needle", UserDefault data storage and reading

We have introduced the use of the first two scenarios, involving several core classes, menus, label labels, etc. in the middle, we have also realized the switching of the two scenarios and the display of the level numbers. So how do we record game related information?

Next, we will go to the data store of cocos2dx, UserDefault.

1, About UserDefault

cocos2dx provides a data storage class UserDefault, which can be used as a lightweight database. It supports the storage of five basic data: bool, int, float, double and string. 3.x also adds Date type
The new types are as follows, which will not be introduced here.

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

UserDefault, like the director class we saw earlier, is a singleton pattern. It uses XML storage technology, which is a general key value pair, similar to the map mapping (k,v) in C + +. A keyword corresponds to a value. The implemented interface is also simple and practical, and the value is accessed and modified by the traditional set() and get() methods. The method of use is as follows:

1. Set data

UserDefault::getInstance()->setStringForKey("string","value");  //Write std::string type string according to key value
UserDefault::getInstance()->setIntegerForKey("int",1);
UserDefault::getInstance()->setFloatForKey("float",1.0f);
UserDefault::getInstance()->setDoubleForKey("double",1.0);
UserDefault::getInstance()->setBoolForKey("bool",false);

Don't forget to store it on the hard disk after writing

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

2. Read data

auto s = UserDefault::getInstance()->getStringForKey("string");    //Get std::string type string according to key value
auto i = UserDefault::getInstance()->getIntegerForKey("int");
auto f = UserDefault::getInstance()->getFloatForKey("float");
auto d = UserDefault::getInstance()->getDoubleForKey("double");
auto b = UserDefault::getInstance()->getBoolForKey("bool");

Is it easy to store data?
So where is the data we store?

Let's open it and see what's in it

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

It records whether the game data has been initialized and the current maximum number of levels.
When we want to initialize some data only once, we can process it as follows:

if(!UserDefault::getInstance()->getBoolForKey("_isInitGameData")){
	//Determine whether it is the first call
	UserDefault::getInstance()->setBoolForKey("_isInitGameData",true);
}

The main usage of UserDefault is over. Of course, there are many kinds of data storage supported by cocos2dx, such as Json, xml, plist, csv, sqlite3, etc., you can try it slowly, and then introduce it through specific examples.

2, New class

The previous part mainly introduces the related basic knowledge. Now we will go further and add new data storage classes. Let's see which classes have been added
The new classes are

1,CSVParser
For CSV data format analysis, please refer to
https://blog.csdn.net/slaron/article/details/55517440
In this case, the game configuration uses the CSV data format

It stores the id of the level, the number of default balls, the number of balls players need to insert, the rotation direction of the center disk, and the start and end angle of the mask layer (later introduction)
2,GameDataManage
Let's take a look at what GameDataManage has done. In order to see more clearly, here we simply use a single method to get various properties. Partners can use other methods to get, such as structure object to save properties.

Header file 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:
	//Load game data
	void loadGameData(int level);
	//Get the number of balls that appear
	int getObsBallNum(int level);
	//Get user's number of balls
	int getUserBallNum(int level);
	//Get the speed of rotation
	int getRotateSpeed(int level);
	//Get the direction of rotation
	int getTurnDir(int level);
	//Get mask circle start angle
	int getStartAngle(int level);
	//Get mask circle end angle
	int getEndAngle(int level);
public:
	//Get whether to initialize
	bool getIsFirstInit();
	//Whether the storage is initialized
	void setIsFirstInit(bool init);

	//Get the current number of completed levels
	int getCurrentMaxLevel();
	//Save the number of currently completed levels
	void setCurrentMaxLevel(int level);
public:
	//The number of small balls at the beginning
	int obsBallNum;
	//User's number of balls
	int userBallNum;
	//Speed of rotation
	int rotateSpeed;
	//Direction of rotation
	int turnDir;
	//Total number of banks
	int totalRow;
	//Mask circle start angle
	int startAngle;
	//Mask circle start angle
	int endAngle;
private:
	Csv csv;
};

Class file 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;
}

//Load game data
void GameDataManage::loadGameData(int level) {
	//Get the number of balls that appear
	getObsBallNum(level);
	//Get user's number of balls
	getUserBallNum(level);
	//Get the direction of rotation
	getTurnDir(level);
	//Get the speed of rotation
	getRotateSpeed(level);
	//Get mask circle start angle
	getStartAngle(level);
	//Get mask circle end angle
	getEndAngle(level);
}

//Get the number of balls that appear
int GameDataManage::getObsBallNum(int level) {
	obsBallNum = std::atoi(csv[level]["ObsNum"].c_str());
	log("%s %d", GBK2UTF("Number of balls:"), obsBallNum);
	return obsBallNum;
}

//Get user's number of balls
int GameDataManage::getUserBallNum(int level) {
	userBallNum = std::atoi(csv[level]["UserNum"].c_str());
	log("%s %d", GBK2UTF("Number of balls for users:"), userBallNum);
	return userBallNum;
}

//Get the direction of rotation
int GameDataManage::getTurnDir(int level) {
	turnDir = std::atoi(csv[level]["TurnDir"].c_str());
	if(turnDir == 1) {
		log("%s", GBK2UTF("Direction of rotation: counter clockwise"));
	} else {
		log("%s", GBK2UTF("Direction of rotation: clockwise"));
	}
	return turnDir;
}


//Get the speed of rotation
int GameDataManage::getRotateSpeed(int level) {
	rotateSpeed = std::atoi(csv[level]["RotateSpeed"].c_str());
	if(turnDir == 1) {
		rotateSpeed = 0 - rotateSpeed;
	}
	log("%s %d", GBK2UTF("Speed of rotation:"), rotateSpeed);
	return rotateSpeed;
}


//Get mask circle start angle
int GameDataManage::getStartAngle(int level) {
	startAngle = std::atoi(csv[level]["StartAngle"].c_str());
	log("%s %d", GBK2UTF("Mask circle start angle:"), startAngle);
	return startAngle;
}

//Get mask circle end angle
int GameDataManage::getEndAngle(int level) {
	endAngle = std::atoi(csv[level]["EndAngle"].c_str());
	log("%s %d", GBK2UTF("Mask circle end angle:"), endAngle);
	return endAngle;
}


/************************************************************************************************
*	user data
************************************************************************************************/
//Get whether to initialize
bool GameDataManage::getIsFirstInit() {
	auto isInit = getBoolFromXML("_isInitGameData");
	return isInit;
}
//Whether the storage is initialized
void GameDataManage::setIsFirstInit(bool init) {
	setBoolToXML("_isInitGameData", init);
}

//Get the current number of completed levels
int GameDataManage::getCurrentMaxLevel() {
	auto level = getIntegerFromXML("current_max_level");
	return level;
}
//Save the number of currently completed levels
void GameDataManage::setCurrentMaxLevel(int level) {
	if(level >= getIntegerFromXML("current_max_level")) {
		setIntegerToXML("current_max_level", level);
	}	
}

There are classes of CSV and GBK2UTF in it. GBK2UTF only performs code conversion. You can create the created file in UTF, so you don't need to convert it. So I won't elaborate on them here

3,UserDefault
The UserDefault class simply encapsulates the original UserDefault. You don't need to pay attention to it. In the following code, data acquisition is changed to the native mode or the data structure defined by you. It's simple to modify~~

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

USING_NS_CC;
USING_NS_CC_EXT;

using namespace std;
//Save data to 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();
}

//Read data from 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);   
}

So the whole data storage has been completed. Let's get the first data in WelcomeScene
Add initialization method to WelcomeScene.h

//Initialize first login data
void initFirstLoginData();

Concrete realization

//Initialize first login data
void WelcomeScene::initFirstLoginData() {
	if (!GameDataManage::getInstance()->getIsFirstInit()) {
		//Settings initialized
		GameDataManage::getInstance()->setIsFirstInit(true);
		//Initialize the current maximum level
		GameDataManage::getInstance()->setCurrentMaxLevel(1);
	}
}

Don't forget to call in init.
Now we have two scenarios including data loading. Let's see the effect of level loading
We add the following code to the gamemenusescene init method

//Get the maximum level data
auto maxLevel = GameDataManage::getInstance()->getCurrentMaxLevel();
GameDataManage::getInstance()->loadGameData(maxLevel);

Let's run it. After running it, we can see whether the data in CSV format has been printed out in the console

We will build the scene and load the data. Is it a step further from the completion of our game.
Don't worry. We introduced replaceScene earlier. Do you think the scene switching is too monotonous? The next section will introduce the transition and related effects of the scene

34 original articles published, 15 praised, 40000 visitors+
Private letter follow

Tags: xml Database encoding JSON

Posted on Tue, 17 Mar 2020 07:52:22 -0400 by bubatalazi