存档

2012年11月 的存档

【Unity3D实战项目:疯狂杀戮】角色模型的行走控制(一)

2012年11月30日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!

原文地址: http://www.cocos2dev.com/?p=342


今天正式开始了第一个实战项目【疯狂杀戮】的开发教程,在教程中我会讲解整个游戏的开发过程,整个开发过程是建立在你已经基本明白了最基本的地形操作和一些基本API的使用。
 
我选的项目第一篇教学是控制角色的移动,这是最基本的游戏控制。
 
OK,开场白结束,开始咯~
 

第一步:简单场景的搭建:


1、首先请打开Unity,新建一个Unity工程。

2、新建的工程默认会加入Main Camera,我们加入一个地形Terrain,Terrain->create Terrain。

这时你发现场景都是黑黑的,我们在加一个方向光光源(可以理解成太阳)Directional light,选中工程的Hierarchy面板,Create->Directional light。

3、导入角色模型文件,可以直接把模型人物文件复制到工程的Assets目录下,Unity会自己刷新并加载模型文件。

4、在Project面板,选中我们导入的模型文件,拖拽到Scene场景中,并调节好位置,起码让自己能看到它在哪。

 

OK,一个最简单的场景搭建完毕。

Scene面板:

【Unity3D实战项目:疯狂杀戮】角色模型的行走控制(一)

Hierarchy面板、Project面板:

【Unity3D实战项目:疯狂杀戮】角色模型的行走控制(一)

第二步:脚本控制角色移动:


1、选中Project面板,新建一个文件夹Create->Folder,命名为MyScript,这个文件夹用来存放我们自己写的脚本文件。良好的编程习惯还是需要滴…

2、选中MyScript文件夹,Create->C# Script,命名为BarbariansControl。当然你也可以使用js脚本。API都是一样的。

3、双击打开创建的BarbariansControl脚本文件,Unity会默认使用MomoDevelop编辑器进行编辑。(你也可以选择使用自己熟悉的IDE)。

4、代码的编写:


Unity对新建的文件会自动创建一些函数。


最常见的Update,系统每一帧都会自己调用。(点击查看更多详细介绍)

代码:

using UnityEngine;
using System.Collections;

public class BarbariansControl : MonoBehaviour {

 public const int HERO_UP= 0;
 public const int HERO_RIGHT= 1;
 public const int HERO_DOWN= 2;
 public const int HERO_LEFT= 3;

public int state = 0;
 public int moveSpeed = 10;

 public void Awake() {
 state = HERO_UP;
 }

 void Update () {

 float KeyVertical = Input.GetAxis ("Vertical") ;
 float KeyHorizontal = Input.GetAxis ("Horizontal");

 if(KeyVertical == 1){
 //up
 setHeroState(HERO_UP);
 }else if(KeyVertical == -1){
 //down
 setHeroState(HERO_DOWN);
 }

 if(KeyHorizontal == 1){
 //right
 setHeroState(HERO_RIGHT);
 }else if(KeyHorizontal == -1){
 //left
 setHeroState(HERO_LEFT);
 }

if(KeyVertical == 0 && KeyHorizontal ==0){
 animation.Play();
 }
 }

 public void setHeroState(int newState) {
 int rotateValue = (newState - state) * 90;
 Vector3 transformValue = new Vector3();
 animation.Play("walk");
 switch(newState){
 case HERO_UP:
 transformValue = Vector3.forward * Time.deltaTime;
 break;
 case HERO_DOWN:
 transformValue = -Vector3.forward * Time.deltaTime;
 break;
 case HERO_LEFT:
 transformValue = Vector3.left * Time.deltaTime;
 break;
 case HERO_RIGHT:
 transformValue = -Vector3.left * Time.deltaTime;
 break;
 }

 transform.Rotate(Vector3.up, rotateValue);
 //move
 transform.Translate(transformValue * moveSpeed, Space.World);

 state = newState;
 }
}


编写完毕后,回到Unity工程,选中Project面板中的BarbariansControl脚本文件,鼠标拖拽到Hierarchy面板中的角色模型上,进行脚本绑定。


OK,Run一个工程,看看效果:

【Unity3D实战项目:疯狂杀戮】角色模型的行走控制(一)


注:

float KeyVertical = Input.GetAxis ("Vertical") ;
float KeyHorizontal = Input.GetAxis ("Horizontal");
这是利用Unity有的的输入管理器,获取水平和竖直方向。目前不清楚的可以先不用管,知道它的返回值1和-1可以表示上下和左右就可以了。后面我会介绍的。

分类: 未分类 标签:

Unity3D导入的FBX场景模型设置物体之间的碰撞

2012年11月29日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!

原文地址: http://www.cocos2dev.com/?p=339


导入的场景文件,发现人物会穿墙,也就是场景没有碰撞。


那么如何添加mesh之间的碰撞呢?最后发现挺简单的。

 

1、在Project中选中你的场景模型

2、在Inspector中勾选 Generate Colliders

3、Apply即可生成模型的mesh collider


Unity3D导入的FBX场景模型设置物体之间的碰撞

分类: 未分类 标签:

如何使用拼合图播放一个序列帧动画

2012年11月29日 没有评论

红孩儿Cocos2d-x学习园地教学资料由 [红孩儿游戏编程教学组] 组织编写.版权所有,盗文必究!

如何使用拼合图播放一个序列帧动画

如何使用拼合图播放一个序列帧动画

                                            本节主讲:红孩儿
[注:本版使用Cocos2d-x 2.02版本]

   

   我们知道,使用拼合图可以更节省内存,那如何使用拼合图播放一个序列帧动画呢?本节我们以HelloCpp为例来讲解一下。

第一步,我们要在将序列帧各帧图片合并成为拼合图。


 我们使用红孩儿纹理打包器1.1版本来进行拼图制做,它具有使用方便,拼图率高的特点。

下载地址:http://download.csdn.net/detail/honghaier/4671677

    我们以之前讲解的操作方法为例:

http://blog.csdn.net/honghaier/article/details/8117963

    最后生成的文件为:

如何使用拼合图播放一个序列帧动画

第二步,我们要将所用到的信息结构定义一下。

         我们在HelloWorldScene.h中加入:

//二进制文件头信息

struct SPackFileHeader

{

   
int
        m_Version;        //
版本

   
char
       m_szBigTexName[64];  //
大图名称

   
int
        m_nImageSize;     //
大图的大小

   
int
        m_nBlockNum;      //
图块数量

};

//二进制文件数据块

struct SPackNode

{

    RECT       m_Rect;             //对应图块矩形

    POINT      m_OffsetPt;         //中心点偏移

   
bool
       m_bRotated;          //
是否顺时针旋转度

};

    这样我们就可以读取出拼图的相应纹理块了。

第三步,创建序列帧动画。

         我们需要在HelloWorld的初始化中来创建这个序列帧动画:

bool 	HelloWorld::init()
{
	//基类初始化
	 if ( !CCLayer::init() )
    {
        return false;
    }
    //取得可视区域的大小
    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
	//取得左下角坐标
    CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
	//创建拼图批次
	CCSpriteBatchNode* pNewBatchNode = CCSpriteBatchNode::create("Big_0.png", 34 );
	//读取二进制文件,将数据读取到容器中
	this->addChild(pNewBatchNode,1);
	//取得资源目录字符串
	string	szResPath(CCFileUtils::sharedFileUtils()->getResourceDirectory());
	//结尾加上拼合图信息文件名称。
	szResPath += "Big_0.blo";
	//读取拼合图信息文件全路径名
	FILE*	hFile = fopen(szResPath.c_str(),"rb");
	if(hFile)
	{
		//读取头信息
		SPackFileHeader	tFileHeader;
		fread(&tFileHeader,sizeof(SPackFileHeader),1,hFile);
		//读取每个纹理块信息
		SPackNode	tPackNode;
		//创建精灵帧存放容器
		CCArray*	tSpriteFrameArray = CCArray::create();
		//for循环进行读取相应数量的纹理块信息
		for(int b = 0 ; b < tFileHeader.m_nBlockNum ; ++b)
		{
			fread(&tPackNode,sizeof(SPackNode),1,hFile);
			//当前纹理块在图集纹理中的矩形位置。
			CCRect		tRect;
			//创建精灵
			if(tPackNode.m_bRotated)
			{
				//如果有旋转,宽高做一个调换。
				tRect = CCRect(tPackNode.m_Rect.left,tPackNode.m_Rect.top,tPackNode.m_Rect.bottom-tPackNode.m_Rect.top,tPackNode.m_Rect.right-tPackNode.m_Rect.left);
			}
			else
			{
				tRect = CCRect(tPackNode.m_Rect.left,tPackNode.m_Rect.top,tPackNode.m_Rect.right-tPackNode.m_Rect.left,tPackNode.m_Rect.bottom-tPackNode.m_Rect.top);
			}
			//
			CCPoint		tOffset(0,0);
			
			//设置纹理不进行抗锯齿模糊,像素精细
			pNewBatchNode->getTexture()->setAliasTexParameters();
			//设置从对应纹理块中读取出一个精灵帧
			CCSpriteFrame*	tpSpriteFrame = CCSpriteFrame::frameWithTexture(pNewBatchNode->getTexture(), tRect, tPackNode.m_bRotated, tOffset, tRect.size);
			//将精灵帧放入容器。	
			tSpriteFrameArray->addObject(tpSpriteFrame);
			
		}
               从精灵帧容器创建一个序列帧动画。
		CCAnimation* animation = CCAnimation::createWithSpriteFrames(tSpriteFrameArray,1.0f);  
		//设置每两帧间时间间隔为1秒。 
		animation->setDelayPerUnit(1.0f);  
		//设置动画结束后仍保留动画帧信息。 
		animation->setRestoreOriginalFrame(true);  
		//由这个动画信息创建一个序列帧动画。 
		CCAnimate* action = CCAnimate::create(animation);  
		//创建精灵。
		CCSprite* pSprite = CCSprite::create();
		//设置精灵位置。
		pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
		//让演员演示这个动画。 
		pSprite->runAction(action);  
		this->addChild(pSprite, 0);

		fclose(hFile);
	}  

    return true;
}

运行后的效果如图示[自已用flash做的gif,大概表示个意思哈~]:

如何使用拼合图播放一个序列帧动画

课后总结:

本节主要的关健是如何读取纹理块信息然后生成精灵帧和动画。




教学组介绍:

红孩儿游戏编程教学组:致力于游戏编程方面的教程编写,目前主要工作重心在Cocos2d-x方向,希望大家支持!

目前成员有:

 

红孩儿: 九年游戏程序开发经验,参与过多款游戏的开发并任职主程序。

Jivin: 在编程路上,以初学者身份慢慢爬行着。博客:http://blog.csdn.net/laijingyao881201

Jovi: 一年多的端游程序开发经验,初步接触cocos2dx引擎。正在开发一款引擎是cocos2dx的手游。

畏天命: 资深游戏策划,项目经理。参与设计多款iOS游戏是教程组内唯一的业余程序员

一年前开始接触C++及cocos2d-x

将讲解涉及cocos2d-x学习中容易遇到的初级问题,

适合零起点选手入门,博客: http://blog.csdn.net/jyzgo

 

同时也欢迎有精力有能力的朋友参与我们。

分类: 未分类 标签:

ObjC转换为cpp的规则

2012年11月27日 没有评论

原文地址: http://www.cocos2d-x.org/projects/cocos2d-x/wiki/Rules_of_translating_objc_to_cpp

1. 是否使用虚拟成员函数

1. 在申明cpp成员函数时最好不要使用“virtual”关键字;

2. 但是,请检查你申明的成员函数是否重写了父类的成员函数,如果重写了,请加上“virtual”;

3. 如果你是继承了父类的虚拟成员函数,那么一定要加上“virtual”。

2. 公有、私有、保护成员函数

1. 默认所有的成员函数是公有的;

2. 以下情况中的成员函数必须得申明成私有的:

      a. 这个函数是在.m文件中申明的;

      b. 这个函数前有“private”关键字。

3. 公有、私有、保护成员变量

1. 申明所有的成员变量为“protected”,没有第二选择。

4. 两个阶段初始化类

1. 如何做:

      a. 第一阶段:在构造函数里初始化所有成员变量,但不要在构造函数里做任何有关逻辑的初始化工作。

      b. 第二阶段:将有关逻辑的初始化工作写在“CMyClass *init(…)”函数里,如果初始化失败,返回NULL。

2. 为什么:

      a. 我们决定放弃C++中的try-catch异常处理机制。这样做是为了减少封装和二进制文件大小。

3. 什么时候:

      a. 并不是所有的类都必须实现两个阶段的初始化,只有那些需要做有关逻辑初始化的类需要。换句话说,在构造函数里做逻辑操作是不允许的,尤其是那些有可能返回错误的操作。    

4. 为调用者:

      a. 如果你调用的类有“bool init(…)”函数,在构造函数之后就需要立马调用它。

 1#define CCX_BREAK_IF(cond) if(cond) break;
 2#define CCX_SAFE_DELETE(p)  if(p) {delete (p); (p) = NULL;}
 3
 4// declaration
 5class CCar
 6{
 7public:
 8    CCar();
 9    bool    init();
10    virtual ~CCar();
11
12protected:
13    CEngine* m_pEngine;
14    bool     m_bStarted;
15    bool     m_bLocked;
16};
17
18// 1st-phase construction
19// only set the default value & alloc memory
20CCar::CCar()
21:m_pEngine(new CEngine)
22,m_bStarted(false)
23,m_bLocked(true)
24{
25    printf("CCar constructor/n");
26}
27
28// 2st-phase construction
29// do logical initialization
30bool CCar::init()
31{
32    bool bRet = false;
33
34    do
35    {
36        m_bLocked = false;
37
38        CCX_BREAK_IF( !m_pEngine );        // defensive
39        CCX_BREAK_IF( !m_pEngine->start() );    // logic
40
41        // success
42        bRet = true;
43
44    } while(0);
45
46    printf("CCar init/n");
47    return bRet;
48}
49
50// destruction
51CCar::~CCar()
52{
53    if (m_pEngine)
54    {
55        delete m_pEngine;
56        m_pEngine = NULL;
57    }
58
59    printf("CCar destructor/n");
60}
61
62// invoker
63int _tmain(int argc, _TCHAR* argv[])
64{
65    // in heap
66    CCar* pCar = new CCar;
67    if (!pCar->init())
68    {
69        CCX_SAFE_DELETE(pCar);
70    }
71
72    // in stack
73    CCar car;
74    if (!car.init())
75    {
76        // do sth.
77    }
78
79    return 0;
80}

5. ObjC中的属性

1/** CCX_PROPERTY_READONLY is used to declare a protected variable.
 2    We can use get method to read the variable.
 3    @param varType : the type of variable.
 4    @param varName : variable name.
 5    @param funName : "get + funName" is the name of the get method.
 6    @warning : The get method is a public virtual function, you should override it first.
 7            The variables and methods declared after CCX_PROPERTY_READONLY are all public.
 8            If you need protected or private, please declare.
 9*/
10#define CCX_PROPERTY_READONLY(varType, varName, funName)/
11    protected: varType varName;/
12    public: virtual varType get##funName(void);
13
14/** CCX_PROPERTY is used to declare a protected variable.
15    We can use get method to read the variable, and use the set method to change the variable.
16    @param varType : the type of variable.
17    @param varName : variable name.
18    @param funName : "get + funName" is the name of the get method.
19                     "set + funName" is the name of the set method.
20    @warning : The get and set methods are public virtual functions, you should override them first.
21            The variables and methods declared after CCX_PROPERTY are all public.
22            If you need protected or private, please declare.
23*/
24#define CCX_PROPERTY(varType, varName, funName)/
25    protected: varType varName;/
26    public: virtual varType get##funName(void);/
27    public: virtual void set##funName(varType var);
28
29/** CCX_SYNTHESIZE_READONLY is used to declare a protected variable.
30    We can use get method to read the variable.
31    @param varType : the type of variable.
32    @param varName : variable name.
33    @param funName : "get + funName" is the name of the get method.
34    @warning : The get method is a public inline function.
35            The variables and methods declared after CCX_SYNTHESIZE_READONLY are all public.
36            If you need protected or private, please declare.
37*/
38#define CCX_SYNTHESIZE_READONLY(varType, varName, funName)/
39    protected: varType varName;/
40    public: inline varType get##funName(void){ return varName; }
41
42/** CCX_SYNTHESIZE is used to declare a protected variable.
43    We can use get method to read the variable, and use the set method to change the variable.
44    @param varType : the type of variable.
45    @param varName : variable name.
46    @param funName : "get + funName" is the name of the get method.
47                     "set + funName" is the name of the set method.
48    @warning : The get and set methods are public  inline functions.
49            The variables and methods declared after CCX_SYNTHESIZE are all public.
50            If you need protected or private, please declare.
51*/
52#define CCX_SYNTHESIZE(varType, varName, funName)/
53    protected: varType varName;/
54    public: inline varType get##funName(void){ return varName; }/
55    public: inline void set##funName(varType var){ varName = var; }

6. id

在objc中有些函数返回id,在cpp中应返回bool。在objc中你可以写[[[MyClass alloc] init] autorelease],因为你不用担心init函数会执行失败并返回nil,因为[nil autorelease]不会引起程序崩溃。但是在cpp中,我们返回bool,为了防止程序员这样写:pClass
= (new MyClass())->init()->foo()
。因为一旦init失败,会返回NULL,NULL->foo()会导致程序崩溃的。另外,如果foo()返回值不是MyClass*类型,比如返回bool,调用者将失去“new MyClass”指针,并无法在堆中删除它,这是很危险的。

@interface CTest
-(id) foo();

必须写成:

class CTest
{
    bool foo();
}

====================================================================================================================================

7. 其他建议

除了以上翻译的,我从http://cn.cocos2d-x.org/resource/show?nid=62摘取了部分转换的注意事项:

1. 不要使用C++里的__super来替代objc里的super。关键字__super仅能在VC++中被识别,但是无法被GCC给编译。因此你最好调用其父类名称,CCLayer::init()

2. 这里没有属性的概念。因此在objc里的属性,我们用get/set方法来代替。例如,如果你想要获取CCSprite的contentSize属性,你必须调用sprite->getContentSize()方法。contentSize的第一个字母应该为大写的“C”,之后为它加上“get”前缀。

3. 使用setter来设置属性的值,“player.position=…”,应变为player->setPosition(…)

4. 但是访问结构体的成员不遵循这一规则。例如,在winSize结构体中,没有“width”&“height”的getter/setter

5. 你不需要像objc那样,解释每一个参数的用途。例如,[CCSprite spriteWithFile…, rect…];直接转换为CCSprite::spriteWithFile(…, ….);

6. 我们已经完成了一些常用的CGGeometry函数,诸如CGRectMake, CGPointMake, CGSizeMake, CGPointZero, CGSizeZero, CGRectZero。你可以在cocos2dx/include/CCGemoetry.h中找到它们。它们的用途和iOS一样。仅有一点点不同在于它们的前缀,为了避免命名上的冲突,在cocos2d-x里,前缀为CG、NS、UI的类都统一改成了CC前缀。

7. 所有cocos2d-x里的游戏元素,例如sprite、layer、scene、label、action都在heap里被分配了内存。因此我们必须用指针“->”来调用它们的方法。

8. 在cpp里,用关键字“this”来代替objc里的“self”

9. 现在init方法的返回值是“bool”类型了。在cpp里没有关键字“id”,因此那些返回值为“id”的方法要么替换成对象的指针,要么换成bool。

10. 对于Android,由于标题栏占用了一些空间,因此你需要在cpp里这样设置玩家的位置(player.contenSize.width/2 + 40, winSize.height/2)。

11. 在c++里继承默认为private继承,因此需要在CCLayerColor类声明前加上“public”。

12. Cocos2d-iphone的主要作者RicardoQuesada建议我们在cocos2d-x中使用命名空间。检查你所调用的cocos2d-x类是在“cocos2d”命名空间还是在“CocosDenshion”命名空间是非常重要的。


还有一个偷懒的办法:objc到cpp的转换脚本

https://github.com/Deepscorn/c2d-to-x/downloads


分类: 未分类 标签:

Cocos2d-x 2.0 序列帧动画 深入分析

2012年11月25日 没有评论

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ2群:44208467加群写:Cocos2d-x
红孩儿Cocos2d-X学习园地QQ群:249941957 [暂满]加群写:Cocos2d-x

本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!

Cocos2d-x 2.0 序列帧动画 深入分析

    今天,我带领大家学习一下Cocos2d-x 2.0的序列帧动画。在Cocos2d-x中,提供了相应的一些类和方法,可以方便的生成序列帧动画,这样我们就可以制做各种人物动作以及动画效果。

 

    序列帧动画主要有几个类:

 

    CCSpriteFrame:精灵帧信息,序列帧动画是依靠多个精灵帧信息来显示相应的纹理图像,一个精灵帧信息包包含了所使用的纹理,对应纹理块的位置以及纹理块是否经过旋转和偏移,这些信息可以取得对应纹理中正确的纹理块区域做为精灵帧显示的图像。

    CCAnimationFrame:序列帧动画单帧信息,它存储了对应的精灵帧信息。

    CCAnimation:序列帧动画信息,它存储了所有的单帧信息,可以对单帧信息进行管理。

    CCAnimate:序列帧动画处理类,它是真正完成动画表演的类。

先学习一下CCSpriteFrame:

#ifndef __SPRITE_CCSPRITE_FRAME_H__
#define __SPRITE_CCSPRITE_FRAME_H__
//相关头文件
#include "base_nodes/CCNode.h"
#include "CCProtocols.h"
#include "cocoa/CCObject.h"
#include "cocoa/CCGeometry.h"
//Cocos2d命名空间
NS_CC_BEGIN
//要用到的类
class CCTexture2D;
class CCZone;

//精灵帧:精灵序列帧动画中的一帧。
class CC_DLL CCSpriteFrame : public CCObject
{
public:
    //取得精灵帧的大小(像素单位)
inline const CCRect& getRectInPixels(void) { return m_obRectInPixels; }
//设置精灵帧的大小(像素单位)
void setRectInPixels(const CCRect& rectInPixels);

	//取得当前帧所用的纹理块是否旋转了90度
inline bool isRotated(void) { return m_bRotated; }
//设置当前帧所用的纹理块旋转90度
    inline void setRotated(bool bRotated) { m_bRotated = bRotated; }

    //取得当前帧所用的纹理块在图集纹理的矩形(点单位)
    inline const CCRect& getRect(void) { return m_obRect; }
    //设置当前帧所用的纹理块在图集中的矩形(点单位)
    void setRect(const CCRect& rect);

    //取得当前帧所用的纹理块在图集中的位置(像素单位)
    const CCPoint& getOffsetInPixels(void);
    //设置当前帧所用的纹理块在图集中的位置(像素单位)

    void setOffsetInPixels(const CCPoint& offsetInPixels);

    //取得纹理块的原始大小(像素单位)。
    inline const CCSize& getOriginalSizeInPixels(void) { return m_obOriginalSizeInPixels; }
    //设置纹理块的原始大小(像素单位)。
    inline void setOriginalSizeInPixels(const CCSize& sizeInPixels) { m_obOriginalSizeInPixels = sizeInPixels; }

    //取得纹理块的原始大小(点单位)。
    inline const CCSize& getOriginalSize(void) { return m_obOriginalSize; }
    //设置纹理块的原始大小(点单位)。
    inline void setOriginalSize(const CCSize& sizeInPixels) { m_obOriginalSize = sizeInPixels; }

    //取得当前帧所使用的图集纹理
    CCTexture2D* getTexture(void);
    //设置当前帧所使用的图集纹理
void setTexture(CCTexture2D* pobTexture);

	//取得纹理块在合并时去掉周围空白边后的锚点偏移(点单位).
const CCPoint& getOffset(void);
//设置纹理块在合并时去掉周围空白边后的锚点偏移(点单位).
    void setOffset(const CCPoint& offsets);

public:
	//析构
~CCSpriteFrame(void);
//创建一个当前实例的拷贝
    virtual CCObject* copyWithZone(CCZone *pZone);

	//从一个图集纹理中创建出一帧,内部调用对应createWithTexture函数实现,参一为图集纹理,参二为纹理块矩形。
    CC_DEPRECATED_ATTRIBUTE static CCSpriteFrame* frameWithTexture(CCTexture2D* pobTexture, const CCRect& rect);

	//从一张图集图片中创建出一帧,内部调用对应create函数实现,参一为图集图片名称,参二为纹理块矩形。
    CC_DEPRECATED_ATTRIBUTE static CCSpriteFrame* frameWithTextureFilename(const char* filename, const CCRect& rect);

	//从一个图集纹理中创建出一帧,内部调用对应createWithTexture函数实现,参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。
    CC_DEPRECATED_ATTRIBUTE static CCSpriteFrame* frameWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize);

	//从一个张图集图片中创建出一帧,内部调用对应create函数实现,参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。
    CC_DEPRECATED_ATTRIBUTE static CCSpriteFrame* frameWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize);

	//从一张图片文件中以指定的矩形创建一个精灵帧。参一为图集图片名称,参二为对应纹理块的矩形。
    static CCSpriteFrame* create(const char* filename, const CCRect& rect);
    
	//从一个图集图片中创建出一帧,参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。
    static CCSpriteFrame* create(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize);
    
	//从一张图集纹理中以指定的矩形创建一个精灵帧。参一为图集纹理,参二为对应纹理块的矩形。
    static CCSpriteFrame* createWithTexture(CCTexture2D* pobTexture, const CCRect& rect);

//从一个图集纹理中创建出一帧,参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。
    static CCSpriteFrame* createWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize);

public:
    //初始化精灵帧:参一为图集纹理,参二为对应纹理块的矩形。
    bool initWithTexture(CCTexture2D* pobTexture, const CCRect& rect);

//初始化精灵帧:参一为图集图片名称,参二为对应纹理块的矩形。
    bool initWithTextureFilename(const char* filename, const CCRect& rect);

//初始化精灵帧:参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。

    bool initWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize);

 //初始化精灵帧:参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。
    bool initWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize);


protected:
	//去掉纹理块空白后所导致的锚点偏移。
CCPoint m_obOffset;
//纹理块的原始大小(未去掉空白的大小)。
CCSize m_obOriginalSize;
//纹理块的大小(像素单位)。
CCRect m_obRectInPixels;
//矩形块是否旋转。
bool   m_bRotated;
//纹理块
CCRect m_obRect;
//去掉纹理块空白后所导致的锚点偏移(像素单位)。
CCPoint m_obOffsetInPixels;
//纹理块的原始大小(像素单位)。
CCSize m_obOriginalSizeInPixels;	
//图集纹理。
CCTexture2D *m_pobTexture;
//对应的图集图片。
    std::string  m_strTextureFilename;
};


NS_CC_END

#endif //__SPRITE_CCSPRITE_FRAME_H__

再来看CCSpriteFrame.cpp:

#include "textures/CCTextureCache.h"
#include "CCSpriteFrame.h"
#include "CCDirector.h"

//使用Cocos2d命名空间
NS_CC_BEGIN

//从一个图集纹理中创建出一帧,内部调用后面的createWithTexture函数实现,参一为图集纹理,参二为纹理块矩形。
CCSpriteFrame* CCSpriteFrame::frameWithTexture(CCTexture2D *pobTexture, const CCRect& rect)
{
    return CCSpriteFrame::createWithTexture(pobTexture, rect);
}
//从一个图集纹理中创建出一帧,参一为图集纹理,参二为纹理块矩形。
CCSpriteFrame* CCSpriteFrame::createWithTexture(CCTexture2D *pobTexture, const CCRect& rect)
{
	//新帧一个精灵帧,初始化完毕后交由内存管理器进行释放管理。
    CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();;
    pSpriteFrame->initWithTexture(pobTexture, rect);
    pSpriteFrame->autorelease();
	//返回新创建的精灵帧。
    return pSpriteFrame;
}

//从一张图集图片中创建出一帧,内部调用后面的create函数实现,参一为图集图片名称,参二为纹理块矩形。
CCSpriteFrame* CCSpriteFrame::frameWithTextureFilename(const char* filename, const CCRect& rect)
{
    return CCSpriteFrame::create(filename, rect);
}
//从一张图集图片中创建出一帧,参一为图集图片名称,参二为纹理块矩形。
CCSpriteFrame* CCSpriteFrame::create(const char* filename, const CCRect& rect)
{	
//新帧一个精灵帧,初始化完毕后交由内存管理器进行释放管理。
    CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();;
    pSpriteFrame->initWithTextureFilename(filename, rect);
    pSpriteFrame->autorelease();
	//返回新创建的精灵帧。
    return pSpriteFrame;
}

//从一个图集纹理中创建出一帧,内部调用后面的createWithTexture函数实现,参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。
CCSpriteFrame* CCSpriteFrame::frameWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
{
    return CCSpriteFrame::createWithTexture(pobTexture, rect, rotated, offset, originalSize);
}
//从一个图集纹理中创建出一帧,参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。

CCSpriteFrame* CCSpriteFrame::createWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
{
//新帧一个精灵帧,初始化完毕后交由内存管理器进行释放管理。
    CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();;
    pSpriteFrame->initWithTexture(pobTexture, rect, rotated, offset, originalSize);
    pSpriteFrame->autorelease();
	//返回新创建的精灵帧。
    return pSpriteFrame;
}

//从一个张图集图片中创建出一帧,内部调用后面的create函数实现,参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。

CCSpriteFrame* CCSpriteFrame::frameWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
{
    return CCSpriteFrame::create(filename, rect, rotated, offset, originalSize);
}
//从一个张图集图片中创建出一帧,参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。

