cocos2d-x 3.1.1 学习笔记[18] Sprite创建之旅

时间:2023-02-06 19:07:48


//传入一个字符串来创建一个精灵
Sprite* Sprite::create(const std::string& filename)
{
//向系统申请一块内存
/*
std::nothro
在内存不足时,new (std::nothrow)并不抛出异常,而是将指针置NULL。
分配失败是非常普通的,它们通常在植入性和不支持异常的可移动的器件中发生更频繁。因此,应用程序开发者在这个环境中使用nothrow new来替代普通的new是非常安全的。
*/
Sprite *sprite = new (std::nothrow) Sprite();
//如果申请成功且初始化成功
if (sprite && sprite->initWithFile(filename))
{
// 将这个sprite放到池里面
sprite->autorelease();
/*
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
*/
return sprite;
}
//如果创建不成功就安全的删除这个申请的内存且返回nullptr
//#define CC_SAFE_DELETE(p) do { delete (p); (p) = nullptr; } while(0)
CC_SAFE_DELETE(sprite);
return nullptr;
}


/*
调用了Sprite::create(const std::string& filename)以后,
函数内部会调用Sprite::initWithFile(const std::string& filename)
*/
bool Sprite::initWithFile(const std::string& filename)
{
//断言,filename.size必须要大于0,否则就终止程序
/*
Assert - 断言
  使用断言可以创建更稳定,品质更好且不易于出错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。单元测试必须使用断言。
  除了类型检查和单元测试外,断言还提供了一种确定各种特性是否在程序中得到维护的极好的方法。
*/
CCASSERT(filename.size()>0, "Invalid filename for sprite");
// 由名字获取到相关图片的Textture
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
// 如果获取到 Textture
if (texture)
{
Rect rect = Rect::ZERO;
rect.size = texture->getContentSize();
// 调用指定的初始化
return initWithTexture(texture, rect);
}
// 如果没获取到 Textture
// don't release here.
// when load texture failed, it's better to get a "transparent" sprite then a crashed program
// this->release();
return false;
}

/*
调用了bool Sprite::initWithFile(const std::string& filename)以后,
函数内部会调用bool Sprite::initWithTexture(Texture2D *texture, const Rect& rect)
bool Sprite::initWithTexture(Texture2D *texture, const Rect& rect)
{
return initWithTexture(texture, rect, false);
}
之后他调用了自己的另一个重载函数bool Sprite::initWithTexture(Texture2D *texture, const Rect& rect, bool rotated)
*/

