cocos2d-x 笔记(四) TestCpp源码分析:control

Orca原创,转贴请标明链接Orca的移动开发轨迹。

目前整个学习笔记的版本是cocos2d-x 2.1.0beta3。

从这篇开始分析TestCpp的源码。这里借鉴了红孩儿的游戏编程之路的讲解方法,对此致谢。有点撞车,不过以下还是全部由我自己分析的内容。好了,开始吧。

这里看看整个TestCpp的控制流程,也就是说从control开始。

先看Appdelegate.cpp里面与HelloCpp不同的地方

	// 创建TestController对象。
    CCLayer * pLayer = new TestController();
    pLayer->autorelease();
// 	// 改成这样可以吗?
// 	CCLayer *pLayer = TestController::create();

    pScene->addChild(pLayer);
    pDirector->runWithScene(pScene);

我们现在创建新的layer都是使用create(),为什么这里不用这种方法?抱着这种疑问,我们可以去看看CCLayer的create()方法:

CCLayer *CCLayer::create()
{
    CCLayer *pRet = new CCLayer();
    if (pRet && pRet->init())
    {
        pRet->autorelease();
        return pRet;
    }
    else
    {
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
}
这里不再深入解释,总之,一般的情况下,layer的构造方式是使用init()方法,把所有的都写在里面。而接下来我们就可以看到,这个controller的构造方式并非如此,所以不能使用这种方式来进行处理。

言归正传,可见TestCpp里面就是新建了一个TestController的Layer,用这个layer来控制整个testcpp。

好了,这样我们就去看看TestController。

先看头文件TestController.h

#ifndef _CONTROLLER_H_
#define _CONTROLLER_H_

#include "cocos2d.h"

USING_NS_CC;

class TestController : public CCLayer
{
public:
	// 构造函数
    TestController();
	// 析构函数
    ~TestController();
	
	// 菜单的返回方法
    void menuCallback(CCObject * pSender);
	// 关闭的返回方法
    void closeCallback(CCObject * pSender);
	
	// 重写触摸开始的方法
    virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
	// 重写触摸移动的方法
    virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);

private:
	// 开始位置
    CCPoint m_tBeginPos;
	// Menu层
    CCMenu* m_pItemMenu;
};

#endif

其实也没什么好说的,看一下源文件TestController.cpp吧

#include "controller.h"
// tests资源管理
#include "testResource.h"
// tests管理
#include "tests.h"
// 定义每一行标题的高度
#define LINE_SPACE          40