CCSpriteFrame* CCSpriteFrame::create(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
{
//新帧一个精灵帧,初始化完毕后交由内存管理器进行释放管理。

    CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();;
    pSpriteFrame->initWithTextureFilename(filename, rect, rotated, offset, originalSize);
    pSpriteFrame->autorelease();
	//返回新创建的精灵帧。
    return pSpriteFrame;
}

//初始化精灵帧:参一为图集纹理,参二为对应纹理块的矩形。
bool CCSpriteFrame::initWithTexture(CCTexture2D* pobTexture, const CCRect& rect)
{
	//将矩形参数转为当前设备分辨率下的像素单位的大小。
CCRect rectInPixels = CC_RECT_POINTS_TO_PIXELS(rect);
//调用参数更详细的初始化函数。
    return initWithTexture(pobTexture, rectInPixels, false, CCPointZero, rectInPixels.size);
}
//初始化精灵帧:参一为图集图片名称,参二为对应纹理块的矩形。
bool CCSpriteFrame::initWithTextureFilename(const char* filename, const CCRect& rect)
{
	//将矩形参数转为当前设备分辨率下的像素单位的大小。
CCRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect );
//调用参数更详细的初始化函数。
    return initWithTextureFilename(filename, rectInPixels, false, CCPointZero, rectInPixels.size);
}
//初始化精灵帧:参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小
bool CCSpriteFrame::initWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
{
	//保存图集纹理
    m_pobTexture = pobTexture;
	//占用纹理资源,对其引用计数器加1
    if (pobTexture)
    {
        pobTexture->retain();
    }
	//保存像素单位的矩形大小
m_obRectInPixels = rect;
//保存点单位的矩形大小
m_obRect = CC_RECT_PIXELS_TO_POINTS(rect);
//保存像素单位的去掉纹理块空白后所导致的锚点偏移。
m_obOffsetInPixels = offset;
//保存点单位的去掉纹理块空白后所导致的锚点偏移。
m_obOffset = CC_POINT_PIXELS_TO_POINTS( m_obOffsetInPixels );
//保存像素单位的纹理块原始大小
m_obOriginalSizeInPixels = originalSize;
//保存点单位的纹理块原始大小
m_obOriginalSize = CC_SIZE_PIXELS_TO_POINTS( m_obOriginalSizeInPixels );
//保存纹理块是否旋转了90度。
    m_bRotated = rotated;

    return true;
}
//初始化精灵帧:参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉空白后纹理块所导致的偏移,参五为纹理块的原始大小
bool CCSpriteFrame::initWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize)
{
	//图集纹理置空
m_pobTexture = NULL;
//保存图集图片名称。
m_strTextureFilename = filename;
//保存像素单位的矩形大小
m_obRectInPixels = rect;
//保存转换为点单位的矩形大小
m_obRect = CC_RECT_PIXELS_TO_POINTS(rect);
//保存像素单位的去掉纹理块空白后所导致的锚点偏移。
m_obOffsetInPixels = offset;
//保存转换为点单位的去掉纹理块空白后所导致的锚点偏移。
m_obOffset = CC_POINT_PIXELS_TO_POINTS( m_obOffsetInPixels );
//保存像素单位的纹理块原始大小
m_obOriginalSizeInPixels = originalSize;
//保存转换为点单位的纹理块原始大小
m_obOriginalSize = CC_SIZE_PIXELS_TO_POINTS( m_obOriginalSizeInPixels );
//保存纹理块是否旋转了90度。
    m_bRotated = rotated;

    return true;
}
//析构
CCSpriteFrame::~CCSpriteFrame(void)
{
    CCLOGINFO("cocos2d: deallocing %p", this);
    CC_SAFE_RELEASE(m_pobTexture);
}
//创建一个当前实例的拷贝
CCObject* CCSpriteFrame::copyWithZone(CCZone *pZone)
{
CC_UNUSED_PARAM(pZone);
//新创一个CCSpriteFrame实例对象。
    CCSpriteFrame *pCopy = new CCSpriteFrame();
    //以当前实例的各属性值为参数对其进行初始化。
pCopy->initWithTextureFilename(m_strTextureFilename.c_str(), m_obRectInPixels, m_bRotated, m_obOffsetInPixels, m_obOriginalSizeInPixels);
//设置同样的纹理图集。
pCopy->setTexture(m_pobTexture);
//返回创建的CCSpriteFrame实例对象。
    return pCopy;
}
//设置当前帧所用的纹理块在图集中的矩形
void CCSpriteFrame::setRect(const CCRect& rect)
{
//保存点单位的矩形大小
m_obRect = rect;
//保存转换为像素单位的矩形大小
    m_obRectInPixels = CC_RECT_POINTS_TO_PIXELS(m_obRect);
}
//设置当前帧所用的纹理块在图集中的矩形(像素单位)
void CCSpriteFrame::setRectInPixels(const CCRect& rectInPixels)
{
	//保存像素单位的矩形大小
m_obRectInPixels = rectInPixels;
//保存转换为点单位的矩形大小
    m_obRect = CC_RECT_PIXELS_TO_POINTS(rectInPixels);
}
//取得纹理块在合并时去掉周围空白边后的锚点偏移(点单位).
const CCPoint& CCSpriteFrame::getOffset(void)
{
    return m_obOffset;
}
//设置纹理块在合并时去掉周围空白边后的锚点偏移(点单位).

void CCSpriteFrame::setOffset(const CCPoint& offsets)
{
	//保存纹理块在合并时去掉周围空白边后的锚点偏移(点单位).
m_obOffset = offsets;
//保存转换为像素单位的纹理块在合并时去掉周围空白边后的锚点偏移。
    m_obOffsetInPixels = CC_POINT_POINTS_TO_PIXELS( m_obOffset );
}
//取得纹理块在合并时去掉周围空白边后的锚点偏移(像素单位).
const CCPoint& CCSpriteFrame::getOffsetInPixels(void)
{
    return m_obOffsetInPixels;
}
//设置纹理块在合并时去掉周围空白边后的锚点偏移(像素单位).
void CCSpriteFrame::setOffsetInPixels(const CCPoint& offsetInPixels)
{
	//保存像素单位的纹理块在合并时去掉周围空白边后的锚点偏移。
m_obOffsetInPixels = offsetInPixels;
//保存转换为点单位的纹理块在合并时去掉周围空白边后的锚点偏移.
    m_obOffset = CC_POINT_PIXELS_TO_POINTS( m_obOffsetInPixels );
}
//设置图集纹理
void CCSpriteFrame::setTexture(CCTexture2D * texture)
{
	//删放旧图集纹理,使用新图集纹理。
    if( m_pobTexture != texture ) {
        CC_SAFE_RELEASE(m_pobTexture);
		//对应CC_SAFE_RELEASE对引用计数器减一操作, CC_SAFE_RETAIN对引用计数器加一操作.
        CC_SAFE_RETAIN(texture);
        m_pobTexture = texture;
    }
}
//取得图集纹理。
CCTexture2D* CCSpriteFrame::getTexture(void)
{
	//如果纹理指针有效,直接返回。
    if( m_pobTexture ) {
        return m_pobTexture;
    }
	//否则查看纹理图片是否有效,如果有效,加载到纹理管理器中返回创建的纹理。
    if( m_strTextureFilename.length() > 0 ) {
        return CCTextureCache::sharedTextureCache()->addImage(m_strTextureFilename.c_str());
    }
    // 如果都无效,返回NULL。
    return NULL;
}

NS_CC_END

现在来看CCAnimationFrame 和CCAnimation:

#ifndef __CC_ANIMATION_H__
#define __CC_ANIMATION_H__
//引入相关头文件。
#include "platform/CCPlatformConfig.h"
#include "cocoa/CCObject.h"
#include "cocoa/CCArray.h"
#include "cocoa/CCDictionary.h"
#include "cocoa/CCGeometry.h"
#include "CCSpriteFrame.h"
#include <string>
//使用Cocos2d命名空间。
NS_CC_BEGIN
//用到的类。
class CCTexture2D;
class CCSpriteFrame;

//序列帧动画单帧:序列帧动画中的一帧。
class CC_DLL CCAnimationFrame : public CCObject
{
public:
	//构造
CCAnimationFrame();
//析构
virtual ~CCAnimationFrame();
//创建一个当前实例的拷贝。
    virtual CCObject* copyWithZone(CCZone* pZone);
    //初始化单帧:参一为精灵帧,参二为当前帧到下一帧的时间间隔?错,这里有问题,后面讲,参三为存储各帧所用的词典。
    bool initWithSpriteFrame(CCSpriteFrame* spriteFrame, float delayUnits, CCDictionary* userInfo);
    
   //定义一个精灵帧成员指针变量m_pSpriteFrame,代表当前动画帧所对应的精灵帧。CC_SYNTHESIZE_RETAIN宏创建对变量的set 和 get 操作的函数,函数名分别为setSpriteFrame和getSpriteFrame, 相应的set函数会释放旧的指针变量,并将参数保存到指针变量,并自动的对其引用计数器加一。
    CC_SYNTHESIZE_RETAIN(CCSpriteFrame*, m_pSpriteFrame, SpriteFrame)

    //定义一个float成员变量m_fDelayUnits,代表当前帧到下一帧的时间间隔?错,这个变量其实没有毛用处?看多了反而容易乱,先略过吧。最后统一分析。CC_SYNTHESIZE宏创建对变量的set 和 get 操作的函数,函数名分别为setDelayUnits和getDelayUnits。
    CC_SYNTHESIZE(float, m_fDelayUnits, DelayUnits)

    //定义一个词典成员指针变量m_pUserInfo,CC_SYNTHESIZE_RETAIN宏创建对变量的set 和 get 操作的函数,函数名分别为set UserInfo和get UserInfo, 相应的set函数会释放旧的指针变量,并将参数保存到指针变量,并自动的对其引用计数器加一。
    CC_SYNTHESIZE_RETAIN(CCDictionary*, m_pUserInfo, UserInfo)
};

//CCAnimation:序列帧动画类,管理所有动画中的CCAnimationFrame。
class CC_DLL CCAnimation : public CCObject
{
public:
	//构造函数。
CCAnimation();
//析构函数。
    ~CCAnimation(void);
public:
    //创建一个序列帧动画,内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCAnimation* animation(void);

    //创建一个序列帧动画,内部调用createWithSpriteFrames实现。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。
    CC_DEPRECATED_ATTRIBUTE static CCAnimation* animationWithSpriteFrames(CCArray* arrayOfSpriteFrameNames, float delay = 0.0f);

    //创建一个序列帧动画,内部调用相应create实现。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。
    CC_DEPRECATED_ATTRIBUTE static CCAnimation* animationWithAnimationFrames(CCArray *arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops);

    //创建一个序列帧动画。
    static CCAnimation* create(void);

//创建一个序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。
    static CCAnimation* createWithSpriteFrames(CCArray* arrayOfSpriteFrameNames, float delay = 0.0f);

    //创建一个序列帧动画。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。
    static CCAnimation* create(CCArray *arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops);

    //新加一帧到序列帧动画中,参数为CCSpriteFrame指针。
    void addSpriteFrame(CCSpriteFrame *pFrame);

    //新加一帧加入到序列帧动画中,参数为单帧图片名称。  
    void addSpriteFrameWithFileName(const char *pszFileName);

    //新加一帧加入到序列帧动画中,参一为图集纹理,参二为图集纹理中的图块矩形。  
    void addSpriteFrameWithTexture(CCTexture2D* pobTexture, const CCRect& rect);

	//初始化一个空白的动画。
    bool init();

//初始化序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。
    bool initWithSpriteFrames(CCArray *pFrames, float delay = 0.0f);

//初始化序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。
    bool initWithAnimationFrames(CCArray* arrayOfAnimationFrames, float delayPerUnit, unsigned int loops);

	//创建一个当前类的实例对象。
    virtual CCObject* copyWithZone(CCZone* pZone);

    //定义一个float成员变量m_ fTotalDelayUnits,代表总帧数。CC_SYNTHESIZE_READONLY宏创建对变量的get 操作的函数,函数为getTotalDelayUnits。
    CC_SYNTHESIZE_READONLY(float, m_fTotalDelayUnits, TotalDelayUnits)

    //定义一个float成员变量m_fDelayPerUnit,代表每两帧间的时间间隔,CC_SYNTHESIZE宏创建对变量的set 和 get 操作的函数,函数名分别为setDelayPerUnit和getDelayPerUnit。
    CC_SYNTHESIZE(float, m_fDelayPerUnit, DelayPerUnit)

    //定义一个float成员变量m_fDuration,这个变量没用,不知作者为什么要加这个变量~。CC_SYNTHESIZE_READONLY宏创建对变量的get 操作的函数,函数为getDuration。
    CC_PROPERTY_READONLY(float, m_fDuration, Duration)

    //定义一个词典成员指针变量m_pFrames,用于保存每个动画帧,CC_SYNTHESIZE_RETAIN宏创建对变量的set 和 get 操作的函数,函数名分别为setFrames和getFrames, 相应的set函数会释放旧的指针变量,并将参数保存到指针变量,并自动的对其引用计数器加一。
    CC_SYNTHESIZE_RETAIN(CCArray*, m_pFrames, Frames)

    //定义一个bool成员变量m_bRestoreOriginalFrame,用于指定是否在动画结束后释放各帧。CC_SYNTHESIZE宏创建对变量的set 和 get 操作的函数,函数名分别为setRestoreOriginalFrame和getRestoreOriginalFrame。
    CC_SYNTHESIZE(bool, m_bRestoreOriginalFrame, RestoreOriginalFrame)

    //定义一个unsigned int成员变量m_uLoops,代表循环播放次数。CC_SYNTHESIZE宏创建对变量的set 和 get 操作的函数,函数名分别为setLoops和getLoops。
    CC_SYNTHESIZE(unsigned int, m_uLoops, Loops)
};

NS_CC_END

#endif // __CC_ANIMATION_H__

再看CPP:

#include "CCAnimation.h"
#include "textures/CCTextureCache.h"
#include "textures/CCTexture2D.h"
#include "ccMacros.h"
#include "sprite_nodes/CCSpriteFrame.h"
#include "cocoa/CCZone.h"
//Cocos2d命名空间
NS_CC_BEGIN
//构造
CCAnimationFrame::CCAnimationFrame()
: m_pSpriteFrame(NULL)
, m_fDelayUnits(0.0f)
, m_pUserInfo(NULL)
{

}
//初始化单帧:参一为精灵帧,参二为当前帧到下一帧的时间间隔,参三为存储各帧所用的词典。
bool CCAnimationFrame::initWithSpriteFrame(CCSpriteFrame* spriteFrame, float delayUnits, CCDictionary* userInfo)
{
	//设置为当前单帧所用的各成员变量。
    setSpriteFrame(spriteFrame);
    setDelayUnits(delayUnits);
    setUserInfo(userInfo);

    return true;
}
//析构
CCAnimationFrame::~CCAnimationFrame()
{    
    CCLOGINFO( "cocos2d: deallocing %s", this);
	//对占用的精灵帧和词典引用计数估减一操作。
    CC_SAFE_RELEASE(m_pSpriteFrame);
    CC_SAFE_RELEASE(m_pUserInfo);
}
//创建当前单帧的实例拷贝。
CCObject* CCAnimationFrame::copyWithZone(CCZone* pZone)
{
	//定义拷贝类变量指针
CCZone* pNewZone = NULL;
//定义单帧类指针变量pCopy用于存储创建结果,置空。
CCAnimationFrame* pCopy = NULL;
//如果参数pZone有效且已经有内部产生的实例拷贝。
    if(pZone && pZone->m_pCopyObject) 
    {
        //直接取得拷贝返回给pCopy.
        pCopy = (CCAnimationFrame*)(pZone->m_pCopyObject);
    }
    else
{
	 //如果参数pZone无值或者无内部产生的拷贝,重新生成单帧以及相应单帧的拷贝。
        pCopy = new CCAnimationFrame();
        pNewZone = new CCZone(pCopy);
    }
	//对pCopy进行初始化,注意相应成员变量也要调用copy以产生拷贝,并调用autorelease以交由内存管理器进行释放管理。

pCopy->initWithSpriteFrame((CCSpriteFrame*)m_pSpriteFrame->copy()->autorelease(),
        m_fDelayUnits, m_pUserInfo != NULL ? (CCDictionary*)m_pUserInfo->copy()->autorelease() : NULL);
	//释放pNewZone
CC_SAFE_DELETE(pNewZone);
//返回pCopy。
    return pCopy;
}

//创建动画
CCAnimation* CCAnimation::animation(void)
{
    return CCAnimation::create();
}
//创建动画。
CCAnimation* CCAnimation::create(void)
{
	//使用new创建一个动画,初始化并交由内存管理器进行释放管理。
    CCAnimation *pAnimation = new CCAnimation();
    pAnimation->init();
    pAnimation->autorelease();
	//返回创建成功的动画。
    return pAnimation;
} 
//创建一个序列帧动画,内部调用相应createWithSpriteFrames实现。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。
CCAnimation* CCAnimation::animationWithSpriteFrames(CCArray *frames, float delay/* = 0.0f*/)
{
    return CCAnimation::createWithSpriteFrames(frames, delay);
}
//创建一个序列帧动画。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。

CCAnimation* CCAnimation::createWithSpriteFrames(CCArray *frames, float delay/* = 0.0f*/)
{
	//使用new创建一个动画实例,初始化后交由内存管理器进行释放管理。
    CCAnimation *pAnimation = new CCAnimation();
    pAnimation->initWithSpriteFrames(frames, delay);
    pAnimation->autorelease();
	//返回创建的动画实例。
    return pAnimation;
}
//创建一个序列帧动画,内部调用相应create实现。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。

CCAnimation* CCAnimation::animationWithAnimationFrames(CCArray* arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops)
{
    return CCAnimation::create(arrayOfAnimationFrameNames, delayPerUnit, loops);
}
//创建一个序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。

CCAnimation* CCAnimation::create(CCArray* arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops)
{
	//使用new创建一个动画实例,初始化后交由内存管理器进行释放管理。
    CCAnimation *pAnimation = new CCAnimation();
    pAnimation->initWithAnimationFrames(arrayOfAnimationFrameNames, delayPerUnit, loops);
pAnimation->autorelease();
//返回创建的动画实例。
    return pAnimation;
}
//初始化一个空白的动画。
bool CCAnimation::init()
{
    return initWithSpriteFrames(NULL, 0.0f);
}
//初始化序列帧动画,参数一为存储精灵帧的数组,参数二为动画的每一帧与下帧的时间间隔。
bool CCAnimation::initWithSpriteFrames(CCArray *pFrames, float delay/* = 0.0f*/)
{
	//对pFrames是否是CCSpriteFrame指针数组进行检查。
    CCARRAY_VERIFY_TYPE(pFrames, CCSpriteFrame*);
	//将循环次数置1.
m_uLoops = 1;
//保存每一帧与下一帧的时间间隔。
m_fDelayPerUnit = delay;
//创建一个CArray,返回它的地址给指针pTmpFrames。
CCArray* pTmpFrames = CCArray::create();
//将pTmpFrames设置为当前动画的帧数组指针。
    setFrames(pTmpFrames);
	//如果代表单帧数组的参数pFrames有值,遍历各帧存入pTmpFrames.
    if (pFrames != NULL)
{
	//定义临时变量pObj用来存储遍历时的元素。
        CCObject* pObj = NULL;
		//遍历pFrames,以pObj为每次循环的元素指针。
        CCARRAY_FOREACH(pFrames, pObj)
        {
			 //将pObj转为精灵帧指针。
            CCSpriteFrame* frame = (CCSpriteFrame*)pObj;
			 //创建一个动画单帧,并将精灵单帧做为参数对其进行初始化。
            CCAnimationFrame *animFrame = new CCAnimationFrame();
            animFrame->initWithSpriteFrame(frame, 1, NULL);
			 //将初始化完成后的动画单帧放入动画帧容器。
            m_pFrames->addObject(animFrame);
			 //对动画单帧的引用计数器减1,不会被释放的,因为上面一行代码中addObject对它的引用计数器内部会做加1操作。这样“该接手的加1,该放手的减1”完成了交接工作。
            animFrame->release();
			 //动画帧数加一。
            m_fTotalDelayUnits++;
        }
    }

    return true;
}
//初始化序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参数三为循环播放次数。
bool CCAnimation::initWithAnimationFrames(CCArray* arrayOfAnimationFrames, float delayPerUnit, unsigned int loops)
{
	//对pFrames是否是CCSpriteFrame指针数组进行检查。
    CCARRAY_VERIFY_TYPE(arrayOfAnimationFrames, CCAnimationFrame*);
//保存每一帧与下一帧的时间间隔。
m_fDelayPerUnit = delayPerUnit;
//保存循环次数
    m_uLoops = loops;
//将arrayOfAnimationFrames设置为当前动画的帧数组指针。
    setFrames(CCArray::createWithArray(arrayOfAnimationFrames));
	//定义临时变量pObj用来存储遍历时的元素。
CCObject* pObj = NULL;
//遍历pFrames,以pObj为每次循环的元素指针。
    CCARRAY_FOREACH(m_pFrames, pObj)
{
	 //将pObj转为动画单帧指针。
        CCAnimationFrame* animFrame = (CCAnimationFrame*)pObj;
		 //这里写的有误,应该改成动画帧数加一,它加动画帧的相应函数干毛?
        m_fTotalDelayUnits += animFrame->getDelayUnits();
}
//返回成功。
    return true;
}
//构造
CCAnimation::CCAnimation()
: m_fTotalDelayUnits(0.0f)
, m_fDelayPerUnit(0.0f)
, m_fDuration(0.0f)
, m_pFrames(NULL)
, m_bRestoreOriginalFrame(false)
, m_uLoops(0)
{

}
//析构
CCAnimation::~CCAnimation(void)
{
    CCLOGINFO("cocos2d, deallocing %p", this);
    CC_SAFE_RELEASE(m_pFrames);
}

//新加一帧到序列帧动画中,参数为CCSpriteFrame指针。 
void CCAnimation::addSpriteFrame(CCSpriteFrame *pFrame)
{
	//利用new新建一个动画帧.
CCAnimationFrame *animFrame = new CCAnimationFrame();
//使用精灵指参数对其进行初始化。
animFrame->initWithSpriteFrame(pFrame, 1.0f, NULL);
//将生成的动画帧放入动画帧容器
m_pFrames->addObject(animFrame);
//对动画单帧的引用计数器减1。(新建的animFrame,交给m_pFrames的addObject函数会对其引用计数器加1,后就要减1才对。交接也要保证引用计数的平衡)
    animFrame->release();
    //动画帧数加一。
    m_fTotalDelayUnits++;
}
//新加一帧加入到序列帧动画中,参数为单帧图片名称。
void CCAnimation::addSpriteFrameWithFileName(const char *pszFileName)
{
	//先由图片名称取得创建的纹理。
CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFileName);
//定义临时矩形保存图片大小。
    CCRect rect = CCRectZero;
rect.size = pTexture->getContentSize();
//由当前图片和对应大小创建出一个精灵帧 。
CCSpriteFrame *pFrame = CCSpriteFrame::createWithTexture(pTexture, rect);
//使用创建的精灵帧来调用上一个函数增加一帧动画。
    addSpriteFrame(pFrame);
}
//新加一帧加入到序列帧动画中,参数一为图集纹理,参数二为对应的纹理块矩形。
void CCAnimation::addSpriteFrameWithTexture(CCTexture2D *pobTexture, const CCRect& rect)
{
//由当前图片和对应大小创建出一个精灵帧 。
CCSpriteFrame *pFrame = CCSpriteFrame::createWithTexture(pobTexture, rect);
//使用创建的精灵帧来增加一帧动画。
    addSpriteFrame(pFrame);
}
//取得序列帧动画总时长。
float CCAnimation::getDuration(void)
{
	//总时长 = 帧数x帧间隔时间。
    return m_fTotalDelayUnits * m_fDelayPerUnit;
}
//创建当前序列帧动画实例的拷贝。
CCObject* CCAnimation::copyWithZone(CCZone* pZone)
{
	//定义拷贝类变量指针
CCZone* pNewZone = NULL;
//定义序列帧动画类指针变量pCopy用于存储创建结果,置空。
CCAnimation* pCopy = NULL;
//如果参数pZone有效且已经有内部产生的实例拷贝。

    if(pZone && pZone->m_pCopyObject) 
    {
        //直接取得拷贝返回给pCopy.
        pCopy = (CCAnimation*)(pZone->m_pCopyObject);
    }
    else
{
	//如果参数pZone无值或者无内部产生的拷贝,重新生成单帧以及相应单帧的拷贝。
        pCopy = new CCAnimation();
        pNewZone = new CCZone(pCopy);
    }
	//初始化序列序动画。
pCopy->initWithAnimationFrames(m_pFrames, m_fDelayPerUnit, m_uLoops);
//设置相应变理。
    pCopy->setRestoreOriginalFrame(m_bRestoreOriginalFrame);
	//释放pNewZone.
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}

NS_CC_END

最后我们看一下CCAnimate:

class CCTexture2D;
//序列帧动画
class CC_DLL CCAnimate : public CCActionInterval
{
public:
//构造与析构。
    CCAnimate();
    ~CCAnimate();

    //初始化动画。
    bool initWithAnimation(CCAnimation *pAnimation);
//重载基类的相应函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void stop(void);
    virtual void update(float t);
    virtual CCActionInterval* reverse(void);

public:
    //创建一个序列帧动画,内部调用create实现。参数为序列帧动画信息结构指针。
    CC_DEPRECATED_ATTRIBUTE static CCAnimate* actionWithAnimation(CCAnimation *pAnimation);
	//创建一个序列帧动画。
    static CCAnimate* create(CCAnimation *pAnimation);
	//定义一个序列帧动画信息结构指针变量以及存取此变量的函数。
    CC_SYNTHESIZE_RETAIN(CCAnimation*, m_pAnimation, Animation)
protected:
//保存每一帧要切换时的进度的容器指针,由动画信息结构指针取得。
std::vector<float>* m_pSplitTimes;
//当前要播放的下一帧序号。
int                m_nNextFrame;
//各帧中保存的精灵信息。
CCSpriteFrame*  m_pOrigFrame;
//循环次数。
    unsigned int    m_uExecutedLoops;
};
具体实现:
 //创建一个序列帧动画,内部调用create实现。参数为动画信息结构。
CCAnimate* CCAnimate::actionWithAnimation(CCAnimation *pAnimation)
{
    return CCAnimate::create(pAnimation);
}
//创建一个序列帧动画。
CCAnimate* CCAnimate::create(CCAnimation *pAnimation)
{
//使用new创建一个CCAnimate实例对象,初始化,交由内存管理器。
    CCAnimate *pAnimate = new CCAnimate();
    pAnimate->initWithAnimation(pAnimation);
    pAnimate->autorelease();
	//返回创建的实例对象。
    return pAnimate;
}
//初始化序列动画。
bool CCAnimate::initWithAnimation(CCAnimation *pAnimation)
{
//有效性判断。
    CCAssert( pAnimation!=NULL, "Animate: argument Animation must be non-NULL");
//取得序列的时长。
    float singleDuration = pAnimation->getDuration();
//乘以循环次数做为当前动画总时长来进行初始化。
    if ( CCActionInterval::initWithDuration(singleDuration * pAnimation->getLoops() ) ) 
{
//初始化变量。
        m_nNextFrame = 0;
		 //将参数pAnimation保存到动画信息结构指针变量m_pAnimation.
        setAnimation(pAnimation);
        m_pOrigFrame = NULL;
        m_uExecutedLoops = 0;
//设置容器大小。这里我认为应该写成resize而不是reserver!!!
        m_pSplitTimes->reserve(pAnimation->getFrames()->count());
        //初始化变量。
        float accumUnitsOfTime = 0;
 //序列播放一轮的时间/
        float newUnitOfTimeValue = singleDuration / pAnimation->getTotalDelayUnits();
//取得序列信息中的帧信息数组。
        CCArray* pFrames = pAnimation->getFrames();
//判断pFrames是否是CCAnimationFrame指针类型,确保是。
        CCARRAY_VERIFY_TYPE(pFrames, CCAnimationFrame*);
//定义临时变量pObj来遍历取得帧信息中的单帧信息。
        CCObject* pObj = NULL;
        CCARRAY_FOREACH(pFrames, pObj)
        {
 //取得单帧信息。
            CCAnimationFrame* frame = (CCAnimationFrame*)pObj;
 //计算播放当前单帧时的进度,这里又用到了单帧信息的接口getDelayUnits。后面讲一下。
            float value = (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration;
            accumUnitsOfTime += frame->getDelayUnits();
 //将当前单帧的进度存入容器。
            m_pSplitTimes->push_back(value);
        }    
//返回成功。
        return true;
}
//如果初始化失败,返回false。
    return false;
}
//创建拷贝。
CCObject* CCAnimate::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCAnimate* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCAnimate*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCAnimate();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithAnimation((CCAnimation*)m_pAnimation->copy()->autorelease());

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//构造,注意在这里申请了一个vector<float>容器,并将成员指针变量m_pSplitTimes指向它的。
CCAnimate::CCAnimate()
: m_pAnimation(NULL)
, m_pSplitTimes(new std::vector<float>)
, m_nNextFrame(0)
, m_pOrigFrame(NULL)
, m_uExecutedLoops(0)
{

}
//析构。
CCAnimate::~CCAnimate()
{
    CC_SAFE_RELEASE(m_pAnimation);
    CC_SAFE_RELEASE(m_pOrigFrame);
    CC_SAFE_DELETE(m_pSplitTimes);
}
//设置演示当前序列动画的演员。
void CCAnimate::startWithTarget(CCNode *pTarget)
{
//先调用基类的相应函数。
CCActionInterval::startWithTarget(pTarget);
//序列帧动画必须是个精灵。
    CCSprite *pSprite = (CCSprite*)(pTarget);
//释放上一个
    CC_SAFE_RELEASE(m_pOrigFrame);
//如果有帧数据。
    if (m_pAnimation->getRestoreOriginalFrame())
{
 //取得精灵的帧信息。
        m_pOrigFrame = pSprite->displayFrame();
		 //对帧信息占用,引用数加一。
        m_pOrigFrame->retain();
    }
    m_nNextFrame = 0;
    m_uExecutedLoops = 0;
}
//停止当前动画。
void CCAnimate::stop(void)
{
//如果动画有帧数据且有演员。
    if (m_pAnimation->getRestoreOriginalFrame() && m_pTarget)
{
	//设置演员显示当前帧。
        ((CCSprite*)(m_pTarget))->setDisplayFrame(m_pOrigFrame);
    }
	//停止当前动画。
    CCActionInterval::stop();
}
//更新动画。
void CCAnimate::update(float t)
{
// 如果整个动画未播放,先计算循环是否够次数。
if( t < 1.0f ) {
	//计算出当前进度播放的循环数。
        t *= m_pAnimation->getLoops();

		 // 通过先取整再判断是否大于当前的已经循环次数来判断是否是新的循环,如果是将下一帧置零,已经循环的次数加1 。
        unsigned int loopNumber = (unsigned int)t;
        if( loopNumber > m_uExecutedLoops ) {
            m_nNextFrame = 0;
            m_uExecutedLoops++;
        }

        // 对t进行浮点取模值,将其限制在0~1之间,这样等于又转换成为了当前动画播放的进度值。
        t = fmodf(t, 1.0f);
    }
	//取得动画的帧信息容器和帧数。
    CCArray* frames = m_pAnimation->getFrames();
    unsigned int numberOfFrames = frames->count();
    //精灵图片信息。
CCSpriteFrame *frameToDisplay = NULL;
	//找出要播放的帧图片设置为精灵要显示的图片,这里用了一个for循环。从下一帧开始到结束帧进行遍历,判断是否到了这一帧。
for( unsigned int i=m_nNextFrame; i < numberOfFrames; i++ ) {
	 //取出循环中的当前帧的播出时间进度。
        float splitTime = m_pSplitTimes->at(i);
		//如果这一帧的进度小于当前动画的播放进度,即代表进入了这一帧。
        if( splitTime <= t ) {
			//取得对应的动画帧信息。
            CCAnimationFrame* frame = (CCAnimationFrame*)frames->objectAtIndex(i);
			 //取得当前帧的精灵图片信息。
            frameToDisplay = frame->getSpriteFrame();
			 //这才是显示动画的关键,找到相应的精灵帧并设置为演员要显示的当前帧。
            ((CCSprite*)m_pTarget)->setDisplayFrame(frameToDisplay);
			 //通过动画帧信息取得其附加的用户词典信息,这个词典存储的是用于需要通知的目标。
            CCDictionary* dict = frame->getUserInfo();
            if( dict )
            {
				  //暂忽略了。
                //TODO: [[NSNotificationCenter defaultCenter] postNotificationName:CCAnimationFrameDisplayedNotification object:target_ userInfo:dict];
            }
			 //帧数加一。
            m_nNextFrame = i+1;

            break;
        }
    }
}
//创建一个反向播放的序列帧动画。
CCActionInterval* CCAnimate::reverse(void)
{
    CCArray* pOldArray = m_pAnimation->getFrames();
    CCArray* pNewArray = CCArray::createWithCapacity(pOldArray->count());
   
    CCARRAY_VERIFY_TYPE(pOldArray, CCAnimationFrame*);

    if (pOldArray->count() > 0)
    {
        CCObject* pObj = NULL;
        CCARRAY_FOREACH_REVERSE(pOldArray, pObj)
        {
            CCAnimationFrame* pElement = (CCAnimationFrame*)pObj;
            if (! pElement)
            {
                break;
            }

            pNewArray->addObject((CCAnimationFrame*)(pElement->copy()->autorelease()));
        }
    }

    CCAnimation *newAnim = CCAnimation::create(pNewArray, m_pAnimation->getDelayPerUnit(), m_pAnimation->getLoops());
    newAnim->setRestoreOriginalFrame(m_pAnimation->getRestoreOriginalFrame());
    return create(newAnim);
}

         源码看完了,来看一个小示例 ,以ActionsTest为例,我将ActionAnimate做了稍许修改,只有一个演员演示一个14帧的序列帧动画。我们来看一下实际的动作生成过程。

void ActionAnimate::onEnter()
{
    ActionsDemo::onEnter();
	//创建一个演员,站在屏幕中央。
    centerSprites(1);
    //创建一个空白的序列帧动画信息。
    CCAnimation* animation = CCAnimation::create();
	//共有14帧,这里用for循环将对应的序列图加入到动画中。
    for( int i=1;i<15;i++)
    {
        char szName[100] = {0};
        sprintf(szName, "Images/grossini_dance_%02d.png", i);
        animation->addSpriteFrameWithFileName(szName);
    }
    //设置每两帧间时间间隔为1秒。
    animation->setDelayPerUnit(1.0f);
	//设置动画结束后仍保留动画帧信息。
    animation->setRestoreOriginalFrame(true);
	//由这个动画信息创建一个序列帧动画。
    CCAnimate* action = CCAnimate::create(animation);
	//让演员演示这个动画。
	 m_grossini->runAction(action);
}

 

       流程是先创建动画帧信息,然后由动画帧信息生成动画信息,最后由动画信息创建出序列帧动画供精灵演示。

       这里面的关健函数是addSpriteFrameWithFileName,我们来看一下在调用它时的程序流程,当我们把图片名称赋给它后,它创建出纹理并调用createWithTexture函数来进行创建一个精灵单帧,初始化后使用这个精灵单帧信息创建一帧动画放到动画信息容器中。

void CCAnimation::addSpriteFrameWithFileName(const char *pszFileName)
{
    CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFileName);
    CCRect rect = CCRectZero;
    rect.size = pTexture->getContentSize();
    CCSpriteFrame *pFrame = CCSpriteFrame::createWithTexture(pTexture, rect);
    addSpriteFrame(pFrame);
}

       在createWithTexture函数中,传入的是纹理和纹理大小。其内部实现了创建精灵帧并初始化的功能。