// designated initializer
bool Sprite::initWithTexture(Texture2D *texture, const Rect& rect, bool rotated)
{
bool result;
if (Node::init())
/*
为什么可以通过类名直接访问这个方法?因为Sprite继承Node,所以Sprite内部可以调用Node::init();
为什么两个函数都被执行了?其实只是一个函数执行了 Sprite::create(void) 中间会调用 Sprite::init();
父类这个函数只返回true
virtual bool init();
bool Node::init()
{
return true;
}
Reimplemented in LayerMultiplex, Sprite, LayerGradient, ParticleSystem, LayerColor, Scale9Sprite, ControlButton, Control, SpriteBatchNode, Layer, Menu, ClippingNode, Armature, ScrollView, DrawNode, Scene, Bone, ControlColourPicker, and BatchNode.
bool Sprite::init(void)
{
return initWithTexture(nullptr, Rect::ZERO );
}
*/
{
// 初始化一些基本属性

_batchNode = nullptr;

_recursiveDirty = false;
setDirty(false);

_opacityModifyRGB = true;

/*
const BlendFunc BlendFunc::ALPHA_PREMULTIPLIED = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA};
#define GL_ONE 1
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
*/
_blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;

_flippedX = _flippedY = false;

// default transform anchor: center
// 默认的anchorPoint就是在这设置为(0.5,0.5)的。
setAnchorPoint(Vec2(0.5f, 0.5f));

// zwoptex default values
_offsetPosition = Vec2::ZERO;

// clean the Quad
/*
void *memset(void *s, int ch, size_t n);
函数解释:将s中前n个字节 (typedef unsigned int size_t)用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 。
*/
memset(&_quad, 0, sizeof(_quad));

// Atlas: Color
/*
// 顶点坐标,纹理坐标和颜色信息
// vertex coords, texture coords and color info
V3F_C4B_T2F_Quad _quad;

//! 4 Vertex3FTex2FColor4B
struct V3F_C4B_T2F_Quad
{
//! top left
V3F_C4B_T2F tl;
//! bottom left
V3F_C4B_T2F bl;
//! top right
V3F_C4B_T2F tr;
//! bottom right
V3F_C4B_T2F br;
};

//! a Vec2 with a vertex point, a tex coord point and a color 4B
struct V3F_C4B_T2F
{
//! vertices (3F)
Vec3 vertices; // 12 bytes

//! colors (4B)
Color4B colors; // 4 bytes

// tex coords (2F)
Tex2F texCoords; // 8 bytes
};
*/
_quad.bl.colors = Color4B::WHITE;
_quad.br.colors = Color4B::WHITE;
_quad.tl.colors = Color4B::WHITE;
_quad.tr.colors = Color4B::WHITE;


/*
void Node::setGLProgramState(cocos2d::GLProgramState *glProgramState)
{
if(glProgramState != _glProgramState) {
//#define CC_SAFE_RELEASE(p) do { if(p) { (p)->release(); } } while(0)
CC_SAFE_RELEASE(_glProgramState);
_glProgramState = glProgramState;
//#define CC_SAFE_RETAIN(p) do { if(p) { (p)->retain(); } } while(0)
CC_SAFE_RETAIN(_glProgramState);
}
}
*/
// shader state
setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));

setTexture(texture);
setTextureRect(rect, rotated, rect.size);

// by default use "Self Render".
// if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
setBatchNode(nullptr);
result = true;
}
else
{
result = false;
}
_recursiveDirty = true;
setDirty(true);
return result;
}


/*
bool Sprite::initWithTexture(Texture2D *texture, const Rect& rect, bool rotated) 中间调用了
void Sprite::setTexture(Texture2D *texture)
*/
void Sprite::setTexture(Texture2D *texture)
{
// If batchnode, then texture id should be the same
CCASSERT(! _batchNode || texture->getName() == _batchNode->getTexture()->getName(), "CCSprite: Batched sprites should use the same texture as the batchnode");
// accept texture==nil as argument
/*
dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void*;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)
这个运算符会传回适当转型过的指针。如果downcast不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)
*/
CCASSERT( !texture || dynamic_cast<Texture2D*>(texture), "setTexture expects a Texture2D. Invalid argument");

// 如果Texture为空就自己创建
if (texture == nullptr)
{
// Gets the texture by key firstly.
texture = Director::getInstance()->getTextureCache()->getTextureForKey(CC_2x2_WHITE_IMAGE_KEY);

// If texture wasn't in cache, create it from RAW data.
if (texture == nullptr)
{
Image* image = new Image();
bool isOK = image->initWithRawData(cc_2x2_white_image, sizeof(cc_2x2_white_image), 2, 2, 8);
CC_UNUSED_PARAM(isOK);
CCASSERT(isOK, "The 2x2 empty texture was created unsuccessfully.");

texture = Director::getInstance()->getTextureCache()->addImage(image, CC_2x2_WHITE_IMAGE_KEY);
CC_SAFE_RELEASE(image);
}
}

/*
如果不是batchnode 且 textture(默认为nullptr)和原来的不一样

Sprite::Sprite(void)
: _shouldBeHidden(false)
, _texture(nullptr)
, _insideBounds(true)
{
}
*/
if (!_batchNode && _texture != texture)
{
CC_SAFE_RETAIN(texture);
CC_SAFE_RELEASE(_texture);
_texture = texture;
updateBlendFunc();
}
}

