1.纹理控制。
Sprite *pSprite = Sprite::create("background.png"); TexParams params = {GL_NEAREST,GL_NEAREST,GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE}; pSprite->getTexture()->setTexParameters(params);
上面最重要的函数就是setTexParameters();他就是根据ccTexParameters来控制纹理图像的纹理如何对应到屏幕的像素。根据什么来实现纹理的缩放和纹理的重复(重复纹理)等效果。
/** Extension to set the Min / Mag filter */ typedef struct _TexParams { GLuint minFilter; GLuint magFilter; GLuint wrapS; GLuint wrapT; }TexParams;
先看看TexParams这个结构体,他的前两个成员变量叫做基本过滤器。用来指定按照什么规则来控制纹理的缩放效果。
两个基本的纹理过滤规则:
(1)GL_LINEAR:“当显示纹理时,显示的大小大于或者小于原纹理的尺寸时,使用邻近像素点来插值补点”
特点:图像拉伸或者缩小后,看起来失真了,但是效果比GL_NEAREST好,看起来没有人工操作后的痕迹.
(2)GL_NEAREST:(最邻近过滤)最简单最快捷的过滤方式。
特点:当纹理拉伸到特别大的时候,会出现大片斑驳状像素。
TexParameters后面的两个成员变量叫做纹理环绕模式.是用来控制当绘制纹理边界范围之外的像素点的时候应该怎么去处理。openGL纹理的坐标范围是(0.0f, 1.0f).意思就是当纹理坐标大于1.0f或者纹理坐标小于0.0f的时候怎么去处理纹理。
两个常用的纹理环绕模式:
(1)GL_REPEAT:在纹理超过1.0f的方向上对纹理进行重复。如果你想显示纹理边界之外的像素点时,把它旁边的纹理像素点平铺过去 .
cocos2d::Texture2D::TexParams tp ; tp.minFilter = GL_LINEAR; tp.magFilter = GL_LINEAR; tp.wrapS = GL_REPEAT; tp.wrapT = GL_REPEAT; matteLayer=Sprite::create("middle_background.png"); matteLayer->retain(); matteLayer->setPosition(s.width/2,s.height/2); matteLayer->setScale(4.0f, 4.0f); matteLayer->getTexture()->setTexParameters(tp); this->addChild(matteLayer); this->schedule(schedule_selector(BackgroundLayer::changColor), 1.0f); this->schedule(schedule_selector(BackgroundLayer::scrollBackground), 0.01f);
在scrollBackground()中:
void BackgroundLayer::update(float dt){ int nLen = 100; static int nOffset = 0; nOffset += nLen*dt; matteLayer->setTextureRect(Rect(0,-nOffset, matteLayer->getTexture()->getPixelsWide(),matteLayer->getTexture()->getPixelsHigh())); }
上面的代码通过不断增加nOffset来绘制精灵的纹理边界之外的区域。由于之前设置了环绕模式为GL_REPEAT。则绘制精灵之外的区域的时候把旁边的纹理像素进行平铺。形成重复纹理的效果。
程序中这个精灵的纹理不断的会从右向左滚动。注意:精灵的位置不会发生变化.这就用GL_REPEAT来实现了重复纹理的效果.
还需要注意的是RepeatSprite.png的宽和高必须是2的N次方大小。否则的话当你用GL_REPEAT来作为环绕模式的时候会出现警告提示:GL_REPEAT mode must be POT.意思就是用GL_REPEAT模式图片大小必须是2的指数幂(POT).警告提示完了之后就是崩溃.
(2)GL_CLAMP:作用就是纹理的绘制坐标大于1.0则就设置成1.0, 小于0.0则设置成0.0;
如果绘制的x坐标大于了纹理宽度,则就用纹理最右边的一个像素来绘制后面的区域.
2.纹理缓冲区.(TextureCache)
Sprite::create()这个函数里面有句代码:auto TexureCache=Director::getInstance()->getTextureCache();
精灵就是用这句代码来根据提供的图片生成精灵的纹理对象。所以,不难看出用图片创建了精灵对象时,图片数据被加载到内存中,这块内存就叫做纹理缓冲区(TextureCache),只要不手动就从纹理缓冲区中把图片数据删除则图片数据永远占着内存不释放。所以这就带出了针对纹理缓冲区的一些议题.cocos2d中存放被加载的图片数据的缓冲区(内存)分两类:存放由多个小图拼接成的大图的区域 和 存放单个图片的区域。具体分别对应于SpriteFrameCache和 TextureCache这两个类。
为什么会存在这两个类呢?从名字就知道他们是缓冲区,存在的唯一理由是下次如果需要同一个图片数据的时候直接从缓冲区去取,而不需要再次把图片文件载入到内存中去。这样的话节省了内存还提高了渲染速度。
假设有一张图片example.png被加载到缓冲区中去,如果这张图片是第一次被加载到内存,则它首先被加载到缓冲区中去,然后再从缓冲区中读取出来,用于创建纹理对象。
如果这个图片的数据在内存中没有被删除,则在随后程序员又想用图片example.png来创建纹理对象的时候,cocos2d首先从纹理缓冲区中查找一下看是否存在这个图片的数据。如果存在就直接从缓冲区中读取。就不需要再次从硬盘载入到内存。多么方便啦!难道不是吗?这样为你节省了内存和提高了效率。
释放:
如果游戏有很多场景,在切换场景的时候可以把前一个场景的内存全部释放,防止总内存过高.
Director::getInstance()->getTextureCache()->removeAllTextures(); 释放到目前为止所有加载的图片
Director::getInstance()->getTextureCache()->removeUnusedTextures();将引用计数为1的图片释放掉
Director::getInstance()->getTextureCache()->removeTexture();单独释放某个图片
SpriteFrameCache 与 TextureCache 释放的方法差不多。
值得注意的是释放的时机,一般在切换场景的时候释放资源,如果从A场景切换到B场景,调用的函数顺序为B::init()---->A::exit()---->B::onEnter() 可如果使用了切换效果,比如CTransitionJumpZoom::transitionWithDuration这样的函数,则函数的调用顺序变为B::init()---->B::onEnter()---->A::exit() 而且第二种方式会有一瞬间将两个场景的资源叠加在一起,如果不采取过度,很可能会因为内存吃紧而崩溃。
有时强制释放全部资源时,会使某个正在执行的动画失去引用而弹出异常,可以调用Director::getInstance()->getActionManager()->removeAllActions();来解决。
Cocos2d-x 3.0 纹理