(五)C++自制植物大战僵尸游戏LoadingScene的实现讲解

devtools/2024/9/22 16:12:01/

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


一、类介绍

游戏启动后就会立即切换到游戏加载场景中。只有游戏资源文件加载完成后,才能进入游戏。Loadingscene类继承Cocos2d-x中的Scene父类,表明Loadingscene是一个场景类。切换到LoadingScene场景中,首先会创建该场景,会调用init函数,在该函数中调用自己定义的方法实现指定功能。

代码文件的位置如下图所示。

LoadingScene.h

/***Copyright (c) 2019 LZ. All Right Reserved*Author : LZ*Date: 2019.7.15*Emal: 2117610943@qq.com*/
#pragma once
#include "cocos2d.h"
#include "Scenes/MainMenuScene/MainMenu.h"
#include "ui/CocosGUI.h"
#include "Based/GlobalVariable.h"
#include "network/CCDownloader.h"
#include "Based/UserData.h"using namespace cocos2d;
using namespace cocos2d::ui;
using namespace cocos2d::experimental;class LoadingScene :public Scene
{
public:CREATE_FUNC(LoadingScene); static Scene* createLaodingScene();static void loadUserFileData();static void caveUserFileData();CC_CONSTRUCTOR_ACCESS:LoadingScene();~LoadingScene();virtual bool init() override; private:void update(float Time) override;                 /* 定时器 */void setSystem();                                 /* 设置系统参数 */void calculateFileNumbers();                      /* 计算文件总数 */void setRunFirstTime();                           /* 获取第一次运行时间 */void loadUserData();                              /* 加载用户数据 */void loadingText();                               /* 加载文字 */void loadingImage();                              /* 加载图片 */void loadingMusic();                              /* 加载音乐 */void loadingAnimation();                          /* 加载动画 */void showLoadingBackGround();                     /* 展示背景 */void showTileAndLoadingBar();                     /* 展示进度条 */void showLoadingBarFlower(const int &ID);         /* 展示进度条上的花 */void beginLoadingImageAndMusic();                 /* 开始加载图片与音乐 */void runLoGoCallBack(Node* node,const int &ID);   /* 展示logo回调 */void loadingTextCallBack();                       /* 加载文字回调 */void loadingAnimationCallBack();                  /* 加载动画回调 */void beginGameCallBack(Ref* pSender);             /* 开始游戏回调 */int openResourcesPath(map<string, string>& Path, const std::string& xml, bool IsEncryption = false);  /* 打开资源路径 */void throwException();void checkEdition();void changeFiles();private:int _textNumbers;                                 // 文本数int _loadFileNumbers;                             // 文件加载数int _allFileNumbers;                              // 文件总数(图片,音乐,动画,文本)bool _flowerVisible[5] = { false };               // 加载花朵是否可见 float _loadingPrecent;                            // 加载进度 Sprite* _sprite[8];                               // 精灵图片 MenuItemLabel* _label;                            // 文字标签 LoadingBar* _loadingBar;                          // 进度条 EventListenerTouchOneByOne* _listener;            // 加载监听 Global* _global;                                  // 全局变量单例Director* _director;                              // 导演单例 FileUtils* _files;                                // 文件单例 UserData* _userData;std::unique_ptr<network::Downloader> _downloader;
};

LoadingScene.cpp 

由于cpp文件中的代码数量太多,所以就不全部放到这里了。

