存档

2014年5月 的存档

游戏服务端pomelo完整安装配置过程

2014年5月30日 没有评论

游戏服务端pomelo安装配置

一、安装环境

debian 7.0 amd64

二、安装需要的组件

1、安装nodejs

注:debian下nodejs没有相应的apt包,所以无法用apt-get安装,只能通过nodejs的源码包安装,

这里有比较全的其他系统环境下安装nodejs的方式https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager

a、安装所需的组件,python g++

#apt-get install python g++ make checkinstall fakeroot 

b、到http://nodejs.org/获取源码包的下载地址,下载nodejs的gz包

wget http://nodejs.org/dist/v0.10.28/node-v0.10.28.tar.gz

c、解压编译安装

tar zxvf node-v0.10.28.tar.gz
cd node-v0.10.28
./configure
make
sudo make install

d、检查是否安装成功

#node -v

v0.10.28

三、安装pomelo

安装的说明:https://github.com/NetEase/pomelo/wiki/%E5%AE%89%E8%A3%85pomelo

方式一、使用npm(node包管理工具)全局安装pomelo:

$ npm install pomelo -g

等待一会儿就开始下载包安装,最后提示以下信息,说明安装成功

> ws@0.4.25 install /usr/local/lib/node_modules/pomelo/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

方式二、通过下载源代码的方式安装
$ git clone https://github.com/NetEase/pomelo.git
$ cd pomelo
$ npm install -g

四、安装问题

安装pomelo时,提示

gyp WARN EACCES user "root" does not have permission to access the dev dir "/root/.node-gyp/0.10.28"
gyp WARN EACCES attempting to reinstall using temporary dev dir "/root/.nvm/v0.10.28/lib/node_modules/pomelo/node_modules/pomelo-rpc/node_modules/toobusy/.node-gyp"

命令换成 npm install pomelo -g –unsafe-perm 就可以成功,运行到gyp http GET http://nodejs.org/dist/v0.10.28/node-v0.10.28.tar.gz时, 整个安装过程会有点久,需耐心等待

五、第一个游戏服务端项目

pomelo命令行手册https://github.com/NetEase/pomelo/wiki/pomelo%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8

1、创建项目

#pomelo init ./mygameserver

2、安装依赖包:

#cd mygameserver

#sh npm-install.sh

3、启动game-server服务器:
$ cd game-server
$ pomelo start
4、启动web-server服务器:
$ cd web-server
$ node app

4、启动浏览器,运行http://你的服务器ip:3001/,看到pomelo的欢迎页面

5、点击‘Test Game Server’按钮,此时没反应,

进入项目目录mygameserver下的

#cd ./web-server/public

#vim index.html

修改index.html 中var host = "127.0.0.1";为var host = "你的服务器ip";

此时点击‘Test Game Server’按钮,就会出现game server is ok 消息框。

好了 搭建成功!

来自东子哥的Blog

福建手游开发技术群:13734312

分类: 未分类 标签:

[cocos2d-js]按钮整合成大图后打APK后不显示

2014年5月28日 没有评论

网页版本都能正常显示碎图和整合成大图的

手机版本不正常

var btnKick = cc.MenuItemImage.create(
            "#btn_kick.png",
            "#btn_kick1.png",
            this.onKickKeyDown);

手机版本用cc.MenuItemSprite就正常

var kickUp = cc.Sprite.create("#btn_kick.png");
        var kickDown = cc.Sprite.create("#btn_kick1.png");
        var kickDisable = cc.Sprite.create("#btn_kick1.png");
        var btnKick = cc.MenuItemSprite.create(kickUp, kickDown, kickDisable, this.onKickKeyDown, this);
        btnKick.x = 380;
        btnKick.y = 20;
        btnKick.scale = 0.65;
        this.addChild(btnKick, 9999);

 

// 暂停
        var btnPause = cc.MenuItemImage.create(
            "res/pause.png",
            "res/pause2.png",
            function () {
                this.onStop();
            }, this);
        btnPause.x = winSize.width - 30;
        btnPause.y = winSize.height - 70;
        var menu = cc.Menu.create(this._btnPower, btnPause);
        menu.x = 0;
        menu.y = 20;
        this.addChild(menu, 120);
        this._btnPower.visible = false;

 

 

 

分类: cocos2d 标签:

Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

2014年5月25日 没有评论