CCSpriteFrame* CCSpriteFrame::createWithTexture(CCTexture2D *pobTexture, const CCRect& rect)
{
    CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();;
    pSpriteFrame->initWithTexture(pobTexture, rect);
    pSpriteFrame->autorelease();

    return pSpriteFrame;
}

       这里的关帧是initWithTexture函数,传入的是纹理和纹理大小。

bool CCSpriteFrame::initWithTexture(CCTexture2D* pobTexture, const CCRect& rect)
{
    CCRect rectInPixels = CC_RECT_POINTS_TO_PIXELS(rect);
    return initWithTexture(pobTexture, rectInPixels, false, CCPointZero, rectInPixels.size);
}

我们看,此函数内部以不旋转,不偏移,原始大小为当前大小等参数来进行了初始化精灵单帧的处理。这样做说明使用的就是单图集的纹理。

如果我们使用的是图集纹理,需要在调用addSpriteFrameWithFileName函数时加上相应的纹理块的相关设置参数。

最后,我来解答一下CCAnimationFrame类中的成员变量m_fDelayUnits的。这个变量其实是无用的,在整个动画处理过程中它出现过二次。一次是CCAnimation::initWithAnimationFrames函数中

m_fTotalDelayUnits+= animFrame->getDelayUnits();

另一次是CCAnimate::initWithAnimation函数中在for循环中的

 

    float value = (accumUnitsOfTime* newUnitOfTimeValue) / singleDuration;

accumUnitsOfTime+= frame->getDelayUnits();

 

先看第一次,我们知道m_fTotalDelayUnits代表的是帧的数量,其实对于单帧来说,它本身只有一帧,也就是说animFrame->getDelayUnits()的结果应该是1,所以m_fTotalDelayUnits += animFrame->getDelayUnits();应该改为m_fTotalDelayUnits
++;
就好了。第二次中也一样,frame->getDelayUnits();也应该为一,因为float value =(accumUnitsOfTime * newUnitOfTimeValue) / singleDuration;这一句是用来进算当前帧在整个动画播放中的进度,float newUnitOfTimeValue = singleDuration
/pAnimation->getTotalDelayUnits();这一句中singleDuration代表动画的时长,pAnimation->getTotalDelayUnits()代表总帧数,那newUnitOfTimeValue即是每两帧间的时间时间隔。既然newUnitOfTimeValue的意义是每两帧间的时间时间隔,则accumUnitsOfTime应该代表的就是当前遍历到第几帧了,它是帧索引。所以我认为CCAnimationFrame类中的成员变量m_fDelayUnits本身没有必要存在,就是1,这个变量的存在反而会影响到大家对于动画的理解,如果说CCAnimationFrame本身又支持存储多个精灵帧,那倒是需要一个变量来代表精灵帧的数量,但目前看并不是。

 

好了,啰哩吧嗦这么多,无非是希望把序列帧动画讲透彻。望大家看过后对在下的见解提出异议。下课。

 


分类: cocos2d, cocos2d-x 标签:

Cocos2d-x 2.0 之 Actions “三板斧” 之二

2012年11月22日 没有评论

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ2群:44208467 加群写:Cocos2d-x 
红孩儿Cocos2d-X学习园地QQ群:249941957 [暂满]加群写:Cocos2d-x 

本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!

Cocos2d-x 2.0 之 Actions 三板斧 之二

另:本章所用Cocos2d-x版本为:

cocos2d-2.0-x-2.0.2 @ Aug 30 2012

http://cn.cocos2d-x.org/download

        在TestCpp工程里,ActionsTest演示了大量的动画,这些动画看起来非常酷。可是,它是怎么实现的呢?本节,咱们就谈谈这些动画的原理。

       在上一节中,我们学习了基类CCAction,我们知道它有必须有一个“目标结点”。它是动画的使用者,即演示动画的精灵。我们让一个精灵调用runAction函数,则这个精灵就是使用者。它会被记录在动画的“目标结点”中。CCAction通过step函数来取得时间的流逝并计算动画进度。但在CCAction中,step函数是一个虚函数,它并没有在CCAction中进行相应功能的实现。而是由CCAction 派生到CCActionInterval这个类时才进行了相应功能的实现。可以说CCActinInterval这个类才真正的实现了时间流逝的动画计算功能。这是怎么回事呢?那现在就从CCActinInterval这个类开始我们的源码之旅吧!

       打开CCActionInterval.h:


CCActionInterVal:

#ifndef __ACTION_CCINTERVAL_ACTION_H__
#define __ACTION_CCINTERVAL_ACTION_H__
//加入相关头文件
#include "base_nodes/CCNode.h"
#include "CCAction.h"
#include "CCProtocols.h"
#include "sprite_nodes/CCSpriteFrame.h"
#include "sprite_nodes/CCAnimation.h"
#include <vector>
//使用Cocos2d命名空间
NS_CC_BEGIN

//可以看到,CCActionInterval是由时间动画基类CCFiniteTimeAction派生的,它有基础的时间长度属性接口。

class CC_DLL CCActionInterval : public CCFiniteTimeAction
{
public:
    //取得动画播放了多久。
    inline float getElapsed(void) { return m_elapsed; }

    //初始化,设置一个固定时长的动画。
    bool initWithDuration(float d);

    //动画是否播放结束。
    virtual bool isDone(void);
	//产生一个当前动画实例的拷贝。
    virtual CCObject* copyWithZone(CCZone* pZone);
	//上一节我们讲过,step是用来通过当前帧与上一帧的时间流逝值来计算动画播放进度的。
    virtual void step(float dt);
	//设置使用者结点,即记录表演动画的演员。
    virtual void startWithTarget(CCNode *pTarget);
    //创建一个反向播放的动画。
    virtual CCActionInterval* reverse(void);

public:
    //静态函数:创建一个时间动画,其内部调用create来实现。
    CC_DEPRECATED_ATTRIBUTE static CCActionInterval* actionWithDuration(float d);

    //静态函数:创建一个时间动画
    static CCActionInterval* create(float d);

public:
    // 
    void setAmplitudeRate(float amp);
    float getAmplitudeRate(void);

protected:
	//当前帧与上一帧的时间流逝值。单位是秒。
    float  m_elapsed;
	//这里定义一个布尔变量来判断是否是第一次计算时间,相当于一个时间计算方式的开关。
    bool   m_bFirstTick;
};
为了更直接的学习这些动画,我把CPP中的实现直接贴到相应的类定义后面。
//静态函数:创建一个时间动画,其内部调用create来实现。
CCActionInterval* CCActionInterval::actionWithDuration(float d)
{
    return CCActionInterval::create(d);
}
//静态函数:创建一个时间动画
CCActionInterval* CCActionInterval::create(float d)
{
	//先new出一个时间动画
    CCActionInterval *pAction = new CCActionInterval();
	//调用初始化函数。
    pAction->initWithDuration(d);
	//设置其使用内存管理器进行释放。
    pAction->autorelease();
	//返回动画。
    return pAction;
}
//初始化时间动画。
bool CCActionInterval::initWithDuration(float d)
{
	//记录动画时长。
    m_fDuration = d;
	//如果动画时长为0,这里取一个极小值,防止在计算中产生除零中断。
    if (m_fDuration == 0)
    {
        m_fDuration = FLT_EPSILON;
    }
	//初始化代表时间累和的变量为0。
    m_elapsed = 0;
	//初始化代表第一次进行时间间隔计算的布尔变量为true。
    m_bFirstTick = true;
    return true;
}
//产生一个时间动画的拷贝。
CCObject* CCActionInterval::copyWithZone(CCZone *pZone)
{
	//定义用于保存新拷贝指针的变量,参看CCZone的定义可以知道,它的功能仅仅是保存一个CCObject指针。
    CCZone* pNewZone = NULL;
	//定义用于返回结果的CCActionInterval指针变量
    CCActionInterval* pCopy = NULL;
	//如果pZone不为空且它保存了有效的CCObject,则返回给pCopy
    if(pZone && pZone->m_pCopyObject) 
    {
        pCopy = (CCActionInterval*)(pZone->m_pCopyObject);
    }
    else
    {
		//如果pZone为空或没有拷贝,则创建一个CCActionInterval返回给pCopy,并由pCopy做为拷贝取得一个新的CCZone返回给pZone。这个pCopy的变量名称写的好,很清楚。比上一节的变量名称好。
        pCopy = new CCActionInterval();
        pZone = pNewZone = new CCZone(pCopy);
    }
	//调用基类的产生拷贝函数,可参见上一节内容。
    CCFiniteTimeAction::copyWithZone(pZone);
	//释放pNewZone
    CC_SAFE_DELETE(pNewZone);
	//对产生的拷贝调用初始化函数
    pCopy->initWithDuration(m_fDuration);
	//返回拷贝对象。
    return pCopy;
}
//返回动画是否播放结束。
bool CCActionInterval::isDone(void)
{
	//返回时间累和值是否大于等于动画总时长。
    return m_elapsed >= m_fDuration;
}
//时间流逝的动画处理
void CCActionInterval::step(float dt)
{
	//如果是播放开始第一次进行时间流逝处理,将时间累和值设为0。
    if (m_bFirstTick)
    {
        m_bFirstTick = false;
        m_elapsed = 0;
    }
    else
    {
		 //动画时间累和。
        m_elapsed += dt;
    }
    //调用update函数更新动画。参数的结果是动画的时间插值结果,它代表了动画的进度,上一节讲过它取0~1之间的值。这里MIN和MAX用来将计算结果限定在0~1间。
    this->update(MAX (0,                                  // needed for rewind. elapsed could be negative
                      MIN(1, m_elapsed /
                          MAX(m_fDuration, FLT_EPSILON)   // division by 0
                          )
                      )
                 );
}
//设置振幅。子类重载进行功能实现。
void CCActionInterval::setAmplitudeRate(float amp)
{
    CC_UNUSED_PARAM(amp);
    // Abstract class needs implementation
    CCAssert(0, "");
}
//取得振幅。
float CCActionInterval::getAmplitudeRate(void)
{
    // Abstract class needs implementation
    CCAssert(0, "");

    return 0;
}
//动画初始化处理,设置将要表演动画的“演员”。
void CCActionInterval::startWithTarget(CCNode *pTarget)
{
	//调用子类相应函数。
    CCFiniteTimeAction::startWithTarget(pTarget);
	//设置累和值为零并设置是第一次进行动画时间流逝计算。
    m_elapsed = 0.0f;
    m_bFirstTick = true;
}
//产生一个反向播放的动画。
CCActionInterval* CCActionInterval::reverse(void)
{
    CCAssert(false, "CCIntervalAction: reverse not implemented.");
    return NULL;
}

CCSequence:

//动画序列:动画序列是将多个动画串联起来,使它们能够按照顺序一个一个的播放,这个类是一个很有用的类。
class CC_DLL CCSequence : public CCActionInterval
{
public:
	//析构
    ~CCSequence(void);

    //初始化,参数为两个动画。初始化完成后会将两个动画保存在数组中。
    bool initWithTwoActions(CCFiniteTimeAction *pActionOne, CCFiniteTimeAction *pActionTwo);
	//重载基类函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void stop(void);
    virtual void update(float t);
    virtual CCActionInterval* reverse(void);

public:
    //静态函数:创建一个串联多个动画的动画序列。参数使用…来表示参数个数根据实际填写的个数为准。
    CC_DEPRECATED_ATTRIBUTE static CCFiniteTimeAction* actions(CCFiniteTimeAction *pAction1, ...);
    //同上,只是参数为一个指针数组,数组里存放了要串联在一起的所有动画,即动画个数由数组大小决定。
    CC_DEPRECATED_ATTRIBUTE static CCFiniteTimeAction* actionWithArray(CCArray *arrayOfActions);
    //静态函数:将两个时间动画串联成一个动画序列,其内部调用createWithTwoActions来实现。
    CC_DEPRECATED_ATTRIBUTE static CCSequence* actionOneTwo(CCFiniteTimeAction *pActionOne, CCFiniteTimeAction *pActionTwo);

    //静态函数:将两个时间动画串联成一个动画序列。
    static CCFiniteTimeAction* create(CCFiniteTimeAction *pAction1, ...);
    //同上。只是参数为一个指针数组,数组里存放了要串联在一起的所有动画,即动画个数由数组大小决定。
    static CCFiniteTimeAction* create(CCArray *arrayOfActions);
    //静态函数:将两个时间动画串联成一个动画序列。
    static CCSequence* createWithTwoActions(CCFiniteTimeAction *pActionOne, CCFiniteTimeAction *pActionTwo);

protected:
	//存放串联在一起的两个动画的数组。
    CCFiniteTimeAction *m_pActions[2];
	//第一个串联动画时长占总时长的比率,代表了两个动画的分界进度。
    float m_split;
	//保存上一帧正在播放的动画在串联动画数组中的索引,每帧更新为当前帧正在播放的的动画在串联动画数组中的索引。
    int m_last;
};

具体实现:

//静态函数:将两个时间动画串联成一个动画序列,其内部调用createWithTwoActions来实现。
CCSequence* CCSequence::actionOneTwo(CCFiniteTimeAction *pActionOne, CCFiniteTimeAction *pActionTwo)
{
    return CCSequence::createWithTwoActions(pActionOne, pActionTwo);
}
//静态函数:将两个时间动画串联成一个动画序列。
CCSequence* CCSequence::createWithTwoActions(CCFiniteTimeAction *pActionOne, CCFiniteTimeAction *pActionTwo)
{
	//使用new创建一个CCSequence实例对象。
    CCSequence *pSequence = new CCSequence();
    //对其调用初始化函数。
	pSequence->initWithTwoActions(pActionOne, pActionTwo);
	//将其交由内存管理器进行释放。
    pSequence->autorelease();
	//返回新创建的动画序列。
    return pSequence;
}
//静态函数:创建一个串联多个动画的动画序列。参数使用…来表示参数个数根据实际填写的个数为准。
CCFiniteTimeAction* CCSequence::actions(CCFiniteTimeAction *pAction1, ...)
{
	//建立一个参数列表
	va_list params;
	//将所有的CCFiniteTimeAction指针参数放入列表中、
    va_start(params, pAction1);
	//定义临时CCFiniteTimeAction指针变量用于代表参数列表中当前项和上一项。
	//当前项
	CCFiniteTimeAction *pNow;
	//上一项,初始化为pAction1。
    CCFiniteTimeAction *pPrev = pAction1;
	//利用while循环来取得参数列表中的所有参数。
    while (pAction1)
	{	
		 //取得参数队列当前游标指向的参数。每调用一次,会将队列中头部的移除,下一个自动成为头部。
        pNow = va_arg(params, CCFiniteTimeAction*);
		 //如果有值
        if (pNow)
        {
			 //将上一项与当前项串联成一个序列动画存入上一项,这个处理就不断的把所有的序列动画串联在一起了。其关键点是理解序列本身也是动画。
            pPrev = CCSequence::createWithTwoActions(pPrev, pNow);
        }
        else
        {
			 //如果无值,中断退出循环。
            break;
        }
    }
	//关闭参数表。
	va_end(params);
	//将最终串联在一起的动画返回。
    return pPrev;
}
//同上,只是函数名不同。
CCFiniteTimeAction* CCSequence::create(CCFiniteTimeAction *pAction1, ...)
{
    va_list params;
    va_start(params, pAction1);

    CCFiniteTimeAction *pNow;
    CCFiniteTimeAction *pPrev = pAction1;

    while (pAction1)
    {
        pNow = va_arg(params, CCFiniteTimeAction*);
        if (pNow)
        {
            pPrev = createWithTwoActions(pPrev, pNow);
        }
        else
        {
            break;
        }
    }

    va_end(params);
    return pPrev;
}
//同上,只是参数是一个指针数组,数组中存放的是所有要串联在一起的动画,内部调用create实现。
CCFiniteTimeAction* CCSequence::actionWithArray(CCArray* arrayOfActions)
{
    return CCSequence::create(arrayOfActions);
}
//同上,只是参数是一个指针数组,数组中存放的是所有要串联在一起的动画。
CCFiniteTimeAction* CCSequence::create(CCArray* arrayOfActions)
{
	//先取得数组中第一项做为上一项。
    CCFiniteTimeAction* prev = (CCFiniteTimeAction*)arrayOfActions->objectAtIndex(0);
	//然后从第二基开始遍历。分别取上一项与当前项来产生充列动画,并将产生的新的序列动画存入上一项中。
    for (unsigned int i = 1; i < arrayOfActions->count(); ++i)
    {
        prev = createWithTwoActions(prev, (CCFiniteTimeAction*)arrayOfActions->objectAtIndex(i));
    }
	//返回上一项。
    return prev;
}
//这个函数才是真正的串联实现。
bool CCSequence::initWithTwoActions(CCFiniteTimeAction *pActionOne, CCFiniteTimeAction *pActionTwo)
{
	//有效性判断
    CCAssert(pActionOne != NULL, "");
    CCAssert(pActionTwo != NULL, "");
	//取得两个动画的动画时长累加后做为当前序列动画的总时长。
    float d = pActionOne->getDuration() + pActionTwo->getDuration();
    CCActionInterval::initWithDuration(d);
	//将代表上一个动画的指针存入串联数组项的第一项中。
	m_pActions[0] = pActionOne;
	//因为整个过程占用此动画项,所以对其引用计数器加一。
    pActionOne->retain();
	//将代表当前动画的指针存入串联数组项的第二项中。
	m_pActions[1] = pActionTwo;
	//因为整个过程占用此动画项,所以对其引用计数器加一。
    pActionTwo->retain();
	//返回true.
    return true;
}
//返回当前动画的拷贝,因讲的太多,故这里略过。
CCObject* CCSequence::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCSequence* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
	{
	    pCopy = (CCSequence*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCSequence();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

//使用当前串联的两个动画的拷贝做为新生成的序列动画的串联动画。     pCopy->initWithTwoActions((CCFiniteTimeAction*)(m_pActions[0]->copy()->autorelease()), 
                (CCFiniteTimeAction*)(m_pActions[1]->copy()->autorelease()));

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//析构,做释放处理,对两个占用的动画的引有计数做减一操作。
CCSequence::~CCSequence(void)
{
    CC_SAFE_RELEASE(m_pActions[0]);
    CC_SAFE_RELEASE(m_pActions[1]);
}
//设置表演当前序列动画的演员。
void CCSequence::startWithTarget(CCNode *pTarget)
{
	CCActionInterval::startWithTarget(pTarget);
	//计算两个串联动画的分界进度。
	m_split = m_pActions[0]->getDuration() / m_fDuration;
	//将m_last先置为-1。
    m_last = -1; 
}
//停止当前序列动画。
void CCSequence::stop(void)
{
	// 如果m_last值有效,则停止对应的串联动画。
    if( m_last != - 1)
    {
        m_pActions[m_last]->stop();
    }
	//调用基类的stop停止序列动画。
    CCActionInterval::stop();
}
//动画的播放计算处理
void CCSequence::update(float t)
{
	//定义整型变量found来代表播放到第几个动画。
	int found = 0;
	//定义浮点变量new_t来代表正在播放的串联动画的进度。
    float new_t = 0.0f;
	//如果播放进度值小于分界进度值,即还在播放串联的第一个动画。
    if( t < m_split ) {
        // 将变量found设置0。
        found = 0;
		 //这里是计算第一个串联动画的播放进度。
        if( m_split != 0 )
            new_t = t / m_split;
        else
            new_t = 1;

    } else {
        //否则,设置found变量为1,代表播放第二个串联动画。
        found = 1;
		 //这时是计算第二个串联动画的播放进度。
        if ( m_split == 1 )
            new_t = 1;
        else
            new_t = (t-m_split) / (1 - m_split );
    }
	//如果在播放第二个串联动画。
    if ( found==1 ) {
		//如果m_last为-1 ? 这个该怎么理解呢?为什么m_last会为-1,那不是说t >= m_split ?在什么情况下会发生这种情况呢?只有当m_split为0的情况!在startWithTarget函数中我们看下m_split的计算公式在什么情况下m_split为0呢?只有串联动画1的时长为0!那什么时候动画的时长为0呢?好吧,有许多动画时长为0,立即播放一次搞定!比如CCCallFuncN!的确,有不少这种东西被放在动画序列里做为播放完一个时间动画后调用某个函数处理的功能。
	    if( m_last == -1 ) {
            // 那就立即播放这个动画吧!播放完就停止。            				  m_pActions[0]->startWithTarget(m_pTarget);
            m_pActions[0]->update(1.0f);
            m_pActions[0]->stop();
        }
        else if( m_last == 0 )
        {   //如果上一帧播放的是第一个动画。即首次播放第二个串联动画。
            //设置第一个串联动画播放到尾部并停止。
            m_pActions[0]->update(1.0f);
            m_pActions[0]->stop();
        }
    }

    // 如果当前正在播放的动画已播放结束,直接返回。
    if( found == m_last && m_pActions[found]->isDone() )
    {
        return;
    }

    // 如果found与m_last不同,代表当前帧要播放的动画与上一帧播放的动画不同。即要播新动画。
    if( found != m_last )
    {
        m_pActions[found]->startWithTarget(m_pTarget);
    }
	//将新动画的进度传给新动画调用的update函数来实现播放。
	m_pActions[found]->update(new_t);
	//将found值赋值给m_last。
    m_last = found;
}
//创建一个序列动画的反向播放动画。
CCActionInterval* CCSequence::reverse(void)
{
	//内部通过串联两个串联动画的反向动画来实现。注意的是,这时候串联的第一个动画应该为原串联动画的第二个动画的反向动画,第二个动画应该为原串联动画的第一个动画的反向动画。好好理解一下。
    return CCSequence::createWithTwoActions(m_pActions[1]->reverse(), m_pActions[0]->reverse());
}

CCRePeat:

//重复动画。这个动画可以实现对另一个时间动画设置循环播放指定次数。
class CC_DLL CCRepeat : public CCActionInterval
{
public:
    ~CCRepeat(void);
	//初始化,传入一个要进行循环播放的时间动画,次数取1~pow(2,30)之间的整数。
    bool initWithAction(CCFiniteTimeAction *pAction, unsigned int times);

	//重载基类函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void stop(void);
    virtual void update(float dt);
    virtual bool isDone(void);
    virtual CCActionInterval* reverse(void);
	//设置要进行循环的动画。
    inline void setInnerAction(CCFiniteTimeAction *pAction)
    {
		 //释放原来设置的动画,更新为参数指定的动画。
        if (m_pInnerAction != pAction)
        {
            CC_SAFE_RETAIN(pAction);
            CC_SAFE_RELEASE(m_pInnerAction);
            m_pInnerAction = pAction;
        }
    }
	//取得要进行循环的动画。
    inline CCFiniteTimeAction* getInnerAction()
    {
        return m_pInnerAction;
    }

public:
    //静态函数:创建一个控制循环播放的动画。内部调用create来实现。参1为要进行循环播放的时间动画,参2为循环次数。
    CC_DEPRECATED_ATTRIBUTE static CCRepeat* actionWithAction(CCFiniteTimeAction *pAction, unsigned int times);

   //静态函数:创建一个控制循环播放的动画。
    static CCRepeat* create(CCFiniteTimeAction *pAction, unsigned int times);
protected:
	//当前循环次数
    unsigned int m_uTimes;
	//总的循环次数
    unsigned int m_uTotal;
	//
    float m_fNextDt;
	//是否有控制循环播放的动画
    bool m_bActionInstant;
    //所控制的要进行循环播放的动画
    CCFiniteTimeAction *m_pInnerAction;
};

具体实现:
//静态函数:创建一个控制循环播放的动画。内部调用create来实现。
CCRepeat* CCRepeat::actionWithAction(CCFiniteTimeAction *pAction, unsigned int times)
{
    return CCRepeat::create(pAction, times);
}
//静态函数:创建一个控制循环播放的动画。
CCRepeat* CCRepeat::create(CCFiniteTimeAction *pAction, unsigned int times)
{
	//先new出一个CCRepeat。
	CCRepeat* pRepeat = new CCRepeat();
	//初始化
	pRepeat->initWithAction(pAction, times);
	//设置交由内存管理器进行释放
    pRepeat->autorelease();
	//返回创建的CCRepeat实例指针。
    return pRepeat;
}
//初始化函数。
bool CCRepeat::initWithAction(CCFiniteTimeAction *pAction, unsigned int times)
{
	//总时长等于一次动画的时长乘以循环次数。
    float d = pAction->getDuration() * times;
	//设置动画的时长
    if (CCActionInterval::initWithDuration(d))
	{
	//保存动画循环次数。
        m_uTimes = times;
        //保存控制循环播放的内部动画。
        m_pInnerAction = pAction;
	 //因为会占用此动画,所以对其引用计数器值加一。
        pAction->retain();
	 //这里用dynamic_cast来判断pAction是否是CCActionInstant*的子类,其实是限制要求pAction要是一个有时长的时间动画而不是一个调用一次立即播放完成的动画。
        m_bActionInstant = dynamic_cast<CCActionInstant*>(pAction) ? true : false;
	//如果属于调用一次立即播放完成的动画,就不能循环。
        if (m_bActionInstant) 
        {
            m_uTimes -=1;
        }
	 //总时长变量初始化置0。
        m_uTotal = 0;

        return true;
    }

    return false;
}
//略过,参见前面。
CCObject* CCRepeat::copyWithZone(CCZone *pZone)
{
    
    CCZone* pNewZone = NULL;
    CCRepeat* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCRepeat*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCRepeat();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

	pCopy->initWithAction((CCFiniteTimeAction*)(m_pInnerAction->copy()->autorelease()), m_uTimes);

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//析构
CCRepeat::~CCRepeat(void)
{
	//不再占用内部控制循环的动画,在此对其引用计数器减1。
    CC_SAFE_RELEASE(m_pInnerAction);
}
//设置演示当前控制动画的演员。
void CCRepeat::startWithTarget(CCNode *pTarget)
{
    //总时长变量初始化置0。
	m_uTotal = 0;
	//计算下一次循环切换时刻的动画进度。
	m_fNextDt = m_pInnerAction->getDuration()/m_fDuration;
	//调用基类的初始化处理。
	CCActionInterval::startWithTarget(pTarget);
	//对所控制的动画进行初始化。
    m_pInnerAction->startWithTarget(pTarget);
}
//停止动画。
void CCRepeat::stop(void)
{
	//停止所控制的动画
	m_pInnerAction->stop();
	//停止当前动画。
    CCActionInterval::stop();
}

//更新动画的播放,参数为时间进度。
void CCRepeat::update(float dt)
{
	//如果已经到了该进行循环切换的时候!
    if (dt >= m_fNextDt)
	{
	//如果当前时间进度超过了循环切换的进度位置并且循环次数仍不够,则将循环的动画播放到结尾,并停止动画后重新初始化。
        while (dt > m_fNextDt && m_uTotal < m_uTimes)
        {
	 //将循环的动画播放到结尾。
            m_pInnerAction->update(1.0f);
	 //循环次数加1
            m_uTotal++;
	 //停止动画后重新初始化。
            m_pInnerAction->stop();
            m_pInnerAction->startWithTarget(m_pTarget);
	 //计算下一次要切换时的进度位置。
            m_fNextDt += m_pInnerAction->getDuration()/m_fDuration;
        }

        // 下面这一句是为了防止上面一句m_fNextDt因为浮点不精确可能出现的意外情况:即最后一次循环时可能未及时循环次数加1以致后面不能够停止。
        if(dt >= 1.0f && m_uTotal < m_uTimes) 
        {
            m_uTotal++;
        }

	 // 限制要求pAction要是一个有时长的时间动画而不是一个调用一次立即播放完成的动画。

        if (!m_bActionInstant)
        {
	  //如果是最后一次循环结束,停止动画。
            if (m_uTotal == m_uTimes)
            {
                m_pInnerAction->update(1);
                m_pInnerAction->stop();
            }
            else
            {
               //否则更新动画。进度值等于当前时刻总进度dt减去上一次切换时刻的总进度( m_fNextDt - m_pInnerAction->getDuration()/m_fDuration),因为当前时刻不会正好等于切换时刻,总会有点余值,这个要计算到新循环中才精确。

               m_pInnerAction->update(dt - (m_fNextDt - m_pInnerAction->getDuration()/m_fDuration));
            }
        }
    }
    else
	{
	 //未到循环切换时,计算进度,这里计算进度使用了浮点取模函数fmodf,咱们“笨人”一般应该是写成“(dt - m_fNextDt)/(m_pInnerAction->getDuration()/m_fDuration) ”,看看人家咋写的!
	
        m_pInnerAction->update(fmodf(dt * m_uTimes,1.0f));
    }
}
//当前循环是否结束
bool CCRepeat::isDone(void)
{
	//通过循环次数来判断。
    return m_uTotal == m_uTimes;
}
//创建一个反向播放的循环动画。
CCActionInterval* CCRepeat::reverse(void)
{
    return CCRepeat::create(m_pInnerAction->reverse(), m_uTimes);
}

CCRepeatForver:

//无限循环动画。
class CC_DLL CCRepeatForever : public CCActionInterval
{
public:
	//构造与析构。
    CCRepeatForever()
        : m_pInnerAction(NULL)
    {}
    virtual ~CCRepeatForever();

    //初始化及一些基类重载函数。
    bool initWithAction(CCActionInterval *pAction);
    virtual CCObject* copyWithZone(CCZone *pZone);
    virtual void startWithTarget(CCNode* pTarget);
    virtual void step(float dt);
    virtual bool isDone(void);
    virtual CCActionInterval* reverse(void);
	//设置要设置为无限循环播放的动画。
    inline void setInnerAction(CCActionInterval *pAction)
    {
	//删除旧动画,更换新动画。
        if (m_pInnerAction != pAction)
        {
            CC_SAFE_RELEASE(m_pInnerAction);
            m_pInnerAction = pAction;
            CC_SAFE_RETAIN(m_pInnerAction);
        }
    }
	//取得设置为无限循环播放的动画。
    inline CCActionInterval* getInnerAction()
    {
        return m_pInnerAction;
    }

public:
    //创建一个无限循环播放的动画,依然是调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCRepeatForever* actionWithAction(CCActionInterval *pAction);
    //创建一个无限循环播放的动画。
    static CCRepeatForever* create(CCActionInterval *pAction);
protected:
    //设置为无限循环播放的动画。
    CCActionInterval *m_pInnerAction;
};
具体实现:
//析构
CCRepeatForever::~CCRepeatForever()
{
    CC_SAFE_RELEASE(m_pInnerAction);
}
//创建一个无限循环播放的动画,依然是调用create实现。
CCRepeatForever *CCRepeatForever::actionWithAction(CCActionInterval *pAction)
{
    return CCRepeatForever::create(pAction);
}
//创建一个无限循环播放的动画。
CCRepeatForever *CCRepeatForever::create(CCActionInterval *pAction)
{
	//使用new创建一个无限循环动画。
	CCRepeatForever *pRet = new CCRepeatForever();
	//对其进行初始化
    if (pRet && pRet->initWithAction(pAction))
	{
	//成功则将其设置为交由内存管理器释放。
        pRet->autorelease();
        return pRet;
	}
	//如果出现任何失败,释放返回
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//初始化。
bool CCRepeatForever::initWithAction(CCActionInterval *pAction)
{
	//参数有效性判断
	CCAssert(pAction != NULL, "");
	//占用pAction,故对其引用计数器加1
	pAction->retain();
	//记录所控制的动画。
    m_pInnerAction = pAction;
    return true;
}
//产生一个拷贝,讲的头痛,略过。
CCObject* CCRepeatForever::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCRepeatForever* pRet = NULL;
    if(pZone && pZone->m_pCopyObject)     {
        pRet = (CCRepeatForever*)(pZone->m_pCopyObject);
    }
    else
    {
        pRet = new CCRepeatForever();
        pZone = pNewZone = new CCZone(pRet);
    }
    CCActionInterval::copyWithZone(pZone);    pRet->initWithAction((CCActionInterval*)(m_pInnerAction->copy()->autorelease())); 
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}
//设置演示当前动画的演员。
void CCRepeatForever::startWithTarget(CCNode* pTarget)
{
    CCActionInterval::startWithTarget(pTarget);
    m_pInnerAction->startWithTarget(pTarget);
}
//计算播放进度。
void CCRepeatForever::step(float dt)
{
	//调用控制的动画的相应函数。
	m_pInnerAction->step(dt);
	//如果此动画播放结束。重新初始化后给出新一轮循环的进度。
    if (m_pInnerAction->isDone())
	{
	 //动画播放结束后,其当前时间长度大于动画时长,这里计算超了多少,做为下次循环的起始时间点,这样可以保证时间精确。
        float diff = m_pInnerAction->getElapsed() - m_pInnerAction->getDuration();
        m_pInnerAction->startWithTarget(m_pTarget);
        // 为什么要多调一次step(0.0f),因为在上一句初始化时其内部变量代表第一次播放的变量m_bFirstTick为true,第一次调用step时其参数会置0。只有第二次调用时设的值才管用。
        m_pInnerAction->step(0.0f);
        m_pInnerAction->step(diff);
    }
}
//当前无限循环动画是否播放结束。那当然不可能结束,要不怎么叫无限循环动画呢?
bool CCRepeatForever::isDone()
{
    return false;
}
//返回一个反向播放的无限循环动画。
CCActionInterval *CCRepeatForever::reverse()
{
    return (CCActionInterval*)(CCRepeatForever::create(m_pInnerAction->reverse()));
}

CCSpawn:

//组合动画。与序列动画的不同为序列动画是一个一个顺序播放,组合动画是几个动画一起播放。
class CC_DLL CCSpawn : public CCActionInterval
{
public:
	//析构
    ~CCSpawn(void);

    //初始化当前组合动画,将两个动画进行组合。
    bool initWithTwoActions(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2);
	//重载基类相关函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void stop(void);
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);

public:
    //创建组合动画,内部调用
    CC_DEPRECATED_ATTRIBUTE static CCFiniteTimeAction* actions(CCFiniteTimeAction *pAction1, ...);

    //创建组合动画。
    CC_DEPRECATED_ATTRIBUTE static CCFiniteTimeAction* actionWithArray(CCArray *arrayOfActions);

    //将两个动画进行组合。
    CC_DEPRECATED_ATTRIBUTE static CCSpawn* actionOneTwo(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2);

    //将多个动画进行组合。
    static CCFiniteTimeAction* create(CCFiniteTimeAction *pAction1, ...);

    //创建组合动画。参数为动画数组。
    static CCFiniteTimeAction* create(CCArray *arrayOfActions);

    //创建组合动画。参数为两个动画。
    static CCSpawn* createWithTwoActions(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2);

protected:
	//要组合的第一个动画。
	CCFiniteTimeAction *m_pOne;
	//要组合的第二个动画。
    CCFiniteTimeAction *m_pTwo;
};

具体实现:
//创建组合动画,内部调用
CCFiniteTimeAction* CCSpawn::actions(CCFiniteTimeAction *pAction1, ...)
{
	//定义参数列表
	va_list params;
	//将以pAction1为参数头的所有参数放到列表中。
    va_start(params, pAction1);
	//定义临时变量来循环取出相应的参数。
    CCFiniteTimeAction *pNow;
    CCFiniteTimeAction *pPrev = pAction1;
	//如果有参数,利用while来循环取出所有参数。
    while (pAction1)
	{
	//取出新参数
        pNow = va_arg(params, CCFiniteTimeAction*);
        if (pNow)
        {
	  //利用pPrev和pNow来创建组合动画,最后生成的组合动画返回给pPrev继续组合。
            pPrev = CCSpawn::createWithTwoActions(pPrev, pNow);
        }
        else
        {
            break;
        }
    }
	//取参操作结束。
    va_end(params);
    return pPrev;
}
//同上,只是名称不同。
CCFiniteTimeAction* CCSpawn::create(CCFiniteTimeAction *pAction1, ...)
{
    va_list params;
    va_start(params, pAction1);

    CCFiniteTimeAction *pNow;
    CCFiniteTimeAction *pPrev = pAction1;

    while (pAction1)
    {
        pNow = va_arg(params, CCFiniteTimeAction*);
        if (pNow)
        {
            pPrev = createWithTwoActions(pPrev, pNow);
        }
        else
        {
            break;
        }
    }

    va_end(params);
    return pPrev;
}
//同上,只是参数形式变为动画数组。
CCFiniteTimeAction* CCSpawn::actionWithArray(CCArray *arrayOfActions)
{
    return CCSpawn::create(arrayOfActions);
}
//同上。名称不同。
CCFiniteTimeAction* CCSpawn::create(CCArray *arrayOfActions)
{
	//先取数组第一个放入临时CCFiniteTimeAction* 变量。
    CCFiniteTimeAction* prev = (CCFiniteTimeAction*)arrayOfActions->objectAtIndex(0);
	//从第二个起遍历数组进行组合,生成的动画存入临时CCFiniteTimeAction* 变量供下一次循环操作。
    for (unsigned int i = 1; i < arrayOfActions->count(); ++i)
    {
        prev = createWithTwoActions(prev, (CCFiniteTimeAction*)arrayOfActions->objectAtIndex(i));
    }

    return prev;
}
//组合两个动画,内部调用createWithTwoActions。
CCSpawn* CCSpawn::actionOneTwo(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2)
{
    return CCSpawn::createWithTwoActions(pAction1, pAction2);
}
//组合两个动画。
CCSpawn* CCSpawn::createWithTwoActions(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2)
{
	//使用new创建一个CCSpawn。
	CCSpawn *pSpawn = new CCSpawn();
	//初始化
    pSpawn->initWithTwoActions(pAction1, pAction2);	//交由内存管理器进行释放。
    pSpawn->autorelease();
	//返回新生成的CCSpawn实例对象指针。
    return pSpawn;
}
//初始化。
bool CCSpawn:: initWithTwoActions(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2)
{
	//有效性判断
    CCAssert(pAction1 != NULL, "");
    CCAssert(pAction2 != NULL, "");
	//临时变量bRet,先置false
    bool bRet = false;
	//将两个动画的时长取出存入临时变量d1,d2。
    float d1 = pAction1->getDuration();
    float d2 = pAction2->getDuration();
	//取出两个时长的最大值做为当前动画的时长。
    if (CCActionInterval::initWithDuration(MAX(d1, d2)))
	{
	//将两个动画存入成员动画指针变量。
        m_pOne = pAction1;
        m_pTwo = pAction2;
	//如果第一个动画时长大于第二个动画时长。
        if (d1 > d2)
        {
	 //第二个动画结束后保持不动。
            m_pTwo = CCSequence::createWithTwoActions(pAction2, CCDelayTime::create(d1 - d2));
        } 
        else if (d1 < d2)
        {
            //反之,第一个动画结束后保持不动。
            m_pOne = CCSequence::createWithTwoActions(pAction1, CCDelayTime::create(d2 - d1));
        }
        //占用两个动画,所以引用计数器都加一。
        m_pOne->retain();
        m_pTwo->retain();
	 //返回初始化成功。
        bRet = true;
    }

    
    return bRet;
}
//除了略过还是略过,头疼ing...
CCObject* CCSpawn::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCSpawn* pCopy = NULL;

    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCSpawn*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCSpawn();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithTwoActions((CCFiniteTimeAction*)(m_pOne->copy()->autorelease()), 
                    (CCFiniteTimeAction*)(m_pTwo->copy()->autorelease()));

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//析构
CCSpawn::~CCSpawn(void)
{
    CC_SAFE_RELEASE(m_pOne);
    CC_SAFE_RELEASE(m_pTwo);
}
//设置演员。
void CCSpawn::startWithTarget(CCNode *pTarget)
{
    CCActionInterval::startWithTarget(pTarget);
    m_pOne->startWithTarget(pTarget);
    m_pTwo->startWithTarget(pTarget);
}
//停止动画
void CCSpawn::stop(void)
{
    m_pOne->stop();
    m_pTwo->stop();
    CCActionInterval::stop();
}
//更新当前动画的播放进度。
void CCSpawn::update(float time)
{
    //看,真的是分别对两个动画做了update调用。
    if (m_pOne)
    {
        m_pOne->update(time);
    }
    if (m_pTwo)
    {
        m_pTwo->update(time);
    }
}
//创建反向播放的动画。
CCActionInterval* CCSpawn::reverse(void)
{
    return CCSpawn::createWithTwoActions(m_pOne->reverse(), m_pTwo->reverse());
}

CCRotateTo:

//旋转动画:旋转到某个角度。
class CC_DLL CCRotateTo : public CCActionInterval
{
public:
    //初始化
    bool initWithDuration(float duration, float fDeltaAngle);
	//重载基类函数
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);

public:
    //创建一个旋转动画,内部调用create函数。参数一为动画时长,参数二为旋转到的目标角度。
    CC_DEPRECATED_ATTRIBUTE static CCRotateTo* actionWithDuration(float duration, float fDeltaAngle);
	//创建一个旋转动画。
    static CCRotateTo* create(float duration, float fDeltaAngle);
protected:
	//目标角度
    float m_fDstAngle;
	//起始角度
    float m_fStartAngle;
	//起始角度与目标角度之差。
    float m_fDiffAngle;
};