/***Copyright (c) 2019 LZ.All Right Reserved*Author : LZ*Date: 2019.7.15*Emal: 2117610943@qq.com*/#include "LoadingScene.h"
#include "tinyxml2/tinyxml2.h"
#include "Based/LevelData.h"
#include "Based/UserInformation.h"
#include "Based/PlayMusic.h"
#include "AudioEngine.h"#define MYDEBUG 1LoadingScene::LoadingScene() :_loadFileNumbers(0),_textNumbers(0),_allFileNumbers(0),_loadingPrecent(0),_label(nullptr),_loadingBar(nullptr),_listener(nullptr),_global(Global::getInstance()),_director(Director::getInstance()),_files(FileUtils::getInstance()),_userData(UserData::getInstance())
{_downloader.reset(new network::Downloader());
}LoadingScene::~LoadingScene()
{SpriteFrameCache::getInstance()->removeSpriteFramesFromFile("resources/images/LoadingScene/LoadingScene.plist");
}Scene* LoadingScene::createLaodingScene()
{return LoadingScene::create();
}bool LoadingScene::init()
{if (!Scene::init())return false;#if MYRELEASE
#   if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32checkEdition();          /* 检查版本 */
#   endif
#endifsetRunFirstTime();       /* 获取第一次运行时间 */calculateFileNumbers();  /* 计算文件总数 */setSystem();             /* 设置系统参数 */loadUserData();          /* 加载用户信息 */showLoadingBackGround(); /* 展示加载界面 */return true;
}//... 省略,代码数量太多,请到github/gitee仓库中查看

二、函数讲解

init()函数

bool LoadingScene::init()
{if (!Scene::init())return false;#if MYRELEASE
#   if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32checkEdition();          /* 检查版本 */
#   endif
#endifsetRunFirstTime();       /* 获取第一次运行时间 */calculateFileNumbers();  /* 计算文件总数 */setSystem();             /* 设置系统参数 */loadUserData();          /* 加载用户信息 */showLoadingBackGround(); /* 展示加载界面 */return true;
}

 LoadingScene中的init函数重写父类Scene中的init函数。在该函数中首先调用父类init函数,如果父类init函数初始化失败,表示场景创建失败,所以直接返回false,退出场景。否则继续向下执行。

#if MYRELEASE
#   if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32checkEdition();          /* 检查版本 */
#   endif
#endif

上述代码使用了条件编译,如果是release发布的版本,并且平台是windows,则会执行版本更新检查函数。否则不会编译这段代码。在debug版本中不会进行版本更新检查。

之后init函数会继续往下执行以下函数。如果全部执行成功,最后返回true,表示场景创成功。

setRunFirstTime();       /* 获取第一次运行时间 */
calculateFileNumbers();  /* 计算文件总数 */
setSystem();             /* 设置系统参数 */
loadUserData();          /* 加载用户信息 */
showLoadingBackGround(); /* 展示加载界面 */

setRunFirstTime()函数

void LoadingScene::setRunFirstTime()
{time_t tt;struct tm* nowtime;time(&tt);nowtime = localtime(&tt);if (UserDefault::getInstance()->getStringForKey("FIRSTRUNTIME").size() == 0){UserDefault::getInstance()->setStringForKey("FIRSTRUNTIME", to_string(nowtime->tm_year + 1900) + "年 " +to_string(nowtime->tm_mon) + "月 " + to_string(nowtime->tm_mday) + "日 星期" + to_string(nowtime->tm_wday) + " " +to_string(nowtime->tm_hour) + "时 " + to_string(nowtime->tm_min) + "分 " + to_string(nowtime->tm_sec) + "秒");}UserDefault::getInstance()->setIntegerForKey("BEGINDAY", nowtime->tm_mday);UserDefault::getInstance()->setIntegerForKey("BEGINHOUR", nowtime->tm_hour);UserDefault::getInstance()->setIntegerForKey("BEGINMIN", nowtime->tm_min);UserDefault::getInstance()->setIntegerForKey("BEGINSEC", nowtime->tm_sec);
}

在该函数中首先获取系统时间,然后判断是否是第一次执行,如果是的话就会记录当前执行的时间。

Cocos2d-x中使用UserDefault类来存储游戏信息,它存储的格式是html格式,使用键值对的方式存储信息。其中它的key值是唯一的,value值可以是字符串,整数,浮点数等类型。下面这行代码如果返回为空则表示第一次运行,然后更新该key(FIRSTRUNTIME),后续再次运行返回值就不为空,表示不是第一次运行。