// 光标位置
static CCPoint s_tCurPos = CCPointZero;
/************************************************************************/
/*  创建每一个测试场景                                                                     */
/************************************************************************/
static TestScene* CreateTestScene(int nIdx)
{
	// 释放所有缓存
    CCDirector::sharedDirector()->purgeCachedData();
	
	// 创建指向场景的指针。TestScene是在testbasic.h里面声明的
    TestScene* pScene = NULL;
	
	// 根据参数不同创建不同的场景
    switch (nIdx)
    {
	// 枚举常量TEST_XXX是在tests.h里面声明的,以下每个场景不再单独注释
    case TEST_ACTIONS:
        pScene = new ActionsTestScene(); break;
    case TEST_TRANSITIONS:
        pScene = new TransitionsTestScene(); break;
     case TEST_PROGRESS_ACTIONS:
         pScene = new ProgressActionsTestScene(); break;
    case TEST_EFFECTS:
        pScene = new EffectTestScene(); break;
    case TEST_CLICK_AND_MOVE:
        pScene = new ClickAndMoveTestScene(); break;
    case TEST_ROTATE_WORLD:
        pScene = new RotateWorldTestScene(); break;
    case TEST_PARTICLE:
        pScene = new ParticleTestScene(); break;
    case TEST_EASE_ACTIONS:
        pScene = new ActionsEaseTestScene(); break;
    case TEST_MOTION_STREAK:
        pScene = new MotionStreakTestScene(); break;
    case TEST_DRAW_PRIMITIVES:
        pScene = new DrawPrimitivesTestScene(); break;
    case TEST_COCOSNODE:
        pScene = new CocosNodeTestScene(); break;
    case TEST_TOUCHES:
        pScene = new PongScene(); break;
    case TEST_MENU:
        pScene = new MenuTestScene(); break;
    case TEST_ACTION_MANAGER:
        pScene = new ActionManagerTestScene(); break;
    case TEST_LAYER:
        pScene = new LayerTestScene(); break;
    case TEST_SCENE:
        pScene = new SceneTestScene(); break;
    case TEST_PARALLAX:
        pScene = new ParallaxTestScene(); break;
    case TEST_TILE_MAP:
        pScene = new TileMapTestScene(); break;
    case TEST_INTERVAL:
        pScene = new IntervalTestScene(); break;
    case TEST_LABEL:
        pScene = new AtlasTestScene(); break;
    case TEST_TEXT_INPUT:
        pScene = new TextInputTestScene(); break;
    case TEST_SPRITE:
        pScene = new SpriteTestScene(); break;
    case TEST_SCHEDULER:
        pScene = new SchedulerTestScene(); break;
    case TEST_RENDERTEXTURE:
        pScene = new RenderTextureScene(); break;
    case TEST_TEXTURE2D:
        pScene = new TextureTestScene(); break;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE)
    case TEST_CHIPMUNK:
        pScene = new ChipmunkAccelTouchTestScene(); break;
#endif
    case TEST_BOX2D:
        pScene = new Box2DTestScene(); break;
    case TEST_BOX2DBED:
        pScene = new Box2dTestBedScene(); break;
    case TEST_EFFECT_ADVANCE:
        pScene = new EffectAdvanceScene(); break;
    case TEST_ACCELEROMRTER:
        pScene = new AccelerometerTestScene(); break;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
    case TEST_KEYPAD:
        pScene = new KeypadTestScene(); break;
#endif
    case TEST_COCOSDENSHION:
        pScene = new CocosDenshionTestScene(); break;
    case TEST_PERFORMANCE:
        pScene = new PerformanceTestScene(); break;
    case TEST_ZWOPTEX:
        pScene = new ZwoptexTestScene(); break;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE)
// bada don't support libcurl
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
    case TEST_CURL:
        pScene = new CurlTestScene(); break;
#endif
#endif
    case TEST_USERDEFAULT:
        pScene = new UserDefaultTestScene(); break;
    case TEST_BUGS:
        pScene = new BugsTestScene(); break;
    case TEST_FONTS:
        pScene = new FontTestScene(); break;
    case TEST_CURRENT_LANGUAGE:
        pScene = new CurrentLanguageTestScene(); break;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE)
    case TEST_TEXTURECACHE: pScene = new TextureCacheTestScene(); break;
#endif
    case TEST_EXTENSIONS:
        {
            pScene = new ExtensionsTestScene();
        }
        break;
    case TEST_SHADER:
        pScene = new ShaderTestScene();
        break;
    case TEST_MUTITOUCH:
        pScene = new MutiTouchTestScene();
        break;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE)
    case TEST_CLIPPINGNODE:
        pScene = new ClippingNodeTestScene();
        break;
#endif
    default:
        break;
    }
	// 返回创建的场景
    return pScene;
}

