【Cocos2d-x3.0学习笔记 08】精灵来了

前端之家收集整理的这篇文章主要介绍了【Cocos2d-x3.0学习笔记 08】精灵来了前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1.大量绘制精灵

bool HelloWorld::init(){
	if(!Layer::init()){
		return false;
	}
	//创建一堆的精灵
	for(int i=0;i<20000;i++){
		Sprite* s = Sprite::create("sprite.png");
		s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300));
		this->addChild(s);
	}
}
查看调试信息
  • 第一行的数字表示顶点数量
  • 第二行的数字表示渲染批次,意思就是,要执行多少次渲染;
  • 第三行左边的数字是帧率,右边的是每一帧所需的时间;

在cocos2d-x 2.0的时候,像这样创建这么多精灵的话应该已经卡掉了吧;

然后在3.0,cocos2d-x自己做了优化,会自动处理这样的情况,叫做Auto-batching;

2.3.0的新功能Auto-batching

要Auto-batching生效,需要满足的条件(好吧,看不懂啊)

  • 需要确保精灵对象拥有相同的TextureId(精灵表单spritesheet)
  • 确保他们都是用相同的材质和混合功能
  • 不再把精灵添加SpriteBatchNode

一般情况下,我们用同一张图片,并没有做特殊处理,就能满足Auto-batching的条件,不需要我们去做处理

3.Auto-batching的一些问题

bool HelloWorld::init(){
	if(!Layer::init()){
		return false;
	}
	//创建一堆的精灵
	for(int i=0;i<20000;i++){
		Sprite* s = Sprite::create("sprite.png");
		s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300));
		this->addChild(s);

		s = Sprite::create("sprite1.png");
		s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300));
		this->addChild(s);
	}
}
循环创建了两个不同的精灵,运行的结果和预期的不同,Auto-batching没有生效

Auto-batching的另外一个限制条件,对于不连续的渲染对象,是无法在一个渲染批次里进行渲染的。

怎样才算是“连续的对象”,最简单的解释就是:

  • 如果节点具有相同的globalZOrder值,则是连续的;
  • 否则,如果节点具有相同的localZOrder值,则是连续的;
  • 否则,如果节点具有相同的orderOfArrival值,则是连续的;
  • 连续的节点还必须使用相同的纹理,就是相同的图片咯。
bool HelloWorld::init(){
	if(!Layer::init()){
		return false;
	}
	//创建一堆的精灵
	for(int i=0;i<20000;i++){
		Sprite* s = Sprite::create("sprite.png");
		s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300));
		this->addChild(s);
		s->setGlobalZOrder(1);

		s = Sprite::create("sprite1.png");
		s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*300));
		this->addChild(s);
		s->setGlobalZOrder(2);
	}
}

给每个精灵设置了globalZOrder属性,globalZOrder对排序有了影响,,使得所有的sprite精灵都是连续的,sprite1也是连续的

这就满足了Auto-batching的条件了。

影响精灵排序的优先级:globalZOrder优先于LocalZOrder,localZOrder优先于orderOfArrival


4.深入了解Auto-batching,嗯,我还看不懂,之后再回过来看


5.一次渲染,SpriteBatchNode的特别之处

SpriteBatchNode如果包含了子节点,那么所有的子节点在绘制的时候只会调用一次OpenGL的渲染。只有使用同一纹理的精灵才能添加到SpriteBatchNode上,如果精灵没有添加到SpriteBatchNode上,那么每一个精灵的绘制都会调用一次OpenGL的渲染(当然咯,满足Auto-batching条件的除外咯)。

bool HelloWorld::init(){
	if(!Layer::init()){
		return false;
	}
	//创建批次渲染对象,并添加到场景中
	SpriteBatchNode* batchNode = SpriteBatchNode::create("sprite.png");
	this->addChild(batchNode);

	//创建精灵,加入到批次渲染对象中
	for(int i=0;i<999;i++){
		Sprite* s = Sprite::cretae("sprite.png");
		s->setPosition(Point(CCRANDOM_0_1()*480,120+CCRANDOM_0_1()*200));

		batchNode->addChild(s);
	}
	return true;
}
这边是先将SpriteBatchNode加入到场景中,再将精灵加到SpriteBatchNode中;
当然,SpriteBatchNode也是有限制的,并需把精灵添加到SpriteBatchNode上,并且只允许添加精灵对象,其他的对象是不允许添加的。

6.Texture简单介绍---纹理