UserDefault::getInstance()->getStringForKey("FIRSTRUNTIME")

calculateFileNumbers()函数

由于在加载文件的过程中需要计算加载进度,所有首先要统计需要加载文件的总数。才能根据此时加载文件数量和文件总数来计算加载的进度。

void LoadingScene::calculateFileNumbers()
{
#if MYDEBUG/* 文件总数 = 文本数 + 图片数 + 音乐数 + 动画数 */_allFileNumbers =openResourcesPath(_global->userInformation->getTextPath(), "resources/Text/TextPath.reanim.compiled", true) +openResourcesPath(_global->userInformation->getImagePath(), "resources/Text/ImagePath.reanim.compiled", true) +openResourcesPath(_global->userInformation->getMusicPath(), "resources/Text/MusicPath.reanim.compiled", true) +openResourcesPath(_global->userInformation->getAnimationPath(), "resources/Text/AnimationPath.reanim.compiled", true);
#else_allFileNumbers =openResourcesPath(_global->userInformation->getTextPath(), "resources/Text/TextPath.xml", false) +openResourcesPath(_global->userInformation->getImagePath(), "resources/Text/ImagePath.xml", false) +openResourcesPath(_global->userInformation->getMusicPath(), "resources/Text/MusicPath.xml", false) +openResourcesPath(_global->userInformation->getAnimationPath(), "resources/Text/AnimationPath.xml", false);
#endif
}

openResourcesPath函数用来统计文件数量,资源文件总共被分为四种,分别是文本、图片、音乐以及动画。


openResourcesPath()函数

该函数用于统计文件数量,函数有三个参数,第一个参数是用于保存文件信息,使用C++中的map存储,第二个参数表示存储资源文件目录的文件的地址,该文件存储了游戏中所用到的某一类资源文件的目录。第三个参数是表示打开参数二中的文件是否需要解密,如果参数二中的文件是加密的,则需要将该参数设置为true

int LoadingScene::openResourcesPath(map<string, string>& Path, const std::string& xml, bool IsEncryption)
{tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument();if (IsEncryption){std::string textpath = _files->getStringFromFile(xml);char* passWords = (char*)malloc(sizeof(char) * textpath.size());OpenLevelData::getInstance()->decrypt(textpath, passWords);/* 打开资源路径 */doc->Parse(passWords);}else{/* 打开资源路径 */doc->Parse(_files->getStringFromFile(xml).c_str());}auto root = doc->RootElement();for (auto e = root->FirstChildElement(); e; e = e->NextSiblingElement()){for (auto at = e->FirstAttribute(); at; at = at->Next()){Path.insert(pair<string, string>(at->Name(), at->Value()));}}delete doc;return Path.size();
}

该函数首先打开参数二中的文件,如果加密则先解密,然后读取遍历读取该文件,并将信息保存到map中,然后返回文件数量。


setSystem()函数

该函数用于设置鼠标样式以及图标样式。

void LoadingScene::setSystem()
{/* 设置光标 */_director->getOpenGLView()->setCursor("resources/images/System/cursor.png", Point::ANCHOR_TOP_LEFT);/* 设置图标 */_director->getOpenGLView()->setIcon("resources/images/System/PlantsVsZombies.png");
}

loadUserData()函数

该函数用于加载用户存档信息,并根据信息初始化游戏

游戏最多支持8个用户存档,遍历读取存档。然后初始化游戏设置,比如游戏帧率,是否拉升显示、是否垂直同步、是否隐藏鼠标等设置。这些设置都之前用户的个性化设置信息,加载游戏时自动读取恢复设置,无需用户再次手动设置。

void LoadingScene::loadUserData()
{auto userdefault = UserDefault::getInstance();/* 读取用户存档名称 */for (int i = 0; i < 8; i++){if (!userdefault->getStringForKey(_global->userInformation->getUserCaveFileNameKey(i).c_str()).size()){_global->userInformation->setUserCaveFileName(i, "未命名存档");}else{_global->userInformation->setUserCaveFileName(i, userdefault->getStringForKey(_global->userInformation->getUserCaveFileNameKey(i).c_str()));}}_userData->createNewUserDataDocument();loadUserFileData();/* 用户名称 */_global->userInformation->setUserName(_global->userInformation->getUserCaveFileName(_global->userInformation->getUserCaveFileNumber()));_global->userInformation->setIsUpdate(true);/* 显示信息 */switch (userdefault->getBoolForKey("SHOWINFORMATION")){case true:_director->setDisplayStats(true);_global->userInformation->setIsShowInformation(cocos2d::ui::CheckBox::EventType::SELECTED);break;case false:_director->setDisplayStats(false);_global->userInformation->setIsShowInformation(cocos2d::ui::CheckBox::EventType::UNSELECTED);break;}/* 是否高帧率 */switch (userdefault->getBoolForKey("SHOWHIGHFPS")){case true:_director->setAnimationInterval(1.0f / UserInformation::getScreenDisplayFrequency());_global->userInformation->setIsSelectHighFPS(cocos2d::ui::CheckBox::EventType::SELECTED);break;case false:_director->setAnimationInterval(1.0f / 30);_global->userInformation->setIsSelectHighFPS(cocos2d::ui::CheckBox::EventType::UNSELECTED);break;}/* 是否全屏 */switch (userdefault->getBoolForKey("SHOWFULLSCREEN")){case true:((GLViewImpl*)_director->getOpenGLView())->setFullscreen();_global->userInformation->setIsSelectFullScreen(cocos2d::ui::CheckBox::EventType::SELECTED);break;case false:((GLViewImpl*)_director->getOpenGLView())->setWindowed(1280, 720);_global->userInformation->setIsSelectFullScreen(cocos2d::ui::CheckBox::EventType::UNSELECTED);break;}/* 是否拉伸显示 */switch (userdefault->getBoolForKey("STRETCHINGSHOW")){case true:_global->userInformation->setIsSelectStretchingShow(cocos2d::ui::CheckBox::EventType::SELECTED);_director->getOpenGLView()->setDesignResolutionSize(_director->getWinSize().width, _director->getWinSize().height, ResolutionPolicy::EXACT_FIT);break;case false:_global->userInformation->setIsSelectStretchingShow(cocos2d::ui::CheckBox::EventType::UNSELECTED);_director->getOpenGLView()->setDesignResolutionSize(_director->getWinSize().width, _director->getWinSize().height, ResolutionPolicy::SHOW_ALL);break;}/* 是否垂直同步 */switch (userdefault->getBoolForKey("VERTICALSYNCHRONIZATION")){case true:wglSwapIntervalEXT(1);_global->userInformation->setIsVerticalSynchronization(CheckBox::EventType::SELECTED);break;case false:_global->userInformation->setIsVerticalSynchronization(CheckBox::EventType::UNSELECTED);wglSwapIntervalEXT(0);break;}/* 是否隐藏鼠标 */_global->userInformation->setIsSelectCursorNotHide(_userData->openBoolUserData("CURSORHIDE") ?cocos2d::ui::CheckBox::EventType::SELECTED : cocos2d::ui::CheckBox::EventType::UNSELECTED);/* 是否显示缓入动画 */_global->userInformation->setIsEaseAnimation(_userData->openBoolUserData("EASEANIMATION") ?cocos2d::ui::CheckBox::EventType::SELECTED : cocos2d::ui::CheckBox::EventType::UNSELECTED);if (!userdefault->getBoolForKey("UPDATE0619")){changeFiles();userdefault->setBoolForKey("UPDATE0619", true);}
}

update()函数

设置定时器函数scheduleUpdate后,每一帧都会调用update函数。根据不同的加载进度来播放不同的加载动画。