// 构造函数
TestController::TestController()
: m_tBeginPos(CCPointZero)
{
    // add close menu
	// 增加关闭menu的MenuItemImage
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(s_pPathClose, s_pPathClose, this, menu_selector(TestController::closeCallback) );
	// 创建menu
    CCMenu* pMenu =CCMenu::create(pCloseItem, NULL);
	// 设置menu层的位置在左下角
    pMenu->setPosition( CCPointZero );
	// 设置按钮的位置在右上角。这里的VisibleRect是由包含在tests.h里面的testbasic.h里面的VisibleRect.h来进行声明的,表示的是各个角落
    pCloseItem->setPosition(ccp( VisibleRect::right().x - 30, VisibleRect::top().y - 30));

    // add menu items for tests
	// 创建测试菜单
    m_pItemMenu = CCMenu::create();
	// TESTS_COUNT是在testbasic.h里面声明的
    for (int i = 0; i < TESTS_COUNT; ++i)
    {
// #if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)
//         CCLabelBMFont* label = CCLabelBMFont::create(g_aTestNames[i].c_str(),  "fonts/arial16.fnt");
// #else
		// 创建label,g_aTestNames是在tests.h里面。c_str()是封装的用于转换String类为C风格字符串字面值的方法
        CCLabelTTF* label = CCLabelTTF::create(g_aTestNames[i].c_str(), "Arial", 24);
// #endif        
		// 通过Label创建MenuItem
        CCMenuItemLabel* pMenuItem = CCMenuItemLabel::create(label, this, menu_selector(TestController::menuCallback));
		
		// 在测试菜单中加入这个MenuItem 
        m_pItemMenu->addChild(pMenuItem, i + 10000);
		// 设置这个MenuItem的位置
        pMenuItem->setPosition( ccp( VisibleRect::center().x, (VisibleRect::top().y - (i + 1) * LINE_SPACE) ));
    }

	// 设置测试菜单的容器范围
    m_pItemMenu->setContentSize(CCSizeMake(VisibleRect::getVisibleRect().size.width, (TESTS_COUNT + 1) * (LINE_SPACE)));
    // 设置测试菜单的位置
	m_pItemMenu->setPosition(s_tCurPos);
    // 在本Layer中添加测试菜单
	addChild(m_pItemMenu);

	// 设置本层为可触摸
    setTouchEnabled(true);
	// 在本Layer中添加关闭menu
    addChild(pMenu, 1);

}

TestController::~TestController()
{
}


void TestController::menuCallback(CCObject * pSender)
{
    // get the userdata, it's the index of the menu item clicked
	// 获取用户所点击的MenuItem的指针
    CCMenuItem* pMenuItem = (CCMenuItem *)(pSender);
	// 根据定义时的zOrder获取MenuItem的id
    int nIdx = pMenuItem->getZOrder() - 10000;

    // create the test scene and run it
	// 根据id创建对应的场景
    TestScene* pScene = CreateTestScene(nIdx);
	// 如果创建没有问题
    if (pScene)
    {
		// 运行TestScene各派生类重写的虚函数runThisTest()
        pScene->runThisTest();
        // 释放场景
		pScene->release();
    }
}

void TestController::closeCallback(CCObject * pSender)
{
	// 关闭director
    CCDirector::sharedDirector()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}

void TestController::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
	//  获得当前CCTouch对象的指针
    CCSetIterator it = pTouches->begin();
	// 类型转换
    CCTouch* touch = (CCTouch*)(*it);
	// 设置初始位置为第一个CCTouch的位置
    m_tBeginPos = touch->getLocation();    
}

void TestController::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent)
{
	// 获得当前CCTouch对象的指针
    CCSetIterator it = pTouches->begin();
	// 类型转换
    CCTouch* touch = (CCTouch*)(*it);

	// 获得当前的位置
    CCPoint touchLocation = touch->getLocation();    
	// 获得移动了多少
    float nMoveY = touchLocation.y - m_tBeginPos.y;
	
	// 获取整个Menu的位置
    CCPoint curPos  = m_pItemMenu->getPosition();
	// 根据Y轴的移动量设置Menu的下一个位置
    CCPoint nextPos = ccp(curPos.x, curPos.y + nMoveY);

	// 如果下一个位置的Y坐标低于最低值,则移动到最上面。
    if (nextPos.y < 0.0f)
    {
        m_pItemMenu->setPosition(CCPointZero);
        return;
    }

	// 如果下一个位置的Y坐标高于最高值,则移动到最下面。
    if (nextPos.y > ((TESTS_COUNT + 1)* LINE_SPACE - VisibleRect::getVisibleRect().size.height))
    {
        m_pItemMenu->setPosition(ccp(0, ((TESTS_COUNT + 1)* LINE_SPACE - VisibleRect::getVisibleRect().size.height)));
        return;
    }
	// 不是以上两种情况则移动到下一个位置
    m_pItemMenu->setPosition(nextPos);
	// 用于计算的初始位置移动到当前位置
    m_tBeginPos = touchLocation;
	// 光标位置移动到下一个位置
    s_tCurPos   = nextPos;
}


