cocos2d-x引入自动释放机制后,创建的对象就不需要我们像C++编程那样自己delete了。但是怎么让自己的类也能保持cocos2d-x的风格呢?或者说怎么样才能让自己的类实例也能自动释放。
在cocos2d-x里面大量用到了一个叫create()方法(2.1.0版本)。几乎所有的实体类都会用这个create函数来产生实例而不是他们的构造函数。
看看CCSprite的create()函数
CCSprite* CCSprite::create()
{
CCSprite *pSprite = new CCSprite();//调用构造函数,产生对象
if (pSprite && pSprite->init()) // 创建对象成功并且其init函数返回true则添加到自动释放池
{
pSprite->autorelease();
return pSprite;
}
CC_SAFE_DELETE(pSprite); // 安全释放
return NULL;
}

看到了,create函数里面实质上做了两件事:1.调用构造函数 2.init()成功则添加到自动释放池。
然后看init()函数,就是自己的一些初始化工作了。
对于这个结构大家要非常熟悉,几乎可以说是一个套路。
这个二阶段构造用的很多,为了简化代码提高效率,cocos2d-x有个函数宏专门来干这个活:
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
__TYPE__ *pRet = new __TYPE__(); \
if (pRet && pRet->init()) \
{ \
pRet->autorelease(); \
return pRet; \
} \
else \
{ \
delete pRet; \
pRet = NULL; \
return NULL; \
} \
}

这个跟上面CCSprite的create函数很像啊。结构几乎是一样的。
所以我们自己的类只需要用这个宏定义,然后实现init()函数就可以了。用的时候直接调用create()函数。
比如我写Ship:
#ifndef __MoonWarriorsx__Ship__
#define __MoonWarriorsx__Ship__
#include "cocos2d.h"
#include "UnitSprite.h"
USING_NS_CC;
class Ship : public UnitSprite{
private:
// 速度
int m_speed;
// 子弹速度
int m_bulletSpeed;
// 生命值
int m_HP;
// 子弹类型
int m_bulletTypeValue;
// 子弹威力
int m_bulletPowerValue;
// 是否在投掷炸弹
bool m_throwBombing;
// 是否可被攻击
bool m_canBeAttack;
bool m_isThrowBomb;
int m_zOder;
// 最大子弹威力
int m_maxBulletPowerValue;
// 出现的初始位置
CCPoint m_appearPosition;
int m_hurtColorLife;
bool m_active;
public:
Ship();
~Ship();
// 被攻击使能
void makeAttack(CCNode *pSender);
// 更新
virtual void update(float dt);
// 射击
void shoot(float dt);
// 初始化
virtual bool init();
// 被攻击,受伤
virtual void hurt();
// 销毁飞船
virtual void destroy();
// 获取生存状态
virtual bool isActive();
// 碰撞矩形
virtual CCRect collideRect();
int getZoder();
// 构造器
CREATE_FUNC(Ship);
};
#endif /* defined(__MoonWarriorsx__Ship__) */

然后构造函数只带了一个初始化列表赋初值,没干别的事情。接着实现init()函数,所有初始化工作都在这里实现:
bool Ship::init()
{
// super init first
if ( !CCSprite::init() )
{
return false;
}
// init life
CCTexture2D * shipTextureCache = CCTextureCache::sharedTextureCache()->addImage(s_ship01);
CCRect rec = CCRectMake(0, 0, 60, 38);
this->initWithTexture(shipTextureCache, rec);
this->setPosition(m_appearPosition);
// set frame
CCSpriteFrame *frame0 = CCSpriteFrame::createWithTexture(shipTextureCache, CCRectMake(0, 0, 60, 38));
CCSpriteFrame *frame1 = CCSpriteFrame::createWithTexture(shipTextureCache, CCRectMake(60, 0, 60, 38));
CCArray *animFrames = CCArray::create();
animFrames->addObject(frame0);
animFrames->addObject(frame1);
// ship animate
// 这个方法有差异
CCAnimation *animation = CCAnimation::createWithSpriteFrames(animFrames, 0.1);
CCAnimate *animate = CCAnimate::create(animation);
this->runAction(CCRepeatForever::create(animate));
// 子弹发射
this->schedule(schedule_selector(Ship::shoot), 0.16);
// revive effect
this->m_canBeAttack = false;
CCSprite *ghostSprite = CCSprite::createWithTexture(shipTextureCache, CCRectMake(0, 45, 60, 38));
ccBlendFunc cbl = {GL_SRC_ALPHA, GL_ONE};
ghostSprite->setBlendFunc(cbl);
ghostSprite->setScale(8);
ghostSprite->setPosition(ccp(this->getContentSize().width / 2, 12));
this->addChild(ghostSprite, 3000, 99999);
ghostSprite->runAction(CCScaleTo::create(0.5, 1, 1));
// 闪烁动画
CCBlink *blinks = CCBlink::create(3, 9);
// 回调,攻击使能
// 带执行者回调,谁执行Action传谁。这里是this,所以传的就是this
CCCallFuncN *makeBeAttack = CCCallFuncN::create(this, callfuncN_selector(Ship::makeAttack));
this->runAction(CCSequence::create(CCDelayTime::create(0.5), blinks, makeBeAttack, NULL));
return true;
}

init函数要先调用super的init(),然后写自己的东西。
这样的二阶段构造有个好处,就是将自动释放封装起来了。因为这个create函数是个static的,创建对象成功且初始化成功就将其添加到自动释放池,然后返回对象实例。你通过create获得对象后不用管它的释放问题。
当然,你也可以标新立异,不一定就按这个来。不过关键一点是,不管你怎么构造你的实例,要确保成功并将其加到自动释放池。比如我有个子弹类不是用create创建实例的,其使用如下:
Bullet *bullet_a = new Bullet(m_bulletSpeed, "W1.png", 1);
if (bullet_a) {
bullet_a->autorelease();// 添加到自动释放池
play_bullet->addObject(bullet_a);
this->getParent()->addChild(bullet_a, bullet_a->m_zorder, 901);
bullet_a->setPosition(ccp(position.x + offset, position.y + 3 + contentSize.height * 0.3));
}else{
delete bullet_a;
bullet_a = 0;
}

看,自己的构造函数,还要自己添加自动释放池。一看就很乱。其实这个也可以封装起来,自己实现create函数,不用那个宏定义,后续改下。
这里有一点不一样,就是CCScene的构造。
coco2d-x里面导演执行的单元是CCScene,就是场景。一个场景里面会添加很多CCLayer,即层。层里面又会添加很多元素,比如CCSprite,CCMenu等。那么场景如何构造呢?
demo里面是这一做的,在主Layer里面定义一个static的函数scene(),返回一个CCScene对象,例如:
CCScene* HelloWorld::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::create();
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}

他在这个函数里面将主Layer构造出来,然后添加到场景里面。注意,所有create函数构造的对象都是一个autorelease的对象。
然后执行场景:
// create a scene. it's an autorelease object
CCScene *pScene = HelloWorld::scene();
// run
pDirector->runWithScene(pScene);

看,封装好了。