void LoadingScene::update(float Time)
{if (_loadingPrecent <= 100){_loadingBar->setPercent(_loadingPrecent);              /* 设置加载进度 */_sprite[5]->setScale(1 - _loadingPrecent / 170);       /* 设置精灵大小 */_sprite[5]->setRotation(9 * _loadingPrecent);          /* 设置精旋转度数 */_sprite[5]->setPosition(Vec2(10 + 290 / 100.0 * _loadingPrecent, 100 - _sprite[5]->getContentSize().height / 400 * _loadingPrecent));if (_loadingPrecent >= 20)  showLoadingBarFlower(0);if (_loadingPrecent >= 40)  showLoadingBarFlower(1);if (_loadingPrecent >= 60)  showLoadingBarFlower(2);if (_loadingPrecent >= 80)  showLoadingBarFlower(3);if (_loadingPrecent >= 100) showLoadingBarFlower(4);}if (_loadingPrecent >= 100) /* 如果加载完成 */{_label->setString(_global->userInformation->getGameText().find("点击开始")->second);  /* 重新设置标签文字内容 */auto action = TintBy::create(0.5f, 0, 255, 255);_label->runAction(RepeatForever::create(Sequence::create(action, action->reverse(), nullptr)));_sprite[5]->setVisible(false);  /* 设置精灵5为不可见 */_label->setEnabled(true);       /* 设置触摸可行 */}
}

loadingText()/loadingMusic()/loadingImage()/loadingAnimation()

循环遍历文件,逐个进行加载。Cocos2d-x中文件加载是异步加载方式,会创建新的线程进行文件加载,与渲染线程是同时进行的。如果不使用异步加载方式,在文件加载的过程中会导致游戏卡顿。

void LoadingScene::loadingText()
{/* 循环加载文本 */for (auto& i : _global->userInformation->getTextPath()){ValueVector Text = _files->getValueVectorFromFile(i.second);for (auto& file : Text){auto file_map = file.asValueMap();_global->userInformation->getGameText().insert(pair<string, string>(file_map.at("id").asString(), file_map.at("info").asString()));}/* 加载回调 */this->loadingTextCallBack();}
}void LoadingScene::loadingImage()
{/* 循环加载图片 */for (auto& i : _global->userInformation->getImagePath()){_director->getTextureCache()->addImageAsync(i.second + "pvr.ccz", [=](Texture2D* texture){SpriteFrameCache::getInstance()->addSpriteFramesWithFile(i.second + "plist", texture);_loadFileNumbers++;     /* 文件数加一 */_loadingPrecent = ((_loadFileNumbers * 1.0f) / _allFileNumbers) * 100;  /* 计算加载的百分比 */});}
}void LoadingScene::loadingMusic()
{/* 循环加载音乐 */for (auto& i : _global->userInformation->getMusicPath()){AudioEngine::preload(i.second, [=](bool isSucceed){if (isSucceed)/* 如果加载成功 */{_loadFileNumbers++;     /* 文件数加一 */_loadingPrecent = ((_loadFileNumbers * 1.0f) / _allFileNumbers) * 100;  /* 计算加载的百分比 */}});}
}void LoadingScene::loadingAnimation()
{/* 循环加载动画 */for (auto& i : _global->userInformation->getAnimationPath()){/* 临时存储文件名字 */char JsonName[128], AtlasName[128];/* 转换 */snprintf(JsonName, 128, "resources/Animations/compiled/%s.compiled", (i.second).c_str());snprintf(AtlasName, 128, "resources/Animations/reanim/%s.reanim", (i.second).c_str());/* 加载 */spSkeletonJson* json = spSkeletonJson_createWithLoader((spAttachmentLoader*)Cocos2dAttachmentLoader_create(spAtlas_createFromFile(AtlasName, nullptr)));auto skeletonData = spSkeletonJson_readSkeletonDataFile(json, JsonName);spSkeletonJson_dispose(json);/* 把加载到的动画放入map中*/_global->userInformation->getAnimationData().insert(pair<string, spSkeletonData*>(i.second, skeletonData));/* 进行回调 */this->loadingAnimationCallBack();}
}void LoadingScene::loadingTextCallBack()
{_loadFileNumbers++;     /* 文件数加一 */_loadingPrecent = ((_loadFileNumbers * 1.0f) / _allFileNumbers) * 100;  /* 计算加载的百分比 */
}void LoadingScene::loadingAnimationCallBack()
{_loadFileNumbers++;     /* 文件数加一 */_loadingPrecent = ((_loadFileNumbers * 1.0f) / _allFileNumbers) * 100;  /* 计算加载的百分比 */
}