1、概述

    在游戏的运行过程中,图形的绘制是非常大的开销。对于良莠不齐的Android手机市场,绘制优化较好的游戏,可以在更多的手机上运行,因此也是优化的重中之重。图形方面的优化主要体现在减少GPU的绘制次数上。这里我们分别从自动优化渲染批次和绘制剔除两个方面来看新版本在绘制上的优化。

2、自动batch


    在Cocos2d-x 3.x中,抛弃了先前手动编写BatchNode,采用自动管理的方式。说起BatchNode,就难免涉及到显卡底层的绘制原理。简单的说,每提交一条绘制指令到显卡都会产生消耗,因此尽量少的提交指令就可以优化性能。更具体的说,当整个场景绘制都放在同一条指令中时,是最佳的状态。

    只介绍理论很难说明问题,我们动手写个Demo做测试。

    创建一个新工程。更改init函数如下。

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    Node* node = Node::create();
    char name[32];
    for(int i  = 0;i<100;++i)
    {
        memset(name, 0, sizeof(name));
        sprintf(name, "%d.png",i%10);
		auto sprite = Sprite::create(name);
        sprite->setPosition(Point(i*5,i*5));
        node->addChild(sprite, 0);
    }
    this->addChild(node);
    return true;
}
    这段代码创建了100个图片。我将示例工程中的按钮复制了9个,并将第三个按钮稍作了修改。这样程序会循环创建这10张图片。图片资源如下图所示。

Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

    编译运行程序,我们可以看到下面的运行画面。

Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

    我们关注的是左下角信息的第二行。“GL calls”代表每一帧中OpenGL指令的调用次数。这个数字越小,程序的绘制性能就越好。现在每有101次绘制,其中100个元素每个元素绘制一次,多出来的一次是绘制这个左下角信息自己。

    接下来,我们使用合图软件,将这10张图合成一张大图和一个plist文件。在使用CocoStudio导出时,选择“使用大图”即可将小图合成一张大图。当然我们也可以选择TexturePacker这种专业的合图软件。合成的图片分为“test.png”和“test.plist”两部分,如上面的资源文件图片所示。

    更改init代码如下。

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    CCSpriteFrameCache::getInstance()->addSpriteFramesWithFile("test.plist","test.png");
    Node* node = Node::create();
    char name[32];
    for(int i  = 0;i<100;++i)
    {
        memset(name, 0, sizeof(name));
        sprintf(name, "%d.png",i%10);
		//auto sprite = Sprite::create(name);
        auto sprite = Sprite::createWithSpriteFrameName(name);
        sprite->setPosition(Point(i*5,i*5));
        node->addChild(sprite, 0);
    }
    this->addChild(node);
    return true;
}

    这段代码中,我们调用addSpriteFramesWithFile函数,将大图载入到内存中,创建对象时,调用createWithSpriteFrameName从缓存纹理中载入图片。如此做我们所有的绘制调用都可以合并到一次OpenGL指令中,这些绘制指令的计算与合并都由Cocos2d-x引擎完成。编译运行如下图所示。

Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

    我们可以非常明显的看到,优化后的程序“GL calls”变成了2次。

3、绘制剔除

    另一方面优化是绘制剔除。相对于上一种优化,这个要更容易理解。它是指当一个元素移动到屏幕之外,就不进行绘制。

    接着刚才的例子,我们测试一下这个特性。更改init函数如下。

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    //CCSpriteFrameCache::getInstance()->addSpriteFramesWithFile("test.plist","test.png");
    Node* node = Node::create();
    char name[32];
    for(int i  = 0;i<100;++i)
    {
        memset(name, 0, sizeof(name));
        sprintf(name, "%d.png",i%10);
		auto sprite = Sprite::create(name);
        //auto sprite = Sprite::createWithSpriteFrameName(name);
        sprite->setPosition(Point(i*5,i*5));
        node->addChild(sprite, 0);
    }
    this->addChild(node);
  
    auto listener = EventListenerTouchOneByOne::create();
    listener->onTouchBegan = [=](Touch *pTouch, Event *pEvent)
    {
        return true;
    };
    listener->onTouchMoved = [=](Touch *pTouch, Event *pEvent)
    {
        node->setPosition(node->getPosition()+pTouch->getDelta());
    };
    Director::getInstance()->getEventDispatcher()->
        addEventListenerWithSceneGraphPriority(listener, this);
    
    return true;
}

    首先,我们将自动Batch的优化更改回来,否则无法进行测试。接下来,我们在场景中加入一个点击事件,点击拖动屏幕时,移动这100个元素。编译运行,运行效果如下图。


Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

    可以看到,当部分图片被移出屏幕时,“GL calls”的数量会下降。

