这篇文章是分析第一个小实例ActionTest的源码。其实所有实例程序的结构都是一样的,只有特定方法里的代码不同,大的框架都是一样的。也就是说看完这篇文章你就可以自己开始分析其他源码了。

废话不多说,咱们接着上一篇文章开始讲。上一篇文章最后我们讲到开启下个场景的代码

void TestController::menuCallback(CCObject * pSender)
{
// 获取被点击的子菜单项,
CCMenuItem* pMenuItem = (CCMenuItem *)(pSender);
//获取子菜单项的Zorder值,这个值就是在Z轴上的一个值,通过设置他可以影响遮挡关系
//这里呢,子菜单之间是没有遮挡关系的,这个值其实就是用来获取这个子菜单对应场景的index值
int nIdx = pMenuItem->getZOrder() - 10000; // 通过静态方法CreateTestScene获得相应的场景并开始场景
TestScene* pScene = CreateTestScene(nIdx);
if (pScene)
{
pScene->runThisTest();
pScene->release();
}
}

pScene就是我们要启动的下一个场景,这里你或许会注意到,这个pScene的类型不是CCScene,而是一个我们不认识的类名。我们找到定义TestScene地方,在testBase.h里面

#ifndef _TEST_BASIC_H_
#define _TEST_BASIC_H_ #include "cocos2d.h"
#include "VisibleRect.h" USING_NS_CC;
using namespace std; class TestScene : public CCScene
{
public:
TestScene(bool bPortrait = false);
virtual void onEnter(); virtual void runThisTest() = 0; // The CallBack for back to the main menu scene
virtual void MainMenuCallback(CCObject* pSender);
}; typedef CCLayer* (*NEWTESTFUNC)();
#define TESTLAYER_CREATE_FUNC(className) \
static CCLayer* create##className() \
{ return new className(); } #define CF(className) create##className #endif

我们可以看到类TestScene是继承CCScene,这就没问题了,他是个CCScene我们是可以使用它的。这个类里面定义了构造函数和三个虚构函数。所谓虚构函数我们可以简单的理解成是JAVA里面的抽象函数,就是说在他的子类必须要实现这三个虚构函数。如果你想对virtual这个关键字有更多了解你可以参考这里:C++ Virtual详解

接下来的typedef CCLayer* (*NEWTESTFUNC)();  这个地方NEWTESTFUNC是一个函数指针,该函数的参数为空,返回值为CCLayer的指针。参考:[C++语法] 关键字typedef用法(转)

再往下

#define TESTLAYER_CREATE_FUNC(className) \
static CCLayer* create##className() \
{ return new className(); }

这个地方你可以理解成简单的替换。没当出现TESTLAYER_CREATE_FUNC(className),他就会自动的替换为后面的代码。

最后一句我们也可以这样简单的理解。

看完了TestScene的定义,让我们来看看他的实现。

#include "testBasic.h"
#include "controller.h" //构造方法,调用父类的init()方法
TestScene::TestScene(bool bPortrait)
{
CCScene::init();
}
//重写父类的onEnter方法,想场景里添加返回主菜单的MainMenu按钮
void TestScene::onEnter()
{
CCScene::onEnter(); //add the menu item for back to main menu
//#if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)
// CCLabelBMFont* label = CCLabelBMFont::create("MainMenu", "fonts/arial16.fnt");
//#else
CCLabelTTF* label = CCLabelTTF::create("MainMenu", "Arial", 20);
//#endif
CCMenuItemLabel* pMenuItem = CCMenuItemLabel::create(label, this, menu_selector(TestScene::MainMenuCallback));
CCMenu* pMenu =CCMenu::create(pMenuItem, NULL);
pMenu->setPosition( CCPointZero );
pMenuItem->setPosition( ccp( VisibleRect::right().x - 50, VisibleRect::bottom().y + 25) ); addChild(pMenu, 1);
}
//按钮被按下的回调函数,(作用:返回主菜单)
void TestScene::MainMenuCallback(CCObject* pSender)
{
CCScene* pScene = CCScene::create();
CCLayer* pLayer = new TestController();
pLayer->autorelease();
pScene->addChild(pLayer);
CCDirector::sharedDirector()->replaceScene(pScene);
}

代码很简单,主要是实现返回主菜单的功能。

所有的小实例场景都会继承这个TestScene,这样我们就不用在每个小实例场景里添加这个返回主菜单按钮了。

让我们在回头上面ActionTest这个小实例。当我们点击主菜单里的ActionTest选项时, CreateTestScene(nIdx)方法会返回一个ActionsTestScene的实例,然后调用这个实例的runThisTest()方法,下面我们来看一下这个方法。

void ActionsTestScene::runThisTest()
{
sceneIdx = -1;
addChild(nextAction()); CCDirector::sharedDirector()->replaceScene(this);
}

这个方法代码很少,首先初始化sceneIdx为-1,然后调用nextAction()函数,这个函数会返回一个CCLayer (层),然后把这个层加到场景中,最后开始场景。

我们再看一下nextAction()这个函数。

static CCLayer* nextAction()
{
sceneIdx++;
sceneIdx = sceneIdx % MAX_LAYER;
//根据sceneIdx的值,获取相应的CCLayer
CCLayer* pLayer = (createFunctions[sceneIdx])();
pLayer->init();
pLayer->autorelease(); return pLayer;
}

这个函数主要是把层的索引加一,然后根据索引获得相应的层并初始化,设置自动释放。这里面createFunctions是个函数指针数组,根据索引返回相应的函数指针。这里面有个很有意思的地方(我也不知道我理解的是否正确,如果错误请指正)。

typedef CCLayer* (*NEWTESTFUNC)();

NEWTESTFUNC定义如上   [C++语法] 关键字typedef用法(转)

在他里面我们可以看到,每一项都是形如CF(Name)的代码。在编译的时候这个会被替换为reateName。这个是使用的define关键字

#define CF(className) create##className

不管怎么说,这个就是为了快速方便的获取所要的层。

我们从runThisTest函数开始看一下:在这里初始化索引为-1,然后调用nextAction函数,在nextAction函数里,先把索引加一(这个时候索引就变成0了),在函数指针数组里找到相应的函数指针(索引为0时的函数指针指向的是ActionManual,从下方数组可查到)。

static NEWTESTFUNC createFunctions[] = {
CF(ActionManual),
CF(ActionMove),
//省略部分代码
CF(Issue1327),
CF(Issue1398)
};

根据CF的定义,我们获得的函数指针是createActionManual。我们要执行这个createActionManual方法了,你可能会纳闷。在ActionStest.cpp里面没有这个函数啊。这里呢我得看另外一个define(直接理解为后面的替代前面的东西就行)

#define TESTLAYER_CREATE_FUNC(className) \
static CCLayer* create##className() \
{ return new className(); }

上面这段代码是在ActionsTest.h里定义的,你还可以在ActionsTest.cpp里看到他的使用

TESTLAYER_CREATE_FUNC(ActionManual);
TESTLAYER_CREATE_FUNC(ActionMove);
TESTLAYER_CREATE_FUNC(ActionRotate);
//省略部分代码
TESTLAYER_CREATE_FUNC(Issue1327);
TESTLAYER_CREATE_FUNC(Issue1398);

在编译的时候,ESTLAYER)CREATE_FUNC(ActionManual)就被替换为了

static CCLayer* createActionManual()
{
return new ActionManual();
}

终于我们找到这个函数了,这个函数只是简单的创建了一个实例。他的onEnter方法会被自动调用。onEnter方法里的代码就是每个小实例的不同了,其他代码都是一样的。

[置顶] Cocos2d-x 实例源码分析之二 小实例的主框架的更多相关文章

  1. DataTable源码分析(二)

    DataTable源码分析(二) ===================== DataTable函数分析 ---------------- DataTable作为整个插件的入口,完成了整个表格的数据初 ...

  2. 一个普通的 Zepto 源码分析(二) - ajax 模块

    一个普通的 Zepto 源码分析(二) - ajax 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块,以 ...

  3. Zepto源码分析(二)奇淫技巧总结

    Zepto源码分析(一)核心代码分析 Zepto源码分析(二)奇淫技巧总结 目录 * 前言 * 短路操作符 * 参数重载(参数个数重载) * 参数重载(参数类型重载) * CSS操作 * 获取属性值的 ...

  4. Koa源码分析(二) -- co的实现

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...

  5. Unity时钟定时器插件——Vision Timer源码分析之二

      Unity时钟定时器插件——Vision Timer源码分析之二 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 前面的已经介绍了vp_T ...

  6. Tomcat源码分析(二)------ 一次完整请求的里里外外

    Tomcat源码分析(二)------ 一次完整请求的里里外外   前几天分析了一下Tomcat的架构和启动过程,今天开始研究它的运转机制.Tomcat最本质就是个能运行JSP/Servlet的Web ...

  7. spark 源码分析之二十一 -- Task的执行流程

    引言 在上两篇文章 spark 源码分析之十九 -- DAG的生成和Stage的划分 和 spark 源码分析之二十 -- Stage的提交 中剖析了Spark的DAG的生成,Stage的划分以及St ...

  8. Django之DRF源码分析(二)---数据校验部分

    Django之DRF源码分析(二)---数据校验部分 is_valid() 源码 def is_valid(self, raise_exception=False): assert not hasat ...

  9. Vue.js 源码分析(十二) 基础篇 组件详解

    组件是可复用的Vue实例,一个组件本质上是一个拥有预定义选项的一个Vue实例,组件和组件之间通过一些属性进行联系. 组件有两种注册方式,分别是全局注册和局部注册,前者通过Vue.component() ...

随机推荐

  1. oracle的性能优化

    (1)      选择最有效率的表名顺序(只在基于规则的优化器中有效):ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table) ...

  2. 水果项目第1集-想法>需求->功能->数据库设计->类设计

    懒,懒人,我是个懒人. 懒人想做点事,总是拖拖拉拉,迟迟没有开始. 很久很久以前,就想做属于自己的产品,但是至今还没有一个属于自己的产品. 两年前,终于想好,要做一个网上卖水果的系统,手机上点点,水果 ...

  3. javascript高级编程笔记05(面向对象)

    面向对象设计 es中有两种属性:数据属性和访问器属性 数据属性: 数据属性包含一个数据值的位置,在这个位置可以读取和写入值,数据属性有4个描述其行为的特性 [[Configurable]]:表示能否通 ...

  4. Socket 学习

    Socket一般应用模式(服务器端和客户端) 服务器端Socket(至少有两个) ->一个负责接收客户端连接请求(但不负责和客户端通信) ->没成功接收到一个客户端的连接便在服务端生成一个 ...

  5. [Linux] 解压缩 tar 命令详解

    在Linux环境软件安装过程中通常需要用到解压命令,故在此总结下,以方便以后使用,若有不对之处,欢迎指正.   1. 文件压缩      通过压缩算法将文件的体积缩小,同时会将多个文件合并成至一起方便 ...

  6. 从JDK源码角度看java并发的原子性如何保证

    JDK源码中,在研究AQS框架时,会发现很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面 ...

  7. 硬件笔记之MacMini开启HiDPI

    0x00 概述 先科普一下,有关retina和HiDPI那点事 ,Macmini在2k显示器的显示太小了,看起来费眼,没办法,苹果原生HiDPI是支持4k显示器的,所以以后买显示器,直接买4k的一步到 ...

  8. SQL Server 经典案例

    1.先进先出 例1 WITH [ta] ([商品编号], [批次号], [库存数量]) AS ( UNION ALL UNION ALL UNION ALL ),[tb] ([商品编号], [订货数量 ...

  9. diff比较两个文件 linux

    功能:比较两个文件的差异,并把不同地方的信息显示出来.默认diff格式的信息. diff比较两个文件或文件集合的差异,并记录下来,生成一个diff文件,这也是我们常说的补丁文件.也使用patch命令对 ...

  10. Servlet】(2)有关Servlet实现的几个类:GenericServlet、HttpServlet、ServletConfig、ServletContext

    一.GenericServlet 1.所有的成员方法: 1.在javaWeb项目中: 2.web.xml <?xml version="1.0" encoding=" ...