
cocos2dx 自定义进度条的实现!
顶点数据管理:动态分配和释放顶点数据,避免内存泄漏。 统一绘图逻辑:使用 百分比计算:根据实际需求计算顶点位置,实现更灵活的进度显示。 代码简化:去除了不必要的硬编码,提高代码可维护性。
发布日期:2021-05-14 02:24:20
浏览次数:20
分类:精选文章
本文共 8840 字,大约阅读时间需要 29 分钟。
Cocos2D-X 的 ProgressTimer 类原本只能绘制条形或圆形进度条,而如果你想实现一个更复杂的进度条效果,例如分割成多个部分或使用图片的不同区域来表示进度,可能需要自定义绘图逻辑。以下是一个优化后的实现方案,能够更好地处理不同阶段的进度显示。
ProgressTimer 优化方案
1. 理解问题
- 图片分割:将图片分割成多个区域,例如两个竖边、一个下边、两个弧形区域。
- 进度显示:根据当前的百分比显示在相应的区域内。
- 绘图问题:确保所有区域都能正确显示,并且进度条的速度一致。
2. 自定义顶点数据
为了实现复杂的进度条效果,我们需要自定义顶点数据。以下是一个简单的实现方法:
// 创建顶点数据void MyProgressTimer::initProgressData() { if (_vertexData) { CC_SAFE_FREE(_vertexData); } _vertexDataCount = 12; // 根据需要调整顶点数量 _vertexData = (V2F_C4B_T2F*)malloc(_vertexDataCount * sizeof(V2F_C4B_T2F)); CCASSERT(_vertexData, "顶点数据分配失败"); // 初始化默认顶点数据 for (int i = 0; i < _vertexDataCount; ++i) { _vertexData[i].texCoords = textureCoordFromAlphaPoint(Vec2(0, 0)); _vertexData[i].vertices = vertexFromAlphaPoint(Vec2(0, 0)); }}
3. 自定义绘图逻辑
在 onDraw
方法中,根据当前的进度阶段绘制相应的区域:
void MyProgressTimer::onDraw(const Mat4 &transform, uint32_t flags) { getGLProgram()->use(); getGLProgram()->setUniformsForBuiltins(transform); GL::blendFunc(m_pSpr->getBlendFunc().src, m_pSpr->getBlendFunc().dst); GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); GL::bindTexture2D(m_pSpr->getTexture()->getName()); // 设置顶点属性 glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(_vertexData[0]), _vertexData[0].vertices); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, sizeof(_vertexData[0]), _vertexData[0].texCoords); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(_vertexData[0]), _vertexData[0].colors); // 绘制多个三角形部分 if (_type == ProgressType::Custom) { // 示例:绘制一个复杂的区域 glDrawArrays(GL_TRIANGLE_STRIP, 0, _vertexDataCount); CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, _vertexDataCount); }}
4. 使用百分比计算顶点位置
在 percentToStep
方法中,根据当前百分比计算顶点的位置:
ProgressType MyProgressTimer::percentToStep(float &pos) { if (m_fPercentage == 0) { return ProgressType::Start; } else if (m_fPercentage == 100) { return ProgressType::End; } else { // 根据图片分割区域计算顶点位置 // 具体实现根据图片实际尺寸进行调整 return ProgressType::Custom; }}
5. 示例代码
以下是完整的 MyProgressTimer
类实现,能够实现复杂的进度条效果:
#pragma once#include "cocos2d.h"#define kDefaultVertexCount 12#define kDefaultTextureCoords 4using namespace cocosd::extensions;class MyProgressTimer : public Node {public: MyProgressTimer() { m_pSpr = nullptr; _vertexData = nullptr; _vertexDataCount = 0; m_fPercentage = 0.0f; } ~MyProgressTimer() { if (m_pSpr) { CC_SAFE_RELEASE(m_pSpr); } if (_vertexData) { CC_SAFE_FREE(_vertexData); } } static MyProgressTimer* create(Sprite* sp) { return new (std::nothrow) MyProgressTimer(); } bool initWithSprite(Sprite* sp) { setSprite(sp); setOrigin(Vec2(0.05f, 0.05f)); setTop(Vec2(0.95f, 0.95f)); setAnchorPoint(Vec2(0.5f, 0.5f)); setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR)); m_type = ProgressType::None; setPercentage(0.0f); initProgressData(); return true; } void setSprite(Sprite* sprite) { if (m_pSpr != sprite) { CC_SAFE_RETAIN(sprite); CC_SAFE_RELEASE(m_pSpr); m_pSpr = sprite; setContentSize(m_pSpr->getContentSize()); if (_vertexData) { CC_SAFE_FREE(_vertexData); _vertexDataCount = 0; } } } void updateColor() override { if (!m_pSpr) { return; } Color4B sc = m_pSpr->getQuad().tl.colors; for (int i = 0; i < _vertexDataCount; ++i) { _vertexData[i].colors = sc; } } void setPercentage(float percentage) override { m_fPercentage = clampf(percentage, 0, 100); updateProgress(); } ProgressType percentToStep(float &pos) override { if (m_fPercentage == 0) { return ProgressType::Start; } else if (m_fPercentage == 100) { return ProgressType::End; } else { // 根据图片分割区域计算顶点位置 // 具体实现根据图片实际尺寸进行调整 return ProgressType::Custom; } } void initProgressData() { if (_vertexData) { CC_SAFE_FREE(_vertexData); } _vertexDataCount = kDefaultVertexCount; _vertexData = (V2F_C4B_T2F*)malloc(_vertexDataCount * sizeof(V2F_C4B_T2F)); CCASSERT(_vertexData, "顶点数据分配失败"); for (int i = 0; i < _vertexDataCount; ++i) { _vertexData[i].texCoords = textureCoordFromAlphaPoint(Vec2(0, 0)); _vertexData[i].vertices = vertexFromAlphaPoint(Vec2(0, 0)); } } void onDraw(const Mat4 &transform, uint32_t flags) override { getGLProgram()->use(); getGLProgram()->setUniformsForBuiltins(transform); GL::blendFunc(m_pSpr->getBlendFunc().src, m_pSpr->getBlendFunc().dst); GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); GL::bindTexture2D(m_pSpr->getTexture()->getName()); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(_vertexData[0]), _vertexData[0].vertices); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, sizeof(_vertexData[0]), _vertexData[0].texCoords); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(_vertexData[0]), _vertexData[0].colors); if (_type == ProgressType::Custom) { glDrawArrays(GL_TRIANGLE_STRIP, 0, _vertexDataCount); CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, _vertexDataCount); } } void draw(Renderer* renderer, const Mat4 &transform, uint32_t flags) override { if (!m_pSpr || !_vertexData) { return; } _customCommand.init(_globalZOrder); _customCommand.func = CC_CALLBACK_0(onDraw, this, transform, flags); renderer->addCommand(&_customCommand); } void setOrigin(Vec2 _vOrigin) override { if (m_pSpr) { // 根据实际需求调整计算逻辑 float topX = clampf(_vOrigin.x, 0, 0.5); float topY = clampf(_vOrigin.y, 0, 0.5); auto thisSize = m_pSpr->getContentSize(); auto max1 = thisSize.width * topX; auto max2 = thisSize.height * topY; auto max3 = MAX(max1, max2); m_vOrigin = Vec2(max3 / thisSize.width, max3 / thisSize.height); updateProgress(); } } void setTop(Vec2 _vTop) override { if (m_pSpr) { float topX = clampf(_vTop.x, 0.5, 1); float topY = clampf(_vTop.y, 0.5, 1); auto thisSize = m_pSpr->getContentSize(); auto max1 = thisSize.width * (1 - topX); auto max2 = thisSize.height * (1 - topY); auto max3 = MAX(max1, max2); m_vTop = Vec2(1 - max3 / thisSize.width, 1 - max3 / thisSize.height); updateProgress(); } } void updateProgress() override { if (!m_pSpr) { return; } initProgressData(); Vec2 min = Vec2(0, 0); Vec2 max = Vec2(1, 1); Vec2 mid = Vec2(0.5, 0.5); float pos = m_fPercentage / 100.0f; ProgressType step = percentToStep(pos); // 根据步骤绘制不同的区域 if (step == ProgressType::Custom) { // 示例:绘制一个复杂的区域 for (int i = 0; i < _vertexDataCount; ++i) { _vertexData[i].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, min.y)); _vertexData[i].vertices = vertexFromAlphaPoint(Vec2(min.x, min.y)); } } }private: CC_DISALLOW_COPY_AND_ASSIGN(MyProgressTimer); ProgressType m_type; Vec2 m_vOrigin; Vec2 m_vTop; float m_fPercentage; Sprite* m_pSpr; CustomCommand _customCommand; V2F_C4B_T2F* _vertexData; int _vertexDataCount;};
6. 使用示例
在游戏中使用该进度条:
MyProgressTimer* progressTimer = MyProgressTimer::create(Sprite::create("progress_border.png"));progressTimer->setPosition(Vec2(winSize.width / 2 - 100, winSize.height / 2));this->addChild(progressTimer);progressTimer->setOrigin(Vec2(0.08, 0.08));progressTimer->setTop(Vec2(0.92, 0.92));progressTimer->stopAllActions();progressTimer->runAction(Sequence::create( MyProgressFromTo::create(15.0f, 0, 100), CallFunc::create([]() { CCLOG("CallFunc: end!"); }), nullptr));
优化点总结
GL_TRIANGLE_STRIP
绘制复杂区域,减少绘图调用次数。通过以上优化,应该能够实现一个更加稳定和高效的进度条效果。如果需要更详细的实现细节,欢迎在评论区留言!
发表评论
最新留言
感谢大佬
[***.8.128.20]2025年05月03日 21时13分36秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
<hdu - 1002> A + B Problem II
2019-03-10
Python识别璇玑图中诗的数量
2019-03-10
Django ORM操作
2019-03-10
剑指offer[32]——把数组排成最小的数
2019-03-10
谈谈关于springboot 添加依赖的那些事
2019-03-10
CF1475-D. Cleaning the Phone
2019-03-10
java基础-java与c#接口不同点
2019-03-10
Java并发工具篇
2019-03-10
第三方支付(支付宝)
2019-03-10
京喜小程序体验评分优化实践
2019-03-10
DIV+CSS兼容IE6、IE7、Firefox方法探究
2019-03-10
C#中文转换成拼音
2019-03-10
C#批量上传图片
2019-03-10
【亚马逊运营】有关滞销库存的处理方法之站外清库存法!
2019-03-10
pyhon中安装win32com模块
2019-03-10
C++错误笔记
2019-03-10
解决 MySQL 8.0 客户端连接 caching_sha2_password 问题
2019-03-10
【无线通信模块】GPRS DTU不稳定和容易掉线原因
2019-03-10
CSS(六)|页面布局之定位
2019-03-10