看完以后可以回答一下我们刚才的疑问。因为这里直接使用构造函数创建,而不是在init()里面构建的,所以无法使用create,而是必须new以后再autorelease()。

好,接下来我们看看tests.h和testResource.h。

testResource.h里面全部都是资源的名字,没什么可说的。tests.h里面则是包含所有的头文件以及全局用的枚举值,也没什么好说的。

然后VisibleRect.h和VisibleRect.cpp主要是用来计算屏幕各参考点的位置的,基本没什么好说的。

接下来我们看一下在所有的测试例子中都引用了的testBasic.h及其源文件testBasic.cpp。

首先是testBasic.h:

#ifndef _TEST_BASIC_H_
#define _TEST_BASIC_H_

#include "cocos2d.h"
#include "VisibleRect.h"

USING_NS_CC;
using namespace std;
/************************************************************************/
/*  TestScene类,所有的Test例子的场景都由这个类派生                                                                     */
/************************************************************************/
class TestScene : public CCScene
{
public: 
	// 构造函数
    TestScene(bool bPortrait = false);
	// 重写进入scene时的onEnter()
    virtual void onEnter();

	// 纯虚函数runThisTest,说明本类不会被实例化。
    virtual void runThisTest() = 0;

    // The CallBack for back to the main menu scene
	// 返回主菜单的CallBack函数
    virtual void MainMenuCallback(CCObject* pSender);
};

// 定义了一个指向无参数,返回值为CCLayer的函数指针NEWTESTFUNC
typedef CCLayer* (*NEWTESTFUNC)();
// 定义了这样一个创建方法的宏
#define TESTLAYER_CREATE_FUNC(className) \
static CCLayer* create##className() \
{ return new className(); }
// 定义了创建宏
#define CF(className) create##className

#endif

这里稍微有点不是很容易理解的就是这里的两个宏定义吧,这些大致会是在一些派生类中方便调用的。接下来看下testBasic.cpp好了。



#include "testBasic.h"
#include "controller.h"

TestScene::TestScene(bool bPortrait)
{
    // 直接调用CCScene基类的init方法
    CCScene::init();
}

void TestScene::onEnter()
{
	// 先调用基类的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
	// 创建返回主页面的label文字
    CCLabelTTF* label = CCLabelTTF::create("MainMenu", "Arial", 20);
//#endif
	// 以“MainMenu”文字作为MenuItem,它的callback参数就是MaiMenuCallback
    CCMenuItemLabel* pMenuItem = CCMenuItemLabel::create(label, this, menu_selector(TestScene::MainMenuCallback));
	// 创建MainMenu这个Item的Menu
    CCMenu* pMenu =CCMenu::create(pMenuItem, NULL);

	// MenuItem放到左下角
    pMenu->setPosition( CCPointZero );
	// MenuItem放到右中间的位置
    pMenuItem->setPosition( ccp( VisibleRect::right().x - 50, VisibleRect::bottom().y + 25) );

	// 把这个menu加入本scene
    addChild(pMenu, 1);
}

void TestScene::MainMenuCallback(CCObject* pSender)
{
	// 创建一个scene
    CCScene* pScene = CCScene::create();
	// scene中创建testController,并加入自动回收
    CCLayer* pLayer = new TestController();
    pLayer->autorelease();
	// pLayer加入scene
    pScene->addChild(pLayer);
	// 替换掉当前Scene
    CCDirector::sharedDirector()->replaceScene(pScene);
}

整个控制代码基本就看完了。看完这些代码,其实已经可以完成一个完整的SceneManager了。如果有看我博客的朋友,可以自己试一下,很简单的。

下一篇开始正式进入各个测试类的内容当中

你可能感兴趣的:(cocos2d-x 笔记(四) TestCpp源码分析:control)