4、小结

    总的来说,这两点优化可以说是对程序性能有了极大提升。同时在开发的过程中,也使程序员不必过多的纠结于渲染效率的优化。

相关代码下载:http://download.csdn.net/detail/fansongy/7398941


PS:最近工作比较忙,博客更新的比较少了。忙过了这段,尽量多写些文章补上,以飨读者。

   本篇博客出自阿修罗道,转载请注明出处,禁止用于商业用途:http://blog.csdn.net/fansongy/article/details/26968473 

分类: 未分类 标签:

BMFont 使用

2014年5月25日 没有评论

1首先 下载安装windows下免费的位图字体制作工具Bitmap Font Generator

下载地址http://www.angelcode.com/products/bmfont/install_bmfont_1.13.exe

2 打开软件,选择Opotion->FontSetting,来设定需要导出的字体

我这里用微软雅黑。 Size可以设定字体大小,最好需要多大就设定多大,这里是32 Height可以设定字体的拉伸高度,保持默认100%就可以了

BMFont 使用

3. 选择菜单 Edit->Clear all chars in font 清空下字符

4. 选择菜单 Edit->Selecting text from file… 选择一个文件,里面包含你想生成字体的文字

生成一个*.Txt文本,里面包含你想要生成字体的文件,如下图

BMFont 使用

选中刚才创建的txt文件,如果有提示失败,请检查txt文件是不是unicode或者utf-8编码的文件以及字体是否包含文本内的字体

5.Option->ExportOptions来设定导出的样式等

BMFont 使用

这里解释一下几个重要的地方

Padding,文字的内边框,或者理解为文字的周边留空要多大 做后期样式时这个属性很重要,需要预留空间来给描边、发光等特效使用 比如我预计我的样式要加一个2px的边框,然后加一个右下角2px的投影效果,所以我设定了padding:2px 4px 4px 2px

BitDepth,必须32位,否则没有透明层

Presets,字体初始化的预设的颜色通道设定,也就是说字体的初始颜色设定是什么样的,建议都用白色字,可以直接设定为White text with alpha,即白色字透明底。

Font descript,字体描述文件,可以使用text或者xml 也就是fnt文件格式

Textures,纹理图片格式,果断png。

6.option->Save bitmap font as…

导出成一个文理文件和一个描述文件,如果按照上面的配置,会有一个*.png和一个*.fnt

这是导出后的png图片,为了看得清楚,我选择了黑色加透明 具体颜色视你的应用情况而定

 

BMFont 使用

7.用photoshop处理导出的png文件 后期处理了 视自己的情况定,我这里就没有做处理

分类: 未分类 标签:

cocos2d 设置按钮不可用

2014年5月19日 没有评论

 

需要两步设置按钮变灰,然后不可点击

btnBuy.setBright(false);
            btnBuy.setTouchEnabled(false);

 

或者直接不显示按钮

btnBuy.setEnabled(false);

 

分类: cocos2d 标签:

cocos2dx 处理精灵触摸事件

2014年5月16日 没有评论

要使精灵能够接收到触摸事件,无非要做三件事。
注册触摸事件;
接收触摸事件;
处理触摸事件。
下面就从这三点出发,来了解一下精灵如何响应触摸事件。
1.注册触摸事件
精灵类 SpriteNode 继承Sprite和CCTargetedTouchDelegate,并重写CCTargetedTouchDelegate的三个函数
ccTouchBegan,
ccTouchMoved,
ccTouchEnded
同时加入辅助函数rect()和containTouchPoint(CCTouch* touch)用于后面的判断。
SpriteNode.h文件:
class SpriteNode : public cocos2d::CCSprite , public cocos2d::CCTargetedTouchDelegate
{
public:
SpriteNode(void);
SpriteNode(char *img);
virtual ~SpriteNode(void);
cocos2d::CCRect rect();
virtual void onEnter();
virtual void onExit();
bool containTouchPoint( cocos2d::CCTouch* touch);
virtual bool ccTouchBegan( cocos2d::CCTouch *touch , cocos2d::CCEvent *event);
virtual void ccTouchMoved( cocos2d::CCTouch *touch , cocos2d::CCEvent *event);
virtual void ccTouchEnded( cocos2d::CCTouch *touch , cocos2d::CCEvent *event);
CREATE_FUNC(SpriteNode);
};
SpriteNode.cpp文件:
SpriteNode::SpriteNode(void){
}

