(十三)C++自制植物大战僵尸游戏多用户存档实现(二)

植物大战僵尸游戏开发教程专栏地址icon-default.png?t=N7T8http://t.csdnimg.cn/8UFMs


UserData.h

在头文件中定义了枚举类型openUserDataReturnType,用于表示打开用户数据文件的返回状态。FileExistError表示文件存在但是打开错误,FileExistCorrect表示文件在且正确,FileNotExist表示文件不存在。

enum class openUserDataReturnType
{FileExistError = 1,FileExistCorrect,FileNotExist
};

 UserData是一个单例类,通过getInstance()方法获取其唯一的实例。

class UserData :public Director
{
public:static UserData* getInstance();void flushUserData();void flushLevelData();void flushSurvivalData();void caveUserData(char* key, double value);void caveUserData(char* key, bool value);void caveUserData(char* key, char* value);void caveUserData(char* key, int value);void caveLevelData(char* key);void openLevelData(char* key);bool isHaveLevelData(char* key);void caveSurvivalData(char* key);void openSurvivalData(char* key);bool isHaveSurvivalData(char* key);void openLevelPlantsData(char* key);void openLevelZombiesData(char* key);void openLevelSelectCardData(char* key);void openLevelSunData(char* key);void openLevelCoinData(char* key);void openLevelCarData(char* key);void openLevelBulletData(char* key);void openLevelOtherData(char* key);void openSurvivalOtherData(char* key);void removeLevelData(char* key);int openIntUserData(char* key);double openDoubleUserData(char* key);bool openBoolUserData(char* key);const char* openStringUserData(char* key);void createNewUserDataDocument();void createNewLevelDataDocument();void setAnewReadData(const bool newRead);private:UserData();~UserData();string getUserDataFileName();string getLevelDataFileName();string getSurvivalDataFileName();openUserDataReturnType openUserData();openUserDataReturnType openLevelData();openUserDataReturnType openSurvivalData();bool isHaveMember(char* key);bool readLevelData();void caveLevelPlantsData(char* key);void caveLevelZombiesData(char* key);void caveLevelSelectPlantsData(char* key);void caveLevelSunData(char* key);void caveLevelCoinData(char* key);void caveLevelCarData(char* key);void caveLevelBulletData(char* key);void caveLevelOtherData(char* key);void caveSurvivalOtherData(char* key);void replaceScene();
#ifndef DLLTESTstring encryption(string& str);string decryption(string& str);
#endif // !DLLTESTprivate:Document* _userDataDocument;Document* _levelDataDocument;FileUtils* _fileUtils;Global* _global;string _userData;string _levelData;bool _isAnewReadData;vector<GSScene*>_gsScene;static UserData* _instance;
};
  • 公有函数:
    • flushUserData()flushLevelData()flushSurvivalData():用于将用户数据、关卡数据和生存模式数据刷新到文件中。
    • caveUserData():用于向用户数据中存储不同类型的值,如doubleboolcharint
    • caveLevelData():根据键值key保存关卡数据。
    • openLevelData():打开关卡数据中指定键的值。
    • isHaveLevelData():检查关卡数据中是否存在指定键的值。
    • caveSurvivalData():根据键值key保存生存模式数据。
    • openSurvivalData():打开生存模式数据中指定键的值。
    • isHaveSurvivalData():检查生存模式数据中是否存在指定键的值。
    • openLevelPlantsData()openLevelZombiesData()等:打开关卡数据中不同类型的值,如植物数据、僵尸数据、选择卡片数据等。
    • removeLevelData():从关卡数据中移除指定键的值。
    • openIntUserData()openDoubleUserData()openBoolUserData()openStringUserData():打开用户数据中不同类型的值。
    • createNewUserDataDocument()createNewLevelDataDocument():创建新的用户数据和关卡数据文档
    • setAnewReadData():设置是否重新读取数据的标志。
  • 私有函数:
    • string getUserDataFileName():返回用户数据文件的名称。该函数会返回一个字符串,指定用于存储用户数据的文件名。
    • string getLevelDataFileName():返回关卡数据文件的名称。该函数会返回一个字符串,指定用于存储关卡数据的文件名。
    • string getSurvivalDataFileName():返回生存模式数据文件的名称。该函数会返回一个字符串,指定用于存储生存模式数据的文件名。
    • openUserDataReturnType openUserData():打开用户数据。该函数会返回枚举值,用于指示用户数据的打开状态或结果。
    • openUserDataReturnType openLevelData():打开关卡数据。类似于openUserData(),该函数会返回一个枚举值,用于指示关卡数据的打开状态或结果。
    • openUserDataReturnType openSurvivalData():打开生存模式数据。类似于openUserData(),该函数会返回一个枚举值值,用于指示生存模式数据的打开状态或结果。
    • bool isHaveMember(char* key):检查指定键是否存在于数据中。该函数接受一个char*类型的键作为参数,并返回一个布尔值,指示该键是否存在于数据中。
    • bool readLevelData():读取关卡数据。该函数可能会返回一个布尔值。
    • void caveLevelPlantsData(char* key):向关卡数据中存储植物数据。该函数接受一个char*类型的键作为参数,并将植物数据存储在关卡数据中。
    • void caveLevelZombiesData(char* key):向关卡数据中存储僵尸数据。类似于caveLevelPlantsData(char* key),该函数将僵尸数据存储在关卡数据中。
    • void caveLevelSelectPlantsData(char* key):向关卡数据中存储选择的植物数据。类似于前面的函数,该函数将选择的植物数据存储在关卡数据中。
    • void caveLevelSunData(char* key):向关卡数据中存储阳光数据。类似于前面的函数,该函数将阳光数据存储在关卡数据中。
    • void caveLevelCoinData(char* key):向关卡数据中存储金币数据。类似于前面的函数,该函数将金币数据存储在关卡数据中。
    • void caveLevelCarData(char* key):向关卡数据中存储车辆数据。类似于前面的函数,该函数将车辆数据存储在关卡数据中。
    • void caveLevelBulletData(char* key):向关卡数据中存储子弹数据。类似于前面的函数,该函数将子弹数据存储在关卡数据中。
    • void caveLevelOtherData(char* key):向关卡数据中存储其他类型的数据。类似于前面的函数,该函数将其他类型的数据存储在关卡数据中。
    • void caveSurvivalOtherData(char* key):向生存模式数据中存储其他类型的数据。类似于前面的函数,该函数将其他类型的数据存储在生存模式数据中。
    • void replaceScene():替换场景。
  • 私有变量:
    • _userDataDocument_levelDataDocument:指向用户数据和关卡数据的Document对象。
    • _fileUtils:指向FileUtils对象,用于文件操作。
    • _global:指向Global对象。
    • _userData_levelData:存储用户数据和关卡数据的字符串。
    • _isAnewReadData:是否重新读取数据的标志。
    • _gsScene:存储GSScene对象的向量。

UserData.cpp 

构造函数

UserData::UserData() :_global(Global::getInstance())
, _fileUtils(FileUtils::getInstance())
, _userDataDocument(nullptr)
, _levelDataDocument(nullptr)
, _isAnewReadData(false)
{
}

构造函数UserData::UserData()初始化了UserData类的成员变量。具体来说:

  • _global(Global::getInstance()):将Global类的唯一实例赋值给_global成员变量。Global类可能是游戏中的全局管理类,通过调用getInstance()方法获取其唯一实例。

  • _fileUtils(FileUtils::getInstance()):将FileUtils类的唯一实例赋值给_fileUtils成员变量。FileUtils类可能是用于文件操作的工具类,通过调用getInstance()方法获取其唯一实例。

  • _userDataDocument(nullptr)_levelDataDocument(nullptr):将用户数据和关卡数据的文档指针初始化为nullptr,表示当前没有打开的数据文档。

  • _isAnewReadData(false):将重新读取数据的标志初始化为false,表示默认情况下不重新读取数据。


析构函数 

UserData::~UserData()
{if (_userDataDocument) delete _userDataDocument, _userDataDocument = nullptr;if (_levelDataDocument) delete _levelDataDocument, _levelDataDocument = nullptr;_isAnewReadData = false;
}

析构函数UserData::~UserData()用于清理UserData类的资源。具体来说:

  • if (_userDataDocument) delete _userDataDocument, _userDataDocument = nullptr;:如果_userDataDocument不为nullptr,则删除它指向的对象,并将其设置为nullptr,以释放用户数据文档的内存资源。

  • if (_levelDataDocument) delete _levelDataDocument, _levelDataDocument = nullptr;:如果_levelDataDocument不为nullptr,则删除它指向的对象,并将其设置为nullptr,以释放关卡数据文档的内存资源。

  • _isAnewReadData = false;:将重新读取数据的标志设置为false。


getInstance()函数

UserData* UserData::getInstance()
{if (_instance == nullptr){_instance = new (std::nothrow)UserData;}return _instance;
}

该函数实现了单例模式,用于获取UserData类的唯一实例。具体来说:

  • if (_instance == nullptr):检查静态成员变量_instance是否为nullptr,即是否已经创建了实例。

  • _instance = new (std::nothrow) UserData;:如果_instancenullptr,则创建一个新的UserData对象,并将其赋值给_instance。这里使用了std::nothrow,表示在内存分配失败时不会抛出异常,而是返回nullptr

  • return _instance;:返回_instance,即UserData类的唯一实例。

通过调用UserData::getInstance(),可以获取到UserData类的单例实例,确保在整个程序中只有一个UserData对象存在。


flushUserData()函数 

void UserData::flushUserData()
{StringBuffer buffer;rapidjson::Writer<StringBuffer> Writer(buffer);_userDataDocument->Accept(Writer);string str = string(buffer.GetString());#ifdef DEBUG_fileUtils->writeStringToFile(str, getUserDataFileName());
#else
#   ifndef DLLTEST_fileUtils->writeStringToFile(encryption(str), getUserDataFileName());
#   elsechar* buf = new char[str.length() * 3];encryption(str.c_str(), buf);_fileUtils->writeStringToFile(buf, getUserDataFileName());CC_SAFE_DELETE(buf);
#   endif
#endif_isAnewReadData = false; // if flush must anew read data
}

该函数用于将用户数据刷新到文件中。具体来说:

  • _userDataDocument中的数据序列化为字符串形式,并保存在str中。

  • 根据编译选项进行不同的处理:

    • #ifdef DEBUG:如果定义了DEBUG宏,则直接将字符串数据写入文件,不进行加密处理。
    • #ifndef DLLTEST:如果未定义DLLTEST宏,则使用加密函数encryption()对字符串进行加密,并将加密后的数据写入文件。
    • #else:如果定义了DLLTEST宏,则先将字符串转换为char*类型的缓冲区buf,然后使用加密函数encryption()对数据进行加密,最后将加密后的数据写入文件。注意,在写入文件后,需要使用CC_SAFE_DELETE删除缓冲区的内存。
  • _isAnewReadData = false;:将重新读取数据的标志设置为false,表示在刷新数据后不需要重新读取数据。

该函数的作用是将_userDataDocument中的数据保存到文件中,根据编译选项和宏定义进行不同的处理(如加密),以满足特定的需求和安全性要求。


flushLevelData()函数 

void UserData::flushLevelData()
{StringBuffer buffer;rapidjson::Writer<StringBuffer> Writer(buffer);_levelDataDocument->Accept(Writer);string str = string(buffer.GetString());#ifdef DEBUG_fileUtils->writeStringToFile(str, getLevelDataFileName());
#else
#   ifndef DLLTEST_fileUtils->writeStringToFile(encryption(str), getLevelDataFileName());
#   elsechar* buf = new char[str.length() * 3];encryption(str.c_str(), buf);_fileUtils->writeStringToFile(buf, getLevelDataFileName());CC_SAFE_DELETE(buf);
#   endif
#endif
}

该函数用于将关卡数据刷新到文件中。具体来说:

  • _levelDataDocument中的数据序列化为字符串形式,并保存在str中。

  • 根据编译选项进行不同的处理:

    • #ifdef DEBUG:如果定义了DEBUG宏,则直接将字符串数据写入文件,不进行加密处理。
    • #ifndef DLLTEST:如果未定义DLLTEST宏,则使用加密函数encryption()对字符串进行加密,并将加密后的数据写入文件。
    • #else:如果定义了DLLTEST宏,则先将字符串转换为char*类型的缓冲区buf,然后使用加密函数encryption()对数据进行加密,最后将加密后的数据写入文件。注意,在写入文件后,需要使用CC_SAFE_DELETE删除缓冲区的内存。

该函数的作用是将_levelDataDocument中的数据保存到文件中,根据编译选项和宏定义进行不同的处理(如加密),以满足特定的需求和安全性要求。


openUserData()函数 

openUserDataReturnType UserData::openUserData()
{// 如果有这个存档if (_fileUtils->isFileExist(getUserDataFileName())){if (_userData.empty()|| !_isAnewReadData){_isAnewReadData = true;
#ifdef DEBUG_userData = _fileUtils->getStringFromFile(getUserDataFileName());_userDataDocument->Parse<rapidjson::kParseDefaultFlags>(_userData.c_str());
#else
#   ifndef DLLTEST_userData = _fileUtils->getStringFromFile(getUserDataFileName());_userDataDocument->Parse<rapidjson::kParseDefaultFlags>(decryption(_userData).c_str());
#   else_userData = _fileUtils->getStringFromFile(getUserDataFileName());char* buf = new char[_userData.length()];if (decryption(_userData.c_str(), buf)) {_userDataDocument->Parse<rapidjson::kParseDefaultFlags>(buf);}else {CC_SAFE_DELETE(buf);return openUserDataReturnType::FileExistError;}CC_SAFE_DELETE(buf);
#   endif
#endifif (_userDataDocument->HasParseError()) {return openUserDataReturnType::FileExistError;}}return openUserDataReturnType::FileExistCorrect;}else{if (!_userDataDocument->IsObject()){_userDataDocument->SetObject();rapidjson::Value _object(rapidjson::kObjectType);_userDataDocument->AddMember("UserData", _object, _userDataDocument->GetAllocator());}return openUserDataReturnType::FileNotExist;}
}

该函数用于打开用户数据文件并读取数据。具体来说:

  • 首先判断用户数据文件是否存在,通过调用_fileUtils->isFileExist(getUserDataFileName())来检查。

  • 如果文件存在,执行以下操作:

    • 检查 _userData 是否为空或 _isAnewReadData 是否为 false
    • 如果满足条件,进行数据的读取和解析:
      • #ifdef DEBUG:如果定义了 DEBUG 宏,则直接从文件中读取字符串数据到 _userData,然后使用 _userDataDocument 对象进行解析。
      • #ifndef DLLTEST:如果未定义 DLLTEST 宏,则先从文件中读取经过加密的字符串数据到 _userData,然后使用解密函数 decryption() 对数据进行解密,并使用 _userDataDocument 对象进行解析。
      • #else:如果定义了 DLLTEST 宏,则先从文件中读取字符串数据到 _userData,然后创建一个缓冲区 buf,调用解密函数 decryption() 对数据进行解密,将解密后的数据通过 _userDataDocument 进行解析。如果解密失败,则释放缓冲区内存,返回 openUserDataReturnType::FileExistError
    • 检查解析过程中是否出错,通过调用 _userDataDocument->HasParseError() 来判断。如果出错,返回 openUserDataReturnType::FileExistError
  • 如果文件不存在,执行以下操作:

    • 检查 _userDataDocument 是否为对象类型,如果不是,则将其设置为对象类型,并添加一个名为 "UserData" 的成员对象。
    • 返回 openUserDataReturnType::FileNotExist

该函数的作用是打开用户数据文件,根据文件的存在与否执行相应的读取和解析操作,并返回相应的状态枚举值,用于判断操作是否成功以及文件的存在状态。


openLevelData()函数 