使用相同的图片或者使用相同图片的一部分具有相同的纹理

bool HelloWorld::init(){
<span style="white-space:pre">	</span>if(!Layer::init()){
<span style="white-space:pre">		</span>return false;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>//截取图片的一部分来创建精灵
<span style="white-space:pre">	</span>Sprite* s1 = Sprite::createWithSpriteFrame(SpriteFrame::create("sprite.png",Rect(0,60,50)));
<span style="white-space:pre">	</span>Spritr* s2 = Sprite::create("sprite.png");
<span style="white-space:pre">	</span>s1->setPosition(Point(100,200));
<span style="white-space:pre">	</span>s2->setPosition(Point(300,200));
<span style="white-space:pre">	</span>this->addChild(s1);
<span style="white-space:pre">	</span>this->addChild(s2);


<span style="white-space:pre">	</span>//获取两个精灵的纹理对象,通过控制台观察,发现两个精灵对象的值是一样的
<span style="white-space:pre">	</span>Texture2D* t1 = s1->getTexture();
<span style="white-space:pre">	</span>Texture2d* t2 = s2->getTexture();
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>return true;
}

7.TexturePacker工具

当需创建多个精灵的时候,图片不一样,纹理就没有办法相同的,那就不能使用Auto-batching或者SpriteBatchNode了。

这个时候,我们就要将多个图片拼接的一个图片中了。

当一张图片中拥有多个精灵时。

bool HelloWorld::init(){
	if(!Layer::init()){
		return false;
	}
	//截取图片的一部分来创建精灵
	Sprite* s1 = Sprite::createWithSpriteFrame("sprites.png",50));//精灵一
	Spritr* s2 = Sprite::createWithSpriteFrame("sprites.png",Rect(30,30,50));//精灵二
	s1->setPosition(Point(100,200));
	s2->setPosition(Point(300,200));
	this->addChild(s1);
	this->addChild(s2);
	
	return true;
}
以上是手动计算精灵在图中的位置,不过相对来说是比较麻烦的

接下来,我们就要使用到工具了

TexturePacker图片打包工具:http://www.codeandweb.com/texturepacker

不过打开来相对是比较慢的,还有,好像官网是不允许下载旧版本的,要输入什么东西

TexturePacker一些选项的作用

  • Data Format:生成配置文件的格式类型,不同引擎支持不同的格式
  • Texture Format:生成图片的格式
  • Image Format:图片色彩模式,木头使用的是RGBA4444
  • Dithering:抖动显示,如果我们选择的图片色彩模式比较低(颜色数较少)导致图片失真严重,可以通过抖动显示来缓解失真程度
  • Size constraints:图片打包都是按2的次方的
  • Allow rotation:是否允许图片旋转,旋转在某些时候可以缩小图片
  • Trim:是否允许把图片透明部分去掉

点击“Publish”按钮,导出打包后的文件"*.plist"和"*.png"

8.加载打包后的图片

既然用TexturePacker打包好了图片,我们当然就要使用它了

将两个文件复制到resources文件夹下

bool HelloWorld::init(){
	if(!Layer::init()){
		return false;
	}
	//将图片添加到精灵缓存池
	SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
	frameCache->addSpriteFramesWithFile("sprites.plist","sprites.png");

	//用小图片的名字即可创建精灵,即使是旋转了的图片,这样创建出来也是正常的
	Sprite* s = Sprite::createWithSpriteName("sprite1.png");
	s->setPosition(Point(100,200));
	This->addChild(s);
	
	return true;
}

创建精灵时使用的是打包前图片的名字,plist文件里记录了各个图片在大图片中的位置和大小

9.动画Animation

Animate* HelloWorld::createAnimate1(){
	//将15张动作的图片放到resources文件夹中
	int sNum = 15;//动作图片数量
	SpriteFrame* frame = NULL;//每个图片为一个SpriteFrame对象
	Vector<SpriteFrame*> frameVec;

	//用一个列表保存所有SpriteFrame对象
	for(int i=1;i<=sNum;i++){
		frame = SpriteFrame::create(StringUtils::format("run%d.png",i),130,130));
		frameVec.pushBack(frame);
	}
	//使用SpriteFrame列表创建动画对象
	Animation* animation = Animation::createWithSpriteFrames(frameVec);
	animation->setLoops(-1);//循环次数,-1表示无限循环
	animation->setDelayPerUnit(0.1f);//设置每一帧播放延迟

	//将动画包装成一个动作
	Animate* action = Animate::create(animation);
	return action;
}

bool HelloWorld::init(){
	if(!Layer::init()){
		return false;
	}
	Sprite* sprite = Sprite::create("run1.png");
	sprite->setPosition(Point(200,200));
	sprite->runAction(createAnimate1);//直接执行就可以了
}
在没有打包图片的时候,需要指定图片的大小,接下来我们就用打包后的图片来创建动画
Animate* HelloWorld::createAnimate1(){
	//加载图片帧到缓存池
	SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
	frameCache.addSpriteFramesWithFile("boys.plist","boys.png");

	//将15张动作的图片放到resources文件夹中
	int sNum = 15;//动作图片数量
	SpriteFrame* frame = NULL;//每个图片为一个SpriteFrame对象
	Vector<SpriteFrame*> frameVec;

	//用一个列表保存所有SpriteFrame对象
	for(int i=1;i<=sNum;i++){
		frame = frameCache->getSpriteFrameByName(StringUtils::format("run%d.png",i));
		frameVec.pushBack(frame);
	}
	//使用SpriteFrame列表创建动画对象
	Animation* animation = Animation::createWithSpriteFrames(frameVec);
	animation->setLoops(-1);//循环次数,-1表示无限循环
	animation->setDelayPerUnit(0.1f);//设置每一帧播放延迟

	//将动画包装成一个动作
	Animate* action = Animate::create(animation);
	return action;
}

10.创建动画的辅助类

AnimationUtil

文件

#ifndef _ANIMATION_UTIL_H_
#define _ANIMATION_UTIL_H_
#include "cocos2d.h"
class AnimationUtil{
	static cocos2d::Animation* createWithStringFrameName(const char* name,float delay,int iLoops);
	static cocos2d::Animation* createWithStringFrameNameAndNum(const char* name,int num,int iLoops);
};

#endif
cpp
#include "AnimationUtil.h"
USING_NS_CC;
//根据名字来创建
Animation* AnimationUtil::createWithStringFrameName(const char* name,int iLoops){
<span style="white-space:pre">	</span>SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
<span style="white-space:pre">	</span>Vector<SpriteFrame*> frameVec;
<span style="white-space:pre">	</span>SpriteFrame* frame = NULL;
<span style="white-space:pre">	</span>int index = 1;
<span style="white-space:pre">	</span>do{
<span style="white-space:pre">		</span>frame = frameCache->getSpriteFrameByName(StringUtils::format("%s%d.png",name,index++));
<span style="white-space:pre">		</span>if(frame == NULL){
<span style="white-space:pre">			</span>break;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>frameVec.pushBack(frame);
<span style="white-space:pre">	</span>}while(true);


<span style="white-space:pre">	</span>Animation* animation = Animation::createWithSpriteFrames(frameVec);
<span style="white-space:pre">	</span>animation->setLoops(iLoops);
<span style="white-space:pre">	</span>animation->setRestoreOriginalFrame(true);
<span style="white-space:pre">	</span>animation->setDelayPerUnit(delay);


<span style="white-space:pre">	</span>return animation;
}
//根据名字和数量来创建
Animation* AnimationUtil::createWithStringFrameNameAndNum(const char* name,int iLoops){
<span style="white-space:pre">	</span>SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
<span style="white-space:pre">	</span>Vector<SpriteFrame*> frameVec;
<span style="white-space:pre">	</span>SpriteFrame* frame = NULL;
<span style="white-space:pre">	</span>int index = 1;
<span style="white-space:pre">	</span>for(int i = 1;i<=num;i++){
<span style="white-space:pre">		</span>frame = frameCache->getSpriteFrameByName(StringUtils::format("%s%d.png",index++));
<span style="white-space:pre">		</span>if(frame == NULL){
<span style="white-space:pre">			</span>break;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>frameVec.pushBack(frame);
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>Animation* animation = Animation::createWithSpriteFrames(frameVec);
<span style="white-space:pre">	</span>animation->setLoops(iLoops);
<span style="white-space:pre">	</span>animation->setRestoreOriginalFrame(true);
<span style="white-space:pre">	</span>animation->setDelayPerUnit(delay);


<span style="white-space:pre">	</span>return animation;
}
使用方法
SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
frameCache->addSpriteFramesWithFile("runs.plist","runs.png");

Animation* action = AnimationUtil::createWithStringFrameName("run",0.1f,-1);
sprite->runAction(Animate::create(action));

猜你在找的Cocos2d-x相关文章