具体实现:
//创建一个旋转动画,内部调用create函数。
CCRotateTo* CCRotateTo::actionWithDuration(float duration, float fDeltaAngle)
{
    return CCRotateTo::create(duration, fDeltaAngle);
}
	
//创建一个旋转动画。
CCRotateTo* CCRotateTo::create(float duration, float fDeltaAngle)
{
	//使用new创建一个CCRotateTo对象。
	CCRotateTo* pRotateTo = new CCRotateTo();
	//对其初始化。
	pRotateTo->initWithDuration(duration, fDeltaAngle);
	//交由内顾虑管理器进行释放。
    pRotateTo->autorelease();
	//返回实例指针
    return pRotateTo;
}
//初始化。
bool CCRotateTo::initWithDuration(float duration, float fDeltaAngle)
{
	//先调用基类初始化函数。
    if (CCActionInterval::initWithDuration(duration))
	{
		//保存目标角度。
        m_fDstAngle = fDeltaAngle;
        return true;
    }

    return false;
}
//略
CCObject* CCRotateTo::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCRotateTo* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject)
    {
        //in case of being called at sub class
        pCopy = (CCRotateTo*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCRotateTo();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_fDstAngle);

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前动画的演员
void CCRotateTo::startWithTarget(CCNode *pTarget)
{
	//调用基类的设置函数。
    CCActionInterval::startWithTarget(pTarget);
	//取得演员的当前旋转角度做为起始角度。
    m_fStartAngle = pTarget->getRotation();
	//如果演员当前有旋转角度,则把角度值限制在相应范围内。
    if (m_fStartAngle > 0)
    {	//如果正值限定在0~360之间。
        m_fStartAngle = fmodf(m_fStartAngle, 360.0f);
    }
    else
    {	//如果负值限定在-360~0之间。
        m_fStartAngle = fmodf(m_fStartAngle, -360.0f);
    }
	//计算出目标角度与起始角度之差做为要旋转的角度。
	m_fDiffAngle = m_fDstAngle - m_fStartAngle;
	//如果大于180度。则减去360度,为啥呢?因为“地球是圆的!”,正向转,反向转,你都能到美国,那当然要捡最近的路径转动。这个处理就是要达到转动最少的角度达到同样的目的,不过话说回来,貌似旋转动画应该设定旋转方向会更好。
    if (m_fDiffAngle > 180)
    {
        m_fDiffAngle -= 360;
    }
	//如果小于-180度。则加上360度。
    if (m_fDiffAngle < -180)
    {
        m_fDiffAngle += 360;
    }
}
//更新动画播放。
void CCRotateTo::update(float time)
{
	//设置演员的旋转状态。
    if (m_pTarget)
    {	//对要进行旋转的角度m_fDiffAngle进行时间进度插值做为当前时刻旋转的角度。
        m_pTarget->setRotation(m_fStartAngle + m_fDiffAngle * time);
    }}


CCRotateBy:

//另一个旋转动画:从当前旋转角度再继续旋转多少。它与上个动画的不同是一个是追求结果,一个是追求过程。
class CC_DLL CCRotateBy : public CCActionInterval
{
public:
    //初始化。
    bool initWithDuration(float duration, float fDeltaAngle);
	//重载基类函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);

public:
    //创建旋转动画,内部调用create。
    CC_DEPRECATED_ATTRIBUTE static CCRotateBy* actionWithDuration(float duration, float fDeltaAngle);
    //创建旋转动画
    static CCRotateBy* create(float duration, float fDeltaAngle);
protected:
	//要旋转的角度。
    float m_fAngle;
	//起始角度。
    float m_fStartAngle;
};
具体实现:
//创建旋转动画,内部调用create。
CCRotateBy* CCRotateBy::actionWithDuration(float duration, float fDeltaAngle)
{
    return CCRotateBy::create(duration, fDeltaAngle);
}
//创建旋转动画。
CCRotateBy* CCRotateBy::create(float duration, float fDeltaAngle)
{
	//使用new创建一个CCRotateBy。
	CCRotateBy *pRotateBy = new CCRotateBy();
	//对其初始化。
	pRotateBy->initWithDuration(duration, fDeltaAngle);
	//交由内存管理器进行释放。
    pRotateBy->autorelease();
	//返回实例指针。
    return pRotateBy;
}
//初始化。
bool CCRotateBy::initWithDuration(float duration, float fDeltaAngle)
{
	//先调用基类的初始化函数。
    if (CCActionInterval::initWithDuration(duration))
	{
		//将要旋转的角度保存到成员变量m_fAngle。
        m_fAngle = fDeltaAngle;
        return true;
    }

    return false;
}
//略。
CCObject* CCRotateBy::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCRotateBy* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCRotateBy*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCRotateBy();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_fAngle);

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前旋转动画的演员。
void CCRotateBy::startWithTarget(CCNode *pTarget)
{
	//调用基类的相应函数。
	CCActionInterval::startWithTarget(pTarget);
	//先记录一下演员的当前旋转状态。
    m_fStartAngle = pTarget->getRotation();
}
//更新动画播放
void CCRotateBy::update(float time)
{
    //原理同上一个动画,对要进行旋转的角度m_fAngle进行时间进度插值做为当前时刻演员的旋转角度。
    if (m_pTarget)
    {
        m_pTarget->setRotation(m_fStartAngle + m_fAngle * time);
    }
}
//创建一个反向播放的旋转动画。
CCActionInterval* CCRotateBy::reverse(void)
{
	//其实就是向反方向旋转相同角度大小的旋转动画。
    return CCRotateBy::create(m_fDuration, -m_fAngle);
}

CCMoveTo:

//位移动画:移动到某个位置。
class CC_DLL CCMoveTo : public CCActionInterval
{
public:
    //初始化。
    bool initWithDuration(float duration, const CCPoint& position);
	//重载基类的相关函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);

public:
    //创建一个移动动画,内部调用create实现。参数一为动画时长,参数二为要移动到的目标点。
    CC_DEPRECATED_ATTRIBUTE static CCMoveTo* actionWithDuration(float duration, const CCPoint& position);
    //创建一个移动动画。
    static CCMoveTo* create(float duration, const CCPoint& position);
protected:
	//目标点位置
    CCPoint m_endPosition;
	//当前起始位置
    CCPoint m_startPosition;
	//移动的偏移。
    CCPoint m_delta;
};
具体实现:
//创建一个移动动画,内部调用create实现。
CCMoveTo* CCMoveTo::actionWithDuration(float duration, const CCPoint& position)
{
    return CCMoveTo::create(duration, position);
}
//创建一个移动动画。
CCMoveTo* CCMoveTo::create(float duration, const CCPoint& position)
{
    CCMoveTo *pMoveTo = new CCMoveTo();
    pMoveTo->initWithDuration(duration, position);
    pMoveTo->autorelease();

    return pMoveTo;
}
//初始化函数。
bool CCMoveTo::initWithDuration(float duration, const CCPoint& position)
{	//先调用基类的初始化函数。
    if (CCActionInterval::initWithDuration(duration))
    {	//保存目标位置。
        m_endPosition = position;
        return true;
    }

    return false;
}
//略,你懂的,你珍惜我的脑细胞,对么?
CCObject* CCMoveTo::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCMoveTo* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCMoveTo*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCMoveTo();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_endPosition);

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示位移动画的演员。
void CCMoveTo::startWithTarget(CCNode *pTarget)
{
	//调用其类的相应函数。
	CCActionInterval::startWithTarget(pTarget);
	//记录演员的当前起始位置。
	m_startPosition = pTarget->getPosition();
	//保存起始位置和目标位置之差。
    m_delta = ccpSub(m_endPosition, m_startPosition);
}
//更新动画的播放。
void CCMoveTo::update(float time)
{
	//对位置偏移进行进度插值,再加上起始位置,即是当前时刻位置。
    if (m_pTarget)
    {
        m_pTarget->setPosition(ccp(m_startPosition.x + m_delta.x * time,
            m_startPosition.y + m_delta.y * time));
    }
}

CCMoveBy:

//位移动画:是上一个位移动画的子类。同上一个位移动画的不同是这个动画的目的是从当前位置移动多少偏移,是追求过程。
class CC_DLL CCMoveBy : public CCMoveTo
{
public:
    //初始化。
    bool initWithDuration(float duration, const CCPoint& position);
	//重载基类的函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual CCActionInterval* reverse(void);

public:
    //创建一个位移动画,内部调用create实现,参数一为动画时长,参数二为要移动的偏移。
    CC_DEPRECATED_ATTRIBUTE static CCMoveBy* actionWithDuration(float duration, const CCPoint& position);
    //创建一个位移动画。
    static CCMoveBy* create(float duration, const CCPoint& position);
};
具体实现:
//创建一个位移动画,内部调用create实现。
CCMoveBy* CCMoveBy::actionWithDuration(float duration, const CCPoint& position)
{
    return CCMoveBy::create(duration, position);
}
//创建一个位移动画,讲的太多。也略。
CCMoveBy* CCMoveBy::create(float duration, const CCPoint& position)
{
    CCMoveBy *pMoveBy = new CCMoveBy();
    pMoveBy->initWithDuration(duration, position);
    pMoveBy->autorelease();

    return pMoveBy;
}
//初始化。
bool CCMoveBy::initWithDuration(float duration, const CCPoint& position)
{
	//先调用基类初始化。
    if (CCActionInterval::initWithDuration(duration))
	{
	//保存要旋转的角度。
        m_delta = position;
        return true;
    }
	//如果初始化失败返回false
    return false;
}
//产生拷贝,略。
CCObject* CCMoveBy::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCMoveBy* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCMoveBy*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCMoveBy();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCMoveTo::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_delta);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前动画的演员。
void CCMoveBy::startWithTarget(CCNode *pTarget)
{
	//因为CCMoveBy是由CCMoveTo派生,m_delta是CCMoveTo的成员变量。而下一句调用CCMoveTo的初始化会改变m_delta的值。所以这里做个临时变量保存一下再调用CCMoveTo的实始化函数。
    CCPoint dTmp = m_delta;
    CCMoveTo::startWithTarget(pTarget);
    m_delta = dTmp;
}
//产生一个反向播放的偏移动画。
CCActionInterval* CCMoveBy::reverse(void)
{
    return CCMoveBy::create(m_fDuration, ccp(-m_delta.x, -m_delta.y));
}

CCSkewTo:

//扭曲动作:这个动画是我在本节中遇到的最难解释的动画。我想了好久该怎么说,却仍然有些晦涩。扭曲动画可以理解为对精灵的两对对角分别进行X,Y方向的拉伸扭曲到目标角度的动画。
class CC_DLL CCSkewTo : public CCActionInterval
{
public:
	//构造
    CCSkewTo();
	//初始化及重载基类函数。
    virtual bool initWithDuration(float t, float sx, float sy);
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);

public:
    //创建一个扭曲动画,内部调用create实现。参一为动画时长,参二为右上角在X轴方向上的目标扭曲角度,参三为Y轴方向上的目标扭曲角度.
    CC_DEPRECATED_ATTRIBUTE static CCSkewTo* actionWithDuration(float t, float sx, float sy);

    //创建一个扭曲动画。
    static CCSkewTo* create(float t, float sx, float sy);
protected:
	
    float m_fSkewX;//子类CCSkewBy所用的变量,用于保存数据。
    float m_fSkewY; //子类CCSkewBy所用的变量,用于保存数据。
    float m_fStartSkewX;//X方向起始扭曲角度
    float m_fStartSkewY;//Y方向起始扭曲角度
    float m_fEndSkewX;//X方向目标扭曲角度
    float m_fEndSkewY;//Y方向目标扭曲角度
    float m_fDeltaX;//X方向上要扭曲多少角度
    float m_fDeltaY;//Y方向上要扭曲多少角度
};
具体实现://创建一个扭曲动画,内部调用create实现。
CCSkewTo* CCSkewTo::actionWithDuration(float t, float sx, float sy)
{
    return CCSkewTo::create(t, sx, sy);
}
//创建一个扭曲动画。
CCSkewTo* CCSkewTo::create(float t, float sx, float sy)
{
	//使用new创建一个扭曲动画对象实例。
    CCSkewTo *pSkewTo = new CCSkewTo();
    if (pSkewTo)
	{	
	 //如果成功,对其初始化。
        if (pSkewTo->initWithDuration(t, sx, sy))
        {	
	 //初始化成功,交由内存管理器进行释放。
            pSkewTo->autorelease();
        }
        else
        {
            //否则删除。
            CC_SAFE_DELETE(pSkewTo);
        }
    }
	 //返回创建的动画实例对象指针。
    return pSkewTo;
}
//初始化。参一为动画时长,参二为右上角在X轴方向上的目标扭曲角度,参三为Y轴方向上的目标扭曲角度.
bool CCSkewTo::initWithDuration(float t, float sx, float sy)
{
    bool bRet = false;
	//初始化基类
    if (CCActionInterval::initWithDuration(t))
	{
	//记录扭曲的目标角度。
        m_fEndSkewX = sx;
        m_fEndSkewY = sy;

        bRet = true;
    }

    return bRet;
}
//产生一个拷贝。
CCObject* CCSkewTo::copyWithZone(CCZone* pZone)
{
    CCZone* pNewZone = NULL;
    CCSkewTo* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        pCopy = (CCSkewTo*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCSkewTo();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_fEndSkewX, m_fEndSkewY);

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前动画的演员。
void CCSkewTo::startWithTarget(CCNode *pTarget)
{
	//初始化。
    CCActionInterval::startWithTarget(pTarget);
	//起始的X扭曲角度。
    m_fStartSkewX = pTarget->getSkewX();
	//
    if (m_fStartSkewX > 0)
    {	//如果m_fStartSkewX为正方向值,把它限制在0~180度之内。
        m_fStartSkewX = fmodf(m_fStartSkewX, 180.f);
    }
    else
    {  //如果m_fStartSkewX为负方向值,把它限制在0~180度之内。
        m_fStartSkewX = fmodf(m_fStartSkewX, -180.f);
    }
	//计算将要扭曲的X轴角度。
    m_fDeltaX = m_fEndSkewX - m_fStartSkewX;
	//如旋转一样,计算最小扭曲值。
    if (m_fDeltaX > 180)
    {
        m_fDeltaX -= 360;
    }
    if (m_fDeltaX < -180)
    {
        m_fDeltaX += 360;
    }
	//起始的Y扭曲角度。
    m_fStartSkewY = pTarget->getSkewY();

    if (m_fStartSkewY > 0)
    {	//如果m_fStartSkewY为正方向值,把它限制在0~180度之内。
        m_fStartSkewY = fmodf(m_fStartSkewY, 360.f);
    }
    else
    {	//如果m_fStartSkewY为负方向值,把它限制在0~180度之内。
        m_fStartSkewY = fmodf(m_fStartSkewY, -360.f);
    }
	//计算将要扭曲的Y轴角度。
    m_fDeltaY = m_fEndSkewY - m_fStartSkewY;
	//如旋转一样,计算最小扭曲值。
    if (m_fDeltaY > 180)
    {
        m_fDeltaY -= 360;
    }
    if (m_fDeltaY < -180)
    {
        m_fDeltaY += 360;
    }
}
//更新动画播放。
void CCSkewTo::update(float t)
{
	//类似旋转的计算,通过时间进度对扭曲角度进行插值,计算出最终的扭曲状态。
    m_pTarget->setSkewX(m_fStartSkewX + m_fDeltaX * t);
    m_pTarget->setSkewY(m_fStartSkewY + m_fDeltaY * t);
}
//构造函数。
CCSkewTo::CCSkewTo()
: m_fSkewX(0.0)
, m_fSkewY(0.0)
, m_fStartSkewX(0.0)
, m_fStartSkewY(0.0)
, m_fEndSkewX(0.0)
, m_fEndSkewY(0.0)
, m_fDeltaX(0.0)
, m_fDeltaY(0.0)
{
}

CCSkewBy:

//扭曲动画:从当前状态起在X,Y方向上设置扭曲多少角度,注意,它是CCSkewTo的子类。
class CC_DLL CCSkewBy : public CCSkewTo
{
public:
	//初始化。
    virtual bool initWithDuration(float t, float sx, float sy);
	//重载设置演示当前扭曲动画的演员。
    virtual void startWithTarget(CCNode *pTarget);
	//创建一个反向播放的当前扭曲动画。
    virtual CCActionInterval* reverse(void);

public:
    //创建一个扭曲动画,内部调用create实现。参一为动画时长,参二为右上角在X轴方向上的扭曲角度,参三为Y轴方向。
    CC_DEPRECATED_ATTRIBUTE static CCSkewBy* actionWithDuration(float t, float deltaSkewX, float deltaSkewY);
    //创建一个扭曲动画。
    static CCSkewBy* create(float t, float deltaSkewX, float deltaSkewY);
};
具体实现://创建一个扭曲动画,内部调用create实现。参一为动画时长,参二为右上角在X轴方向上的扭曲角度,参三为Y轴方向上的扭曲角度。
CCSkewBy* CCSkewBy::actionWithDuration(float t, float sx, float sy)
{
    return CCSkewBy::create(t, sx, sy);
}
//创建一个扭曲动画。
CCSkewBy* CCSkewBy::create(float t, float sx, float sy)
{
	//使用new创建一个CCSkewBy实例对象。
    CCSkewBy *pSkewBy = new CCSkewBy();
    if (pSkewBy)
    {	//对其初始化。
        if (pSkewBy->initWithDuration(t, sx, sy))
        {
			 //如果初始化成功,将其交由内存管理器进行释放。
            pSkewBy->autorelease();
        }
        else
        {	//如果初始化失败,直接释放。
            CC_SAFE_DELETE(pSkewBy);
        }
    }
	//返回创建的扭曲动画。
    return pSkewBy;
}
//初始化函数。
bool CCSkewBy::initWithDuration(float t, float deltaSkewX, float deltaSkewY)
{
	//定义布尔变量做为初始化成功与否的结果。
    bool bRet = false;
	//调用基类的初始化函数,但初始化之后,目标角度和要扭曲的角度都不对的,需要重置。
    if (CCSkewTo::initWithDuration(t, deltaSkewX, deltaSkewY))
	{
		//将要扭曲的角度保存到变量中用于在设置演员时计算目标角度和要扭曲的角度,话说这两个变量真是应该放到CCSkewBy中而不是CCSkewTo中。
        m_fSkewX = deltaSkewX;
        m_fSkewY = deltaSkewY;
		 //设置布尔变量代表初始化成功。 
        bRet = true;
    }

    return bRet;
}
//设置演示当前动画的演员。
void CCSkewBy::startWithTarget(CCNode *pTarget)
{
	//调用基类的设置演员函数。
	CCSkewTo::startWithTarget(pTarget);
	//重置目标角度和要扭曲的角度
    m_fDeltaX = m_fSkewX;
    m_fDeltaY = m_fSkewY;
    m_fEndSkewX = m_fStartSkewX + m_fDeltaX;
    m_fEndSkewY = m_fStartSkewY + m_fDeltaY;
}
//创建一个反向播放的扭曲动画。
CCActionInterval* CCSkewBy::reverse()
{
    return create(m_fDuration, -m_fSkewX, -m_fSkewY);
}

CCJumpBy:

//跳跃移动动画:从当前位置经过几次跳跃并移动多远。
class CC_DLL CCJumpBy : public CCActionInterval
{
public:
    //初始化跳跃动画,参数一为时长,参数二为跳跃移动到的目标位置,参数三为每次跳跃的高度,参数四为跳跃次数。
    bool initWithDuration(float duration, const CCPoint& position, float height, unsigned int jumps);
	//重载基类相关函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);

public:
    //创建一个跳跃移动动画。内部调用create函数实现。参数一为时长,参数二为跳跃移动多少偏移,参数三为每次跳跃的高度,参数四为跳跃次数。

    CC_DEPRECATED_ATTRIBUTE static CCJumpBy* actionWithDuration(float duration, const CCPoint& position, float height, unsigned int jumps);
    //创建一个跳跃移动动画。
    static CCJumpBy* create(float duration, const CCPoint& position, float height, unsigned int jumps);
protected:
	//起始位置
    CCPoint            m_startPosition;
	//从当前位置所要移动的偏移。
    CCPoint            m_delta;
	//跳跃的高度。
    float            m_height;
	//跳跃的次数。
    unsigned int    m_nJumps;
};
具体实现://创建一个跳跃移动动画。内部调用create函数实现。

CCJumpBy* CCJumpBy::actionWithDuration(float duration, const CCPoint& position, float height, unsigned int jumps)
{
    return CCJumpBy::create(duration, position, height, jumps);
}
//创建一个跳跃移动动画。
CCJumpBy* CCJumpBy::create(float duration, const CCPoint& position, float height, unsigned int jumps)
{
	//new创建出跳跃动画,调用初始化并交由内存管理器。呵呵,估计是作者也觉得烦了。连pJumpBy是否为空也不判断了,还是应该加上的。
    CCJumpBy *pJumpBy = new CCJumpBy();
    pJumpBy->initWithDuration(duration, position, height, jumps);
    pJumpBy->autorelease();
	//返回创建的跳跃动画。
    return pJumpBy;
}
//初始化函数。
bool CCJumpBy::initWithDuration(float duration, const CCPoint& position, float height, unsigned int jumps)
{
	//调用基类初始化函数。
    if (CCActionInterval::initWithDuration(duration))
	{
		//如果成功将参数保存到相应成员变量中。
        m_delta = position;
        m_height = height;
        m_nJumps = jumps;
		 //直接返回成功。
        return true;
    }
	//否则返回失败。呵呵,作者也不用布尔变量了,还是这样简洁。
    return false;
}
//创建一个当前动画的拷贝,略过。
CCObject* CCJumpBy::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCJumpBy* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCJumpBy*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCJumpBy();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_delta, m_height, m_nJumps);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前动画的演员
void CCJumpBy::startWithTarget(CCNode *pTarget)
{
	//调用基类函数。
	CCActionInterval::startWithTarget(pTarget);
	//将当前演员的位置保存到起始位置变量。
    m_startPosition = pTarget->getPosition();
}
//更新当前动画的播放。
void CCJumpBy::update(float time)
{
    // 演员有效性判断。
    if (m_pTarget)
	{
		//计算当前的跳跃次数进度并取浮点模值,即在一次跳跃中的进度。
        float frac = fmodf(time * m_nJumps, 1.0f);
		//跳跃的实现关健:通过高度公式和进度计算出一个跳跃高度。
        float y = m_height * 4 * frac * (1 - frac);
		//这个高度加上位移的位置高度做为当前的位置高度。
        y += m_delta.y * time;
		//计算当前的X方向位置。
        float x = m_delta.x * time;
		//设置演员的当前位置。
        m_pTarget->setPosition(ccp(m_startPosition.x + x, m_startPosition.y + y));
    }
}
//创建一个反向播放的跳跃动画。
CCActionInterval* CCJumpBy::reverse(void)
{
    return CCJumpBy::create(m_fDuration, ccp(-m_delta.x, -m_delta.y),
        m_height, m_nJumps);
}

