转自:http://blog.csdn.net/qq51931373/article/details/9152227

1.纹理控制。

看此代码:

CCSprite *pSprite = CCSprite::create("ship.png");

ccTexParams params = {GL_NEAREST,GL_NEAREST,GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE};

pSprite->getTexture()->setTexParameters(&params);

上面最重要的函数就是setTexParameters();他就是根据ccTexParameters来控制纹理图像的纹理如何对应到屏幕的像素。根据什么来实现纹理的缩放和纹理的重复(重复纹理)等效果。

typedef struct _ccTexParams {
GLuint minFilter;
GLuint magFilter;
GLuint wrapS;
GLuint wrapT;
} ccTexParams;

先看看ccTexParams这个结构体,他的前两个成员变量叫做基本过滤器。用来指定按照什么规则来控制纹理的缩放效果。

两个基本的纹理过滤规则:

(1)GL_LINEAR:“当显示纹理时,显示的大小大于或者小于原纹理的尺寸时,使用邻近像素点来插值补点”

特点:图像拉伸或者缩小后,看起来失真了,但是效果比GL_NEAREST好,看起来没有人工操作后的痕迹.

(2)GL_NEAREST:(最邻近过滤)最简单最快捷的过滤方式。

特点:当纹理拉伸到特别大的时候,会出现大片斑驳状像素。

ccTexParameters后面的两个成员变量叫做纹理环绕模式.是用来控制当绘制纹理边界范围之外的像素点的时候应该怎么去处理。openGL纹理的坐标范围是(0.0f, 1.0f).意思就是当纹理坐标大于1.0f或者纹理坐标小于0.0f的时候怎么去处理纹理。

两个常用的纹理环绕模式:

(1)GL_REPEAT:在纹理超过1.0f的方向上对纹理进行重复。如果你想显示纹理边界之外的像素点时,把它旁边的纹理像素点平铺过去 .

看代码:

pSprite = CCSprite::create("RepeatSprite.png");
pSprite->retain();
CCSize size = CCDirector::sharedDirector()->getWinSize();
pSprite->setPosition(CCPointMake(size.width/, size.height/));
addChild(pSprite);
ccTexParams tp ;
tp.minFilter = GL_LINEAR;
tp.magFilter = GL_LINEAR;
tp.wrapS = GL_REPEAT;
tp.wrapT = GL_REPEAT;
pSprite->getTexture()->setTexParameters(&tp);
//不断的修改pSprite的纹理矩形区域,通过修改纹理的x坐标,来绘制纹理区域之外的部分。
schedule(schedule_selector(HelloWorld::UpdateTexture),0.05f);

在UpdateTexture()中:

void HelloWorld::UpdateTexture(float dt)
{
int nLen = ;
static int nOffset = ;
nOffset += nLen*dt;
pSprite->setTextureRect(CCRectMake(nOffset, , pSprite->getTexture()->getPixelsWide(),pSprite->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.纹理缓冲区.(CCTextureCache)

CCSprite::create()这个函数里面有句代码:CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFilename);

精灵就是用这句代码来根据提供的图片生成精灵的纹理对象。所以,不难看出用图片创建了精灵对象时,图片数据被加载到内存中,这块内存就叫做纹理缓冲区(TextureCache),只要不手动就从纹理缓冲区中把图片数据删除则图片数据永远占着内存不释放。所以这就带出了针对纹理缓冲区的一些议题。

cocos2d中存放被加载的图片数据的缓冲区(内存)分两类:存放由多个小图拼接成的大图的区域   和  存放单个图片的区域。具体分别对应于CCSpriteFrameCache和 CCTextureCache这两个类。CCSpriteFrameCache主要是做动画用的,请参考我的这篇文章:http://blog.csdn.net/qq51931373/article/details/9151363

为什么会存在这两个类呢?从名字就知道他们是缓冲区,存在的唯一理由是下次如果需要同一个图片数据的时候直接从缓冲区去取,而不需要再次把图片文件载入到内存中去。这样的话节省了内存还提高了渲染速度。

假设有一张图片example.png被加载到缓冲区中去,如果这张图片是第一次被加载到内存,则它首先被加载到缓冲区中去,然后再从缓冲区中读取出来,用于创建纹理对象。

如果这个图片的数据在内存中没有被删除,则在随后程序员又想用图片example.png来创建纹理对象的时候,cocos2d首先从纹理缓冲区中查找一下看是否存在这个图片的数据。如果存在就直接从缓冲区中读取。就不需要再次从硬盘载入到内存。多么方便啦!难道不是吗?这样为你节省了内存和提高了效率。

释放:

如果游戏有很多场景,在切换场景的时候可以把前一个场景的内存全部释放,防止总内存过高.

CCTextureCache::sharedTextureCache()->removeAllTextures();   //释放到目前为止所有加载的图片
CCTextureCache::sharedTextureCache()->removeUnusedTextures(); //将引用计数为1的图片释放掉
CCTextureCache::sharedTextureCache()->removeTexture(); //单独释放某个图片

CCSpriteFrameCache 与 CCTextureCache 释放的方法差不多。

值得注意的是释放的时机,一般在切换场景的时候释放资源,如果从A场景切换到B场景,调用的函数顺序为B::init()---->A::exit()---->B::onEnter() 可如果使用了切换效果,比如CTransitionJumpZoom::transitionWithDuration这样的函数,则函数的调用顺序变为B::init()---->B::onEnter()---->A::exit() 而且第二种方式会有一瞬间将两个场景的资源叠加在一起,如果不采取过度,很可能会因为内存吃紧而崩溃。

有时强制释放全部资源时,会使某个正在执行的动画失去引用而弹出异常,可以调用CCActionManager::sharedManager()->removeAllActions();来解决。

下面用简短的代码来证实2中这些阐述的事实.

我就用cocos2d-x中的例子Texture2dTest中的 CCTextureCache:remove 和 CCTexture2D:drawAtPoit来进行试验,这两个例子的场景是挨着的,CCTextureCache:remove下一个例子就是CCTexture2D:drawAtPoit,为什么要选这两个,看下面的代码。

代码有点长,但是很简单。不信你看:

void TextureCache1::onEnter()
{
TextureDemo::onEnter(); CCSize s = CCDirector::sharedDirector()->getWinSize(); CCSprite *sprite; sprite = CCSprite::create("Images/grossinis_sister1.png");
sprite->setPosition(ccp(s.width/*, s.height/));
sprite->getTexture()->setAliasTexParameters();
sprite->setScale();
addChild(sprite); //从纹理缓冲区中移除grossinis_sister1.png的图片数据
CCTextureCache::sharedTextureCache()->removeTexture(sprite->getTexture()); sprite = CCSprite::create("Images/grossinis_sister1.png");
sprite->setPosition(ccp(s.width/*, s.height/));
sprite->getTexture()->setAntiAliasTexParameters();
sprite->setScale();
addChild(sprite); //从纹理缓冲区中移除grossinis_sister1.png的图片数据
CCTextureCache::sharedTextureCache()->removeTexture(sprite->getTexture());
// 2nd set of sprites sprite = CCSprite::create("Images/grossinis_sister2.png");
sprite->setPosition(ccp(s.width/*, s.height/));
sprite->getTexture()->setAliasTexParameters();
sprite->setScale();
addChild(sprite); //从纹理缓冲区中移除grossinis_sister2.png的图片数据
CCTextureCache::sharedTextureCache()->removeTextureForKey("Images/grossinis_sister2.png"); sprite = CCSprite::create("Images/grossinis_sister2.png");
sprite->setPosition(ccp(s.width/*, s.height/));
sprite->getTexture()->setAntiAliasTexParameters();
sprite->setScale();
addChild(sprite); //从纹理缓冲区中移除grossinis_sister2.png的图片数据
CCTextureCache::sharedTextureCache()->removeTextureForKey("Images/grossinis_sister2.png");
}

上面的代码就是例子:CCTextureCache:remove中的代码.上面在加载了图片到纹理缓冲区后就立即从纹理缓冲区中把图片数据删除了。

在这个例子场景中我们可以看到四个精灵(四个妹子)。

然后再看下一个场景CCTexture2D:drawAtPoit中的代码:

void TextureDrawAtPoint::onEnter()
{
TextureDemo::onEnter(); DWORD dwBegin = GetTickCount();
m_pTex1 = CCTextureCache::sharedTextureCache()->addImage("Images/grossinis_sister1.png");
m_pTex2 = CCTextureCache::sharedTextureCache()->addImage("Images/grossinis_sister2.png");
DWORD dwEnd = GetTickCount()-dwBegin;
m_pTex1->retain();
m_pTex2->retain();
}

在这个场景的例子代码中用到了上一个场景中的两个图片文件:grossinis_sister1.png和 grossinis_sister2.png.由于在前一个场景中这两个图片文件的数据从纹理缓冲区中删除了。所以我打断点测试dwEnd的值是16毫秒。内存多出了240k左右.如果我把上面removeTexture()和removeTextureForKey()屏蔽,则dwEnd的值是0毫秒。内存少了240k,这是多么大的效率提升啊。

当然切换场景的时候还是要把前一个场景中用到的图片数据从纹理缓冲区中删除,方式内存占用过高。系统直接kill掉你的游戏。

个人建议每一个场景的图片单独放在一个文件夹中。切换场景的时候加载场景对应的文件夹中的图片。这样的话思路就很清晰。多个场景中公用图片也放在一个单独的场景中.

cocos2d-x 纹理深入研究的更多相关文章

  1. cocos2d-x 纹理深入研究 第二部分

    转自:http://blog.csdn.net/qq51931373/article/details/9152227 1.纹理控制. 看此代码: CCSprite *pSprite = CCSprit ...

  2. Cocos2D绘制纹理的一般方法

    如果你想在通常情况下绘制纹理,最简单的方法是在CCSprite的子类中实现.否则你将不得不自己创建一个CCRenderState对象传递给blend模式,着色器以及(可选的)纹理给CCRenderer ...

  3. coco2d-x 纹理研究

    转自:http://blog.csdn.net/qq51931373/article/details/9119161 1.通常情况下用PVR格式的文件来进行图片显示的时候,在运行速度和内存消耗方面都要 ...

  4. cocos2d-x 纹理研究

    转自:http://blog.csdn.net/qq51931373/article/details/9119161 1.通常情况下用PVR格式的文件来进行图片显示的时候,在运行速度和内存消耗方面都要 ...

  5. 【转】coco2d-x 纹理研究

    1.通常情况下用PVR格式的文件来进行图片显示的时候,在运行速度和内存消耗方面都要比PNG格式要快和小.一般情况下PVR消耗的内存比PNG消耗的内存小25%左右.PVR格式可以用ZWoptex导出.P ...

  6. iPhone开发与cocos2d 经验谈

    转CSDN jilongliang : 首先,对于一个完全没有mac开发经验,甚至从没摸过苹果系统的开发人员来说,首先就是要熟悉apple的那一套开发框架(含开发环境IDE.开发框架uikit,还有开 ...

  7. 如何优化cocos2d程序的内存使用和程序大小

    在我完成第一个游戏项目的时候,我深切地意识到"使用cocos2d来制作游戏的开发者们,他们大多会被cocos2d的内存问题所困扰".而我刚开始接触cocos2d的时候,社区里面的人 ...

  8. 如何优化cocos2d程序的内存使用和程序大小:第一部分

    译者: 在我完成第一个游戏项目的时候,我深切地意识到“使用cocos2d来制作游戏的开发者们,他们大多会被cocos2d的内存问题所困扰”.而我刚开始接触cocos2d的时候,社区里面的人们讨论了一个 ...

  9. 如何优化cocos2d程序的内存使用和程序大小:第一部分_(转)

    译者: 在我完成第一个游戏项目的时候,我深切地意识到“使用cocos2d来制作游戏的开发者们,他们大多会被cocos2d的内存问题所困扰”.而我刚开始接触cocos2d的时候,社区里面的人们讨论了一个 ...

随机推荐

  1. 1220. Stacks

    1220 又一神题啊  卡内存可以卡到这种地步 省得不行了 开两个[N]数组 一个来记录前驱  一个存数 记录前驱的用unsigned short类型 最大可达65535 不过可以标记一下是否比这个数 ...

  2. bzoj4008

    好题,这题可以分开算每个技能的贡献 orz popoqqq http://blog.csdn.net/popoqqq/article/details/45365759 ..,..] of double ...

  3. poj1062

    经典的图论建模题: 先拿开的等级问题不看: 每个物品本身的价格就是有一个自定义源点到这个点距离: 有了A物品B物品优惠为W就代表由B到A的有向路权值为W: 最后的最小花费就是源点的点1的最短路径(酋长 ...

  4. Repeater实例应用

    在实际开发过程中,涉及到数据绑定,分页,以及一对多展示数据时,遇到这样的需求我们怎么解决呢?下面以帖子展示来逐一说明. 帖子主要由两部分组成,第一部分是发帖人的原创内容部分,第二部分是用户评论部分,这 ...

  5. UVa 1606 (极角排序) Amphiphilic Carbon Molecules

    如果,没有紫书上的翻译的话,我觉得我可能读不懂这道题.=_=|| 题意: 平面上有n个点,不是白点就是黑点.现在要放一条直线,使得直线一侧的白点与另一侧的黑点加起来数目最多.直线上的点可以看作位于直线 ...

  6. UVa 10214 (莫比乌斯反演 or 欧拉函数) Trees in a Wood.

    题意: 这道题和POJ 3090很相似,求|x|≤a,|y|≤b 中站在原点可见的整点的个数K,所有的整点个数为N(除去原点),求K/N 分析: 坐标轴上有四个可见的点,因为每个象限可见的点数都是一样 ...

  7. [VC6]ONMESSAGE()宏编译时出现"sytax error ;"错误时

    自定义消息时编译出错,经排查,在定义消息的头文件里 #define WM_XXX (WM_USER+1000); 最后多加了一个分号引起. 吐血.

  8. 【转】忙里偷闲写的小例子---读取android根目录下的文件或文件夹

    原文网址:http://www.cnblogs.com/wenjiang/p/3140055.html 最近几天真的是各种意义上的忙,忙着考试,还要忙着课程设计,手上又有外包的项目,另一边学校的项目还 ...

  9. SqlServer中decimal(numeric )、float 和 real 数据类型的区别[转]

    decimal(numeric )             同义,用于精确存储数值 float 和 real                      不能精确存储数值   decimal 数据类型最 ...

  10. 新手学习数据库(一)用Powerdesigner设计数据库

    说明: 一.学会用开发语言进行数据库编程,其关键是在于学会sql语言,开发语言只不过给程序员提供了一个操作数据库的接口罢了. 二. 本人也是初学者,采用的数据库设计软件是powerdesigner.利 ...