最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
Cocos2d-x优化之纹理优化
Cocos2d-x的优化涵盖多个方面,包括性能优化、图片优化、精灵优化以及内存优化等。本文将聚焦于纹理优化,主要探讨两个关键问题:纹理像素格式和纹理缓存异步加载。
1. 纹理像素格式
纹理像素格式是纹理优化工作中的一项重要指标。在能够最大程度满足用户对保真度要求的前提下,选择合适的像素格式可以显著提高纹理的处理速度。此外,纹理像素格式与硬件密切相关。
下面我们来详细了解一下主要的纹理像素格式:
- RGBA8888:32位色,这是默认的像素格式。每个通道为8位(比特),每个像素占用4个字节。
- BGRA8888:同样是32位色,每个通道8位(比特),每个像素4个字节。
- RGBA4444:16位色,每个通道4位(比特),每个像素2个字节。
- RGB888:24位色,没有Alpha通道,因此不具备透明度。每个通道8位(比特),每个像素3个字节。
- RGB565:16位色,无Alpha通道,没有透明度。R和B通道各为5位,G通道为6位。
- RGB5A1(或RGBA5551):16位色,每个通道各4位,Alpha通道仅用1位表示。
- PVRTC4:4位PVR压缩纹理格式,该格式专为iOS设备上的PowerVR图形芯片设计。在iOS设备上使用效果极佳,因为可以直接加载到显卡,无需中间的计算转化。
- PVRTC4A:具有Alpha通道的4位PVR压缩纹理格式。
- PVRTC2:2位PVR压缩纹理格式。
- PVRTC2A:具有Alpha通道的2位PVR压缩纹理格式。
此外,PVR格式在保存时还可采用Gzip和zlib压缩格式进行压缩,对应的保存文件为pvr.gz和pvr.ccz。经过压缩后,文件体积更小,加载时占用的内存也更少。虽然在转化为纹理时需要解压,但对CPU的影响极小。
2. 纹理缓存异步加载
在启动游戏或进入场景时,由于需要加载的资源过多,游戏往往会出现“卡顿”现象,严重影响用户体验。我们可以采用纹理缓存(TextureCache)异步加载纹理图片来解决这一问题。TextureCache类的异步加载函数如下:
virtual void addImageAsync(const std::string & filepath,
std::function< void(Texture2D *)> callback)
其中,第一个参数为文件路径,第二个参数是回调函数。下面通过一个实例来介绍纹理缓存异步加载的使用方法。假设有200张小图片需要加载到纹理缓存,加载过程中会在界面上显示一个进度条,示例如下:
纹理缓存异步加载实例
HelloWorldScene.cpp中的主要代码如下:
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
auto closeItem = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2,
origin.y + closeItem->getContentSize().height/2));
auto menu = Menu::create(closeItem, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 1);
_labelLoading = Label::createWithTTF ("loading...", "fonts/Marker Felt.ttf", 35);
_labelPercent = Label::createWithTTF ("0%", "fonts/Marker Felt.ttf", 35);
_labelLoading->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2 - 20));
_labelPercent->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2 + 20));
this->addChild(_labelLoading);
this->addChild(_labelPercent);
_numberOfLoadedSprites = 0;
_imageOffset = 0;
auto sharedFileUtils = FileUtils::getInstance();
std::string fullPathForFilename = sharedFileUtils->fullPathForFilename("ImageMetaData.plist"); // ①
ValueVector vec = FileUtils::getInstance()->getValueVectorFromFile(fullPathForFilename); // ②
_numberOfSprites = vec.size(); // ③
// 加载纹理
for( auto& e : vec) // ④
{
auto row = e.asValueMap();
auto filename = "icons/" + row.at("filename").asString();
Director::getInstance()->getTextureCache()->addImageAsync(filename,
CC_CALLBACK_1(HelloWorld::loadingCallBack, this)); // ⑤
}
return true;
}
void HelloWorld::loadingCallBack(Texture2D *texture) // ⑥
{
++_numberOfLoadedSprites;
__String* str = __String::createWithFormat("%d%%",
(int)(((float)_numberOfLoadedSprites / _numberOfSprites) * 100)); // ⑦
_labelPercent->setString(str->getCString()); // ⑧
Size visibleSize = Director::getInstance()->getVisibleSize();
int i = ++_imageOffset * 60;
auto sprite = Sprite::createWithTexture(texture); // ⑨
sprite->setAnchorPoint(Vec2(0,0));
addChild(sprite, -1);
sprite->setPosition(Vec2( i % (int)visibleSize.width, (i / (int)visibleSize.width) * 60));
if (_numberOfLoadedSprites == _numberOfSprites) // ⑩
{
_numberOfLoadedSprites = 0;
}
}
代码解释
- 第①行代码:获取资源目录下ImageMetaData.plist文件的全路径。ImageMetaData.plist文件用于描述要加载的图标文件名,其内容示例如下:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <array> <dict> <key>filename</key> <string>01-refresh.png</string> </dict> <dict> <key>filename</key> <string>02-redo.png</string> </dict> <dict> <key>filename</key> <string>03-loopback.png</string> </dict> <dict> <key>filename</key> <string>04-squiggle.png</string> </dict> ... </array> </plist>该文件是属性列表文件,内部结构为数组类型。
- 第②行代码:通过FileUtils的getValueVectorFromFile函数将ImageMetaData.plist文件读入到ValueVector类型的变量vec中。
- 第③行代码:
_numberOfSprites = vec.size()获取数组的长度,并将其赋值给成员变量_numberOfSprites,以便计算加载进度。 - 第④行代码:循环遍历数组,数组中的每个元素是键值对结构。通过
auto row = e.asValueMap()获取键值对结构,再通过row.at("filename").asString()从键值对对象row中取出键为filename对应的值。 - 第⑤行代码:调用TextureCache的addImageAsync函数实现异步加载图片缓存,
HelloWorld::loadingCallBack是回调函数,this参数表示回调函数的目标对象。 - 第⑥行代码:定义回调函数
HelloWorld::loadingCallBack的实现。 - 第⑦行代码:
(int)(((float)_numberOfLoadedSprites / _numberOfSprites) * 100)用于计算加载进度,"%d%%"用于显示百分号,其中%d是格式化输出数字,%%用于输出%,前面的%起到转义作用。 - 第⑧行代码:
_labelPercent->setString(str->getCString())用于设置进度标签_labelPercent的内容。 - 第⑨行代码:
auto sprite = Sprite::createWithTexture(texture)通过纹理对象texture创建精灵对象。 - 第⑩行代码:
if (_numberOfLoadedSprites == _numberOfSprites)用于判断是否完成加载任务,_numberOfLoadedSprites表示已加载的图片数,_numberOfSprites表示要加载的全部图片数。
通过以上纹理像素格式的选择和纹理缓存异步加载的实现,可以有效优化Cocos2d-x游戏的纹理处理,提升游戏性能和用户体验。