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 4
using 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 绘制复杂区域,减少绘图调用次数。
  • 百分比计算:根据实际需求计算顶点位置,实现更灵活的进度显示。
  • 代码简化:去除了不必要的硬编码,提高代码可维护性。
  • 通过以上优化,应该能够实现一个更加稳定和高效的进度条效果。如果需要更详细的实现细节,欢迎在评论区留言!

    上一篇:我理解中的cocos2dx之Ref
    下一篇:cocos2dx2.0升级为3.0一些常见变化纪录

    发表评论

    最新留言

    感谢大佬
    [***.8.128.20]2025年05月03日 21时13分36秒