CCJumpTo:

//跳跃移动动画:从一个位置经过几次跳跃移动到目标位置,注意它是CCJumpBy的子类,哈,这次By在前,To在后。
class CC_DLL CCJumpTo : public CCJumpBy
{
public:
	//重载设置演员和拷贝函数。
    virtual void startWithTarget(CCNode *pTarget);
    virtual CCObject* copyWithZone(CCZone* pZone);

public:
     //创建跳跃动画,内部调用create实现。参数一为时长,参数二为跳跃移动到的目标位置,参数三为每次跳跃的高度,参数四为跳跃次数。
    CC_DEPRECATED_ATTRIBUTE static CCJumpTo* actionWithDuration(float duration, const CCPoint& position, float height, int jumps);
	//创建跳跃动画。
	    static CCJumpTo* create(float duration, const CCPoint& position, float height, int jumps);
};
具体实现://创建跳跃动画,内部调用create实现。
CCJumpTo* CCJumpTo::actionWithDuration(float duration, const CCPoint& position, float height, int jumps)
{
    return CCJumpTo::create(duration, position, height, jumps);
}
//创建跳跃动画。
CCJumpTo* CCJumpTo::create(float duration, const CCPoint& position, float height, int jumps)
{
	//new创建出跳跃动画,调用初始化并交由内存管理器。
    CCJumpTo *pJumpTo = new CCJumpTo();
    pJumpTo->initWithDuration(duration, position, height, jumps);
    pJumpTo->autorelease();
	//返回创建的跳跃动画。
    return pJumpTo;
}
//创建拷贝。
CCObject* CCJumpTo::copyWithZone(CCZone* pZone)
{
    CCZone* pNewZone = NULL;
    CCJumpTo* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject)
    {
        //in case of being called at sub class
        pCopy = (CCJumpTo*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCJumpTo();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCJumpBy::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_delta, m_height, m_nJumps);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前动画的演员。
void CCJumpTo::startWithTarget(CCNode *pTarget)
{
	//调用基类的相应函数。
	CCJumpBy::startWithTarget(pTarget);
	//重新计算要移动的偏移。
    m_delta = ccp(m_delta.x - m_startPosition.x, m_delta.y - m_startPosition.y);
}

CCBezierBy:

//贝赛尔曲线结构。什么是“贝赛尔曲线”?有事找度娘。
typedef struct _ccBezierConfig {
    //结束点。
    CCPoint endPosition;
    //第一控制点。
    CCPoint controlPoint_1;
    //第二控制点。
    CCPoint controlPoint_2;
} ccBezierConfig;

//贝赛尔曲线移动动画。这里是按照贝赛尔曲线移动多少偏移。
class CC_DLL CCBezierBy : public CCActionInterval
{
public:
    //初始化动画,参数一为时长,参数二为设置的曲线信息。
    bool initWithDuration(float t, const ccBezierConfig& c);
	//重载相应函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);

public:
    //创建一个贝塞尔曲线,内部调用create实现。参数一为动画时长,参数二为曲线结构。
    CC_DEPRECATED_ATTRIBUTE static CCBezierBy* actionWithDuration(float t, const ccBezierConfig& c);
    //创建一个贝塞尔曲线。
    static CCBezierBy* create(float t, const ccBezierConfig& c);
protected:
	//贝塞尔结构。
    ccBezierConfig m_sConfig; 
	//起始点。
    CCPoint m_startPosition;
};
具体实现:
//创建一个贝塞尔曲线移动动画,内部调用create实现。
CCBezierBy* CCBezierBy::actionWithDuration(float t, const ccBezierConfig& c)
{
    return CCBezierBy::create(t, c);
}
//创建一个贝塞尔曲线移动动画。
CCBezierBy* CCBezierBy::create(float t, const ccBezierConfig& c)
{
	//使用new创建一个贝塞尔曲线动画实例对象,对其初始化并交由内存管理器释放,又忘做有效性判断了,唉。
    CCBezierBy *pBezierBy = new CCBezierBy();
    pBezierBy->initWithDuration(t, c);
    pBezierBy->autorelease();
	//返回创建的动画实例对象。
    return pBezierBy;
}
//初始化贝塞尔曲线移动动画。
bool CCBezierBy::initWithDuration(float t, const ccBezierConfig& c)
{
	//调用基类初始化函数。
    if (CCActionInterval::initWithDuration(t))
    {//保存贝塞尔曲线信息,并返回true。
        m_sConfig = c;
        return true;
    }
	//如果初始化失败返回false。
    return false;
}
//设置演示当前动画的演员。
void CCBezierBy::startWithTarget(CCNode *pTarget)
{
	//调用基类的相应函数。
	CCActionInterval::startWithTarget(pTarget);
	//取得演员的起始位置保存到成员变量。
    m_startPosition = pTarget->getPosition();
}
//产生拷贝。
CCObject* CCBezierBy::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCBezierBy* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCBezierBy*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCBezierBy();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_sConfig);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//更新动画的播放。
void CCBezierBy::update(float time)
{
	//演员的有效性判断。
    if (m_pTarget)
	{
		//x方向上的四个点位置。起始点,第一控制点,第二控制点,结束点。
        float xa = 0;
        float xb = m_sConfig.controlPoint_1.x;
        float xc = m_sConfig.controlPoint_2.x;
        float xd = m_sConfig.endPosition.x;
		//y方向上的四个点位置。
        float ya = 0;
        float yb = m_sConfig.controlPoint_1.y;
        float yc = m_sConfig.controlPoint_2.y;
        float yd = m_sConfig.endPosition.y;
		//通过四个点的位置和进度插值来调用贝塞尔曲线公式函数bezierat计算出当前的位置。
        float x = bezierat(xa, xb, xc, xd, time);
        float y = bezierat(ya, yb, yc, yd, time);
		//因为是By动画,所以要加上起始位置后设置为演员当前位置。
        m_pTarget->setPosition(ccpAdd(m_startPosition, ccp(x, y)));
    }
}
//创建一个反向播放的贝塞尔曲线移动动画。
CCActionInterval* CCBezierBy::reverse(void)
{
	//定义一个新的贝塞尔曲线信息结构。
    ccBezierConfig r;
	//ccpNeg是取负数,这里代表结尾的偏移位置改为反方向的相应偏移位置。
    r.endPosition = ccpNeg(m_sConfig.endPosition);
    r.controlPoint_1 = ccpAdd(m_sConfig.controlPoint_2, ccpNeg(m_sConfig.endPosition));
    r.controlPoint_2 = ccpAdd(m_sConfig.controlPoint_1, ccpNeg(m_sConfig.endPosition));

    CCBezierBy *pAction = CCBezierBy::create(m_fDuration, r);
    return pAction;
}

CCBezierTo:

//贝赛尔曲线移动动画。这里是按照贝赛尔曲线移动到目标点。它是CCBezierBy的子类。
class CC_DLL CCBezierTo : public CCBezierBy
{
public:
	//重载基类函数。
    virtual void startWithTarget(CCNode *pTarget);
    virtual CCObject* copyWithZone(CCZone* pZone);

public:
    //创建一个贝塞尔曲线移动动画,内部调用create实现。参数一为动画时长,参数二为曲线结构。
    CC_DEPRECATED_ATTRIBUTE static CCBezierTo* actionWithDuration(float t, const ccBezierConfig& c);

    //创建一个贝塞尔曲线移动动画。
    static CCBezierTo* create(float t, const ccBezierConfig& c);
};
具体实现:
//创建一个贝塞尔曲线移动动画,内部调用create实现。
CCBezierTo* CCBezierTo::actionWithDuration(float t, const ccBezierConfig& c)
{
    return CCBezierTo::create(t, c);
}
//创建一个贝塞尔曲线移动动画。
CCBezierTo* CCBezierTo::create(float t, const ccBezierConfig& c)
{
	//使用new创建一个贝塞尔曲线动画实例对象,对其初始化并交由内存管理器释放,有效性判断呢,唉。
    CCBezierTo *pBezierTo = new CCBezierTo();
    pBezierTo->initWithDuration(t, c);
    pBezierTo->autorelease();
	//返回创建的动画。
    return pBezierTo;
}

//创建拷贝。
CCObject* CCBezierTo::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCBezierBy* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCBezierTo*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCBezierTo();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCBezierBy::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_sConfig);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演员当前动画的演员。
void CCBezierTo::startWithTarget(CCNode *pTarget)
{
	//调用基类的相应函数。
	CCBezierBy::startWithTarget(pTarget);
	//因为是移动到目标位置,所以这里要对曲线的结构进行一个重置,都加上起始的位置。曲线移动就由By变To了~。
    m_sConfig.controlPoint_1 = ccpSub(m_sConfig.controlPoint_1, m_startPosition);
    m_sConfig.controlPoint_2 = ccpSub(m_sConfig.controlPoint_2, m_startPosition);
    m_sConfig.endPosition = ccpSub(m_sConfig.endPosition, m_startPosition);
}

CCScaleTo:

//缩放动画:缩放到指定大小
class CC_DLL CCScaleTo : public CCActionInterval
{
public:
    //初始化等比缩放动画,参一为时长,参二为等比缩放目标值.
    bool initWithDuration(float duration, float s);
    //初始化非等比缩放动画,参一为时长,参二为X方向缩放目标值,参三为Y方向缩放目标值.
    bool initWithDuration(float duration, float sx, float sy);
	//拷贝.
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);

public:
    //创建一个等比缩放动画.内部调用create实现.
    CC_DEPRECATED_ATTRIBUTE static CCScaleTo* actionWithDuration(float duration, float s);

    //创建一个非等比缩放动画.内部调用create实现.
    CC_DEPRECATED_ATTRIBUTE static CCScaleTo* actionWithDuration(float duration, float sx, float sy);

    //创建一个等比缩放动画.
    static CCScaleTo* create(float duration, float s);

    //创建一个非等比缩放动画.
    static CCScaleTo* create(float duration, float sx, float sy);
protected:
	//又是给子类CCScaleBy用的.Why放在这里?
    float m_fScaleX;
	float m_fScaleY;
	//起始缩放值
    float m_fStartScaleX;
    float m_fStartScaleY;
	//目标缩放值
    float m_fEndScaleX;
	float m_fEndScaleY;
	//动画过程要缩放的大小.
    float m_fDeltaX;
    float m_fDeltaY;
};
具体实现:
//创建一个等比缩放动画.
CCScaleTo* CCScaleTo::actionWithDuration(float duration, float s)
{
    return CCScaleTo::create(duration, s);
}
//创建一个等比缩放动画.
CCScaleTo* CCScaleTo::create(float duration, float s)
{
	//使用new创建一个等比缩放动画,之后初始化并交由内存管理器进行释放处理.
    CCScaleTo *pScaleTo = new CCScaleTo();
    pScaleTo->initWithDuration(duration, s);
    pScaleTo->autorelease();
	//返回创建的等比缩放动画.
    return pScaleTo;
}
//初始化等比缩放动画.
bool CCScaleTo::initWithDuration(float duration, float s)
{
	//调用基类的初始化函数.
    if (CCActionInterval::initWithDuration(duration))
	{
	//保存目标缩放大小.
        m_fEndScaleX = s;
        m_fEndScaleY = s;

        return true;
    }

    return false;
}
//创建一个非等比缩放动画.内部调用create实现.
CCScaleTo* CCScaleTo::actionWithDuration(float duration, float sx, float sy)
{
    return CCScaleTo::create(duration, sx, sy);
}
//创建一个非等比缩放动画.
CCScaleTo* CCScaleTo::create(float duration, float sx, float sy)
{
	 //使用new创建一个非等比缩放动画,之后初始化并交由内存管理器进行释放处理.
	CCScaleTo *pScaleTo = new CCScaleTo();
    pScaleTo->initWithDuration(duration, sx, sy);
    pScaleTo->autorelease();
	//返回创建的非等比缩放动画.
    return pScaleTo;
}

//初始化非等比缩放动画.
bool CCScaleTo::initWithDuration(float duration, float sx, float sy)
{
	//调用基类的初始化函数.
    if (CCActionInterval::initWithDuration(duration))
	{
	//保存目标缩放大小.
        m_fEndScaleX = sx;
        m_fEndScaleY = sy;

        return true;
    }

    return false;
}
//创建拷贝.
CCObject* CCScaleTo::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCScaleTo* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCScaleTo*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCScaleTo();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);


    pCopy->initWithDuration(m_fDuration, m_fEndScaleX, m_fEndScaleY);

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前动画的演员.
void CCScaleTo::startWithTarget(CCNode *pTarget)
{
	//调用基类函数.
	CCActionInterval::startWithTarget(pTarget);
	//保存起始缩放值.
    m_fStartScaleX = pTarget->getScaleX();
	m_fStartScaleY = pTarget->getScaleY();
	//保存动画过程中要缩放大小.
    m_fDeltaX = m_fEndScaleX - m_fStartScaleX;
    m_fDeltaY = m_fEndScaleY - m_fStartScaleY;
}
//更新动画
void CCScaleTo::update(float time)
{
	//如果演员有效,通过时间进度插值来计算当前的缩放大小,并设置为演员的缩放值.
    if (m_pTarget)
    {
        m_pTarget->setScaleX(m_fStartScaleX + m_fDeltaX * time);
        m_pTarget->setScaleY(m_fStartScaleY + m_fDeltaY * time);
    }
}

CCScaleBy:

//缩放动画:控制演员由当前缩放值缩放多少.是CCScaleTo的子类.
class CC_DLL CCScaleBy : public CCScaleTo
{
public:
	//重载相关函数.
    virtual void startWithTarget(CCNode *pTarget);
    virtual CCActionInterval* reverse(void);
    virtual CCObject* copyWithZone(CCZone* pZone);

public:
    //创建等比缩放动画.内部调用create函数.
    CC_DEPRECATED_ATTRIBUTE static CCScaleBy* actionWithDuration(float duration, float s);
    //创建非等比缩放动画.内部调用create函数.
    CC_DEPRECATED_ATTRIBUTE static CCScaleBy* actionWithDuration(float duration, float sx, float sy);
	//创建等比缩放动画.
    static CCScaleBy* create(float duration, float s);
	//创建非等比缩放动画.
    static CCScaleBy* create(float duration, float sx, float sy);
};
具体实现:
//创建等比缩放动画.内部调用create函数.
CCScaleBy* CCScaleBy::actionWithDuration(float duration, float s)
{
    return create(duration, s);
}
//创建非等比缩放动画.内部调用create函数.
CCScaleBy* CCScaleBy::actionWithDuration(float duration, float sx, float sy)
{
    return create(duration, sx, sy);
}
//创建等比缩放动画.
CCScaleBy* CCScaleBy::create(float duration, float s)
{	
	//使用new创建一个等比缩放动画,之后初始化并交由内存管理器进行释放处理.
    CCScaleBy *pScaleBy = new CCScaleBy();
    pScaleBy->initWithDuration(duration, s);
    pScaleBy->autorelease();
	//返回创建的动画实例对象指针.
    return pScaleBy;
}
//创建非等比缩放动画.
CCScaleBy* CCScaleBy::create(float duration, float sx, float sy)
{
	//使用new创建一个非等比缩放动画,之后初始化并交由内存管理器进行释放处理.
    CCScaleBy *pScaleBy = new CCScaleBy();
    pScaleBy->initWithDuration(duration, sx, sy);
    pScaleBy->autorelease();
	//返回创建的动画实例对象指针.
    return pScaleBy;
}
//创建拷贝.
CCObject* CCScaleBy::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCScaleTo* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject)
    {
        //in case of being called at sub class
        pCopy = (CCScaleBy*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCScaleBy();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCScaleTo::copyWithZone(pZone);


    pCopy->initWithDuration(m_fDuration, m_fEndScaleX, m_fEndScaleY);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前动画的演员.
void CCScaleBy::startWithTarget(CCNode *pTarget)
{
	//调用基类的相应函数.
	CCScaleTo::startWithTarget(pTarget);
	//重新计算要缩放的大小.
    m_fDeltaX = m_fStartScaleX * m_fEndScaleX - m_fStartScaleX;
    m_fDeltaY = m_fStartScaleY * m_fEndScaleY - m_fStartScaleY;
}
//创建一个反向播放的CCScaleBy动画.注意,CCScaleTo动画没有这个函数哦,当然也不是不能,可能作者懒,没空加吧!有兴趣的可以自已改一下CCScaleTo,加上反向播放动画.~
CCActionInterval* CCScaleBy::reverse(void)
{
	//取倒数做为反向缩放动画的参数.
    return CCScaleBy::create(m_fDuration, 1 / m_fEndScaleX, 1 / m_fEndScaleY);
}

CCBlink:

//闪现动画:"一闪一闪亮晶晶,满天都是小星星."
class CC_DLL CCBlink : public CCActionInterval
{
public:
    //初始化闪现动画.
    bool initWithDuration(float duration, unsigned int uBlinks);
	//重载相应函数.
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);

public:
    //创建一个闪现动画.内部调用create实现.参一为动画时长,参二为闪现次数.
    CC_DEPRECATED_ATTRIBUTE static CCBlink* actionWithDuration(float duration, unsigned int uBlinks);
    //创建一个闪现动画.
    static CCBlink* create(float duration, unsigned int uBlinks);
protected:
    unsigned int m_nTimes;
};
具体实现:
 //创建一个闪现动画
CCBlink* CCBlink::actionWithDuration(float duration, unsigned int uBlinks)
{
    return CCBlink::create(duration, uBlinks);
}
//创建一个闪现动画.
CCBlink* CCBlink::create(float duration, unsigned int uBlinks)
{
	//使用new创建一个闪现动画,之后初始化并交由内存管理器进行释放处理.
	
    CCBlink *pBlink = new CCBlink();
    pBlink->initWithDuration(duration, uBlinks);
    pBlink->autorelease();

    //返回创建的动画
	return pBlink;
}
//初始化闪现动画.
bool CCBlink::initWithDuration(float duration, unsigned int uBlinks)
{
	//调用基类的初始化函数.
    if (CCActionInterval::initWithDuration(duration))
	{
	//保存闪现次数.
        m_nTimes = uBlinks;
        return true;
    }

    return false;
}
//创建拷贝.
CCObject* CCBlink::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCBlink* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCBlink*)(pZone->m_pCopyObject);

    }
    else
    {
        pCopy = new CCBlink();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, (unsigned int)m_nTimes);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//更新闪现动画.
void CCBlink::update(float time)
{
	//如果有演员且动画未结束.
    if (m_pTarget && ! isDone())
	{
	//计算每一次闪现的时间进度存入变量slice.
        float slice = 1.0f / m_nTimes;
 		//当前的时间进度与slice取模计算本次闪现的时间进度.
        float m = fmodf(time, slice);
	 //如果时间进度走过了本次闪现的一半进度,切换显示或不显示.
        m_pTarget->setVisible(m > slice / 2 ? true : false);
    }
}
//创建一个反向播放的闪现动画.这个没什么正反的,都一样~
CCActionInterval* CCBlink::reverse(void)
{
    return CCBlink::create(m_fDuration, m_nTimes);
}

CCFadeIn:

//淡入动画:渐渐显现的动画.
class CC_DLL CCFadeIn : public CCActionInterval
{
public:
	//重载相应函数.
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);
    virtual CCObject* copyWithZone(CCZone* pZone);

public:
    //创建一个淡入动画,内部调用create实现.参数为淡入时间.
    CC_DEPRECATED_ATTRIBUTE static CCFadeIn* actionWithDuration(float d);
    //创建一个淡入动画
    static CCFadeIn* create(float d);
};
具体实现:
//创建一个淡入动画,内部调用create实现.
CCFadeIn* CCFadeIn::actionWithDuration(float d)
{
    return CCFadeIn::create(d);
}
//创建一个渐渐显现的淡入动画.
CCFadeIn* CCFadeIn::create(float d)
{
	//利用new创建一个淡入动画.初始化后交由内存管理器进行释放处理.
    CCFadeIn* pAction = new CCFadeIn();

    pAction->initWithDuration(d);
    pAction->autorelease();
	//返回创建的淡入动画.
    return pAction;
}
//创建一个拷贝.
CCObject* CCFadeIn::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCFadeIn* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject)
    {
        //in case of being called at sub class
        pCopy = (CCFadeIn*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCFadeIn();
        pZone = pNewZone = new CCZone(pCopy);
    }
    
    CCActionInterval::copyWithZone(pZone);

    CC_SAFE_DELETE(pNewZone);

    return pCopy;
}
//更新淡入动画.
void CCFadeIn::update(float time)
{
	//将演员由精灵类降为色彩控制基类CCRGBAProtocol .
	CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(m_pTarget);
    if (pRGBAProtocol)
    {	//如果转换成功,按时间进度设置透明度.透明度取值0~255,所以要乘以255.
        pRGBAProtocol->setOpacity((GLubyte)(255 * time));
    }
    /*m_pTarget->setOpacity((GLubyte)(255 * time));*/
}
//创建一个反向播放的淡入动画.其实是创建一个相同时间的渐渐消失的淡出动画.
CCActionInterval* CCFadeIn::reverse(void)
{
    return CCFadeOut::create(m_fDuration);
}

CCFadeOut:

//淡出动画:渐渐消失的动画.
class CC_DLL CCFadeOut : public CCActionInterval
{
public:
	//重载相应函数.
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);
    virtual CCObject* copyWithZone(CCZone* pZone);

public:
    //创建一个淡出动画,内部调用create实现.参数为淡入时间.
    CC_DEPRECATED_ATTRIBUTE static CCFadeOut* actionWithDuration(float d);

    //创建一个淡出动画.
    static CCFadeOut* create(float d);
};
具体实现:
//创建一个淡出动画,内部调用create实现.参数为淡入时间.
CCFadeOut* CCFadeOut::actionWithDuration(float d)
{
    return CCFadeOut::create(d);
}
//创建一个淡出动画.
CCFadeOut* CCFadeOut::create(float d)
{
	//利用new创建一个淡出动画.初始化后交由内存管理器进行释放处理.
    CCFadeOut* pAction = new CCFadeOut();

    pAction->initWithDuration(d);
    pAction->autorelease();
	//返回创建的动画实例对象指针.
    return pAction;
}
//创建拷贝.
CCObject* CCFadeOut::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCFadeOut* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCFadeOut*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCFadeOut();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    CC_SAFE_DELETE(pNewZone);

    return pCopy;
}
//更新淡出动画.
void CCFadeOut::update(float time)
{
	//将演员由精灵类降为色彩控制基类CCRGBAProtocol .
	CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(m_pTarget);
    if (pRGBAProtocol)
    {	//如果转换成功,按时间进度设置透明度.透明度取值0~255,所以要乘以255.
        pRGBAProtocol->setOpacity(GLubyte(255 * (1 - time)));
    }
    /*m_pTarget->setOpacity(GLubyte(255 * (1 - time)));*/    
}
//创建一个反向播放的淡出动画.其实是创建一个相同时间的渐渐显示的淡入动画.
CCActionInterval* CCFadeOut::reverse(void)
{
    return CCFadeIn::create(m_fDuration);
}

//透明度渐变动画:从当前透明度渐渐变化到指定的透明度.
class CC_DLL CCFadeTo : public CCActionInterval
{
public:
    //初始化,参数一为时间长度,参数二为目标透明度。
    bool initWithDuration(float duration, GLubyte opacity);
	//重载基类相应函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);

public:
    //创建一个透明度渐变动画,内部调用create实现,参数一为时间长度,参数二为目标透明度。
    CC_DEPRECATED_ATTRIBUTE static CCFadeTo* actionWithDuration(float duration, GLubyte opacity);
    //创建一个透明度渐变动画。
    static CCFadeTo* create(float duration, GLubyte opacity);
protected:
	//目标透明度
	GLubyte m_toOpacity;
	//当前透明度。
    GLubyte m_fromOpacity;
};
具体实现:
//创建一个透明度渐变动画,内部调用create实现,参数一为时间长度,参数二为目标透明度。
CCFadeTo* CCFadeTo::actionWithDuration(float duration, GLubyte opacity)
{
     return CCFadeTo::create(duration, opacity);
}
//创建一个透明度渐变动画。
CCFadeTo* CCFadeTo::create(float duration, GLubyte opacity)
{
	//利用new创建一个透明度渐变动画.初始化后交由内存管理器进行释放处理.
    CCFadeTo *pFadeTo = new CCFadeTo();
    pFadeTo->initWithDuration(duration, opacity);
    pFadeTo->autorelease();
	//返回创建的动画实例对象指针。
    return pFadeTo;
}
//初始化。
bool CCFadeTo::initWithDuration(float duration, GLubyte opacity)
{
	//调用基类的初始化函数。
    if (CCActionInterval::initWithDuration(duration))
	{
	//保存目标透明度值并返回成功。
        m_toOpacity = opacity;
        return true;
    }
	//否则返回失败。
    return false;
}
//创建拷贝。
CCObject* CCFadeTo::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCFadeTo* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCFadeTo*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCFadeTo();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_toOpacity);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示当前动画的演员。
void CCFadeTo::startWithTarget(CCNode *pTarget)
{
	//调用基类相应函数。
    CCActionInterval::startWithTarget(pTarget);
	//将演员由精灵类降为色彩控制基类CCRGBAProtocol .
    CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(pTarget);
    if (pRGBAProtocol)
	{
	//取得当前演员的透明度做为起始透明度。
        m_fromOpacity = pRGBAProtocol->getOpacity();
    }
    /*m_fromOpacity = pTarget->getOpacity();*/
}
//更新动画。
void CCFadeTo::update(float time)
{
	//将演员由精灵类降为色彩控制基类CCRGBAProtocol .
    CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(m_pTarget);
    if (pRGBAProtocol)
	{
	//通过时间插值计算当前演员的透明度值。
        pRGBAProtocol->setOpacity((GLubyte)(m_fromOpacity + (m_toOpacity - m_fromOpacity) * time));
    }
    /*m_pTarget->setOpacity((GLubyte)(m_fromOpacity + (m_toOpacity - m_fromOpacity) * time));*/
}

CCTintTo:

//变色动画:从当前演员的色彩值变化到目标色彩。
class CC_DLL CCTintTo : public CCActionInterval
{
public:
    //初始化:参一为动画时长,参二,三,四分别代表目标色彩的红色,绿色,蓝色的分量。
    bool initWithDuration(float duration, GLubyte red, GLubyte green, GLubyte blue);
	//重载基类函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);

public:
    //创建一个变色动画,内部调用create实现。参一为动画时长,参二,三,四分别代表目标色彩的红色,绿色,蓝色的分量。
    CC_DEPRECATED_ATTRIBUTE static CCTintTo* actionWithDuration(float duration, GLubyte red, GLubyte green, GLubyte blue);
    //创建一个变色动画。
    static CCTintTo* create(float duration, GLubyte red, GLubyte green, GLubyte blue);
protected:
	//目标的三原色。
	ccColor3B m_to;
	//起始的三原色。
    ccColor3B m_from;
};
具体实现:
//创建一个变色动画,内部调用create实现。参一为动画时长,参二,三,四分别代表目标色彩的红色,绿色,蓝色的分量。
CCTintTo* CCTintTo::actionWithDuration(float duration, GLubyte red, GLubyte green, GLubyte blue)
{
    return CCTintTo::create(duration, red, green, blue);
}
//创建一个变色动画。
CCTintTo* CCTintTo::create(float duration, GLubyte red, GLubyte green, GLubyte blue)
{
	//利用new创建一个变色动画.初始化后交由内存管理器进行释放处理.
    CCTintTo *pTintTo = new CCTintTo();
    pTintTo->initWithDuration(duration, red, green, blue);
    pTintTo->autorelease();
	//返回创建的动画实例对象指针。
    return pTintTo;
}
//初始化。
bool CCTintTo::initWithDuration(float duration, GLubyte red, GLubyte green, GLubyte blue)
{
	//先调用基类的初始化函数。
    if (CCActionInterval::initWithDuration(duration))
	{
	//保存目标色彩值。
        m_to = ccc3(red, green, blue);
	//返回成功。
        return true;
    }
	//如果初始化失败返回false。
    return false;
}
//创建拷贝。
CCObject* CCTintTo::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCTintTo* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCTintTo*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCTintTo();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_to.r, m_to.g, m_to.b);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演示变色动画的演员。
void CCTintTo::startWithTarget(CCNode *pTarget)
{
	//调用基类的相应函数。
	CCActionInterval::startWithTarget(pTarget);
	//将演员由精灵类降为色彩控制基类CCRGBAProtocol .
    CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(m_pTarget);
    if (pRGBAProtocol)
	{
	//取得演员的色彩值做为起始色彩值。
        m_from = pRGBAProtocol->getColor();
    }
    /*m_from = pTarget->getColor();*/
}
//更新动画的播放。
void CCTintTo::update(float time)
{
	//将演员由精灵类降为色彩控制基类CCRGBAProtocol .
    CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(m_pTarget);
    if (pRGBAProtocol)
	{
	//利用时间对色彩变化进行插值,计算出演员的当前色彩值。
        pRGBAProtocol->setColor(ccc3(GLubyte(m_from.r + (m_to.r - m_from.r) * time), 
            (GLbyte)(m_from.g + (m_to.g - m_from.g) * time),
            (GLbyte)(m_from.b + (m_to.b - m_from.b) * time)));
    }    
}