其他函数

还有很多函数这里就不一一列举了,代码比较简单,可以自行调试理解。

三、后续

下一篇会详细讲解游戏关卡文件的结构组成以及游戏读取关卡信息的代码细节。


http://www.ppmy.cn/devtools/4779.html

相关文章

opencv/cv.h: No such file or directory

错误信息显示 opencv/cv.h: No such file or directory 表明编译时未能找到 OpenCV 的头文件。这通常发生在 OpenCV 未被正确安装或者路径未被正确配置到编译环境中。以下是一些步骤来解决这个问题&#xff1a; 确保 OpenCV 已安装 首先&#xff0c;你需要确认 OpenCV 已经在你…

Redis-批处理

Redis-批处理 Redis 批处理是一种提高与Redis服务器交互效率的技术&#xff0c;它允许客户端一次性发送一组命令并在服务器端一次性执行&#xff0c;减少了网络往返时间和命令解析的开销。批处理通常用于执行一系列相关操作&#xff0c;如数据批量插入、更新或查询。以下是关于…

中颖51芯片学习8. ADC模数转换

中颖51芯片学习8. ADC模数转换 一、ADC工作原理简介1. 概念2. ADC实现方式3. 基准电压 二、中颖芯片ADC功能介绍1. 中颖芯片ADC特性2. ADC触发源&#xff08;1&#xff09;**软件触发**&#xff08;2&#xff09;**TIMER4定时器触发**&#xff08;3&#xff09;**外部中断2触发…

【MATLAB基础绘图第21棒】绘制比例弦图 (Chord Diagram)

MATLAB绘制比例弦图 Chord Diagram 1 简介1.1 弦图简介1.2 比例弦图简介 2 MATLAB绘制比例弦图2.1 数据准备2.2 基本绘制2.3 添加方向箭头2.4 添加绘图间隙2.5 添加刻度2.6 修改标签2.7 颜色设置2.8 弧块及弦属性设置2.8.1 弧块属性设置2.8.2 弦属性设置 2.9 字体设置 参考 1 简…

Cpp_SDay01

菩提本无树&#xff0c;明镜亦非台 文章目录 前言一、Cpp二、#include iostream总结 前言 此路之难如逆水行舟&#xff0c;不进则退 一、Cpp 一种静态编译编程语言&#xff0c;以其广泛应用于软件基础架构而闻名又以其难学而声名狼藉。 1979年由BJARNE STROUSTRUP在贝尔实验室…

C#面:泛型的主要约束和次要约束是什么

在 C# 中&#xff0c;泛型的约束是用来限制泛型类型参数的行为和能力的。 主要约束和次要约束是两种不同的约束方式。 主要约束&#xff08;Primary Constraint&#xff09;&#xff1a; 主要约束指定了泛型类型参数必须满足的最基本的条件&#xff0c;它可以是一个类、一个接…

NLP和LLMs: 理解它们之间的区别

NLP&#xff08;自然语言处理&#xff09;和LLMs&#xff08;大型语言模型&#xff09;都与处理自然语言相关&#xff0c;但它们的重点和范围略有不同。 自然语言处理&#xff08;NLP&#xff09;&#xff1a; 定义&#xff1a; 自然语言处理&#xff08;NLP&#xff09;是人…

【Java探索之旅】用面向对象的思维构建程序世界

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、初识面向对象1.1 什么是面向对象&#xff1f;1.2 面向对象与面向过程 二、类的定义…