void Sprite::updateBlendFunc(void)
{
CCASSERT(! _batchNode, "CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a SpriteBatchNode");

// it is possible to have an untextured sprite
if (! _texture || ! _texture->hasPremultipliedAlpha())
{
_blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
setOpacityModifyRGB(false);
}
else
{
_blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
setOpacityModifyRGB(true);
}
}

void Sprite::setTextureRect(const Rect& rect, bool rotated, const Size& untrimmedSize)
{
_rectRotated = rotated;

setContentSize(untrimmedSize);
// 设置矩形的定点
setVertexRect(rect);
// 设置textture的坐标
setTextureCoords(rect);

Vec2 relativeOffset = _unflippedOffsetPositionFromCenter;

// issue #732
if (_flippedX)
{
relativeOffset.x = -relativeOffset.x;
}
if (_flippedY)
{
relativeOffset.y = -relativeOffset.y;
}

_offsetPosition.x = relativeOffset.x + (_contentSize.width - _rect.size.width) / 2;
_offsetPosition.y = relativeOffset.y + (_contentSize.height - _rect.size.height) / 2;

// rendering using batch node
if (_batchNode)
{
// update dirty_, don't update recursiveDirty_
setDirty(true);
}
else
{
// self rendering

// Atlas: Vertex
float x1 = 0 + _offsetPosition.x;
float y1 = 0 + _offsetPosition.y;
float x2 = x1 + _rect.size.width;
float y2 = y1 + _rect.size.height;

// Don't update Z.
_quad.bl.vertices = Vec3(x1, y1, 0);
_quad.br.vertices = Vec3(x2, y1, 0);
_quad.tl.vertices = Vec3(x1, y2, 0);
_quad.tr.vertices = Vec3(x2, y2, 0);
}
}

void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount;

// 等于0的话就该检测是否应该被移除
if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto poolManager = PoolManager::getInstance();
// 检测调用者是否存在自动释放池里面,如果在自动释放池里面的话,count必须大于等于1,则会触发断言
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
{
// Trigger an assert if the reference count is 0 but the Ref is still in autorelease pool.
// This happens when 'autorelease/release' were not used in pairs with 'new/retain'.
//
// Wrong usage (1):
//
// auto obj = Node::create(); // Ref = 1, but it's an autorelease Ref which means it was in the autorelease pool.
// obj->autorelease(); // Wrong: If you wish to invoke autorelease several times, you should retain `obj` first.
//
// Wrong usage (2):
//
// auto obj = Node::create();
// obj->release(); // Wrong: obj is an autorelease Ref, it will be released when clearing current pool.
//
// Correct usage (1):
//
// auto obj = Node::create();
// |- new Node(); // `new` is the pair of the `autorelease` of next line
// |- autorelease(); // The pair of `new Node`.
//
// obj->retain();
// obj->autorelease(); // This `autorelease` is the pair of `retain` of previous line.
//
// Correct usage (2):
//
// auto obj = Node::create();
// obj->retain();
// obj->release(); // This `release` is the pair of `retain` of previous line.
CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
}
#endif


// 作用相当于一个开关,根据你的需要更改宏的定义,执行相应的处理。
// 这个宏默认是关闭的
// #define CC_USE_MEM_LEAK_DETECTION 0
// 内存泄漏检测
#if CC_USE_MEM_LEAK_DETECTION
untrackRef(this);
#endif
// 如果 count为0,且不在自动释放池里面就要删除它
delete this;
}
}


Sprite::create(const std::string& filename)
->Sprite::initWithFile(const std::string& filename)
->Sprite::initWithTexture(Texture2D *texture, const Rect& rect, bool rotated)
->setTexture(texture);
->Sprite::updateBlendFunc(void)
->setTextureRect(rect, rotated, rect.size);









笔记于: http://blog.csdn.net/zhouyunxuan