CCTintBy:

//变色动画:从当前演员的色彩值变化多少。
class CC_DLL CCTintBy : public CCActionInterval
{
public:
    //初始化:参一为动画时长,参二,三,四分别代表变化色彩的红色,绿色,蓝色的分量。
    bool initWithDuration(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue);
	//重载基类的函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);

public:
    //创建一个变色动画,内部调用create实现。参一为动画时长,参二,三,四分别代表要变化的色彩的红色,绿色,蓝色的分量。
    CC_DEPRECATED_ATTRIBUTE static CCTintBy* actionWithDuration(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue);
    //创建一个变色动画。
    static CCTintBy* create(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue);
protected:
	//色彩变化值的三分量。我真想跪了,前面不是用ccColor3B 挺好么?
    GLshort m_deltaR;
    GLshort m_deltaG;
    GLshort m_deltaB;
	//起始色彩值的三分量。
    GLshort m_fromR;
    GLshort m_fromG;
    GLshort m_fromB;
};
具体实现:
//创建一个变色动画,内部调用create实现。参一为动画时长,参二,三,四分别代表要变化的色彩的红色,绿色,蓝色的分量。
CCTintBy* CCTintBy::actionWithDuration(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue)
{
    return CCTintBy::create(duration, deltaRed, deltaGreen, deltaBlue);
}
//创建一个变色动画。
CCTintBy* CCTintBy::create(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue)
{	
	//利用new创建一个变色动画.初始化后交由内存管理器进行释放处理.
    CCTintBy *pTintBy = new CCTintBy();
    pTintBy->initWithDuration(duration, deltaRed, deltaGreen, deltaBlue);
    pTintBy->autorelease();
	//返回创建的变色动画。
    return pTintBy;
}
//初始化函数。
bool CCTintBy::initWithDuration(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue)
{
	//调用基类的初始化函数。
    if (CCActionInterval::initWithDuration(duration))
	{
	//将要变化的色彩的三分量保存。
        m_deltaR = deltaRed;
        m_deltaG = deltaGreen;
        m_deltaB = deltaBlue;
	//返回成功。
        return true;
    }
	//如果初始化失败返回false。
    return false;
}
//创建当前动画的拷贝。
CCObject* CCTintBy::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCTintBy* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCTintBy*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCTintBy();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, (GLubyte)m_deltaR, (GLubyte)m_deltaG, (GLubyte)m_deltaB);

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//设置演员当前动画的演员。
void CCTintBy::startWithTarget(CCNode *pTarget)
{
	//调用基类的相应函数。
    CCActionInterval::startWithTarget(pTarget);
	//将演员由精灵类降为色彩控制基类CCRGBAProtocol .
    CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(pTarget);
    if (pRGBAProtocol)
	{
	//将演员的色彩值取出保存为起始色彩值。
        ccColor3B color = pRGBAProtocol->getColor();
        m_fromR = color.r;
        m_fromG = color.g;
        m_fromB = color.b;
    }    
}
//更新当前动画播放。
void CCTintBy::update(float time)
{
	//将演员由精灵类降为色彩控制基类CCRGBAProtocol .
    CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(m_pTarget);
    if (pRGBAProtocol)
	{
	//利用时间对色彩变化进行插值,计算出演员的当前色彩值。
        pRGBAProtocol->setColor(ccc3((GLubyte)(m_fromR + m_deltaR * time),
            (GLubyte)(m_fromG + m_deltaG * time),
            (GLubyte)(m_fromB + m_deltaB * time)));
    }    
}
//创建一个反向播放的变色动画。
CCActionInterval* CCTintBy::reverse(void)
{
    return CCTintBy::create(m_fDuration, -m_deltaR, -m_deltaG, -m_deltaB);
} 

CCDelayTime:

//暂停动画:这个动画就是代表在时长内保持没有任何变化效果。
class CC_DLL CCDelayTime : public CCActionInterval
{
public:
	//重载基类相应函数。
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);
    virtual CCObject* copyWithZone(CCZone* pZone);

public:
    //创建一个暂停动画。参数为动画的时间长度。内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCDelayTime* actionWithDuration(float d);

    //创建一个暂停动画。
    static CCDelayTime* create(float d);
};
具体实现:
//创建一个暂停动画。参数为动画的时间长度。
CCDelayTime* CCDelayTime::actionWithDuration(float d)
{
    return CCDelayTime::create(d);
}
//创建一个暂停动画。
CCDelayTime* CCDelayTime::create(float d)
{
    CCDelayTime* pAction = new CCDelayTime();

    pAction->initWithDuration(d);
    pAction->autorelease();

    return pAction;
}
//创建一个暂停动画的拷贝。
CCObject* CCDelayTime::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCDelayTime* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCDelayTime*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCDelayTime();
        pZone = pNewZone = new CCZone(pCopy);
    }

    
    CCActionInterval::copyWithZone(pZone);

    CC_SAFE_DELETE(pNewZone);

    return pCopy;
}
//更新当前暂停动画。
void CCDelayTime::update(float time)
{
	//就是啥也不做。
    CC_UNUSED_PARAM(time);
    return;
}
//创建一个反向播放的动画,其实就是自身,有毛意义反向~?
CCActionInterval* CCDelayTime::reverse(void)
{
    return CCDelayTime::create(m_fDuration);
}

CCReverseTime:

//倒带动画:控制一个动画进行反向播放
class CC_DLL CCReverseTime : public CCActionInterval
{
public:
	//析构与构造
    ~CCReverseTime(void);
    CCReverseTime();

    //初始化动画,参数为控制的时间动画。
    bool initWithAction(CCFiniteTimeAction *pAction);
	//重载基类相应函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void stop(void);
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);

public:
    //创建一个倒带动画,内部调用create实现。参数为控制的时间动画。
    CC_DEPRECATED_ATTRIBUTE static CCReverseTime* actionWithAction(CCFiniteTimeAction *pAction);
    //创建一个倒带动画。
    static CCReverseTime* create(CCFiniteTimeAction *pAction);
protected:
    CCFiniteTimeAction *m_pOther;
};
具体实现:
////创建一个动画,内部调用create实现。

CCReverseTime* CCReverseTime::actionWithAction(CCFiniteTimeAction *pAction)
{
    return CCReverseTime::create(pAction);
}
//创建一个动画。
CCReverseTime* CCReverseTime::create(CCFiniteTimeAction *pAction)
{
	//亲, 创建,初始化,交由内存管理器进行释放处理,包邮返回一条龙哦~
    CCReverseTime *pReverseTime = new CCReverseTime();
    pReverseTime->initWithAction(pAction);
    pReverseTime->autorelease();

    return pReverseTime;
}
//初始化
bool CCReverseTime::initWithAction(CCFiniteTimeAction *pAction)
{
	//有效性判断
    CCAssert(pAction != NULL, "");
    CCAssert(pAction != m_pOther, "");
	//先调用基类初始化处理。
    if (CCActionInterval::initWithDuration(pAction->getDuration()))
    {
        // Don't leak if action is reused
        CC_SAFE_RELEASE(m_pOther);
	 //保存要控制的动画。
        m_pOther = pAction;
	 //占用它,就让它的引用计数器加一!
        pAction->retain();
	 //返回成功。
        return true;
    }
	//如果初始化失败,返回false。
    return false;
}
//创建拷贝。
CCObject* CCReverseTime::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCReverseTime* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCReverseTime*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCReverseTime();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithAction((CCFiniteTimeAction*)(m_pOther->copy()->autorelease()));

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//构造。
CCReverseTime::CCReverseTime() : m_pOther(NULL) 
{

}
//析构。
CCReverseTime::~CCReverseTime(void)
{
	//占用它,就要释放它!当然,到不到释放时候由内存管理器通过引用计数来判断和处理。
    CC_SAFE_RELEASE(m_pOther);
}
//设置演示当前动画的演员。
void CCReverseTime::startWithTarget(CCNode *pTarget)
{
	//调用基类的相应函数。
	CCActionInterval::startWithTarget(pTarget);
	//设置演员演示当前控制的动画。
    m_pOther->startWithTarget(pTarget);
}
//停止动画。
void CCReverseTime::stop(void)
{
	//控制动画停止。
	m_pOther->stop();
	//当前动画停止。
    CCActionInterval::stop();
}
//更新动画的播放。
void CCReverseTime::update(float time)
{	
	//如果控制的动画有效,则计算倒带进度播放动画。
    if (m_pOther)
    {
        m_pOther->update(1 - time);
    }
}
//反向动画的反向动画。真蛋疼啊~
CCActionInterval* CCReverseTime::reverse(void)
{
    return (CCActionInterval*)(m_pOther->copy()->autorelease());
}

CCAnimate:

class CCTexture2D;
//序列帧动画
class CC_DLL CCAnimate : public CCActionInterval
{
public:
//构造与析构。
    CCAnimate();
    ~CCAnimate();

    //初始化动画。
    bool initWithAnimation(CCAnimation *pAnimation);
//重载基类的相应函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void stop(void);
    virtual void update(float t);
    virtual CCActionInterval* reverse(void);

public:
    //创建一个序列帧动画,内部调用create实现。参数为动画信息结构指针。
    CC_DEPRECATED_ATTRIBUTE static CCAnimate* actionWithAnimation(CCAnimation *pAnimation);
	//创建一个序列帧动画。
    static CCAnimate* create(CCAnimation *pAnimation);
	//定义一个动画信息结构指针变量以及存取此变量的函数。
    CC_SYNTHESIZE_RETAIN(CCAnimation*, m_pAnimation, Animation)
protected:
//保存每一帧要切换时的进度的容器指针,由动画信息结构指针取得。
std::vector<float>* m_pSplitTimes;
//当前要播放的下一帧序号。
int                m_nNextFrame;
//各帧中保存的精灵信息。
CCSpriteFrame*  m_pOrigFrame;
//循环次数。
    unsigned int    m_uExecutedLoops;
};
具体实现:
 //创建一个序列帧动画,内部调用create实现。参数为动画信息结构。
CCAnimate* CCAnimate::actionWithAnimation(CCAnimation *pAnimation)
{
    return CCAnimate::create(pAnimation);
}
//创建一个序列帧动画。
CCAnimate* CCAnimate::create(CCAnimation *pAnimation)
{
//创建,初始化,交由内存管理器,包邮~
    CCAnimate *pAnimate = new CCAnimate();
    pAnimate->initWithAnimation(pAnimation);
    pAnimate->autorelease();
	//返回创建的实例对象。
    return pAnimate;
}
//初始化序列动画。
bool CCAnimate::initWithAnimation(CCAnimation *pAnimation)
{
//有效性判断。
    CCAssert( pAnimation!=NULL, "Animate: argument Animation must be non-NULL");
//取得序列的时长。
    float singleDuration = pAnimation->getDuration();
//乘以循环次数做为当前动画总时长来进行初始化。
    if ( CCActionInterval::initWithDuration(singleDuration * pAnimation->getLoops() ) ) 
{
//初始化变量。
        m_nNextFrame = 0;
		 //将参数pAnimation保存到动画信息结构指针变量m_pAnimation.
        setAnimation(pAnimation);
        m_pOrigFrame = NULL;
        m_uExecutedLoops = 0;
//设置容器大小。这里我认为应该写成resize而不是reserver!!!
        m_pSplitTimes->reserve(pAnimation->getFrames()->count());
        //初始化变量。
        float accumUnitsOfTime = 0;
 //序列播放一轮的时间/
        float newUnitOfTimeValue = singleDuration / pAnimation->getTotalDelayUnits();
//取得序列信息中的帧信息数组。
        CCArray* pFrames = pAnimation->getFrames();
//
        CCARRAY_VERIFY_TYPE(pFrames, CCAnimationFrame*);
//定义临时变量pObj来遍历取得帧信息中的单帧信息。
        CCObject* pObj = NULL;
        CCARRAY_FOREACH(pFrames, pObj)
        {
 //取得单帧信息。
            CCAnimationFrame* frame = (CCAnimationFrame*)pObj;
 //计算播放当前单帧时的进度。
            float value = (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration;
            accumUnitsOfTime += frame->getDelayUnits();
 //将当前单帧的进度存入容器。
            m_pSplitTimes->push_back(value);
        }    
//返回成功。
        return true;
}
//如果初始化失败,返回false。
    return false;
}
//创建拷贝。
CCObject* CCAnimate::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCAnimate* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCAnimate*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCAnimate();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithAnimation((CCAnimation*)m_pAnimation->copy()->autorelease());

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}
//构造,注意在这里申请了一个vector<float>容器,并将成员指针变量m_pSplitTimes指向它的。
CCAnimate::CCAnimate()
: m_pAnimation(NULL)
, m_pSplitTimes(new std::vector<float>)
, m_nNextFrame(0)
, m_pOrigFrame(NULL)
, m_uExecutedLoops(0)
{

}
//析构。
CCAnimate::~CCAnimate()
{
    CC_SAFE_RELEASE(m_pAnimation);
    CC_SAFE_RELEASE(m_pOrigFrame);
    CC_SAFE_DELETE(m_pSplitTimes);
}
//设置演示当前序列动画的演员。
void CCAnimate::startWithTarget(CCNode *pTarget)
{
//先调用基类的相应函数。
CCActionInterval::startWithTarget(pTarget);
//序列帧动画必须是个精灵。
    CCSprite *pSprite = (CCSprite*)(pTarget);
//释放上一个
    CC_SAFE_RELEASE(m_pOrigFrame);
//如果有帧数据。
    if (m_pAnimation->getRestoreOriginalFrame())
{
 //取得精灵的帧信息。
        m_pOrigFrame = pSprite->displayFrame();
		 //对帧信息占用,引用数加一。
        m_pOrigFrame->retain();
    }
    m_nNextFrame = 0;
    m_uExecutedLoops = 0;
}
//停止当前动画。
void CCAnimate::stop(void)
{
//如果动画有帧数据且有演员。
    if (m_pAnimation->getRestoreOriginalFrame() && m_pTarget)
{
	//设置演员显示当前帧。
        ((CCSprite*)(m_pTarget))->setDisplayFrame(m_pOrigFrame);
    }
	//停止当前动画。
    CCActionInterval::stop();
}
//更新动画。
void CCAnimate::update(float t)
{
// 如果整个动画未播放,先计算循环是否够次数。
if( t < 1.0f ) {
	//计算出当前进度播放的循环数。
        t *= m_pAnimation->getLoops();

		 // 通过先取整再判断是否大于当前的已经循环次数来判断是否是新的循环,如果是将下一帧置零,已经循环的次数加1 。
        unsigned int loopNumber = (unsigned int)t;
        if( loopNumber > m_uExecutedLoops ) {
            m_nNextFrame = 0;
            m_uExecutedLoops++;
        }

        // 对t进行浮点取模值,将其限制在0~1之间,这样等于又转换成为了当前动画播放的进度值。
        t = fmodf(t, 1.0f);
    }
	//取得动画的帧信息容器和帧数。
    CCArray* frames = m_pAnimation->getFrames();
    unsigned int numberOfFrames = frames->count();
    //精灵图片信息。
CCSpriteFrame *frameToDisplay = NULL;
	//找出要播放的帧图片设置为精灵要显示的图片,这里用了一个for循环。从下一帧开始到结束帧进行遍历,判断是否到了这一帧。
for( unsigned int i=m_nNextFrame; i < numberOfFrames; i++ ) {
	 //取出循环中的当前帧的播出时间进度。
        float splitTime = m_pSplitTimes->at(i);
		//如果这一帧的进度小于当前动画的播放进度,即代表进入了这一帧。
        if( splitTime <= t ) {
			//取得对应的动画帧信息。
            CCAnimationFrame* frame = (CCAnimationFrame*)frames->objectAtIndex(i);
			 //取得当前帧的精灵图片信息。
            frameToDisplay = frame->getSpriteFrame();
			 //设置为精灵的当前显示图片。
            ((CCSprite*)m_pTarget)->setDisplayFrame(frameToDisplay);
			 //通过动画帧信息取得其字典信息。
            CCDictionary* dict = frame->getUserInfo();
            if( dict )
            {
				  //忽略了。
                //TODO: [[NSNotificationCenter defaultCenter] postNotificationName:CCAnimationFrameDisplayedNotification object:target_ userInfo:dict];
            }
			 //帧数加一。
            m_nNextFrame = i+1;

            break;
        }
    }
}
//创建一个反向播放的序列帧动画。
CCActionInterval* CCAnimate::reverse(void)
{
    CCArray* pOldArray = m_pAnimation->getFrames();
    CCArray* pNewArray = CCArray::createWithCapacity(pOldArray->count());
   
    CCARRAY_VERIFY_TYPE(pOldArray, CCAnimationFrame*);

    if (pOldArray->count() > 0)
    {
        CCObject* pObj = NULL;
        CCARRAY_FOREACH_REVERSE(pOldArray, pObj)
        {
            CCAnimationFrame* pElement = (CCAnimationFrame*)pObj;
            if (! pElement)
            {
                break;
            }

            pNewArray->addObject((CCAnimationFrame*)(pElement->copy()->autorelease()));
        }
    }

    CCAnimation *newAnim = CCAnimation::create(pNewArray, m_pAnimation->getDelayPerUnit(), m_pAnimation->getLoops());
    newAnim->setRestoreOriginalFrame(m_pAnimation->getRestoreOriginalFrame());
    return create(newAnim);
}

CCTargetedAction:

//控制动画:就是让目标演员演示一个目标动画。
class CC_DLL CCTargetedAction : public CCActionInterval
{
public:
	//构造与析构。
    CCTargetedAction();
    virtual ~CCTargetedAction();
    //创建一个控制动画,内部调用create实现。参一为演示目标动画的演员,参二为目标动画。
    CC_DEPRECATED_ATTRIBUTE static CCTargetedAction* actionWithTarget(CCNode* pTarget, CCFiniteTimeAction* pAction);
    //创建一个控制动画。
    static CCTargetedAction* create(CCNode* pTarget, CCFiniteTimeAction* pAction);

    //初始化。
    bool initWithTarget(CCNode* pTarget, CCFiniteTimeAction* pAction);
	//重载相应函数。
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void stop(void);
    virtual void update(float time);

    //定义变量m_pForcedTarget存放初始化参数目标演员。
    CC_SYNTHESIZE_RETAIN(CCNode*, m_pForcedTarget, ForcedTarget);
private:
	//定义变量m_pAction存放初始化参数目标动画。
    CCFiniteTimeAction* m_pAction;
};
具体实现:
//构造函数。
CCTargetedAction::CCTargetedAction()
: m_pForcedTarget(NULL)
, m_pAction(NULL)
{

}
//析构函数。
CCTargetedAction::~CCTargetedAction()
{
    CC_SAFE_RELEASE(m_pForcedTarget);
    CC_SAFE_RELEASE(m_pAction);
}
//创建一个控制动画,内部调用create实现。参一为演示目标动画的演员,参二为目标动画。
CCTargetedAction* CCTargetedAction::actionWithTarget(CCNode* pTarget, CCFiniteTimeAction* pAction)
{
    return CCTargetedAction::create(pTarget, pAction);
}
//创建一个控制动画。
CCTargetedAction* CCTargetedAction::create(CCNode* pTarget, CCFiniteTimeAction* pAction)
{
	//又是一条龙。
    CCTargetedAction* p = new CCTargetedAction();
    p->initWithTarget(pTarget, pAction);
    p->autorelease();
    return p;
}

//初始化
bool CCTargetedAction::initWithTarget(CCNode* pTarget, CCFiniteTimeAction* pAction)
{
	//调用基类的初始化函数。
    if(CCActionInterval::initWithDuration(pAction->getDuration()))
	{
	//干掉旧演员,推出新演员。
        CC_SAFE_RETAIN(pTarget);
        m_pForcedTarget = pTarget;
	//干掉旧动画,演示新动画。
        CC_SAFE_RETAIN(pAction);
        m_pAction = pAction;
	//初始化成功,返回true.
        return true;
	}
	//初始化失败返回false.
    return false;
}
//创建拷贝。
CCObject* CCTargetedAction::copyWithZone(CCZone* pZone)
{
    CCZone* pNewZone = NULL;
    CCTargetedAction* pRet = NULL;
    if(pZone && pZone->m_pCopyObject) //in case of being called at sub class
    {
        pRet = (CCTargetedAction*)(pZone->m_pCopyObject);
    }
    else
    {
        pRet = new CCTargetedAction();
        pZone = pNewZone = new CCZone(pRet);
    }
    CCActionInterval::copyWithZone(pZone);
    // win32 : use the m_pOther's copy object.
    pRet->initWithTarget(m_pTarget, (CCFiniteTimeAction*)m_pAction->copy()->autorelease()); 
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}
//设置演示当前动画的演员,话是这么说,但实际这个函数的功能只是在这个时候可以对目标动画进行相应的设置。
void CCTargetedAction::startWithTarget(CCNode *pTarget)
{
	//调用基类相应函数。
	CCActionInterval::startWithTarget(m_pForcedTarget);
	//这里使用目标演员来设置为演示目标动画的演。
    m_pAction->startWithTarget(m_pForcedTarget);
}
//停止目标动画。
void CCTargetedAction::stop(void)
{
    m_pAction->stop();
}
//更新目标动画。
void CCTargetedAction::update(float time)
{
    m_pAction->update(time);
}

NS_CC_END

#endif //__ACTION_CCINTERVAL_ACTION_H__

Cocos2d-x 2.0 之 Actions “三板斧” 之二


        这一斧子下去,真心痛啊!不过不要紧,还有一斧子,最后一斧子哦~.

最后,敬告转载文章不写出处的网站,请看在小生这么拼命写博为你们赚钱的份上,好歹写一下文章出处~

分类: cocos2d, cocos2d-x 标签:

Cocos2d-x 2.0 之 Actions “三板斧” 之一

2012年11月19日 没有评论

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ群:249941957 [暂满]加群写:Cocos2d-x 

红孩儿Cocos2d-X学习园地QQ2群:44208467 加群写:Cocos2d-x 

本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!

Cocos2d-x 2.0 Actions “三板斧之一

另:本章所用Cocos2d-x版本为:

cocos2d-2.0-x-2.0.2 @ Aug 30 2012

http://cn.cocos2d-x.org/download

 

         之前我们讲解了Cocos2d-x中的TestCpp工程,它包含了43个小示例。每个示例都是通过Controller.cpp中的CreateTestScene函数创建的场景来进行演示的,从今天起,我们来详细学习每一个示例。今天我们来学习第一个示例:”ActionsTest”

 

         “ActionsTest”– 这个工程要演示的是什么节目呢?它演示了精灵的各种动画。包括移动,旋转,缩放,跳跃,色彩变化,以及路径动画,帧动画等。这个示例是学习掌握图片精灵的一个很好的教材。注:虽然很明显,这个”ActionsTest”应该称之为“动作”演示,但我更想说“动作”这个词在表达本节众多动画的演示时太狭窄和片面,没有“动画”这个词合适,所以我在这里称之为“动画”。


         为了更好的讲解Actions,我将本章分为三节课,话为“三板斧”。第一斧子,讲解各种Actions的原理。第二斧子,讲解各种衍生的Actions派生类,第三斧子,在前两斧子的基础上捋一下ActionsTest工程源码。相信三斧子下去,你会对于Cocos2d-x引擎的Actions有个系统的掌握。

好,现在我们开始举起斧子,准备好迎接新知识

一,动画基类

话说,一切的Actions都是由基类CCAction派生的,这个类描述了一个基本的动画控制方法。打开CCAction.h,我们来分析一下源码:

#ifndef __ACTIONS_CCACTION_H__
#define __ACTIONS_CCACTION_H__
#include "cocoa/CCObject.h"
#include "cocoa/CCGeometry.h"
#include "platform/CCPlatformMacros.h"
//Cocos2d命名空间
NS_CC_BEGIN
//定义一个枚举值来代表默认Tag无效值
enum {
    kCCActionTagInvalid = -1,
};
//基础动画描述,其规定了一个动画所必须有的属性和接口。
class CC_DLL CCAction : public CCObject 
{
public:
	//构造与析构
    CCAction(void);
    virtual ~CCAction(void);
	//取得当前动画的描述
    const char* description();

	//产生一个当前类的实例对象拷贝
    virtual CCObject* copyWithZone(CCZone *pZone);

    //返回动画是否播放结束了
    virtual bool isDone(void);

    //设置将要演示本动画的结点,这里称为“演员”好了,在动作开始前调用。
    virtual void startWithTarget(CCNode *pTarget);

   //设置动画停止结束,并将本动画的“演员”置空。
    virtual void stop(void);

    //重点:每一帧传入时间流逝值,计算动画的进度。
    virtual void step(float dt);

   //超级重点:传入进度参数,进行相应的动画播放计算。这个参数time取0~1,比如0代表动画开始播放,0.5代表播放到一半,1代表播放到结尾。本函数是个虚拟函数,所有由CCAction派生的子类都可以重载此函数来处理自已的动画算法以达到不同的动画效果,比如移动动画,那这里可以进行移动插值算法,淡入淡出动画,可以进行淡入淡出插值算法。
    virtual void update(float time);
	//取得当前表演该动画的“演员”,哈,我们姑且用“演员”来表示演示动画的结点。
    inline CCNode* getTarget(void) { return m_pTarget; }
    //设置当前表演该动画的“演员”
    inline void setTarget(CCNode *pTarget) { m_pTarget = pTarget; }
    //取得最初设置的“演员”
    inline CCNode* getOriginalTarget(void) { return m_pOriginalTarget; } 
    //设置最初设置的“演员”
    inline void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; }
	//取得当前动画的用户标记。你可以设一个查询值,以方便的在外部来寻询到当前动画。
    inline int getTag(void) { return m_nTag; }
	//设置当前动画的用户标记。
    inline void setTag(int nTag) { m_nTag = nTag; }

public:
    //创建并返回一个CCAction实例,内部是通过调用create函数来实现。
    CC_DEPRECATED_ATTRIBUTE static CCAction* action();

    //上面函数的实现。
    static CCAction* create();
protected:
	//最初设置的“演员”。
    CCNode    *m_pOriginalTarget;
	//当前设置的“演员”。
    CCNode    *m_pTarget;
    //用户标记
    int     m_nTag;
};

//时间动画:在基础动画的基础上加入了时间控制方面的属性和接口。
class CC_DLL CCFiniteTimeAction : public CCAction
{
public:
	//构造与析构
    CCFiniteTimeAction()
        : m_fDuration(0)
    {}
    virtual ~CCFiniteTimeAction(){}
    //取得动画的时长,单位是秒。
    inline float getDuration(void) { return m_fDuration; }
    //设置动画的时长,单位是秒。
    inline void setDuration(float duration) { m_fDuration = duration; }
	//返回一个反向播放的动画,这是什么意思呢?在CCAction的类说明中我们知道它最重要的函数是step和update,依靠step计算出来的进度值传入update来显示相应的动画帧。一般来说时间进度的变化是从0~1,只要我们能让进度从1~0的进度变化就可以实现反向动画。
    virtual CCFiniteTimeAction* reverse(void);
protected:
    //动画的时长。
    float m_fDuration;
};

class CCActionInterval;
class CCRepeatForever;

//可调节目标动画播放速度的动画
class CC_DLL CCSpeed : public CCAction
{
public:
	//构造与析构函数
    CCSpeed()
        : m_fSpeed(0.0)
        , m_pInnerAction(NULL)
    {}
    virtual ~CCSpeed(void);
	//取得动画播放速度
    inline float getSpeed(void) { return m_fSpeed; }
    //设置动画播放速度
    inline void setSpeed(float fSpeed) { m_fSpeed = fSpeed; }

    //初始化动画,参数一是一个CCActionInterval,它是一个匀速动画,代表了当前可调节速度的动画所控制的动画。参数二是手动指定的动画播放速度。
    bool initWithAction(CCActionInterval *pAction, float fSpeed);
	//CCAction的函数重载
    virtual CCObject* copyWithZone(CCZone *pZone);
    virtual void startWithTarget(CCNode* pTarget);
    virtual void stop();
    virtual void step(float dt);
    virtual bool isDone(void);
	//产生一个反向播放动画
    virtual CCActionInterval* reverse(void);
	//设置一个要控制的动画,这里称为“内部动画”,即当前动画的效果作用于另外的动画目标。
    void setInnerAction(CCActionInterval *pAction);
	//取得所控制的“内部动画”
    inline CCActionInterval* getInnerAction()
    {
        return m_pInnerAction;
    }

public:
//创建一个可调节播放速度的动画,内部调用create来实现。参一为“内部动画”,参二为播放速度。
    CC_DEPRECATED_ATTRIBUTE static CCSpeed* actionWithAction(CCActionInterval *pAction, float fSpeed);
    
    //创建一个可调节播放速度的动画
    static CCSpeed* create(CCActionInterval* pAction, float fSpeed);
protected:
	//播放速度
    float m_fSpeed;
	//控制的“内部动画”
    CCActionInterval *m_pInnerAction;
};

//跟随动画:控制演员跟随目标结点的动画运动。
class CC_DLL CCFollow : public CCAction
{
public:
	//构造与析构函数
    CCFollow()
        : m_pobFollowedNode(NULL)
        , m_bBoundarySet(false)
        , m_bBoundaryFullyCovered(false)        
        , m_fLeftBoundary(0.0)
        , m_fRightBoundary(0.0)
        , m_fTopBoundary(0.0)
        , m_fBottomBoundary(0.0)
    {}
    virtual ~CCFollow(void);
    //是否有边界
    inline bool isBoundarySet(void) { return m_bBoundarySet; }
    //设置是否有边界
    inline void setBoudarySet(bool bValue) { m_bBoundarySet = bValue; }

    //初始化,参数1为跟随目标结点,参数2为边界矩形,默认为CCRectZero代表无边界。
    bool initWithTarget(CCNode *pFollowedNode, const CCRect& rect = CCRectZero);
	//CCAction的函数重载
    virtual CCObject* copyWithZone(CCZone *pZone);
    virtual void step(float dt);
    virtual bool isDone(void);
    virtual void stop(void);

public:
//创建一个跟随动画,其内部调用create函数来实现。
    CC_DEPRECATED_ATTRIBUTE static CCFollow* actionWithTarget(CCNode *pFollowedNode, const CCRect& rect = CCRectZero);
//创建一个跟随动画
    static CCFollow* create(CCNode *pFollowedNode, const CCRect& rect = CCRectZero);
protected:
    //跟随的结点
    CCNode *m_pobFollowedNode;

    //是否被限制在边界内
    bool m_bBoundarySet;

    //边界是否只是一个点,代表边界无效。
    bool m_bBoundaryFullyCovered;

    //定义变量保存屏幕尺寸信息,是为了快速取得。
	//屏幕一半大小
    CCPoint m_obHalfScreenSize;
	//屏幕全部大小
    CCPoint m_obFullScreenSize;

    //边界的左,右,上,下的位置。
    float m_fLeftBoundary;
    float m_fRightBoundary;
    float m_fTopBoundary;
    float m_fBottomBoundary;
};

NS_CC_END

#endif // __ACTIONS_CCACTION_H__

       看这个头文件,除了CCAction之外还有几个由CCAction派生的类的定义,将它们都放在CCAction的意义是认为所有的动画都是由这几个类所派生的。

       我们再来看一下CPP它们都是怎么实现的:

#include "CCAction.h"
#include "CCActionInterval.h"
#include "base_nodes/CCNode.h"
#include "support/CCPointExtension.h"
#include "CCDirector.h"
#include "cocoa/CCZone.h"
//使用Cocos2d命名空间
NS_CC_BEGIN
//构造
CCAction::CCAction()
:m_pOriginalTarget(NULL)
,m_pTarget(NULL)
,m_nTag(kCCActionTagInvalid)
{
}
//析构
CCAction::~CCAction()
{
    CCLOGINFO("cocos2d: deallocing");
}
//创建并返回一个CCAction实例,内部是通过调用create函数来实现。
CCAction * CCAction::action()
{
    return CCAction::create();
}
//创建并返回一个CCAction实例。
CCAction* CCAction::create()
{
    CCAction * pRet = new CCAction();
	//新创建的CCAction设为由内存管理器进行释放。
    pRet->autorelease();
    return pRet;
}
//取得当前动画的描述
const char* CCAction::description()
{
	//描述信息通过CCString的格式化字符串函数createWithFormat来创建字符串。
    return CCString::createWithFormat("<CCAction | Tag = %d>", m_nTag)->getCString();
}
//产生一个当前类的实例对象拷贝
CCObject* CCAction::copyWithZone(CCZone *pZone)
{
	//定义用于保存新拷贝指针的变量,参看CCZone的定义可以知道,它的功能仅仅是保存一个CCObject指针。
    CCZone *pNewZone = NULL;
	//定义用于返回结果的CCAction指针变量
    CCAction *pRet = NULL;
	//如果pZone不为空且它保存了有效的CCObject,则返回
    if (pZone && pZone->m_pCopyObject)
    {
        pRet = (CCAction*)(pZone->m_pCopyObject);
    }
    else
    {
		//否则手动创建一个新的动作实例,返回给pRet
        pRet = new CCAction();
		//这一句其实可以不加,因为后面直接释放了,没有使用。
        pNewZone = new CCZone(pRet);
    }
    //拷贝动作的用户数据
    pRet->m_nTag = m_nTag;
	//释放pNewZone。
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}
//设置当前动画的演员。
void CCAction::startWithTarget(CCNode *aTarget)
{
    m_pOriginalTarget = m_pTarget = aTarget;
}
//停止动画
void CCAction::stop()
{
    m_pTarget = NULL;
}
//取得动画是否播放结束
bool CCAction::isDone()
{
    return true;
}
//每一帧传入时间流逝值,计算动画的进度。在基类中并未做相应处理。需要子类进行实现。
void CCAction::step(float dt)
{
    CC_UNUSED_PARAM(dt);
    CCLOG("[Action step]. override me");
}
//传入进度参数,进行相应的动画播放计算。
void CCAction::update(float time)
{
    CC_UNUSED_PARAM(time);
    CCLOG("[Action update]. override me");
}

//时间动画的反向动画播放。基类中也并未做相应处理。需要子类进行实现。
CCFiniteTimeAction *CCFiniteTimeAction::reverse()
{
    CCLOG("cocos2d: FiniteTimeAction#reverse: Implement me");
    return NULL;
}

//
//可调节播放速度的动画
//
//析构函数。
CCSpeed::~CCSpeed()
{
    CC_SAFE_RELEASE(m_pInnerAction);
}
//创建一个可调节播放速度的动画,内部调用create来实现。参一为“内部动画”,参二为播放速度。
CCSpeed * CCSpeed::actionWithAction(CCActionInterval *pAction, float fSpeed)
{
    return CCSpeed::create(pAction, fSpeed);
}
//创建一个可调节播放速度的动画。
CCSpeed* CCSpeed::create(CCActionInterval* pAction, float fSpeed)
{
	//创建一个可调节播放速度的动画返回其实例指针给变量pRet。
    CCSpeed *pRet = new CCSpeed();
	//如果返回的动画指针有效,并且初始化成功。将其交由内存管理器进行释放并返回动画指针。
    if (pRet && pRet->initWithAction(pAction, fSpeed))
    {
        pRet->autorelease();
        return pRet;
    }
	//这是创建失败或者初始化失败的处理,释放并返回NULL。
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//初始化动画。
bool CCSpeed::initWithAction(CCActionInterval *pAction, float fSpeed)
{
	//参数有效性判断
    CCAssert(pAction != NULL, "");
	//因使用pAction,将其引用计数加1
    pAction->retain();
	//将参数“内部动画”指针pAction保存入变量m_pInnerAction。
    m_pInnerAction = pAction;
	//将速度值参数保存入变量m_fSpeed。
    m_fSpeed = fSpeed;    
    return true;
}
//产生一个当前类的实例对象拷贝
CCObject *CCSpeed::copyWithZone(CCZone *pZone)
{
	//定义指针变量
    CCZone* pNewZone = NULL;
    CCSpeed* pRet = NULL;
	//如果pZone有效且本身有拷贝
    if(pZone && pZone->m_pCopyObject) 
    {
		//返回这个拷贝存入变量pRet
        pRet = (CCSpeed*)(pZone->m_pCopyObject);
    }
    else
    {
		//如果pZone为空或没有拷贝,则创建一个CCSpeed返回给pRet,并由pRet做为拷贝取得一个新的CCZone返回给pZone。

        pRet = new CCSpeed();
        pZone = pNewZone = new CCZone(pRet);
    }
	//调用基类函数(对比基类的函数,可以得知这一句的意义是拷贝动作的用户数据到pRet和释放pZone)
    CCAction::copyWithZone(pZone); 
//初始化动画,这里的关健是通过取得内部动画的拷贝做为参数进行初始化。
pRet->initWithAction( (CCActionInterval*)(m_pInnerAction->copy()->autorelease()) , m_fSpeed );
    //释放pNewZone
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}
//设置将要演示本动画的结点
void CCSpeed::startWithTarget(CCNode* pTarget)
{
    CCAction::startWithTarget(pTarget);
    m_pInnerAction->startWithTarget(pTarget);
}
//停止动画
void CCSpeed::stop()
{
	//停止内部动画
    m_pInnerAction->stop();
    CCAction::stop();
}
//每一帧传入时间流逝值,计算动画的进度。
void CCSpeed::step(float dt)
{
	//对内部动画进行相关计算,这里通过对时间流逝值的缩放实现了播放速度的调节。
    m_pInnerAction->step(dt * m_fSpeed);
}
//取得动画是否结束
bool CCSpeed::isDone()
{
	//返回内部动画是否结束
    return m_pInnerAction->isDone();
}
//创建一个反向播放动画。
CCActionInterval *CCSpeed::reverse()
{
	//返回一个内部动画的反向播放动画。
     return (CCActionInterval*)(CCSpeed::create(m_pInnerAction->reverse(), m_fSpeed));
}
//设置内部动画。
void CCSpeed::setInnerAction(CCActionInterval *pAction)
{
	//在这里的处理是释放旧的内部动画,设置新的内部动画。
    if (m_pInnerAction != pAction)
    {
        CC_SAFE_RELEASE(m_pInnerAction);
        m_pInnerAction = pAction;
        CC_SAFE_RETAIN(m_pInnerAction);
    }
}

// 跟随动画
//析构函数
CCFollow::~CCFollow()
{
	//释放跟随的目标结点
    CC_SAFE_RELEASE(m_pobFollowedNode);
}
//新创建一个跟随动画,参数1是跟随的目标,参数2是边界矩形。
CCFollow *CCFollow::actionWithTarget(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
    return CCFollow::create(pFollowedNode, rect);
}
//新创建一个跟随动画。
CCFollow* CCFollow::create(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
	//创建一个跟随动画,并初始化。
    CCFollow *pRet = new CCFollow();
    if (pRet && pRet->initWithTarget(pFollowedNode, rect))
    {
		//如果创建成功并初始化成功则将其交由内存管理器进行释放处理。
        pRet->autorelease();
        return pRet;
    }
	//如果创建失败或初始化失败,释放pRet并返回NULL。
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//初始化跟随动画。
bool CCFollow::initWithTarget(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
	//参数跟随结点的有效性判断
    CCAssert(pFollowedNode != NULL, "");
	//因为要用到跟随节点,所以手动对其引用计数器加1。
    pFollowedNode->retain();
	//将参数跟随结点保存到变量m_pobFollowedNode。
    m_pobFollowedNode = pFollowedNode;
	//如果参数2无效,则将边界开关置为false,即无边界。
    if (rect.equals(CCRectZero))
    {
        m_bBoundarySet = false;
    }
    else
    {
		//否则有边界。
        m_bBoundarySet = true;
    }
    //假设边界是有效矩形。
    m_bBoundaryFullyCovered = false;
	//取和屏幕大小
    CCSize winSize = CCDirector::sharedDirector()->getWinSize();
	//将屏幕大小保存到m_obFullScreenSize。
    m_obFullScreenSize = CCPointMake(winSize.width, winSize.height);
	//计算屏幕半大小保存到m_obHalfScreenSize
    m_obHalfScreenSize = ccpMult(m_obFullScreenSize, 0.5f);
	//如果有边界。将边界的四条边存入相应边量中。
    if (m_bBoundarySet)
    {
        m_fLeftBoundary = -((rect.origin.x+rect.size.width) - m_obFullScreenSize.x);
        m_fRightBoundary = -rect.origin.x ;
        m_fTopBoundary = -rect.origin.y;
        m_fBottomBoundary = -((rect.origin.y+rect.size.height) - m_obFullScreenSize.y);
		//数据错误处理:如果右边界小于左边界,则将左右边界都设为中间位置。
        if(m_fRightBoundary < m_fLeftBoundary)
        {
            m_fRightBoundary = m_fLeftBoundary = (m_fLeftBoundary + m_fRightBoundary) / 2;
        }
		//数据错误处理:如果下边界小于上边界,则将上下边界都设为中间位置。
        if(m_fTopBoundary < m_fBottomBoundary)
        {
            m_fTopBoundary = m_fBottomBoundary = (m_fTopBoundary + m_fBottomBoundary) / 2;
        }
		//如果左右边界位置相等且上下边界位置相等,则这个边界矩形就只是一个点。
        if( (m_fTopBoundary == m_fBottomBoundary) && (m_fLeftBoundary == m_fRightBoundary) )
        {
            m_bBoundaryFullyCovered = true;
        }
    }
    
    return true;
}
//产生一个当前类的实例对象拷贝
CCObject *CCFollow::copyWithZone(CCZone *pZone)
{
    CCZone *pNewZone = NULL;
    CCFollow *pRet = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        pRet = (CCFollow*)(pZone->m_pCopyObject);
    }
    else
    {
        pRet = new CCFollow();
        pZone = pNewZone = new CCZone(pRet);
    }
	//调用基类函数(对比基类的函数,可以得知这一句的意义是拷贝动作的用户数据到pRet和释放pZone)
    CCAction::copyWithZone(pZone);
    //上一句已经实现拷贝动作的用户数据到pRet,这里再次进行拷贝,有点多余了。
    pRet->m_nTag = m_nTag;
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}
//每一帧传入时间流逝值,计算动画的进度。
void CCFollow::step(float dt)
{
    CC_UNUSED_PARAM(dt);
	//如果有边界
    if(m_bBoundarySet)
    {
        //边界只是一个点,则直接返回。
        if(m_bBoundaryFullyCovered)
            return;
		//定义一个位置点做为相对于屏幕中心点的位置。这个位置点等于屏幕中心点减去跟随结点的位置。
        CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
		//把演示此动画的结点位置设置在边界范围内。
        m_pTarget->setPosition(ccp(clampf(tempPos.x, m_fLeftBoundary, m_fRightBoundary), 
                                   clampf(tempPos.y, m_fBottomBoundary, m_fTopBoundary)));
    }
    else
    {
		//定义一个位置点做为相对于屏幕中心点的位置。这个位置点等于屏幕中心点减去跟随结点的位置。设置演示此动画的结点的位置。
        m_pTarget->setPosition(ccpSub(m_obHalfScreenSize, m_pobFollowedNode->getPosition()));
    }
}
//判断动画是否结束。
bool CCFollow::isDone()
{
	//这里是判断跟随的结点是否不在运行中
    return ( !m_pobFollowedNode->isRunning() );
}
//停止动画
void CCFollow::stop()
{
    m_pTarget = NULL;
    CCAction::stop();
}

NS_CC_END

 

    总结:我们在这一节分析了CCAction, CCFiniteTimeAction, CCSpeed,CCFollow四个类:

    CCAction:一切动画的基类。它需要传入一个结点来做为表演动画的“演员”。

    CCFiniteTimeAction:加入了时间属性的动画基类,用于派生各种由时间插值进行计算的动画。

    CCSpeed:用来控制其它动画播放速度的动画,它需要传入一个动画来做控制目标。

    CCFollow:用来控制演员跟随动画移动的基类,它需要传入一个结点来做跟随目标。

    有了以上这几个基类,在后面我们就可以理解很多很多种各种各样的动画是如何创造出来的。

二.动画管理器

     动画是怎么管理和执行的呢。在Cocos2d-x中提供了一个动画管理器类CCActionManager。这个动画管理器负责对所有的动画进行管理。我们来学习一下它。

CCActionManager.h:

#ifndef __ACTION_CCACTION_MANAGER_H__
#define __ACTION_CCACTION_MANAGER_H__
//引用相应的头文件
//动画头文件
#include "CCAction.h"
//CCObject实例指针管理器
#include "cocoa/CCArray.h"
#include "cocoa/CCObject.h"
//使用Cocos2d命名空间
NS_CC_BEGIN
//要用到CCSet类进行存储
class CCSet;
//使用哈希表元素
struct _hashElement;

class CC_DLL CCActionManager : public CCObject
{
public:
	//构造与析构函数
    CCActionManager(void);
    ~CCActionManager(void);

	//将一个CCNode及所应用的动画放入管理器,末参数指定是否立即播放。
    void addAction(CCAction *pAction, CCNode *pTarget, bool paused);

    //移除所有的动画
    void removeAllActions(void);

    //移除指定目标所应用的所有的动画
    void removeAllActionsFromTarget(CCObject *pTarget);

    //移除所有的指定动画
    void removeAction(CCAction *pAction);

    //移除指定目标所应用的某标记的动画
    void removeActionByTag(unsigned int tag, CCObject *pTarget);

    //取得指定目标所应用的某标记的动画
    CCAction* getActionByTag(unsigned int tag, CCObject *pTarget);

    //返回指定目标所应用的动画数量
    unsigned int numberOfRunningActionsInTarget(CCObject *pTarget);

    //停止指定目标的所有的动画。
    void pauseTarget(CCObject *pTarget);

//继续播放指定目标的所有动画
    void resumeTarget(CCObject *pTarget);
    
	//暂停所有正在动行的动画,并将这些暂停的动画存入CCSet中返回。
    CCSet* pauseAllRunningActions();
    
   //继结播放CCSet中的所有动画。
    void resumeTargets(CCSet *targetsToResume);

protected:
	//
void removeActionAtIndex(unsigned int uIndex, struct _hashElement *pElement);
//删除哈希表中的相应项
void deleteHashElement(struct _hashElement *pElement);
//为pElement申请内存存放动画集。
void actionAllocWithHashElement(struct _hashElement *pElement);
//这个函数重要了~,它在每一帧被调用,通过传入的时间流逝长度传给每一个非暂停的动画进行相应的更新。
    void update(float dt);

protected:
	//
    struct _hashElement    *m_pTargets;
    struct _hashElement    *m_pCurrentTarget;
    bool            m_bCurrentTargetSalvaged;
};

NS_CC_END

#endif // __ACTION_CCACTION_MANAGER_H__

老规矩,上CPP

#include "CCActionManager.h"
#include "base_nodes/CCNode.h"
#include "CCScheduler.h"
#include "ccMacros.h"
#include "support/data_support/ccCArray.h"
#include "support/data_support/uthash.h"
#include "cocoa/CCSet.h"
//使用Cocos2d命名空间
NS_CC_BEGIN
//这里定义了一个哈希表项结构,用于存储一个演员的动画集相关信息。
typedef struct _hashElement
{
    struct _ccArray             *actions;	//动画集
    CCObject                    *target;	//演员的指针
    unsigned int                actionIndex;//动画在动画集中的索引。
    CCAction                    *currentAction;//当前正在播放的动画
    bool                        currentActionSalvaged;//当前动画是否被回收。
    bool                        paused;//是否停止播放。
    UT_hash_handle                hh;//哈希表查询句柄。
} tHashElement;

//构造函数
CCActionManager::CCActionManager(void)
: m_pTargets(NULL), 
  m_pCurrentTarget(NULL),
  m_bCurrentTargetSalvaged(false)
{

}

//析构函数
CCActionManager::~CCActionManager(void)
{
    CCLOGINFO("cocos2d: deallocing %p", this);
	//清空所有的动画
    removeAllActions();
}

//删除相应的哈希表项
void CCActionManager::deleteHashElement(tHashElement *pElement)
{
	//释放pElement存储的动画集
    ccArrayFree(pElement->actions);
	//从哈希表m_pTargets中删除pElement项
    HASH_DEL(m_pTargets, pElement);
	//释放pElement
    pElement->target->release();
    free(pElement);
}
//为哈希表项pElement申请内存,以存放动画集
void CCActionManager::actionAllocWithHashElement(tHashElement *pElement)
{
    // 默认每个哈希表项存四个动画,所以如果当前哈希表项的动画集为空,则为其申请可存放4个动画指针的内存大小。
    if (pElement->actions == NULL)
    {
        pElement->actions = ccArrayNew(4);
    }else 
    if (pElement->actions->num == pElement->actions->max)
    {	//如果哈希表项的动作集需要扩大,则扩增为原来的2倍直至达到最大容量。
        ccArrayDoubleCapacity(pElement->actions);
    }

}
//从哈希表项的动画集中移除掉指定的动画。
void CCActionManager::removeActionAtIndex(unsigned int uIndex, tHashElement *pElement)
{
	//通过参数uIndex找到哈希表项的动画集中的对应动画。
    CCAction *pAction = (CCAction*)pElement->actions->arr[uIndex];

	//如果当前动画正在播放中而且回收标记为否
    if (pAction == pElement->currentAction && (! pElement->currentActionSalvaged))
    {
		//引用计数器加1,代表被管理器使用中。
        pElement->currentAction->retain();
		//设其回收标记为true。
        pElement->currentActionSalvaged = true;
    }
	//从动画集中移除指定位置的动画。
    ccArrayRemoveObjectAtIndex(pElement->actions, uIndex, true);
	//
    if (pElement->actionIndex >= uIndex)
    {
        pElement->actionIndex--;
    }
	//如果pElement的动画集的数量为0
    if (pElement->actions->num == 0)
    {
		 //如果当前正有演员在演示本动画,那么先将其要回收标记设为true 。
        if (m_pCurrentTarget == pElement)
        {
            m_bCurrentTargetSalvaged = true;
        }
        else
        {
			  //否则立即释放
            deleteHashElement(pElement);
        }
    }
}
//暂停目标演员身上的所有动画
void CCActionManager::pauseTarget(CCObject *pTarget)
{
	//声明一个哈希表项指针
    tHashElement *pElement = NULL;
	//通过哈希表处理相关宏找到对应的哈希表项。
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);
	//如果找到,则将其暂停开关变量设为true
    if (pElement)
    {
        pElement->paused = true;
    }
}
//继续目标演员身上的所有动画
void CCActionManager::resumeTarget(CCObject *pTarget)
{
	//定义一个哈希表项指针变量并置空。
    tHashElement *pElement = NULL;
	//通过哈希表处理相关宏找到对应的哈希表项。
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);
	//如果找到,则将其暂停开关变量设为false,使其处于播放状态。
    if (pElement)
    {
        pElement->paused = false;
    }
}
//暂停所有正在播放中的动画
CCSet* CCActionManager::pauseAllRunningActions()
{
	//创建一个CCSet容器
    CCSet *idsWithActions = new CCSet();
	//此容器交由内存管理器进行内存的最后释放。无需手动delete。
    idsWithActions->autorelease();
    //遍历所有的哈希表项
    for (tHashElement *element=m_pTargets; element != NULL; element = (tHashElement *)element->hh.next) 
    {
		//如果当前遍历位置的哈希表项处于播放状态,设置其暂停并将其放入CCSet容器中。
        if (! element->paused) 
        {
            element->paused = true;
            idsWithActions->addObject(element->target);
        }
    }    
    //返回容器。
    return idsWithActions;
}
//继续播放容器中的所有动画
void CCActionManager::resumeTargets(cocos2d::CCSet *targetsToResume)
{    
	//定义CCSet容器的迭代器。之后遍历参数容器。将每一个项设为播放状态。
    CCSetIterator iter;
    for (iter = targetsToResume->begin(); iter != targetsToResume->end(); ++iter)
    {
        resumeTarget(*iter);
    }
}

//将一个动画和应用此动画的演员,以及当前动画是否处于暂停状态信息存入管理器中。
void CCActionManager::addAction(CCAction *pAction, CCNode *pTarget, bool paused)
{
	//有效性判断。
    CCAssert(pAction != NULL, "");
    CCAssert(pTarget != NULL, "");
	//定义一个哈希表项的指针变量并置空。
    tHashElement *pElement = NULL;
    //将pTarget转为一个CCObject类型的指针。用于哈希表查询。
    CCObject *tmp = pTarget;
	//通过这个指针,找到对应的哈希表项返回给pElement;
    HASH_FIND_INT(m_pTargets, &tmp, pElement);
	//如果找不到。则代表新加入的哈希表项。则申请内存创建此哈希表项,并将其加入哈希表中。
    if (! pElement)
    {
		//创建一个哈希表项
        pElement = (tHashElement*)calloc(sizeof(*pElement), 1);
	     //设置其是否为暂停状态。
        pElement->paused = paused;
		 //引用计数器加1,代表被管理器使用中。
        pTarget->retain();
		 //设置哈希表项中的演员为参数指定的演员。
        pElement->target = pTarget;
		 //调用哈希表处理宏将哈希表项加入哈希表。
        HASH_ADD_INT(m_pTargets, target, pElement);
    }
	//为哈希表项pElement申请内存,以存放动画集
     actionAllocWithHashElement(pElement);
 	//判断pAction是否在pElement的动画集中。确保只放入一次。
     CCAssert(! ccArrayContainsObject(pElement->actions, pAction), "");
	//将pAction放入pElement的动画集中。
     ccArrayAppendObject(pElement->actions, pAction);
 	 //设置是哪个CCNode要进行当前动画
     pAction->startWithTarget(pTarget);
}

//清除所有的动画
void CCActionManager::removeAllActions(void)
{
	//遍历哈希表中的所有项
    for (tHashElement *pElement = m_pTargets; pElement != NULL; )
    {
		//取得对应项信息中的演员指针
        CCObject *pTarget = pElement->target;
		//这里是用于继续循环的处理,将当前项指向下一个哈希表项。
        pElement = (tHashElement*)pElement->hh.next;
		//释放演员的所有动画
        removeAllActionsFromTarget(pTarget);
    }
}

//删除对应演员的所有动画
void CCActionManager::removeAllActionsFromTarget(CCObject *pTarget)
{
    // 有效性判断
    if (pTarget == NULL)
    {
        return;
    }
	//定义一个哈希表项指针,并通过哈希表的查询宏取得对应项地址返回给指针。
    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);
	//如果找到此项
    if (pElement)
    {   
        //如果此哈希表项有正在播放的动画并且这个动画并未被设为要回收。
        if (ccArrayContainsObject(pElement->actions, pElement->currentAction) && (! pElement->currentActionSalvaged))
        {
			  //将当前正在播放的动画的引用计数器加1并设置其回收标记为true。引用计数加1的目的是人为使其暂不能被正常释放,需要待后面再减1后才可以被释放。
            pElement->currentAction->retain();
            pElement->currentActionSalvaged = true;
        }
		 //清空当前哈希表项的动画集。
        ccArrayRemoveAllObjects(pElement->actions);
		 //如果当前哈希表项正处于使用中,暂不释放,只将其要回收的标记设为true。
        if (m_pCurrentTarget == pElement)
        {
            m_bCurrentTargetSalvaged = true;
        }
        else
        {
			  //释放当前哈希表项
            deleteHashElement(pElement);
        }
    }
    else
    {
//        CCLOG("cocos2d: removeAllActionsFromTarget: Target not found");
    }
}
//将指定动画从动画管理器中移除
void CCActionManager::removeAction(CCAction *pAction)
{
    //有效性判断
    if (pAction == NULL)
    {
        return;
    }
	//定义一个哈希表项指针,并通过哈希表的查询宏取得对应项地址返回给指针。
    tHashElement *pElement = NULL;
    CCObject *pTarget = pAction->getOriginalTarget();
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);
	//如果找到此项
    if (pElement)
    {
		 //取得pAction处于当前项的动画集的索引
        unsigned int i = ccArrayGetIndexOfObject(pElement->actions, pAction);
		 //如果这个索引是有效的,调用函数将pElement的指定索引的动画移除。
        if (UINT_MAX != i)
        {
            removeActionAtIndex(i, pElement);
        }
    }
    else
    {
        CCLOG("cocos2d: removeAction: Target not found");
    }
}
//将指定演员的指定动画删除
void CCActionManager::removeActionByTag(unsigned int tag, CCObject *pTarget)
{
	//有效性判断
    CCAssert((int)tag != kCCActionTagInvalid, "");
    CCAssert(pTarget != NULL, "");
	//定义哈希表项指针变量,并通过哈希表处理宏取得相应哈希表项
    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);
	//如果能找到哈希表项
    if (pElement)
    {
		//取得哈希表项的动画集中的动画数量
        unsigned int limit = pElement->actions->num;
		//遍历动画集中的所有动画
        for (unsigned int i = 0; i < limit; ++i)
        {
			//取得每一项动画
            CCAction *pAction = (CCAction*)pElement->actions->arr[i];
			//查看是否是指定演员的指定动画。
            if (pAction->getTag() == (int)tag && pAction->getOriginalTarget() == pTarget)
            {
				  //如果是,则删除此项。
                removeActionAtIndex(i, pElement);
                break;
            }
        }
    }
}

//取得指定演员的指定动画
CCAction* CCActionManager::getActionByTag(unsigned int tag, CCObject *pTarget)
{
	//有效性判断
    CCAssert((int)tag != kCCActionTagInvalid, "");
	//定义哈希表项指针变量,并通过哈希表处理宏取得相应哈希表项
    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);
	//如果能找到哈希表项
    if (pElement)
    {
		 //如果此哈希表项的动画集不为空
        if (pElement->actions != NULL)
        {
			//取得哈希表项的动画集中的动画数量
            unsigned int limit = pElement->actions->num;
		//遍历动画集中的所有动画

            for (unsigned int i = 0; i < limit; ++i)
            {
			    //取得每一项动画
               CCAction *pAction = (CCAction*)pElement->actions->arr[i];
			    //查看是否是指定动画。
                if (pAction->getTag() == (int)tag)
                {
                    return pAction;
                }
            }
        }
        CCLOG("cocos2d : getActionByTag: Action not found");
    }
    else
    {	 
        // CCLOG("cocos2d : getActionByTag: Target not found");
    }
	//找不到要找的动画。返回NULL。
    return NULL;
}
//取得指定演员的动画集中的动画数量
unsigned int CCActionManager::numberOfRunningActionsInTarget(CCObject *pTarget)
{

	//定义哈希表项指针变量,并通过哈希表处理宏取得相应哈希表项
    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);
    if (pElement)
    {
		 //如果找到了,判断动画集是否为空,如果不为空返回动画数量,否则返回零。
        return pElement->actions ? pElement->actions->num : 0;
    }

    return 0;
}

//动画管理器的更新函数。
void CCActionManager::update(float dt)
{
	//遍历哈希表的所有项。
    for (tHashElement *elt = m_pTargets; elt != NULL; )
    {
		 //将当前哈希表项保存到变量m_pCurrentTarget中。并将此项对应的回收标记m_bCurrentTargetSalvaged 设为false,
        m_pCurrentTarget = elt;
        m_bCurrentTargetSalvaged = false;
		 //如果当前项的动画处于播放状态。
        if (! m_pCurrentTarget->paused)
        {
            //遍历当前项的动画集中的所有动画
            for (m_pCurrentTarget->actionIndex = 0; m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num;
                m_pCurrentTarget->actionIndex++)
            {
				  //取得遍历项的动画
                m_pCurrentTarget->currentAction = (CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex];
				  //如果遍历项动画为空,跳过后遍历下一个动画。
                if (m_pCurrentTarget->currentAction == NULL)
                {
                    continue;
                }
				  //设置回收标记为false。
                m_pCurrentTarget->currentActionSalvaged = false;
				  //更新当前动画的播放
                m_pCurrentTarget->currentAction->step(dt);
				  //如果当前项的回收标记为true,则进行释放处理。
                if (m_pCurrentTarget->currentActionSalvaged)
                {
                    m_pCurrentTarget->currentAction->release();
                } else
                if (m_pCurrentTarget->currentAction->isDone())
                {   //如果当前动画处于结束状态,则停止动画
                    m_pCurrentTarget->currentAction->stop();
					   //为了在removeAction中正确释放动画,这里先创建一个临时变量pAction记录一下要释放的动画。
                    CCAction *pAction = m_pCurrentTarget->currentAction;
                    //在removeAction之前将当前哈希表项中的当前动画设为NULL,否则不能释放。
                    m_pCurrentTarget->currentAction = NULL;
                    removeAction(pAction);
                }

                m_pCurrentTarget->currentAction = NULL;
            }
        }

        //使for循环能够继续
        elt = (tHashElement*)(elt->hh.next);

        // 如果当前哈希表项处于回收状态且其动画集为空,删除此哈希表项。
        if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0)
        {
            deleteHashElement(m_pCurrentTarget);
        }
    }

    // 将变量m_pCurrentTarge置空。
    m_pCurrentTarget = NULL;
}

NS_CC_END

     动画管理器的代码读完了,大概了解了它的功能,但它在哪里使用呢?我们可以打开显示设备管理器CCDirector类的头文件,在其中找到:

CC_PROPERTY(CCActionManager*,m_pActionManager, ActionManager);

并在CPP中搜索m_pActionManager:

 

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(145):   
m_pActionManager = new CCActionManager();

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(146):   m_pScheduler->scheduleUpdateForTarget(m_pActionManager,kCCPrioritySystem, false);

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(175):    CC_SAFE_RELEASE(m_pActionManager);

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(887):    if (m_pActionManager != pActionManager)

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(890):        CC_SAFE_RELEASE(m_pActionManager);

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(891):        m_pActionManager = pActionManager;

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(897):    return m_pActionManager;

 

    第一句在init函数中创建动画管理器。

    第二句在创建后将其加入系统更新函数要调用更新的对象。

    第三句在析构函数中释放动画管理器。

    第四,五,六句在setActionManager中更改动画管理器。

    第七句是取得动画管理器。

    这其中关键是第二句。因为有了它,所以动画管理器的update才会被调用。这样,我们在动画管理器的Update中加断点,启动程序,你可以发现中断并显示调用堆栈。