openUserDataReturnType UserData::openLevelData()
{// 如果有这个存档if (_fileUtils->isFileExist(getLevelDataFileName())){
#ifdef DEBUG_levelData = _fileUtils->getStringFromFile(getLevelDataFileName());_levelDataDocument->Parse<rapidjson::kParseDefaultFlags>(_levelData.c_str());
#else
#   ifndef DLLTEST_levelData = _fileUtils->getStringFromFile(getLevelDataFileName());_levelDataDocument->Parse<rapidjson::kParseDefaultFlags>(decryption(_levelData).c_str());
#   else_levelData = _fileUtils->getStringFromFile(getLevelDataFileName());char* buf = new char[_levelData.length()];if (decryption(_levelData.c_str(), buf)) {_levelDataDocument->Parse<rapidjson::kParseDefaultFlags>(buf);}else {CC_SAFE_DELETE(buf);return openUserDataReturnType::FileExistError;}CC_SAFE_DELETE(buf);
#   endif
#endifif (_levelDataDocument->HasParseError()) {return openUserDataReturnType::FileExistError;}return openUserDataReturnType::FileExistCorrect;}else{return openUserDataReturnType::FileNotExist;}
}

解释同理,见openUserData()函数。


caveUserData()函数

void UserData::caveUserData(char* key, double value)
{switch (openUserData()){case openUserDataReturnType::FileExistCorrect:if (isHaveMember(key))(*_userDataDocument)["UserData"][key].SetDouble(value);else(*_userDataDocument)["UserData"].AddMember(rapidjson::StringRef(key), value, _userDataDocument->GetAllocator());break;case openUserDataReturnType::FileNotExist:(*_userDataDocument)["UserData"].AddMember(rapidjson::StringRef(key), value, _userDataDocument->GetAllocator());break;case openUserDataReturnType::FileExistError:remove(getUserDataFileName().c_str());break;}flushUserData();
}

该函数用于在用户数据中添加或更新指定键(key)对应的双精度浮点数值(value)。具体来说:

  • 调用 openUserData() 函数打开用户数据文件,并根据返回的状态进行不同的操作。

  • 使用 switch 语句根据不同的返回状态进行处理:

    • openUserDataReturnType::FileExistCorrect:如果文件存在且打开成功,执行以下操作:
      • 调用 isHaveMember(key) 函数检查指定键是否存在于用户数据中。
      • 如果键存在,使用 SetDouble() 函数更新对应的双精度浮点数值。
      • 如果键不存在,使用 AddMember() 函数将指定键和值添加到用户数据中。
    • openUserDataReturnType::FileNotExist:如果文件不存在,执行以下操作:
      • 使用 AddMember() 函数将指定键和值添加到用户数据中。
    • openUserDataReturnType::FileExistError:如果文件存在但打开出错,执行以下操作:
      • 调用 remove(getUserDataFileName().c_str()) 函数删除用户数据文件。
  • 调用 flushUserData() 函数将更新后的用户数据刷新到文件中。

该函数的作用是在用户数据中添加或更新指定键对应的双精度浮点数值,并将更新后的用户数据保存到文件中。


openIntUserData()函数

int UserData::openIntUserData(char* key)
{switch (openUserData()){case openUserDataReturnType::FileExistCorrect:if (isHaveMember(key))return (*_userDataDocument)["UserData"][key].GetInt();break;case openUserDataReturnType::FileExistError:remove(getUserDataFileName().c_str());break;default: break;}return 0;
}

该函数用于从用户数据中获取指定键(key)对应的整数值,并返回该整数值。具体来说:

  • 调用 openUserData() 函数打开用户数据文件,并根据返回的状态进行不同的操作。

  • 使用 switch 语句根据不同的返回状态进行处理:

    • openUserDataReturnType::FileExistCorrect:如果文件存在且打开成功,执行以下操作:
      • 调用 isHaveMember(key) 函数检查指定键是否存在于用户数据中。
      • 如果键存在,使用 GetInt() 函数获取对应的整数值,并返回该值。
    • openUserDataReturnType::FileExistError:如果文件存在但打开出错,执行以下操作:
      • 调用 remove(getUserDataFileName().c_str()) 函数删除用户数据文件。
  • 如果未满足上述条件,则返回默认值 0。

该函数的作用是从用户数据中获取指定键对应的整数值,并返回该值。如果用户数据文件存在且打开成功,并且指定键存在于用户数据中,则返回对应的整数值;否则返回默认值 0。如果文件存在但打开出错,则删除用户数据文件。


createNewUserDataDocument()函数 

void UserData::createNewUserDataDocument()
{if (_userDataDocument){delete _userDataDocument;_userDataDocument = nullptr;}_userDataDocument = new Document();_userData.clear();_isAnewReadData = false;
}

该函数用于创建一个新的用户数据文档对象。具体来说:

  • 首先检查 _userDataDocument 是否已存在,如果存在,则执行以下操作:

    • 使用 delete 关键字释放 _userDataDocument 的内存。
    • 将 _userDataDocument 指针设置为 nullptr,以确保不再指向已释放的内存。
  • 创建一个新的 Document 对象,并将其赋值给 _userDataDocument

  • 清空 _userData 字符串。

  • 将 _isAnewReadData 设置为 false,表示没有重新读取数据。

该函数的作用是创建一个新的用户数据文档对象,并进行必要的清理和重置操作,以便重新使用和处理用户数据。


caveLevelData()函数 

void UserData::caveLevelData(char* key)
{switch (openLevelData()){case openUserDataReturnType::FileExistCorrect:if ((*_levelDataDocument).HasMember(key))(*_levelDataDocument).RemoveMember(key);break;case openUserDataReturnType::FileExistError:remove(getLevelDataFileName().c_str());return;break;}if (!_levelDataDocument->IsObject())_levelDataDocument->SetObject();rapidjson::Value object(rapidjson::kObjectType);_levelDataDocument->AddMember(rapidjson::StringRef(key), object, _levelDataDocument->GetAllocator());caveLevelPlantsData(key);caveLevelZombiesData(key);caveLevelSelectPlantsData(key);caveLevelOtherData(key);caveLevelSunData(key);caveLevelCoinData(key);caveLevelCarData(key);caveLevelBulletData(key);flushLevelData();
}

该函数用于在关卡数据中创建一个新的键(key)。具体来说:

  • 调用 openLevelData() 函数打开关卡数据文件,并根据返回的状态进行不同的操作。

  • 使用 switch 语句根据不同的返回状态进行处理:

    • openUserDataReturnType::FileExistCorrect:如果文件存在且打开成功,执行以下操作:
      • 检查关卡数据文档中是否存在指定键(key)。
      • 如果存在,使用 RemoveMember() 函数从关卡数据中移除该键。
    • openUserDataReturnType::FileExistError:如果文件存在但打开出错,执行以下操作:
      • 调用 remove(getLevelDataFileName().c_str()) 函数删除关卡数据文件。
      • 返回函数,即终止函数的执行。
  • 检查 _levelDataDocument 是否为对象类型,如果不是,则使用 SetObject() 函数将其设置为对象类型。

  • 创建一个新的 Value 对象,并将其添加到关卡数据文档中,键为指定键(key),值为空对象。

  • 调用其他函数(如 caveLevelPlantsData()caveLevelZombiesData() 等)来处理关卡数据的其他方面。

  • 调用 flushLevelData() 函数将更新后的关卡数据保存到文件中。

该函数的作用是在关卡数据中创建一个新的键,并进行相关的数据处理和保存操作。


caveLevelPlantsData()函数 

void UserData::caveLevelPlantsData(char* key)
{unsigned int plantsNumber = 0;rapidjson::Value _object(rapidjson::kObjectType);rapidjson::Document::AllocatorType& allocator = _levelDataDocument->GetAllocator();(*_levelDataDocument)[key].AddMember("Plants", _object, allocator);for (auto plant : PlantsGroup){rapidjson::Value object(rapidjson::kObjectType);auto visible = plant.second->getPlantAnimation()->isVisible();if (visible){object.AddMember("PlantsTag", plant.second->getPlantTag(), allocator);object.AddMember("PlantsHealthPoint", plant.second->getPlantHealthPoint(), allocator);object.AddMember("PlantsPositionX", plant.second->getPlantAnimation()->getPositionX(), allocator);object.AddMember("PlantsPositionY", plant.second->getPlantAnimation()->getPositionY(), allocator);object.AddMember("PlantsRow", plant.second->getPlantRow(), allocator);object.AddMember("PlantsColumn", plant.second->getPlantColumn(), allocator);object.AddMember("PlantsLocalZOrder", plant.second->getPlantAnimation()->getLocalZOrder(), allocator);object.AddMember("PlantsType", static_cast<int>(plant.second->getPlantType()), allocator);object.AddMember("PlantVisible", visible, allocator);switch (plant.second->getPlantType()){case PlantsType::SunFlower:object.AddMember("SunShowTime.X", dynamic_cast<SunFlower*>(plant.second)->getSunShowTime().x, allocator);object.AddMember("SunShowTime.Y", dynamic_cast<SunFlower*>(plant.second)->getSunShowTime().y, allocator);break;case PlantsType::PotatoMine:object.AddMember("BreakGround", dynamic_cast<PotatoMine*>(plant.second)->getBreakGround(), allocator);break;default:break;}auto number = to_string(++plantsNumber);char* str = new char[number.size() + 1];strcpy(str, number.c_str());str[number.size()] = '\0';(*_levelDataDocument)[key]["Plants"].AddMember(rapidjson::StringRef(str), object, _levelDataDocument->GetAllocator());}}(*_levelDataDocument)[key]["Plants"].AddMember("PlantsNumber", plantsNumber, allocator);
}

该函数用于处理关卡数据中的植物数据。具体来说:

  • 创建一个无类型的 _object 对象,用于作为关卡数据文档中 "Plants" 键的值。

  • 获取 _levelDataDocument 的分配器(allocator)。

  • 将 _object 添加到关卡数据文档中的指定键(key)下的 "Plants" 键。

  • 遍历 PlantsGroup,这是一个存储植物对象的容器。

  • 对于每个植物对象(plant)进行以下操作:

    • 检查植物动画是否可见,如果可见则继续处理。
    • 创建一个无类型的 object 对象,用于存储植物的数据。
    • 将植物的各种属性(如标签、生命值、位置、行列、层级、类型等)添加到 object 中。
    • 根据植物类型的不同,添加特定类型的属性到 object 中。
    • 生成一个表示植物数量的字符串,将其转换为字符数组,并将其作为键添加到关卡数据文档中的 "Plants" 键下,并将 object 作为值。
  • 最后,将 "PlantsNumber" 键和植物数量值添加到关卡数据文档中的 "Plants" 键下。

该函数的作用是处理关卡数据中的植物相关信息,包括植物的位置、属性和数量,并将其保存到关卡数据文档中。

 


openLevelPlantsData()函数

void UserData::openLevelPlantsData(char* key)
{auto plantsNumbers = (*_levelDataDocument)[key]["Plants"]["PlantsNumber"].GetInt();for (int i = 1; i <= plantsNumbers; ++i){auto type = static_cast<PlantsType>((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsType"].GetInt());auto plants = animationLayerInformation->createDifferentPlants(type);plants->setPlantPosition(Vec2((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsPositionX"].GetFloat(),(*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsPositionY"].GetFloat()));plants->setPlantLocalZOrder((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsLocalZOrder"].GetInt());plants->setPlantRowAndColumn(Vec2((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsRow"].GetInt(),(*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsColumn"].GetInt()));plants->setPlantTag((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsTag"].GetInt());switch (type){case PlantsType::SunFlower:dynamic_cast<SunFlower*>(plants)->setSunShowTime(Vec2((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["SunShowTime.X"].GetFloat(),(*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["SunShowTime.Y"].GetFloat()));break;case PlantsType::PotatoMine:dynamic_cast<PotatoMine*>(plants)->setBreakGround((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["BreakGround"].GetFloat());break;default:break;}plants->createPlantAnimation();plants->setPlantHealthPoint((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsHealthPoint"].GetFloat());plants->getPlantAnimation()->setVisible((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantVisible"].GetBool());plants->getPlantAnimation()->getChildByName("SplashOfSoil")->setOpacity(0);PlantsGroup.insert(pair<int, Plants*>((*_levelDataDocument)[key]["Plants"][to_string(i).c_str()]["PlantsTag"].GetInt(), plants));controlLayerInformation->_gameMapInformation->plantsMap[plants->getPlantColumn()][plants->getPlantRow()] = static_cast<unsigned int>(type);/* 地图记录种植的植物 */}
}

该函数用于读取关卡数据中的植物信息,并根据这些信息创建和设置植物对象。具体来说:

  • 获取存储在关卡数据文档中的 "PlantsNumber" 键的整数值,表示植物的数量。

  • 使用循环遍历每个植物的索引,从 1 到植物数量。

  • 根据索引,获取植物的类型,并将其转换为 PlantsType 枚举类型。

  • 使用 animationLayerInformation 对象的 createDifferentPlants() 方法创建具有指定类型的植物对象,并将其存储在 plants 变量中。

  • 从关卡数据文档中获取植物的位置、层级、行列、标签等属性,并将这些属性设置到植物对象中。

  • 根据植物类型的不同,进一步处理特定类型的属性,如太阳花的太阳出现时间或土豆雷的破土状态。

  • 创建植物的动画,并设置植物的生命值、可见性以及相关动画的属性。

  • 将植物对象添加到 PlantsGroup 容器中,使用植物标签作为键。

  • 在 controlLayerInformation->_gameMapInformation->plantsMap 中记录植物的位置这段代码看起来是一个游戏开发中的函数,用于读取关卡中的植物数据,并创建相应的植物对象。

  • 创建植物的动画,并设置植物的生命值、可见性以及相关动画的属性。

  • 将植物对象添加到 PlantsGroup 容器中,使用植物标签作为键。

  • 在地图数据中记录植物的位置。

这段代码的目的是根据关卡数据创建植物对象,并将其放置在游戏场景中的适当位置上。具体的实现细节可能根据上下文和代码的其他部分有所变化。


removeLevelData()函数 

void UserData::removeLevelData(char* key)
{switch (openLevelData()){case openUserDataReturnType::FileExistCorrect:if ((*_levelDataDocument).HasMember(key))(*_levelDataDocument).RemoveMember(key);break;default:return;break;}flushLevelData();
}

该函数用于从关卡数据中移除指定键值的数据。具体来说:

  • 调用openLevelData()函数,根据返回值判断关卡数据文件是否存在并正确打开。

  • 使用switch语句根据openLevelData()的返回值执行不同的操作。

  • 如果返回值是openUserDataReturnType::FileExistCorrect,表示关卡数据文件存在且正确打开。

  • 检查关卡数据文档是否包含指定的键key,如果存在则执行下一步操作。

  • 使用RemoveMember(key)函数从关卡数据文档中移除指定的键值对。

  • 如果openLevelData()的返回值不是openUserDataReturnType::FileExistCorrect,或者关卡数据文档不包含指定的键,则函数直接返回。

  • 在移除键值对后,调用flushLevelData()函数来刷新关卡数据,将更改写入到磁盘。

该函数的作用是删除关卡数据中特定键值的条目,并将更改保存到磁盘。请注意,函数中使用的openLevelData()flushLevelData()函数的实现没有给出,因此具体的逻辑和实现细节可能在这些函数中。


encryption函数 

string UserData::encryption(string& str)
{char* encryptString, * encryptString1;base64Encode((unsigned char*)str.c_str(), (unsigned int)str.length(), &encryptString);string sss(encryptString);reverse(sss.begin(), sss.end());for (auto& s : sss){if (s >= 'a' && s <= 'z')s = ((s - 'a') + 2) % 26 + 'a';if (s >= 'A' && s <= 'Z')s = ((s - 'A') + 5) % 26 + 'A';if (s >= '0' && s <= '9')s = ((s - '0') + 7) % 10 + '0';if (s == '=')s = '+';}base64Encode((unsigned char*)sss.c_str(), (unsigned int)sss.length(), &encryptString1);string s(encryptString1);CC_SAFE_FREE(encryptString);CC_SAFE_FREE(encryptString1);return s;
}

该函数用于对字符串进行加密处理。具体来说:

  • 使用base64Encode()函数将字符串str进行Base64编码,并将结果存储在encryptString中。

  • 创建一个string类型的变量sss,并将encryptString赋值给它。

  • 使用reverse()函数将sss字符串进行反转。

  • 遍历sss字符串中的每个字符,根据字符的范围进行不同的加密操作:

    • 如果字符在小写字母az之间,将字符进行循环右移2位。
    • 如果字符在大写字母AZ之间,将字符进行循环右移5位。
    • 如果字符在数字09之间,将字符进行循环右移7位。
    • 如果字符是等号=,将其替换为加号+
  • 使用base64Encode()函数将经过加密处理后的sss字符串进行Base64编码,并将结果存储在encryptString1中。

  • 创建一个string类型的变量s,并将encryptString1赋值给它。

  • 使用CC_SAFE_FREE()函数释放encryptStringencryptString1的内存。

  • 返回加密后的字符串s

该函数的作用是对输入的字符串进行加密处理,首先使用Base64编码,然后进行字符位置的循环右移和字符替换操作。


decryption()函数 

string UserData::decryption(string& str)
{unsigned char* decryptString = nullptr, * decryptString1 = nullptr;auto ret = base64Decode((unsigned char*)str.c_str(), (unsigned int)str.length(), &decryptString);if (ret > 0) decryptString[ret] = '\0';else return "";string sss(reinterpret_cast<char*>(decryptString));for (auto& s : sss){if (s >= 'a' && s <= 'z')s = ((s - 'a') + 24) % 26 + 'a';if (s >= 'A' && s <= 'Z')s = ((s - 'A') + 21) % 26 + 'A';if (s >= '0' && s <= '9')s = ((s - '0') + 3) % 10 + '0';if (s == '+')s = '=';}reverse(sss.begin(), sss.end());ret = base64Decode((unsigned char*)sss.c_str(), (unsigned int)sss.length(), &decryptString1);if (ret > 0) decryptString1[ret] = '\0';else { CC_SAFE_FREE(decryptString); return ""; }string s(reinterpret_cast<char*>(decryptString1));CC_SAFE_FREE(decryptString);CC_SAFE_FREE(decryptString1);return s;
}

该函数用于对加密过的字符串进行解密处理。具体来说:

  • 创建两个指针decryptStringdecryptString1,并初始化为nullptr

  • 使用base64Decode()函数将字符串str进行Base64解码,并将结果存储在decryptString中。保存解码后的字节数到ret中。

  • 如果解码成功,将decryptString最后一个字节设置为\0,表示字符串的结束。否则,返回空字符串。

  • 使用reinterpret_castdecryptString转换为string类型,并赋值给sss

  • 遍历sss字符串中的每个字符,根据字符的范围进行不同的解密操作:

    • 如果字符在小写字母az之间,将字符进行循环左移2位。
    • 如果字符在大写字母AZ之间,将字符进行循环左移5位。
    • 如果字符在数字09之间,将字符进行循环左移3位。
    • 如果字符是加号+,将其替换为等号=
  • 使用reverse()函数将sss字符串进行反转。

  • 使用base64Decode()函数将经过解密处理后的sss字符串进行Base64解码,并将结果存储在decryptString1中。保存解码后的字节数到ret中。

  • 如果解码成功,将decryptString1最后一个字节设置为\0,表示字符串的结束。否则,释放decryptString的内存,并返回空字符串。

  • 使用reinterpret_castdecryptString1转换为string类型,并赋值给s

  • 使用CC_SAFE_FREE()函数释放decryptStringdecryptString1的内存。

  • 返回解密后的字符串s

该函数的作用是对输入的加密字符串进行解密处理,包括Base64解码、字符位置的循环左移和字符替换操作。

其他函数

函数众多,列举了部分重要函数,其他函数看自行查看。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/1401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何进行景气分析

景气分析是一种短期经济分析方法。主要分析短时间内&#xff08;一般指一年内&#xff0c; 或几个月内&#xff09;经济运行的态势&#xff0c;包括当前的状态和未来的趋势。景气分析可以为宏观经济政策提供重要的决策与参考信息&#xff0c;例如根据经济运行的方向、强弱可建议…

概念理解: DDR的寻址容量设计

主要内容&#xff1a;DDR寻址容量设计的概念理解示例。 DDR的数据存储在不同的bank上&#xff0c;你知道具体是如何通过数据总线和地址总线的配合&#xff0c;拿到所需的数据吗&#xff1f;通过下面这样一个简单的问题&#xff0c;我们来逐渐了解这个过程的实现。 DDR…

LeetCode 热题 100 题解:普通数组部分

文章目录 题目一&#xff1a;最大子数组和&#xff08;No. 53&#xff09;题解 题目二&#xff1a;合并区间&#xff08;No. 56&#xff09;题解 题目三&#xff1a;轮转数组&#xff08;No. 189&#xff09;题解 题目四&#xff1a;除自身以外数组的乘积&#xff08;No. 238&a…

MySql运维篇

目录 一.日志 1.1日志分类 1.2Error Log 1.3BinaryLog 1.4SlowQuery Log 二.备份 2.1备份原因 2.2备份目标 2.3备份技术 2.3.1物理备份 2.3.2逻辑备份 2.4备份方式 2.4.1完全备份 2.4.2增量备份 2.4.3差异备份 2.5备份环境准备 2.6完全备份实验 2.6.1完全备…

https协议的加密方式详解

各位大佬能多多点赞关注评论收藏&#xff0c;球球各位大佬们了&#xff01;&#xff01; &#xff01; 目录 1.为什么要加密&#xff1f; 2.如何加密 1.密钥&#xff08;yue,第四声&#xff09; 2.对称加密 3.非对称加密 4.公证机构 3.总结 1.为什么要加密&#xff1f;…

趋势分析 一元线性回归及显著性检验 GIS方法 Slope

slope斜率计算公式&#xff1a; 假设有三年的NDVI图像 加载3年栅格数据&#xff0c;公式中n取3计算分子左边&#xff1a; 3*(1*"1990.tif"2*"1991.tif"3*"1992.tif") 计算分子右边&#xff1a; 6*("1990.tif""1991.tif"&q…

算法课程笔记——STL题目

长度为2的字符串&#xff0c;当in下标为一&#xff0c;也就是\n,当i&#xff01;n&#xff0c;就是输出空格 &&且 city从citys里面取 加速后就不能混用scanf

CoFSM基于共现尺度空间的多模态遥感图像匹配方法--论文阅读记录

目录 论文 Multi-Modal Remote Sensing Image Matching Considering Co-Occurrence Filter 参考论文&#xff1a;SIFT系列论文&#xff0c; SIFT Distinctive Image Features from Scale-Invariant Keypoints&#xff0c;作者&#xff1a;David G. Lowe 快速样本共识算法…

汇智知了堂晨会聚焦:NAS应用如何赋能网络安全实战

在近期汇智知了堂网络安全75班的晨会上&#xff0c;一场关于NAS应用的深入分享完美展开。学员们以饱满的热情投入到这场安全讨论中&#xff0c;共同探索网络安全的新天地。 此次分享会聚焦于NAS的应用&#xff0c;旨在帮助学员们更好地了解NAS的定义与功能&#xff0c;掌握其在…

52.基于SpringBoot + Vue实现的前后端分离-房屋租赁系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的房屋租赁系统设计与实现管理工作系统…

【Linux系统化学习】线程控制

目录 前言 POSIX线程库 线程控制 创建线程 线程终止 pthread_exit()函数 pthread_cancel()函数&#xff08;会在下面线程等待部分详解&#xff09; 线程等待 pthread_join()函数 获取线程退出码 分离线程 线程取消(pthread_cancel()函数&#xff09; 线程ID及进程…

open Gauss 数据库-06 openGauss数据库安全指导手册5.0.0

发文章是为了证明自己真的掌握了一个知识&#xff0c;同时给他人带来帮助&#xff0c;如有问题&#xff0c;欢迎指正&#xff0c;祝大家万事胜意&#xff01; 目录 前言 openGauss数据库安全指导 1 用户权限控制 1.1 实验介绍 1.1.1 关于本实验 1.1.2 实验目的 1.2 用户…

富文本编辑器(wangEdit)+(jquery.wordexport)实现web版在线编辑导出

小插曲&#xff1a;最开始的方向是Html5的contenteditable"true"的文档可编辑属性。只能修改文档文字内容&#xff0c;不能修改样式&#xff0c;如修改字体&#xff0c;字号&#xff0c;颜色等。于是用了第一款&#xff08;quil&#xff09;富文本插件。只能说一般&a…

IEEE PDF eXpress Validating Pdf..之后Error in converting file

在将自己写好的pdf论文转化为IEEE出版的pdf论文格式的时候&#xff0c;错误如下图&#xff1a; 解决办法如下&#xff1a;失败之后&#xff0c;那里有一个选项按钮&#xff0c;叫做manual request&#xff0c;也就是人工转换&#xff0c;点那个申请就可以了&#xff0c;然后也挺…

jdk keytool.exe生成keystore https证书,利用jks2pfx转换nginx证书,nginx配置证书

jdk keytool.exe生成keystore https证书,利用jks2pfx转换nginx证书,nginx配置证书 由于项目需要https访问,使用ngigx配置https证书,在用nginx代理到 各个tomcat,比tomcat直接配置https证书方便,记录下自己制作nginx https证书文件,以及配置的过程. 由于证书不是证书机构颁…

我与C++的爱恋:类和对象(四)

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;我与C的爱恋 ​ 朋友们大家好&#xff01;本篇是类和对象的最后一个部分。 一、static成员 声明为static的类成员称为类的静态成员&#xff0c;用static修饰的成员变量&#xff0c;称之…

Ctrl + B 复制图片Base64

简介 这是一个专为Windows系统设计的工具&#xff0c;用于快速获取资源管理器&#xff08;文件夹&#xff09;中选中图片文件的Base64编码。 背景 由于工作需求经常需要获取图片的Base64并粘贴到postman中调用接口。最开始的做法是使用在线工具将图片转换为Base64编码&#…

DBUnit增强:填充随机数据和相对时间数据

痛点 测试环境验证时&#xff0c;遇到与当前相对时间相关的测试吗&#xff1f;准备一份SQL&#xff1f;隔一段时间就不能用了。每过一段时间去更新脚本或重置系统时间&#xff1f;看上去也不是很合适的解决方案。依赖数据测试时要重新做&#xff0c;演示时候得全部改&#xff…

Mini-Gemini: 探索多模态视觉语言模型的新境界

一、背景 在数字化时代&#xff0c;人工智能的发展正以前所未有的速度推进。特别是在多模态学习领域&#xff0c;结合视觉和语言的能力已成为研究的热点。最近&#xff0c;一篇名为“Mini-Gemini: Mining the Potential of Multi-modality Vision Language Models”的文章在arX…

05 MySQL--字段约束、事务、视图

1. CONSTRAINT 约束 创建表时&#xff0c;可以给表的字段添加约束&#xff0c;可以保证数据的完整性、有效性。比如大家上网注册用户时常见的&#xff1a;用户名不能为空。对不起&#xff0c;用户名已存在。等提示信息。 约束包括&#xff1a; 非空约束&#xff1a;not null检…