植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/cFP3z
更新检查
游戏启动后会下载服务器中的版本号然后与本地版本号进行对比,如果本地版本号小于服务器版本号就会弹出更新提示。让用户选择是否更新客户端。
在弹出的更新对话框中有显示最新版本更新的内容,以及更新按钮等。用户可以选择更新方式或者进行更新。
文件位置
代码文件实现位置在Class\Scenes\MainMenuScene文件夹中。
UpdateClient.h
客户端更新类继承与对话框类(Dialog),因为客户端更新也是一个对话对话框供玩家操作。UpdateClient头文件定义如下。
class UpdateClient :public Dialog
{
public:
CREATE_FUNC(UpdateClient);
CC_CONSTRUCTOR_ACCESS:
UpdateClient();
virtual bool init();
private:
enum class Update_Button
{
百度网盘下载,
腾讯微云下载,
直接下载,
退出游戏,
确定
};
void createDiglog(); /* 创建对话框 */
void createButton(const std::string& name, Vec2& vec2, Update_Button button); /* 创建按钮 */
void showText();
void addScrollView();
void addMouseEvent();
void downloadHistoryText();
void downloadData();
void downloadProgress();
void downloadSuccess();
void downloadError();
private:
Sprite* _dialog; /* 对话框 */
std::unique_ptr<network::Downloader> _downloader;
Label* _remindText;
Label* _progressText;
Label* _explanText;
Label* _historyText;
Sprite* _loadBarBackground;
ui::LoadingBar* _loadingBar;
ui::ScrollView* _textScrollView;
bool _isNewDowndload;
};
UpdateClient.cpp
构造函数
在构造函数中对变量进行初始化操作。
UpdateClient::UpdateClient() :
_dialog(nullptr)
, _remindText(nullptr)
, _progressText(nullptr)
, _explanText(nullptr)
, _loadBarBackground(nullptr)
, _loadingBar(nullptr)
, _historyText(nullptr)
, _isNewDowndload(true)
{
_downloader.reset(new network::Downloader());
}
init函数
创建游戏更新对话框,首先会调用init函数。在init函数中首先会在场景中创建一个黑色半透明的遮罩层,使用场景变黑,让玩家聚焦到此对话框中。然后调用createShieldLayer(this)函数屏蔽除本层之外的所以事件监听,该函数的实现在自定义对话框教程(教程九)中有介绍,作用是让玩家只能和该对话框进行交互。最后使用createDialog()函数创建更新菜单。
bool UpdateClient::init()
{
if (!LayerColor::initWithColor(Color4B(0, 0, 0, 180)))return false;
createShieldLayer(this);
createDialog();
return true;
}
createDialog()函数
在该函数中主要实现整个更新菜单的界面。
void UpdateClient::createDialog()
{
_dialog = Sprite::createWithSpriteFrameName("LevelObjiectivesBg.png");
_dialog->setPosition(_director->getWinSize() / 2);
_dialog->setScale(0.9f);
this->addChild(_dialog);
/* 创建触摸监听 */
createTouchtListener(_dialog);
auto PauseAnimation = SkeletonAnimation::createWithData(_global->userInformation->getAnimationData().find("PauseAnimation")->second);
PauseAnimation->setAnimation(0, "animation", true);
PauseAnimation->setPosition(Vec2(530, 650));
_dialog->addChild(PauseAnimation);
showText();
createButton(_global->userInformation->getGameText().find("百度网盘下载")->second, Vec2(165, 100), Update_Button::百度网盘下载);
createButton(_global->userInformation->getGameText().find("腾讯微云下载")->second, Vec2(405, 100), Update_Button::腾讯微云下载);
createButton(_global->userInformation->getGameText().find("直接下载")->second, Vec2(645, 100), Update_Button::直接下载);
createButton(_global->userInformation->getGameText().find("关闭游戏")->second, Vec2(885, 100), Update_Button::退出游戏);
createButton(_global->userInformation->getGameText().find("确定")->second, Vec2(520, 100), Update_Button::确定);
}
创建更新菜单背景,设置位置到屏幕中心,缩放0.9倍大小。
_dialog = Sprite::createWithSpriteFrameName("LevelObjiectivesBg.png");
_dialog->setPosition(_director->getWinSize() / 2);
_dialog->setScale(0.9f);
this->addChild(_dialog);
对创建好的背景进行触摸监听,可以实现更新菜单的拖动。
/* 创建触摸监听 */
createTouchtListener(_dialog);
显示文字内容以及创建多个按钮。
showText();
createButton(_global->userInformation->getGameText().find("百度网盘下载")->second, Vec2(165, 100), Update_Button::百度网盘下载);
createButton(_global->userInformation->getGameText().find("腾讯微云下载")->second, Vec2(405, 100), Update_Button::腾讯微云下载);
createButton(_global->userInformation->getGameText().find("直接下载")->second, Vec2(645, 100), Update_Button::直接下载);
createButton(_global->userInformation->getGameText().find("关闭游戏")->second, Vec2(885, 100), Update_Button::退出游戏);
createButton(_global->userInformation->getGameText().find("确定")->second, Vec2(520, 100), Update_Button::确定);
downloadData()函数
客户端内文件下载更新函数。创建文件下载进度条以及文字信息。
void UpdateClient::downloadData()
{
if (!_loadBarBackground)
{
_loadBarBackground = Sprite::createWithSpriteFrameName("bgFile.png");
_loadBarBackground->setPosition(Vec2(_dialog->getContentSize().width / 2.f, _dialog->getContentSize().height / 2.f - 100));
_loadBarBackground->setScale(1.5f);
_dialog->addChild(_loadBarBackground);
}
if (!_loadingBar)
{
_loadingBar = ui::LoadingBar::create();
_loadingBar->loadTexture("progressFile.png", TextureResType::PLIST);
_loadingBar->setDirection(LoadingBar::Direction::LEFT); /* 设置加载方向 */
_loadingBar->setPercent(0);
_loadingBar->setScale(1.5f);
_loadingBar->setPosition(Vec2(_dialog->getContentSize().width / 2.f, _dialog->getContentSize().height / 2.f - 100));
_dialog->addChild(_loadingBar);
}
_explanText->setColor(Color3B::BLACK);
_explanText->setString("");
const static string sNameList = _global->userInformation->getGameText().find("资源名称")->second + UserInformation::getNewEditionName(true) + ".rar";
const static string path = _global->userInformation->getGameText().find("存放路径")->second + sNameList;
_downloader->createDownloadFileTask(_global->userInformation->getGameText().find("资源网址")->second, path, sNameList);
downloadProgress();
downloadSuccess();
downloadError();
}
创建下载任务,传入服务器文件地址 、文件路径、文件名称。
_downloader->createDownloadFileTask(_global->userInformation->getGameText().find("资源网址")->second, path, sNameList);
调用下载进度、下载成功、下载失败函数 。下载过程会调用downloadProgress()函数,下载成功调用downloadSuccess()函数,下载失败调用downloadError()函数。
downloadProgress();
downloadSuccess();
downloadError();
downloadProgress()函数
在onTaskProgress lamda函数中,会实时计算下载进度,bytesReceived参数是当前下载的文大小,totalBytesExpected是文件总大小,totalBytesReceived是总下载大小。通过这三个参数可以计算下载完成所需事件。
void UpdateClient::downloadProgress()
{
_downloader->onTaskProgress = [=](const network::DownloadTask& task,
int64_t bytesReceived,
int64_t totalBytesReceived,
int64_t totalBytesExpected)
{
_explanText->setString(_global->userInformation->getGameText().find("解释说明_慢")->second);
float percent = float(totalBytesReceived * 100) / totalBytesExpected;
_loadingBar->setPercent(percent);
int hour = (totalBytesExpected - totalBytesReceived) / (bytesReceived * 10) / 3600;
int min = ((totalBytesExpected - totalBytesReceived) / (bytesReceived * 10) - hour * 3600) / 60;
int second = (totalBytesExpected - totalBytesReceived) / (bytesReceived * 10) - hour * 3600 - min * 60;
char buf[128];
if (bytesReceived / 1024.f * 10 >= 1000)
{
std::snprintf(buf, 128, "%.1fMB/s %dKB/%dKB %.2f%% time:%02d:%02d:%02d",
bytesReceived / 1024.f / 1024.f * 10, int(totalBytesReceived / 1024), int(totalBytesExpected / 1024), percent, hour, min, second);
_progressText->setString(buf);
}
else
{
std::snprintf(buf, 128, "%.1fKB/s %dKB/%dKB %.2f%% time:%02d:%02d:%02d",
bytesReceived / 1024.f * 10, int(totalBytesReceived / 1024), int(totalBytesExpected / 1024), percent, hour, min, second);
_progressText->setString(buf);
}
_remindText->setString(_global->userInformation->getGameText().find("文件正在下载中!请稍等!")->second);
};
}
downloadSuccess()函数
成功下载文件后会调用onFileTaskSuccess lamda函数。在函数中显示下载成功文字信息,将按钮隐藏,然后提示用户退出重新启动游戏。
void UpdateClient::downloadSuccess()
{
_downloader->onFileTaskSuccess = [this](const cocos2d::network::DownloadTask& task)
{
_progressText->setString(_global->userInformation->getGameText().find("下载成功")->second +
_global->userInformation->getGameText().find("存放路径")->second + task.identifier + " ]");
_remindText->setString(_global->userInformation->getGameText().find("点击确定退出游戏!")->second);
_explanText->setString(_global->userInformation->getGameText().find("下载成功说明")->second);
((Button*)_dialog->getChildByName("0"))->setVisible(false);
((Button*)_dialog->getChildByName("1"))->setVisible(false);
((Button*)_dialog->getChildByName("2"))->setVisible(false);
((Button*)_dialog->getChildByName("3"))->setVisible(false);
((Button*)_dialog->getChildByName("4"))->setVisible(true);
};
}
downloadError()函数
如果下载失败,会调用onTaskError lamda函数,在函数中先错误信息提示用户。errorCode的是错误代码,errorStr是错误信息,errorCodeInternal是内部错误代码。
void UpdateClient::downloadError()
{
_downloader->onTaskError = [this](const cocos2d::network::DownloadTask& task,
int errorCode,
int errorCodeInternal,
const std::string& errorStr)
{
_remindText->setString(_global->userInformation->getGameText().find("下载失败")->second);
((Button*)_dialog->getChildByName("2"))->setEnabled(true);
((Button*)_dialog->getChildByName("3"))->setEnabled(true);
char str[256];
snprintf(str, 256, "Failed to download : 资源文件, identifier(%s) error code(%d), internal error code(%d) desc(%s) 请检查网络连接是否正常!如果网络连接正常请多试几次!或更换其他方式下载!"
, task.identifier.c_str()
, errorCode
, errorCodeInternal
, errorStr.c_str());
_explanText->setString(str);
_explanText->setColor(Color3B::RED);
#ifdef DEBUG
log("Failed to download : %s, identifier(%s) error code(%d), internal error code(%d) desc(%s)"
, task.requestURL.c_str()
, task.identifier.c_str()
, errorCode
, errorCodeInternal
, errorStr.c_str());
#endif // DEBUG
};
}
其他函数
showText()、createButton()、addScrollView()、addMouseEvent()等函数不再一一列举,可自行查看。
void UpdateClient::showText()
{
addScrollView();
_historyText = Label::createWithTTF(_global->userInformation->getGameText().find("更新信息加载中!")->second, GAME_FONT_NAME_1, 50);
_historyText->setAnchorPoint(Vec2::ANCHOR_MIDDLE_TOP);
_historyText->setColor(Color3B::BLACK);
_historyText->setMaxLineWidth(650);
_textScrollView->addChild(_historyText);
_textScrollView->setInnerContainerSize(_historyText->getContentSize());
_historyText->setPosition(Vec2(_dialog->getContentSize().width / 2.f - 150, _textScrollView->getInnerContainerSize().height - 150));
downloadHistoryText();
/* 标题 */
_remindText = Label::createWithTTF(_global->userInformation->getGameText().find("检测到有新版本,请选择更新方式!")->second, GAME_FONT_NAME_1, 50);
_remindText->setPosition(Vec2(_dialog->getContentSize().width / 2.f, _dialog->getContentSize().height / 2.f + 200));
_remindText->setColor(Color3B::BLACK);
_remindText->setMaxLineWidth(900);
_remindText->setName("Update");
_dialog->addChild(_remindText);
/* 进度文字 */
_progressText = Label::createWithTTF("", GAME_FONT_NAME_1, 25);
_progressText->setMaxLineWidth(900);
_progressText->setPosition(Vec2(_dialog->getContentSize().width / 2.f, _dialog->getContentSize().height / 2.f));
_dialog->addChild(_progressText);
/* 说明文字 */
_explanText = Label::createWithTTF("", GAME_FONT_NAME_1, 30);
_explanText->setPosition(Vec2(_dialog->getContentSize().width / 2.f, _dialog->getContentSize().height / 2.f + 100));
_explanText->setColor(Color3B::BLACK);
_explanText->setMaxLineWidth(900);
_dialog->addChild(_explanText);
}
void UpdateClient::addScrollView()
{
_textScrollView = ui::ScrollView::create();
_textScrollView->setDirection(ui::ScrollView::Direction::VERTICAL);
_textScrollView->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
_textScrollView->setContentSize(Size(720.0f, 320.0f));
_textScrollView->setPosition(_dialog->getContentSize() / 2.0f);
_textScrollView->setBounceEnabled(true);
_textScrollView->setScrollBarPositionFromCorner(Vec2(20, 0));
_textScrollView->setScrollBarWidth(10);
_textScrollView->setScrollBarColor(Color3B::BLACK);
_dialog->addChild(_textScrollView);
addMouseEvent();
}
void UpdateClient::addMouseEvent()
{
/* 鼠标滑动监听 */
auto mouse = EventListenerMouse::create();
mouse->onMouseScroll = [=](Event* event)
{
auto mouseEvent = static_cast<EventMouse*>(event);
float movex = mouseEvent->getScrollY() * 5;
auto minOffset = 0.f;
auto maxOffset = 100.f;
auto offset = _textScrollView->getScrolledPercentVertical();
offset += movex;
if (offset < minOffset)
{
offset = minOffset;
}
else if (offset > maxOffset)
{
offset = maxOffset;
}
_textScrollView->scrollToPercentVertical(offset, 0.5f, true);
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(mouse, _textScrollView);
}
void UpdateClient::downloadHistoryText()
{
const string sURLList = _global->userInformation->getGameText().find("更新信息网址")->second;
_downloader->createDownloadDataTask(sURLList);
_downloader->onDataTaskSuccess = [this](const cocos2d::network::DownloadTask& task,
std::vector<unsigned char>& data)
{
string historyNetWork;
for (auto p : data)
{
historyNetWork += p;
}
TTFConfig ttfConfig(GAME_FONT_NAME_1, 25, GlyphCollection::DYNAMIC);
_historyText->setTTFConfig(ttfConfig);
_historyText->setString(historyNetWork);
_textScrollView->setInnerContainerSize(_historyText->getContentSize());
_historyText->setPosition(Vec2(350, _textScrollView->getInnerContainerSize().height));
};
_downloader->onTaskError = [this](const cocos2d::network::DownloadTask& task,
int errorCode,
int errorCodeInternal,
const std::string& errorStr)
{
_historyText->setString(_global->userInformation->getGameText().find("更新信息加载失败!")->second);
_textScrollView->setInnerContainerSize(_historyText->getContentSize());
};
}