Cocos2d-x 2.0 之 Actions “三板斧” 之一

 

    流程是这样的:在CCApplication的run函数中,显示设备链调用相应的场景显示函数drawScene来绘制场景,然后调用了CCScheduler的update函数,在这个函数里,对所有注册要调用update的对象指针进行遍历并调用其update函数,而CCActionManager的update会对所有演员的动作调用step函数,最终实现各个动画的播放。

    好了,我们现在呼吸一下,听我把这个背后的故事讲给大家。Cocos2d-x 2.0 之 Actions “三板斧” 之一

 

    CCAction
是 动画基类,它派生出了很多可爱的动画类型,当你要让某个演员去表现一个动画的时候,你就对这个演员使用“runAction”来表演对应的动画。这个演员在收到你的指令后,它把自已的名字(其实是自已的指针)和这个动画一起告诉了显示部门长官
—“设备管理器手下的成员之一 
“动画管理器”,“动画管理器”这家伙在收到演员的需求后,就答应在每一次刷新时更新相应的动画。

 

    你懂了么?如果懂了?咱们就暂时休息一下。后面还有两斧子。

 

分类: cocos2d, cocos2d-x 标签:

Cocos2d-x利用CCSpriteBatchNode提高渲染效率

2012年11月14日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!
原文地址: http://www.cocos2dev.com/?p=331


以前写过一个游戏中的天气系统,今天有人说我的天气系统有点卡,不是很好用。我看下了他的代码,发现他改了我的代码,把CCSpriteBatchNode删除了,直接用addChild,每个sprite都是重新读取的贴图。


我想说,同学呀,你把暴风雪所有特效开启后,你有没有注意下你的游戏场景显示了1000多渲染批次。这能不卡吗?


今天刚好有点时间,我介绍下CCSpriteBatchNode,以及利用它优化游戏渲染效率。


在cocos2d-x 2.x 之后,大家都看到了左下角的FPS变成3行,多了两行数据。

最上面一行是指的当前场景的渲染批次。(简单理解为需要渲染多少个贴图出来)

中间一行是渲染每一帧需要的时间。

最下行就是大家熟悉的FPS。


CCSpriteBatchNode介绍:


1、先说下渲染批次:这是游戏引擎中一个比较重要的优化指标,指的是一次渲染凋用。也就是说,渲染的次数越少,游戏的运行效率越高。

2、CCSpriteBatchNode就是cocos2d-x为了降低渲染批次而建立的一个专门管理精灵的类。


举例介绍:

1、使用CCSprite创建1000个Icon.png到场景中,这样渲染批次就是1000(暂且不考虑其他的精灵)

for(int i = 0;i < 1000;++i){
        int x = arc4random()%960;
        int y = arc4random()%640;
        CCSprite* testIcon = CCSprite::create("Icon.png");
        testIcon->setPosition( ccp(x, y) );
        addChild(testIcon);
}

效果图如下:

Cocos2d-x利用CCSpriteBatchNode提高渲染效率

上面的效果图可以看到,创建了1000个Icon到场景中,这是的FPS是22,渲染批次是1000次


2、使用CCSpriteBatchNode批量渲染,一次渲染就把所有的CCSprite绘制出来。大大降低渲染批次。


举例介绍:

1、使用CCSprite创建1000个Icon.png到场景中,但是这里利用了CCSpriteBatchNode批量渲染。这时的渲染批次、FPS如何呢?

CCSpriteBatchNode* batchNode = CCSpriteBatchNode::create("Icon.png", 1000);
batchNode->setPosition(CCPointZero);
this->addChild(batchNode);
    
for(int i = 0;i < 1000;++i){
        int x = arc4random()%960;
        int y = arc4random()%640;
        CCSprite* testIcon = CCSprite::createWithTexture(batchNode->getTexture());
        testIcon->setPosition( ccp(x, y) );
        batchNode->addChild(testIcon);
}

效果图如下:

Cocos2d-x利用CCSpriteBatchNode提高渲染效率

上面的效果图可以看到,创建了1000个Icon到场景中,这是的FPS是39.9,渲染批次是1次

注:
1、
CCSpriteBatchNode::create(const char *fileImage);//利用贴图创建,默认子节点数量29.(数量不够时,系统会自己增加)

CCSpriteBatchNode* batchNode = CCSpriteBatchNode::create(const char *fileImage, unsigned int capacity);//利用贴图创建,并指定子节点数量


2、使用CCSpriteBatchNode时,所使用的贴图必须是同一张图片,也不能指定精灵的深度。所有的精灵都必须在同一渲染层。


3、但是项目中总不可能局限于一张贴图上,所以你可以把多张贴图合并成一张大贴图(合并的工具很多,我不介绍了)。所以利用合成后的大贴图创建一个CCSpriteBatchNode。

然后创建CCSprite的时候,设置贴图的区域就可以了。

分类: 未分类 标签:

ant debug, and sdk.dir

2012年11月9日 没有评论

I have a problem to generate the Android APK file.

When I run ant debug compilation work fine, but when I run ant debug

I have following error:

iMac:proj.android smartmind$ ant debug
Buildfile: /Users/smartmind/Works/Smallthing/cocos2d-x/samples/HelloCpp/proj.android/build.xml

BUILD FAILED
/Users/smartmind/Works/Smallthing/cocos2d-x/samples/HelloCpp/proj.android/build.xml:46: sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var

Total time: 0 seconds

I don’t know what is problem.

Your project should have a local.properties file with an sdk.dir line that points to where your Android SDK is installed:

# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.

# location of the SDK. This is only used by Ant
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/opt/android-sdk-linux_x86

To create this file if it does not exist, run android update project -p ... where the ... is the path to your project.

i had the same problem, i resolve the issue using a -Dvar

ant debug -Dsdk.dir=$SDK_ROOT

where $SDK_ROOT is android SDK path

分类: cocos2d, stackoverflow精选 标签:

Cocos2d-x 的CCObject与autorelease 之深入分析

2012年11月8日 没有评论

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ群:249941957 [暂满]加群写:Cocos2d-x 

红孩儿Cocos2d-X学习园地QQ2群:44208467 加群写:Cocos2d-x 

本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!


 Cocos2d-x 的CCObject与autorelease 之深入分析

另:本章所用Cocos2d-x版本为:

cocos2d-2.0-x-2.0.2
@ Aug 30 2012

http://cn.cocos2d-x.org/download

       

                

    在上一节“Cocos2d-x 2.0 TestCpp框架源码分析”中,我们深入分析了TestController类的实现。其中我们曾经遇到这两句代码:

    //新创建一个TestController对象

    CCLayer* pLayer = newTestController();

    //将它交由内存管理器进行内存释放管理

    pLayer->autorelease();

    在创建了一个TestController实例对象后,对它调用了autorelease函数。这个该怎么理解这一句的含义呢?我们先来看一下基类CCObject是怎么定义的。

 

读前小提示:CCScriptEngineManager是脚本引擎管理器,它对于使用的脚本引擎进行管理。说起脚本引擎,首先要了解什么是脚本?脚本是使用特定的语言格式来编写的描述一定逻辑功能的文本文件,它可以实现在文本文件中通过访问特定格式的函数对系统逻辑进行编程。这个文本文件不被编译进可执行程序中,而是动态的在需要的时候通过系统对这个文本文件的解释来执行。脚本引擎则可以理解为对脚本的使用管理进行封装的相关的类和接口函数。游戏中常用的脚本有Lua和Python。目前Cocos2d-x集成了Lua的脚本引擎模块,使用非常方便。后面的Lua章节我们进行详细讲解。

读前小提示:引用计数器是一种内存管理方式。通过一个无符号的成员变量计算当前有多少使用者在使用本内存。每次外部对象使用本内存时计数器加1,使用完要释放本内存时计数器减1,当计数器减为0时才真正进行占用内存释放。这样做可以实现在多个使用者使用一块内存时,只有当所有的使用者都确定不再使用这块内存的时候才进行内存的释放。避免了在还有使用者在使用内存时提前释放内存而导致的程序崩溃。

重点函数:release()

CCObject.h:

#ifndef __CCOBJECT_H__
#define __CCOBJECT_H__

#include "platform/CCPlatformMacros.h"
//Cocos2d命名空间
NS_CC_BEGIN

//声明以下几种类,在后面会定义相应类的成员变量的指针。以下几个类均是CCObject类的派生类。
class CCZone;  //CCObject指针暂存类
class CCObject;//基类
class CCNode;  //结点类
class CCEvent; //事件类

//这里定义了一个拷贝类
class CC_DLL CCCopying
{
public:
	//虚函数。功能是为CCZone指针所指向的对象复制一份新的CCObject对象指针
    virtual CCObject* copyWithZone(CCZone* pZone);
};
//由CCCopying派生出CCObject
class CC_DLL CCObject : public CCCopying
{
public:
    // 唯一ID
    unsigned int		m_uID;
    // 在LUA脚本引擎中的访问标识ID.暂可不理会,待学习到LUA时再分析
    int                 m_nLuaID;
protected:
    // 引用计数器,进行内存计数。
    unsigned int		m_uReference;
    // 是否是被内存管理器进行托管而自动进行释放。
    bool				m_bManaged;		
public:
	//构造函数
	CCObject(void);
	//析构函数
    virtual ~CCObject(void);
    //释放
	void release(void);
	//保留
	void retain(void);
	//设置实例对象的释放由内存管理器进行管理。实现自动释放。
    CCObject* autorelease(void);
	//取得一个拷贝
CCObject* copy(void);
//本类的实例化对象是否只有一个使用者
bool isSingleRefrence(void);
//返回内存计数器的值,也就是取得使用者的个数
    unsigned int retainCount(void);
    //判断是否与另一个CCObject实例对象相同
    virtual bool isEqual(const CCObject* pObject);
    //更新函数
    virtual void update(ccTime dt) {CC_UNUSED_PARAM(dt);};
    //设定CAutoreleasePool为友元类,这是一个通过CCObject指针容器CCMutableArray来对CCObject实例对象的内存进行管理的类,CCMutableArray在加入CCObject时对其引用计数器加1,在移除CCObject时对其引用计数器减1。
    friend class CCAutoreleasePool;
};

//定义一些函数
//定义定时器访问类成员函数
typedef void (CCObject::*SEL_SCHEDULE)(ccTime);
//定义普通回调类成员函数
typedef void (CCObject::*SEL_CallFunc)();
//定义带结点参数的回调类成函数
typedef void (CCObject::*SEL_CallFuncN)(CCNode*);
//定义带结点参数及1个用户值参数的回调类成员函数
typedef void (CCObject::*SEL_CallFuncND)(CCNode*, void*);
typedef void (CCObject::*SEL_CallFuncO)(CCObject*);
//定义菜单响应类成员函数
typedef void (CCObject::*SEL_MenuHandler)(CCObject*);
//定义事件响应类成员函数
typedef void (CCObject::*SEL_EventHandler)(CCEvent*);
//定义一些宏来取得这些回调成员函数的指针。
#define schedule_selector(_SELECTOR) (SEL_SCHEDULE)(&_SELECTOR)
#define callfunc_selector(_SELECTOR) (SEL_CallFunc)(&_SELECTOR)
#define callfuncN_selector(_SELECTOR) (SEL_CallFuncN)(&_SELECTOR)
#define callfuncND_selector(_SELECTOR) (SEL_CallFuncND)(&_SELECTOR)
#define callfuncO_selector(_SELECTOR) (SEL_CallFuncO)(&_SELECTOR)
#define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR)
#define event_selector(_SELECTOR) (SEL_EventHandler)(&_SELECTOR)
#define compare_selector(_SELECTOR) (SEL_Compare)(&_SELECTOR)

NC_CC_END
#endif // __CCOBJECT_H_

CCObject.cpp:

#include "CCObject.h"
//内存管理器头文件
#include "CCAutoreleasePool.h"
//Cocos2d-x定义的一些宏的头文件
#include "ccMacros.h"
//加入脚本支持
#include "scripte_support/CCScriptSupport.h"

NS_CC_BEGIN
//虚函数。这里简单处理一下。需要进行重载
CCObject* CCCopying::copyWithZone(CCZone *pZone)
{
CC_UNUSED_PARAM(pZone);
//如果不进行重载,则会提示没有重载实现函数功能。
    CCAssert(0, "not implement");
    return 0;
}
//构造
CCObject::CCObject(void)
{
	//定义一个静态UINT类型变量做为实例对象计数器,此值只会增长,不会减少,保证唯一。
	static unsigned int uObjectCount = 0;
	//将计数器加1后赋值给唯一ID。
	//注意:所有由此CCObject类派生的子类也会拥有这个唯一的ID。它可以使我们通过唯一ID来获取相应的实例对象。
	m_uID = ++uObjectCount;
	//脚本ID
     m_nLuaID = 0;

	// 当类进行实例化时,将m_uReference设为1
	m_uReference = 1;
	//初始化时设实例化对象由用户进行内存管理。如果new出一个对象,需要自行delete。
	m_bManaged = false;
}
//析构
CCObject::~CCObject(void)
{
	如果内存是由内存管理器统一管理,则调用内存管理器实例对象的移除函数对自已的内存进行释放。
	if (m_bManaged)
	{
		CCPoolManager::getInstance()->removeObject(this);
	}

    //如果有使用到LUA脚本,调用脚本引擎的实例对象的移除函数将本身从脚本引擎的实例对象中移除。
    if (m_nLuaID)
    {
CCScriptEngineManager::sharedManager()->getScriptEngine()->removeCCObjectByID(m_nLuaID);
    }
}
//返回一个本类实例化对象的拷贝
CCObject* CCObject::copy()
{
    return copyWithZone(0);
}
//供使用者外部调用的释放函数
void CCObject::release(void)
{
	//先确保计数器是大于0的数值,说明正常有效
	CCAssert(m_uReference > 0, "reference count should greater than 0");
	//计数器减1
	--m_uReference;
	//如果计数器减为0,释放本类实例化对象占用的内存
	if (m_uReference == 0)
	{
		delete this;
	}
}
//使用者外部调用的,使用一次时更新计数器。
void CCObject::retain(void)
{
	CCAssert(m_uReference > 0, "reference count should greater than 0");

	++m_uReference;
}
//设置当前类的实例化对象的内存管理交给内存管理器去管理,不手动进行内存计数器的处理。
CCObject* CCObject::autorelease(void)
{
	//调用内存管理器实例对象的addObject函数加入当前CCObject实例对象的指针
	CCPoolManager::getInstance()->addObject(this);
	//打开使用内存管理器的标记
	m_bManaged = true;
	return this;
}
//是否当前类的实例化对象只被一个使用者使用
bool CCObject::isSingleRefrence(void)
{
	//计数器的个数即代表使用者的个数,因为一个使用者使用一次,计数器加1
	return m_uReference == 1;
}
//返回当前类的实例化对象的使用者的个数
unsigned int CCObject::retainCount(void)
{
	return m_uReference;
}
//是否与另一个基类为CCObject的实例化对象是同一个对象
bool CCObject::isEqual(const CCObject *pObject)
{
	return this == pObject;
}

NS_CC_END 

原理说明:

我们看到CCObject其实真的很单纯,它主要就是有两个功能。一个是通过引用计数交给内存管理器进行内存管理。另一个就是通过脚本ID访问相应的脚本。脚本的分析后面专门有一章进行分析探讨,我们暂时只把内存管理的事情搞明白。好,下面我们来重点看一下autorelease函数的意义,顾名思义,“自动释放”。也就是说调用此函数则当前CCObject实例对象不需要用户在外部去手动调用release进行内存的释放工作。我们已经知道它通过引用计数来处理在什么时候内存释放。Cocos2d-x是怎么做到的呢?

在autorelease函数中有这么一句

CCPoolManager::getInstance()->addObject(this);

CCPoolManager代表了内存管理器。此句调用CCPoolManager的实例对象的addObject函数将当前CCObject实例对象的指针交给内存管理器。我们来分析一下内存管理器的原理。

读前小提示:CCMutableArray是一个CCObject指针容器类,它内部通过使用STL的vector容器来存储CCObject指针。在加入一个新CCObject时对其引用计数器加1,在移除CCObject时对其引用计数器减1。请各位同学自行打开CCMutableArray.h及cpp文件进行查看。

重点函数:CCAutoreleasePool::release(),CCPoolManager::finalize()

CCAutoreleasePool.h:

#ifndef __AUTORELEASEPOOL_H__
#define __AUTORELEASEPOOL_H__

#include "CCObject.h"
//加入CCObject指针容器类
#include "CCArray.h"

NS_CC_BEGIN
//这里定义一个自动释放内存结点类。由CCObject派生,这个结点类通过其成员容器对CCObject实例对象指针进行内存的释放操作。
class CC_DLL CCAutoreleasePool : public CCObject
{
	// CCArray是一个CCObject指针容器类。对CCOjbect的实例对象指针进行管理。
	CCArray*	m_pManagedObjectArray;	
public:
	//构造
	CCAutoreleasePool(void);
	//析构
	~CCAutoreleasePool(void);
	//加入一个新的CCObject实例对象指针到容器
	void addObject(CCObject *pObject);
	//从容器中移除一个指定的实例对象CCObject指针
	void removeObject(CCObject *pObject);
	//清空容器
	void clear();
};
//定义类内存管理器,这个类通过其成员容器对上面定义的自动释放内存结点类实例对象指针进行管理。
class CC_DLL CCPoolManager
{	
	// 使用CCArray对要释放的CCAutoreleasePool实例对象指针进行管理
	CCArray*	m_pReleasePoolStack;	
	//当前的CCAutoreleasePool实例对象指针
	CCAutoreleasePool*					m_pCurReleasePool;
	//取得当前的CCAutoreleasePool实例对象指针
	CCAutoreleasePool* getCurReleasePool();
public:
	//构造函数
	CCPoolManager();
	//析构函数
	~CCPoolManager();
	//终结内存管理
	void finalize();
	//新建一个CCAutoreleasePool实例对象,将其指针以压栈方式存放到pReleasePoolStack中
	void push();
	//以出栈方式释放当前CCAutoreleasePool实例对象中管理的所有CCObject实例对象内存
	void pop();
	//从当前CCAutoreleasePool实例对象中移除一个指定的CCObject实例对象指针
	void removeObject(CCObject* pObject);
	//加入一个新的CCObject实例对象指针到CCAutoreleasePool实例对象中的容器
	void addObject(CCObject* pObject);
	//取得单件内存管理器实例对象指针
	static CCPoolManager* sharedPoolManager();
	//销毁单件内存管理器实例对象指针
	static void purgePoolManager();
	//声明CCAutoreleasePool是友元类
	friend class CCAutoreleasePool;
};

NS_CC_END

#endif //__AUTORELEASEPOOL_H__

CCAutoreleasePool.cpp:

#include "CCAutoreleasePool.h"
#include "ccMacros.h"

NS_CC_BEGIN
//定义全局唯一的内存管理器对象
static	CCPoolManager*	s_pPoolManager = NULL;
//构造函数
CCAutoreleasePool::CCAutoreleasePool(void)
{	//新建一个CCObject指针容器类实例对象
	m_pManagedObjectArray = new CCArray();
	m_pManagedObjectArray->init();
}
//析构
CCAutoreleasePool::~CCAutoreleasePool(void)
{	//删除m_pManagedObjectArray
	CC_SAFE_DELETE(m_pManagedObjectArray);
}
//加入一个新的CCObject指针到容器
void CCAutoreleasePool::addObject(CCObject* pObject)
{
	//调用CCObject指针容器对象m_pManagedObjectArray的addObject函数将pObject放入容器,注意:调用后pObject的引用计数器加1。因为它被新的使用者m_pManagedObjectArray暂存了指向内存的指针。
m_pManagedObjectArray->addObject(pObject);
	//因为CCAutoreleasePool是CCObject的友元类,故可以直接访问CCObject的m_uReference变量,m_uReference代表了使用计数器,只有当计数器为0时才能释放,这里判断pObject->m_uReference是否大于1,否则弹出错误提示,因为CCObject类实例化时会引用计数值加1,上一句函数又会使CCObject类实例化对象引用计数加1,所以到这里引用计数至少为2。
	CCAssert(pObject->m_uReference > 1, "reference count should greager than 1");
	//注意:调用释放函数进行引用计数减1操作。大家一定很困惑。这刚刚加1现在就减1是为了啥呀?其实这是内存管理器的非常关键的一句代码。因为我们前面说了,CCObject实例对象在调用其成员函数autorelease的目的是实现用户不需要考虑什么时候手动调用release进行内存释放。而调用CCObject指针容器对象m_pManagedObjectArray的addObject函数将pObject放入容器时,pObject等于被m_pManagedObjectArray所使用。其引用计数器加1.而在后面当pObject被从m_pManagedObjectArray中移除时,m_pManagedObjectArray会对其引用计数器减1,但因为用户不再手动进行release的调用,则引用计数器将始终为1导致其new出来的内存无法得到正确的释放。在这里做一次release对pObject的引用计数减1后,将当pObject被从m_pManagedObjectArray中移除时,m_pManagedObjectArray对其引用计数器减1,此时pObject的引用计数值为0,则会正确释放new出来的内存。当然,如果你对于这个函数的功能含义理解透了,也可以做一些小修改。比如将CCMutableArray的addObject函数中的引用计数加1操作去掉,这里的减1操作也去掉。这样CCMutableArray的身份就只是个存放和释放CCObject的功能容器。没有了使用者的身份,但提高了少许效率。
	pObject->release();  
}
//从容器中移除一个指定的CCObject指针
void CCAutoreleasePool::removeObject(CCObject* pObject)
{
	//调用指针容器模版类m_pManagedObjectArray的removeObject函数将pObject从容器中移除,这个函数只是移除,不对pObject的引用计数作减1操作。
	m_pManagedObjectArray->removeObject(pObject, false);
}
//清空容器中所有的内存节点并释放这些节点的内存
void CCAutoreleasePool::clear()
{
	//如果容器的元素数量不为空
	if(m_pManagedObjectArray->count() > 0)
	{
		//CCAutoreleasePool* pReleasePool;
#ifdef _DEBUG
		int nIndex = m_pManagedObjectArray->count() - 1;
#endif
	   //反向遍历容器
	   CCObject* pObj = NULL;
        CCARRAY_FOREACH_REVERSE(m_pManagedObjectArray, pObj)
        {
			//如果指针为空则退出遍历
			if(!pObj)
				break;
			//设其不再由管理器进行管理
			pObj ->m_bManaged = false;
			//注意:以下两行作者在引擎中注释了,是因为在后面removeAllObjects函数会遍历调用CCObject实例对象指针的release函数,而CCObject的release函数内部会进行自已的delete.
			//(*it)->release();
			//delete (*it);
#ifdef _DEBUG
			nIndex--;
#endif
		}
		//在调用容器的removeAllObjects函数时会遍历容器所有节点并调用其release函数。具体可以参看CCArray.h中对于CCArray类的具体函数实现。
		m_pManagedObjectArray->removeAllObjects();
	}
}

//获取内存管理器的单件实例对象的指针
CCPoolManager* CCPoolManager::sharedPoolManager()
{
    if (s_pPoolManager == NULL)
    {
        s_pPoolManager = new CCPoolManager();
    }
    return s_pPoolManager;
}
//销毁内存管理器的单件实例对象的指针
void CCPoolManager::purgePoolManager()
{
    CC_SAFE_DELETE(s_pPoolManager);
}

//构造函数
CCPoolManager::CCPoolManager()
{	
	//创建一个CCArray来存放CCObject的派生类CCAutoreleasePool的实例对象指针
	m_pReleasePoolStack = new CCArray ();	
	m_pReleasePoolStack->init();
     m_pCurReleasePool = 0;
}
//析构
CCPoolManager::~CCPoolManager()
{
	//终结处理函数,马上后面会讲
	finalize();

	//将m_pCurReleasePool设为空
     m_pCurReleasePool = 0;
	//这里将容器中第一个元素释放掉,为什么不调用removeAllObjects函数呢?[伏笔3.1.1]
	m_pReleasePoolStack->removeObjectAtIndex(0);
	//删除new所申请的容器内存
	CC_SAFE_DELETE(m_pReleasePoolStack);
}
//内存管理器终结函数。
void CCPoolManager::finalize()
{
	//如果容器不为空
	if(m_pReleasePoolStack->count() > 0)
	{
		//遍历容器中每个元素
		CCObject* pObj = NULL;
         CCARRAY_FOREACH(m_pReleasePoolStack, pObj) 
		{
			if(!pObj)
				break;
			//将CCObject指针转为CCAutoreleasePool指针存入临时变量pPool。
			CCAutoreleasePool* pPool = (CCAutoreleasePool*)pObj;
			//调用其clear函数
			pObj ->clear();
		}
	}
}
//新建一个CCAutoreleasePool实例对象,将其指针以压栈方式存放到pReleasePoolStack中
void CCPoolManager::push()
{
	//新建CCAutoreleasePool实例对象,因CCAutoreleasePool为CCObject派生类,所以调用其基类构造时计数器为1
	CCAutoreleasePool* pPool = new CCAutoreleasePool();	   		//ref = 1
	m_pCurReleasePool = pPool;
	//将新建的CCAutoreleasePool实例对象放入容器中。因容器的addObject调用实例对象的retain函数,所以计数器变为2
	m_pReleasePoolStack->addObject(pPool);				   //ref = 2
	//注意:这里释放一下的目的是让计数器做一次减1操作。这样在对容器进行removeObject操作以及在容器clear时,元素不会因为addObject时对元素计数器的加1操作导致计数器值为1而无法delete,当然,你也可以在容器的removeObject和clear函数中对元素调用一次release来实现相同的目的。
	pPool->release();									   //ref = 1
}
//以出栈方式释放当前CCAutoreleasePool实例对象中管理的所有CCObject实例对象内存
void CCPoolManager::pop()
{
	//如果当前没有CCAutoreleasePool实例对象了,就直接返回
    if (! m_pCurReleasePool)
    {
        return;
    }
	//取得容器中有几个CCAutoreleasePool实例对象
 	int nCount = m_pReleasePoolStack->count();
	//清空一下当前使用的CCAutoreleasePool实例对象
	m_pCurReleasePool->clear();
 	//如果容器的CCAutoreleasePool实例对象数量大于1,则释放第nCount-1个CCAutoreleasePool实例对象,在这里硬性的规定了外部调用pop函数只能释放容器中第nCount-1个CCAutoreleasePool实例对象,而将第一个CCAutoreleasePool实例对象在析构函数中进行释放,这就解答了伏笔3.1.1,而且Cocos2d-x并没有在这里直接遍历所有CCAutoreleasePool实例对象进行释放,它在当前只释放了第nCount-1个CCAutoreleasePool实例对象是怎么回事呢?[伏笔3.1.2]
  	if(nCount > 1)
  	{
		m_pReleasePoolStack->removeObjectAtIndex(nCount-1);

// 		if(nCount > 1)
// 		{
// 			m_pCurReleasePool = m_pReleasePoolStack->getObjectAtIndex(nCount - 2);
// 			return;
// 		}
		//将m_pCurReleasePool指向第nCount-2个元素
		m_pCurReleasePool = m_pReleasePoolStack->getObjectAtIndex(nCount - 2);
	}

	/*m_pCurReleasePool = NULL;*/
}
//从当前CCAutoreleasePool实例对象中移除一个指定的CCObject实例对象指针
void CCPoolManager::removeObject(CCObject* pObject)
{
	//先判断一下m_pCurReleasePool是否有效
	CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
	m_pCurReleasePool->removeObject(pObject);
}
//加入一个新的CCObject实例对象指针到CCAutoreleasePool实例对象中的容器
void CCPoolManager::addObject(CCObject* pObject)
{
	//注意:这里与removeObject对比,想想为什么不直接用m_pCurReleasePool?那是因为总是先调用addObject之后才能调用removeObject,故addObject可能存在m_pCurReleasePool为空的情况,而removeObject时不应该存在m_pCurReleasePool为空的情况。getCurReleasePool将会判断m_pCurReleasePool是否为空,如果为空则进行相关处理。
	getCurReleasePool()->addObject(pObject);
}
//取得m_pCurReleasePool
CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{
	//如果m_pCurReleasePool为空则调用push
	if(!m_pCurReleasePool)
	{
		push();
	}
	//如果m_pCurReleasePool为空提示出错。
	CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
	//返回m_pCurReleasePool
	return m_pCurReleasePool;
}

NS_CC_END

       虽然这个cpp讲解完了,但是可能有的朋友仍然会感觉身在云雾中一样。我再总结一下:Cocos2d-x提供了一个内存管理器类CCPoolManager,它有一个容器m_pReleasePoolStack,而这个容器是用来存放了一些容器管理类CCAutoreleasePool的实例对象的。需要自动进行内存释放的CCObject实例对象会把其指针存放在容器管理类CCAutoreleasePool的实例对象中的m_pManagedObjectArray容器里。所有存在其中的CCObject实例对象在进行释放操作时通过使用计数器来进行判断在何时真正delete内存。

             

现在我们来实际操作一下。我们在VC的代码查询里输入“CCPoolManager:: sharedPoolManager ()”然后回车。在查找结果里会看到:

查找全部"CCPoolManager::sharedPoolManager()", 大小写匹配, 子文件夹, 查找结果1,"整个解决方案"

 
C:/cocos2d-2.0-x-2.0.2/cocos2dx/cocoa/CCAutoreleasePool.cpp(90):CCPoolManager*CCPoolManager::sharedPoolManager()

  C:/cocos2d-2.0-x-2.0.2/cocos2dx/cocoa/CCObject.cpp(58):       CCPoolManager::sharedPoolManager()->removeObject(this);

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/cocoa/CCObject.cpp(93):   CCPoolManager::sharedPoolManager()->addObject(this);

  C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(158):   CCPoolManager::sharedPoolManager()->push();

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(181):   CCPoolManager::sharedPoolManager()->pop();

 C:/cocos2d-2.0-x-2.0.2/cocos2dx/CCDirector.cpp(971):         CCPoolManager::sharedPoolManager()->pop();       

第一个结果是函数定义,略过。

第二个结果是CCObject的析构,这里的意义就是如果CCObject的实例对象调用过autorelease将其内存管理交给内存管理器,则在析构时会调用内存管理器对其进行释放。

第三个结果是CCObject的autorelease函数。这里的意义是将CCObject的实例对象指针通过参数传给内存管理器,交由内存管理器对其内存进行管理。

第四个结果是在CCDirector的init函数中。这里的意义是在显示设备初始化时调用内存管理器的push函数新创建一个内存管理结点用来对后面的CCObject进行内存管理。

第五个结果是在CCDirector的析构函数中。这里的意义是在最终结束游戏时调用内存管理器的pop函数对其管理的当前内存管理结点进行清空。

第六个结果是在显示设备的每一帧渲染处理时调用内存管理器的pop函数对其管理的当前内存管理结点进行清空,看到了吧,每一帧进行调用,所以说即使它每一次只能释放容器中第nCount-1个CCAutoreleasePool实例对象。但它因为每帧都调用,所以始终可以保持其内部只有1个CCAutoreleasePool实例对象。在这里解答了伏笔3.1.1伏笔3.1.2。当然,如果你在伏笔3.1.1处将

m_pReleasePoolStack->removeObjectAtIndex(0);

改为:

m_pReleasePoolStack->removeAllObjects();

更好理解一些。

       谢天谢地,这一节终于基本上结束了。相信如果你坚持看完这一节的代码分析之后,你就会对Cocos2d-x的CCObject有一个清楚的认识,因为未来有很多CCObject的派生类需要我们去认识。所以这一节会对以后的理解大有帮助。好了,下一节课再见!