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()。
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了。如果有看我博客的朋友,可以自己试一下,很简单的。
下一篇开始正式进入各个测试类的内容当中