SpriteNode::~SpriteNode(void){

}

1:事件注册
//这里需要再poker.cpp中添加具体的注册行为,onEnter和onExit函数分别是精灵创建和销毁时调用,因为可以在这两个函数中添加注册和销毁注册。
//CCNode进入场景时调用 
void SpriteNode::onEnter()
{
CCDirector* pDirector = CCDirector::sharedDirector();
pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
CCSprite::onEnter();
}

//CCNode退出场景时调用
void SpriteNode::onExit()
{
CCDirector* pDirector = CCDirector::sharedDirector();
pDirector->getTouchDispatcher()->removeDelegate(this);
CCSprite::onExit();

//以上,我们的触摸事件注册流程就完成了。
2.接收触摸事件
//接收触摸事件,实际上就是重写CCTargetedTouchDelegate的三个函数ccTouchBegan,ccTouchMoved,ccTouchEnded。
//在CCSprite内部使用convertTouchToNodeSpaceAR函数就可以将当前触摸点转化成精灵内部坐标系的点。
//判断触摸事件,并处理。
bool SpriteNode::ccTouchBegan(CCTouch *touch ,CCEvent *event)
{
int x = getPositionX();
int y = getPositionY();
if (containTouchPoint(touch))
{
setPosition(ccp(x , y + 30));
return true;
}

CCLog("Poker ccTouchBegan ");
return false;
}

void SpriteNode::ccTouchMoved(CCTouch *touch ,CCEvent *event)
{
CCLog("Poker ccTouchMoved ");
}

void SpriteNode::ccTouchEnded(CCTouch *touch ,CCEvent *event)
{
CCLog("Poker ccTouchEnded ");

//如此,接收流程已经完成,无意外的话,运行可以看到打印日志了。

3.处理触摸事件
//首先要获取当前精灵所在的矩形。
//也即是CCRect Poker::rect()需要做的事情。
//请注意,这里获取的方式的前提是,精灵使用系统默认的锚点,也即是精灵的正中央,如果改变过精灵的锚点(setAnchorPoint),那么就需要改变计算方法了。
CCRect SpriteNode::rect()
{
CCSize size = getTexture()->getContentSize();
return CCRectMake(-size.width / 2 ,-size.height / 2, size.width ,size.height);
}
//如果将当前精灵也看做一个坐标系,若精灵的长为100,宽为100,那么获取的矩形应该是x = -50 , y = -50 ,width = 100 ,height = 100
//其次,将触摸事件的点转化为当前精灵内部坐标系的点。
//(每一个继承自CCNode的结点都可以看做一个坐标系)
bool SpriteNode::containTouchPoint(CCTouch* touch)
{
return rect().containsPoint(convertTouchToNodeSpaceAR(touch));
}

分类: 未分类 标签:

Cocos2d-x v3 Cannot override forward function Draw

2014年5月9日 没有评论
//in my .h file
virtual void draw();

//in .cpp
void GameLayer:draw()
{
   Layer::draw();
   //draw code goes here
}

It shows cannot override forward function node::draw()

As far as I know, it was working on the old versions. Is there any new approach in cocos2d-x 3.0?

Thanks in advance.

in v3.0 overriding the draw() method has changed.

try in .h:

virtual void draw(Renderer* renderer, const kmMat4& transform, bool transformUpdated);

try in cpp:

void draw(Renderer* renderer, const kmMat4& transform, bool transformUpdated)
{

}

If you are running the latest version 3.1 from GitHub, this has changed. Replace kmMat4 with Matrix

Edit: maybe you need to namespace it: cocos2d::Renderer

分类: stackoverflow精选, unity3d 标签:

Cocos2d-x处理双击事件的两种方法!

2014年5月8日 没有评论

在 Cocos2d-x的开发过程中有些时候也是需要用到双击的事件处理,那么由于在cocos2d-x中没有实现对双击的事件的处理,那么我们就需要自己用代码实现。

下面介绍两种方式实现双击事件的处理。

(一)方法一

第一种方法就是利用两次touch之间的时间间隔来判定是单击或者是双击,一般来说,双击,两次touch之间的时间间隔为250ms~300ms(毫秒)。所以利用这个条件就可以对双击进行判断处理了。

注意到由于要获取两次touch之间的touch时间,而且是以毫秒为单位,所以需要用到下面这个方法:

1
2
3
4
5
6
7
//获取系统的毫秒时间
long
millisecondNow()
{
    struct
cc_timeval now;
    CCTime::gettimeofdayCocos2d(&now,
NULL);
    return

(now.tv_sec * 1000 + now.tv_usec / 1000);
}

由于要获取两次touch的时间,所以要定义两个long类型的变量用于记录时间;而且定义了一个bool类型的变量记录是否是双击。

1
2
3
long
preTime;
long
nextTime;
bool
isClickTwo;</font></font>

初始化为:

1
2
3
preTime
= 0.0f;
nextTime
= 0.0f;
isClickTwo 
=
false;

接着就是要在  ccTouchesBegan( CCSet * touches, CCEvent * event) 这个方法中(注意要在began这个方法中进行,而非ended这个方法中)进行单击和双击的判断处理了。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void
HelloWorld::ccTouchesBegan(CCSet* touches, CCEvent* event)
{
    isClickTwo
=
false;
    nextTime
= millisecondNow();
      
    if

((nextTime - preTime < 300) && (nextTime - preTime > 50)) {
        CCLog("double
click"
);
        isClickTwo
=
true;
    }
    //在回调方法中处理单击事件
    scheduleOnce(schedule_selector(HelloWorld::ff),
0.25f);
    preTime
= millisecondNow();
}
  
void
HelloWorld::ff(float tt)
{
    if

(! isClickTwo) {
        CCLog("single
click"
);
    }
}

这样,你单击和双击屏幕的时候就会在终端输出是单击还是双击了。

(二)方法二

下 面这种方法较第一种方法而言,比较简洁,其并不是采用直接比较双击中两次touch的时间间隔,而是比较巧妙的解决这个问题。解决的思路是这样的,我们知 道双击的时间间隔是0.25ms,所以设置了一个延时为0.25ms的schedule,如果在这0.25ms中又touch,那么表示的是双击,否则表 示的是单击。具体的实现过程看代码实现,应该是可以理解的。

这种方法只需要使用到一个bool变量表征是否touch。

1
bool
clicked;

初始化为:

1
clicked
=
false;

处理的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void 
HelloWorld::ccTouchesBegan(CCSet* touches, CCEvent* event)
{
    if

(clicked) {
        clicked
=
false;
        CCLog("double
click"
);
    }
    else
    {
        //延时0.25s执行(注意在这0.25s的延时过程中clicked已经为true),
        //如果在这个过程中再次click,那么就执行上面的双击事件处理了
        //否则,那么就执行下面的回调函数了,处理单击事件
        scheduleOnce(schedule_selector(HelloWorld::ff),
0.25f);
        clicked
=
true;
    }
}
  
//在回调函数中处理单击事件
void
HelloWorld::ff(float tt)
{
    if

(clicked) {
        clicked
=
false;
        CCLog("single
click"
);
    }
}

同样还是在终端中输出是单击还是双击的信息。

通过比较二者实现的方法可以看到,其实二者的实现过程还是有点类似的,只不过判断单击,双击的具体实现过程有点差异,在实际使用过程中建议是使用第二种方法,比较简洁。

转自:http://cocos2d.9tech.cn/news/2014/0324/40088.html

分类: 未分类 标签:

Cocos2d-x项目过程中遇到的一些问题总结

2014年5月8日 没有评论

这几天在用Cocos2D-X尝试着做一个小游戏,当然不是创新,只是单纯的模仿,就是为了将自己这段时间学到的技术应用于实践中。在这个过程中,遇到了一些问题,在此特做一些总结,以免以后遇到类似的问题。与诸君共享,希望能够在诸君以后的开发中起到一点帮助,哪怕只是一点点,也就不负初衷了。

1.进入场景后没有出现预期控件移动的效果

错误原因:没有调用父类的onEnter()方法

2.SimpleAudioEngine找不到

错误原因:没有导入#include "SimpleAudioEngine.h"头文件

没有引用using namespace CocosDenshion;命名空间

3.调用

CCDirector::sharedDirector()->replaceScene(GameAbout::scene());

切换场景后,新场景显式黑屏

错误原因:没使用CREATE_FUNC(GameAbout);宏创建新的场景

4.用VS2012编译后的工程,再用VS2010运行时报出如下错误:

fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏

解决方法如下:

项目/属性/配置属性/清单工具/输入和输出/嵌入清单:原来是“是”,改成“否”。

5.CCSpriteBatchNode only support CCSprite

错误原因:将非CCSprite对象添加到了CCSpriteBatchNode对象上

6.预期出现标题移动的效果,实际出现背景移动的效果

错误原因:标题和背景的tag重复

7.精灵不能响应触摸事件

错误原因:未注册触摸事件代理

CCDirector->sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);

8.注册触摸事件后不能正确判断触摸点的位置

错误原因:未将触摸点坐标转化为CCNode的相对坐标

convertTouchToNodeSpaceAR(touch)

9.触摸移动时,未调用ccTouchMoved方法

错误原因:ccTouchBegan方法返回值为false

10.创建动画后无动画效果

错误原因:未调用以下方法

animation->setDelayPerUnit(0.2f);   // 设置两帧间的时间间隔

animation->setRestoreOriginalFrame(true); // 是否从原始帧开始播放

原文:http://cocos2d.9tech.cn/news/2014/0326/40110.html

分类: 未分类 标签:

Cocos2d-x对精灵的优化

2014年5月8日 没有评论

cocos2dx针对游戏设计的不同方面会有不同的优化方案,可以对声音,对内存,对图片格式,对色彩等等进行优化。有关这些方面的方法请大家查找其他的文章。我今天要说的是如何对精灵进行优化,程序中我们用到 的最多的就是精灵,大到背景、UI,小到
NPC、道具,只要是用图片展示的,都是精灵或它的子类。精灵是什么,在我看来精灵就是一张纹理图片,是按某种方式显示出来的图片。精灵如此的重要,我们 当然要好好的优化优化了。我们可以减小精灵图片的大小,使用缓存Cache的方法将精灵提前加载到内存中,当有很多精灵的时候,使用批次渲染的方法。所以 我就从缓存和批次渲染这俩个方面来说一下如何优化我们的精灵图片。

1、 通过批次渲染的方法来优化精灵。在游戏中的某一时刻,有时候会用到大量的精灵,比如说发射子弹,粒子效果,这些精灵图片所使用的纹理都是相同的,如果一张 一张的图片进行渲染势必会降低效率,大家在开发中看到的窗口的左下角的三行数字中,第一行数字就是渲染批次,这个渲染批次在我看来就是画了多少 次,cocos2dx中使用opengl进行绘图,渲染的批次越少当然越好了,所以这个渲染批次才会显示在左下角让我们参考。我们应当尽量的减小这个渲染 的批次,提高游戏的效率。方法就是使用CCSpriteBatchNode和CCParticleBatchNode精灵批节点类和粒子批节点类。先来看
看在代码中如何使用它们。

1
2
3
4
5
6
7
8
CCSize
visibleSize = CCDirector::sharedDirector()->getVisibleSize();
//创建了十个精灵将这十个精灵添加到当前的层中
for(int
i=0;i<10;i++)
{
    CCSprite
* sprite = CCSprite::create(
"icon.png");
    sprite->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
    this->addChild(sprite);
}

Cocos2d-x对精灵的优化

上边的这种情况是采用一般的方式将精灵添加到层中的,这个时候的渲染批次是10。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建一个CCSpriteBatchNode,传入的参数就是精灵们将要用到的图片
    CCSpriteBatchNode
* batchNode = CCSpriteBatchNode::create(
"icon.png");
    //或者使用texture2d初始化,里边传入一个texture
    //CCSpriteBatchNode
* batch = CCSpriteBatchNode::createWithTexture();
    //这一句写不写都可以,因为node的默认坐标就是(0,0)
    batchNode->setPosition(CCPointZero);
    for(int
i=0;i<10;i++)
    {
        //创建的这些精灵所使用的纹理必须和CCSpriteBatchNode相同,而且所有这些精灵必须在同一个渲染层
        CCSprite
* sprite = CCSprite::createWithTexture(batchNode->getTexture());
        sprite->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
        batchNode->addChild(sprite);
    }
    this->addChild(batchNode);

Cocos2d-x对精灵的优化

采 用上边的方法添加精灵到层中,渲染批次是1,就是因为我们用到了CCSpriteBatchNode这个类,从道理上来说,这个类也是一个node,通常 我们使用node的时候就是为了方便的管理精灵,创建一个node节点,然后将精灵放到这个节点中,而node本身是没法显示出来的,显示出来的东西是放 到它里边的node子节点,node的长和宽都是0,坐标默认是在父节点的左下角也就是原点处。所以对子节点位置的设置不会产生影响。 CCSpriteBatchNode就是这样的一个node,不同的是创建的时候需要一个纹理,要不怎么叫sprite
node呢?里边传入的纹理图片是子节点用到的纹理图片,我们可以设置好这些子精灵节点的坐标,然后添加到这个node中,这个node再添加到其他的层 中,这样就可以批次渲染了。这个node要求它的字精灵节点和它使用相同的纹理,那如果纹理不一样怎么办,那就把纹理都打包到一张图片上,用的时候从这张 图片上截取,工具可以使用texturepacker。

1
2
3
4
5
6
7
8
9
CCTexture2D
* texture = CCTextureCache::sharedTextureCache()->addImage(
"fire.png");
    for(int
i=0;i<10;i++)
    {
        //CCParticleSun里边没有参数
        CCParticleSystem
* particle = CCParticleSun::create();
        particle->setTexture(texture);
        particle->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
        this->addChild(particle);
    }

Cocos2d-x对精灵的优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CCTexture2D
* texture = CCTextureCache::sharedTextureCache()->addImage(
"fire.png");
    //参数同样需要一张纹理图片
    CCParticleBatchNode
* particleBatch = CCParticleBatchNode::createWithTexture(texture);
    //也可以采用如下的方式创建
    //CCParticleBatchNode
* particleBatch = CCParticleBatchNode::create("fire.png");
    for(int
i=0;i<10;i++)
    {
        //CCParticleSun里边没有参数
        CCParticleSystem
* particle = CCParticleSun::create();
        //传入的texture必须和CCParticleBatchNode相同
        particle->setTexture(texture);
        particle->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
        particleBatch->addChild(particle);
    }
    this->addChild(particleBatch);

Cocos2d-x对精灵的优化

这里的道理是和CCSpriteBatchNode相同的,就不用解释了吧。

2、 使用缓存提前加载精灵。当我们使用纹理的时候可以制作一个loading界面,将将要用到的纹理加载到缓存中,同时将它们的引用计数增加一,以便这些纹理 不会被释放。用的时候直接从缓存中取就可以了,这样也可以提高效率。我们用到的缓存类有CCTextureCache、 CCSpriteFrameCache、CCAnimationCache,下面分别说明其用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
bool
HelloWorld::init()
{
    //////////////////////////////
    //
1. super init first
    if(
!CCLayer::init() )
    {
        returnfalse;
    }
 
    CCSize
visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    //缓存其实就是一个数组,把用到的纹理图片放到这个数组中,纹理的引用计数加1,这样的话就不会释放纹理图片了
    //等下一次使用的时候直接从这个数组中取就可以了,这样的话就不必再次加载到内存中了
    CCTexture2D
* texture = CCTextureCache::sharedTextureCache()->addImage(
"icon.png");
    CCLog("%d",texture->retainCount());//count=1
    //使用如下俩种方法可以获得缓存中的纹理,第二种方法如果之前已经加载了纹理,这个时候不会重新加载
    //而是直接返回
    texture
= CCTextureCache::sharedTextureCache()->addImage(
"icon.png");
    texture
= CCTextureCache::sharedTextureCache()->textureForKey(
"icon.png");
    CCLog("%d",texture->retainCount());//count=1
 
    //异步加载图片,开辟一个新的线程专门用来加载图片,加载完毕以后调用loadingCallBack函数
    //所以在这个init函数中是不应该去调用函数textureForKey的,因为你不知道是否加载好了纹理啊
    CCTextureCache::sharedTextureCache()->addImageAsync("icon1.png",
        this,callfuncO_selector(HelloWorld::loadingCallBack));
 
    returntrue;
}
//object就是加载好了的纹理
void
HelloWorld::loadingCallBack(CCObject * object)
{
    CCTexture2D
* texture = (CCTexture2D *)object;
    CCLog("%d",texture->retainCount());//count=2
    //通过以下的方法取得的texture和上边的那个texture相同
    //CCTexture2D
* texture2 = CCTextureCache::sharedTextureCache()->textureForKey("icon1.png");
    //CCLog("%d",texture2->retainCount());
 
    //会清除掉所有的纹理,在其他的地方不可以再引用这些纹理了
    CCTextureCache::sharedTextureCache()->removeAllTextures();
 
    //以下的方法会remove掉count值为1的纹理,icon.png被remove掉了,个人认为实际用的时候就用这个
    //CCTextureCache::sharedTextureCache()->removeUnusedTextures();
 
    //count=1
    CCLog("%d",texture->retainCount());
 
    //查看纹理清除的信息
    CCTextureCache::sharedTextureCache()->dumpCachedTextureInfo();
 
    //切换场景
    CCDirector::sharedDirector()->replaceScene(TestScene::scene());
}

在新的场景中使用加载好的纹理。

1
2
3
4
5
6
7
8
bool
TestScene::init()
{
    //因为将所有的纹理清除掉了,所以这里引用的时候会出错
    CCTexture2D
* texture = CCTextureCache::sharedTextureCache()->textureForKey(
"icon1.png");
    CCLog("%d",texture->retainCount());
 
    returntrue;
}

CCTextureCache 在我看来就是一个数组,将加载的纹理都放到这个数组中,然后将它们的引用计数加1以防释放掉,我们要使用加载好的纹理直接从这个数组中取就可以了,返回一 个CCTexture2D的对象,然后我们使用CCSprite:createWithTexture这个方法来创建出精灵。我觉的这里最主要的问题就是 纹理的释放,个人认为使用removeUnusedTexture这个函数比较好,它会释放掉引用计数为1的纹理,也就说明程序中没有再为它增加引用计 数,肯定是没用了。那些大于1的纹理不会释放掉,即使我们在不知道的情况下再次加载,也只是返回已经加载好了的。接下来是另外俩个缓存了,和这个意思差不
多,直接看代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
bool
HelloWorld::init()
{
    //////////////////////////////
    //
1. super init first
    if(
!CCLayer::init() )
    {
        returnfalse;
    }
 
    CCSize
visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    //可以使用texturepacker生成.plist文件,将plist文件和png文件放到资源目录下,通过add方法将ghosts.plist纹理
    //加载到了缓存中
    CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("ghosts.plist");
 
    //spriteFrameByName中传入的参数可以到.plist文件中查看,通过这个方法可以从缓存中获得一个精灵帧
    CCSpriteFrame
* spriteFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(
"child1.gif");
    CCLog("%d",spriteFrame->retainCount());//count=1
    //之所以创建精灵帧就是为了播放动画做准备的,这里可以是一个循环不到的创建精灵帧然后添加到animation中
    //关于这部分的内容可以查看我前边的文章,这里是为了说明缓存的使用
    CCAnimation
* animation = CCAnimation::create();
 
    animation->addSpriteFrame(spriteFrame);
    CCLog("%d",spriteFrame->retainCount());//count=2
 
    CCLog("%d",animation->retainCount());//count=1,这个1是创建时候的引用计数
    //动画缓存,将animation放到缓存中,里边存入一个key,方便以后取出,如果不把动画放到这个缓存中,下一帧的时候
    //动画就会被释放掉的,再播放动画就会出错了
    CCAnimationCache::sharedAnimationCache()->addAnimation(animation,"a");//count=2
    CCLog("%d",animation->retainCount());
 
    CCAnimate
* animate = CCAnimate::create(CCAnimationCache::sharedAnimationCache()->animationByName(
"a"));
 
    //当不再需要播放动画的时候从动画缓存中清除,应该先清除动画再清除精灵帧
    CCAnimationCache::sharedAnimationCache()->removeAnimationByName("a");
 
    //将引用计数为1的精灵帧从缓存中清除
    CCSpriteFrameCache::sharedSpriteFrameCache()->removeUnusedSpriteFrames();
    //释放掉所有的精灵帧
    //CCSpriteFrameCache::sharedSpriteFrameCache()->removeSpriteFrames();
 
    returntrue;
}

原文:http://cocos2d.9tech.cn/news/2014/0408/40171.html

分类: 未分类 标签: