Cocos2d-x V2.2.2 简介

1. 简介

    随着公司需求变化,看来只会搞系统还不行,还需要会游戏开发。对自己挑战不小,为了生存,不得不迎难而上。

    • Cocos2d-x:基于Cocos2d-iPhone的多平台二维游戏引擎,为开发者封装了功能强大的绘图代码,使开发者专注于游戏开发而不是绘图操作。
    • AppDelegate:Cocos2d-x项目中的程序入口文件,提供对程序生命周期的控制事件。
    • 游戏元素:任何可以呈现出来的元素,例如场景、层和精灵
    • CCNode::addChild方法:用于将一个游戏元素添加到另一个元素中。在创建一个层或者场景时,通常会初始化自己的游戏元素,定义一些特殊的效果,或是将其他的游戏元素组合到一起,而addChild方法就是用于组合游戏元素的。

1.1 基本概念

     游戏开发需要把控的关键点:

     • 基本原理

     • 元素组成

1.1.1 流程控制

     典型的流程控制如下图所示:


1.1.2 导演(CCDirector)

     CCDirector的主要职责如下:

      1) 负责初始化OpenGL ES环境

      2) 以Stack方式管理场景(CCScene), 并负责场景切换、暂停或恢复游戏场景的运行

      3) 游戏呈现方面的设定,包括设定游戏呈现的窗口、FPS显示、默认帧率上限、纹理颜色位宽等。

     CCDirector扮演着全局导演的角色,因而很自然地采用了单实例的设计模式。在程序的任何地方,都可以通过下面的简单代码访问到:
      CCDirector *pDirector = CCDirector::sharedDirector();

      CCDirector中用于管理场景的方法如下所示:
      1) runWithScene(CCScene* scene):

          启动游戏,并运行scene场景。这个方法在主程序启动时第一次启动主场景时调用。 

      2) replaceScene(CCScene* scene):

          直接使用传入的scene替换当前场景来切换画面,当前场景将被释放。这是切换场景时最常用的方法。
      3) pushScene(CCScene* scene):

          将当前运行中的场景暂停并压入到执行场景栈中,再将传入的scene设置为当前运行场景。
       4) popScene(void):

           释放当前场景,再从执行场景栈中弹出栈顶的场景,并将其设置为当前运行场景。如果栈为空,则直接结束应用。与pushScene成对使用,可以达到形如由主界面进入设置界面,然后回到主界面的效果。

       5) pause(void):暂停当前运行场景中的所有计时器和动作,场景仍然会显示在屏幕上。 

       6) resume(void):恢复当前运行场景中被暂停的计时器和动作。它与pause配合使用。
       7) end(void):结束场景,同时退出应用。
       注:以上三种切换场景的方法(replaceScene、pushScene、popScene)均是先将待切换的场景完全加载完毕后,才将当前运行的场景释放掉。所以,在新场景恰好完全加载完毕的瞬间,系统中同时存在着两个场景,这将是对内存的一个考验,若不注意的话,切换场景可能会造成内存不足。

1.1.3 场景(CCScene)其它元素的容器

     一个场景相当于电影的一个镜头,一个游戏可由很多场景组成,但在任意时刻,有且仅有一个场景被显示。一个游戏典型的场景有:介绍、菜单、第一关、第n关、闯关成功、闯关失败、排行榜。

    一个场景可以由一个或多个孩子CCNode(如CCLayer、CCSprite等)组成。

    通过其子类CCTransitionScene可以实现场景间切换的特技效果(如淡入淡出、滑动等)。

1.1.4 层(CCLayer)其它元素的容器

     层是隶属于场景之下的游戏元素。通常,一个复杂场景会拥有多个层,一个层会显示一部分视觉元素,空白部分为透明或半透明,以实现多个层的重叠显示。层与层之间按照顺序叠放在一起,就组成了一个复杂的场景。

    常用的层有:菜单层、触摸层、动画层、背景层。

    CCLayer也是一具CCNode,其主要功能如下:
    1) 处理Touch事件(ccTouchBegan, ccTouchMoved, ccTouchEnded, 或ccTouchCancelled),一个CCLayer可与玩家交互,Touch事件从前向后依次传递给CCScene中的所有层,直到有一层处理了此事件为止。
    2) 渲染它们自己且可能半透明,以让游戏玩家看到其后面的层
    3) 定义游戏画面和行为

    Cocos2d-x提供了几个预定义的层:

    1) CCMenu(一个简单的菜单层)
    2) CCColorLayer(画指定颜色的层)
    3) CCLayerMultiplex(有多个孩子,同一时刻只激活一个孩子)    

    CCLayer可包含任何的CCNode作为孩子,如 CCSprite、CCLabel或其它CCLayer对象。

    CCLayer的一个十分重要的功能是可以接受用户输入事件,包括:触摸、加速度计和键盘输入等。其相关成员如下表所示:

成员类型名称描述
属性m_bTouchEnabled标识是否接收触摸事件
 m_bAccelerometerEnabled标识是否接收触摸事件
 m_bKeypadEnabled标识是否接收触摸事件
方法registerWithTouchDispatcher向Touch Dispatcher注册接收器,设置需要注册的触摸类型
回调函数didAccelerate接收G-Sensor事件
 cTouchBegan单位点触摸
 ccTouchMoved 
 ccTouchEnded 
 ccTouchCancelled 
  
  
 ccTouchesBegan多点触摸
 ccTouchesMoved
 ccTouchesEnded 
 ccTouchesCancelled 

1.1.5 精灵(CCSprite) 玩家可见元素

    CCSprite是一个精灵,它是一个2D图像且可被平移、旋转、拉伸、动画或其它变换。它可以把其它的CCSprite作为其孩子,当进行变换时,它的所有孩子进行同样的变换。

    层和场景是其他游戏元素的容器,如果没有向它们添加可见的游戏元素,它们看起来就一直是透明的。精灵则与层或场景不同,它隶属于层,是场景中出现的可见图形。玩家控制的主角、AI控制的NPC,以及地图上的宝箱、石块,甚至游戏主菜单的背景图片都是精灵。因此,可以这样认为,玩家看到的一切几乎都是由精灵构成的。

   精灵不一定是静态的。通常,一个精灵可以不断变化,变化的方式包括:移动、旋转、缩放、变形、显现消失、动画效果(类似GIF动画)等。精灵按照层次结构组合起来,并与玩家互动,构成了一个完整的游戏

   创建精灵的方法:

   1) 使用图片文件:
       static CCSprite* create(const char *pszFileName);
       static CCSprite* create(const char *pszFileName, const CCRect& rect);
   2) 使用CCTexture2D:(纹理坐标的原点:在左上角,与OpenGL坐标<原点在左下角>不一样)
       static CCSprite* create(CCTexture2D *pTexture);
       static CCSprite* create(CCTexture2D *pTexture, const CCRect& rect);
   3) 使用CCSpriteFrame:
       static CCSprite* create(CCSpriteFrame *pSpriteFrame);

1.1.6 场景+层+精灵相互关系


      为了绘制场景,需要绘制场景中的层,为了绘制层,需要绘制层中的精灵。因此,关系图实质上安排了图元的绘图方式,关系图中的每一个元素称作节点(node),关系图则称作渲染树(rendering tree)。渲染场景的过程就是遍历渲染树的过程。     

     每个节点有一系列属性,包括节点相对于父节点位置、旋转角度、缩放比例和变形参数等。渲染树的优势在于,我们只需要考虑节点相对于父节点的属性,就可以逐层创建复杂的对象或动作。

     Cocos2d-x也采用了渲染树架构。任何可见的游戏元素都派生自Cocos2d-x节点(CCNode),常见的游戏元素有场景(CCScene)、层(CCLayer)和精灵(CCSprite)等。通常游戏按照场景、层、精灵的层次顺序组织,每种节点都有各自的特点。然而在实际开发中,为了实现一些特殊的效果,也不必拘泥于这个层次顺序。层或精灵都是普通的节点,因此,即使向精灵中添加精灵,向场景中添加精灵,甚至向精灵中添加层,这些操作也都没有被禁止

1.1.7 动作(CCAction)

     CCAction是发送给CCNode对象的命令,它经常修改CCNode对象的属性(如:位置、旋转、拉伸等)。如果这些属性在一段时间内修改(有开始时间和结束时间),则它是CCActionInterval;否则它就是一个CCActionInstant。

      CCAction家族图谱如下图所示:


      CCAction分类如下表所示:

类名相关类名
Position
CCMoveBy、CCMoveTo、CCJumpBy、CCJumpTo、CCBezierBy、CCBezierTo、CCPlace
Scale
CCScaleBy、CCScaleTo
Rotation
CCRotateBy、CCRotateTo
Visibility
CCShow、CCHide、CCBlink、CCToggleVisibility
Opacity
CCFadeIn、CCFadeOut、CCFadeTo
Color
CCTintBy、CCTintTo

1.1.7.1 帧动画(Frame Animation:CCAnimation,很少用)

     动画(Animation)是一种特殊的持续性动作,它只能应用于精灵上,用于实现帧动画效果。如同电影胶片一样,一个帧动画由多张静止的图片不停地切换形成。静止的图片叫做帧(frame),帧的序列代表一个动画效果。CCAnimation是CCObject的派生类,其例子代码如下:

CCAnimation *animation = CCAnimation::create();

// load image file from local file system to CCSpriteFrame, then add into CCAnimation
for (int i = 1; i < 15; i++)
{
    char szImageFileName[128] = {0};
    sprintf(szImageFileName, "Images/grossini_dance_%02d.png", i);
    animation->addSpriteFrameWithFileName(szImageFileName);  
}

animation->setDelayPerUnit(2.8f / 14.0f); // This animation contains 14 frames, will continuous 2.8 seconds.
animation->setRestoreOriginalFrame(true); // Return to the 1st frame after the 14th frame is played. 

CCAnimate *action = CCAnimate::create(animation);
sprite->runAction(action);  // run action on sprite object
    从上面的代码可知,CCAnimation是由精灵的帧序列组成,CCAnimate是一个Action,它使用CCAnimation来创建。

1.1.7.2 精灵表动画(Sprite Sheet Animation,CCSpriteBatchNode常用)
    CCSpriteBatchNode好似一个批量节点(batch node),如果它包含孩子,它将在一次OpenGL调用中进行渲染,这样提高了Render性能。且它只能使用一个纹理(一个图像文件,一个纹理集),这样也提高了I/O性能。CCSpriteBatchNode对象包含了所有精灵帧的图像纹理。

    它通过以下两个元素进行创建:

     1).png:包含所有精灵帧的一个png文件

     2).plist:定义了每一帧在png文件中的起始坐标和矩形大小,以及每一帧的名字,如:child1.gif、 father.gif、 sister1.gif、 sister2.gif。

     其示例如下所示:

    //装载精灵帧文件和列表,通过解析,把每个精灵帧保存在Cache中,以便后面使用
    CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("animations/ghosts.plist", "animations/ghosts.png");
    CCNode *aParent;
    CCSprite *l1, *l2a, *l2b, *l3a1, *l3a2, *l3b1, *l3b2;

    //
    // SpriteBatchNode: 3 levels of children
    // 必须被增加到Scene,它是rendering pipeline的一部分
    aParent = CCSpriteBatchNode::create("animations/ghosts.png");
    addChild(aParent, 0, kTagSprite1);

    // parent 根据Cache中的内容和给定的名字创建精灵帧
    l1 = CCSprite::createWithSpriteFrameName("father.gif");
    l1->setPosition(ccp( s.width/2, s.height/2));
    aParent->addChild(l1, 0, kTagSprite2);
    CCSize l1Size = l1->getContentSize();

    // child left根据Cache中的内容和给定的名字创建精灵帧
    l2a = CCSprite::createWithSpriteFrameName("sister1.gif");
    l2a->setPosition(ccp( -25 + l1Size.width/2, 0 + l1Size.height/2));
    l1->addChild(l2a, -1, kTagSpriteLeft);
    CCSize l2aSize = l2a->getContentSize();        


    // child right根据Cache中的内容和给定的名字创建精灵帧
    l2b = CCSprite::createWithSpriteFrameName("sister2.gif");
    l2b->setPosition(ccp( +25 + l1Size.width/2, 0 + l1Size.height/2));
    l1->addChild(l2b, 1, kTagSpriteRight);
    CCSize l2bSize = l2a->getContentSize();


    // child left bottom根据Cache中的内容和给定的名字创建精灵帧
    l3a1 = CCSprite::createWithSpriteFrameName("child1.gif");
    l3a1->setScale(0.65f);
    l3a1->setPosition(ccp(0+l2aSize.width/2,-50+l2aSize.height/2));
    l2a->addChild(l3a1, -1);


1.1.7.3 文件动画(File Animation:CCAnimationCache)

    CCAnimationCache可以加载xml/plist文件,xml/plist文件描述了描述批量节点、精灵帧和他们的矩形区域。示例如下:

CCAnimationCache *cache = CCAnimationCache::sharedAnimationCache(); // "caches" are always singletons in cocos2d
cache->addAnimationsWithFile("animations/animations-2.plist");
CCAnimation animation = cache->animationByName("dance_1");  
CCAnimate animate = CCAnimate::create(animation);  // Don't confused between CCAnimation and CCAnimate :)
sprite->runAction(animate);

1.1.7.4 骨骼动画(Skeletal animation,性能好)

     与精灵表动画相比,它占用更少的内存和加载时间。可使用CocosBuilder创建骨骼动画。在骨骼动画中, 一个精灵由两部分组成:

     1)用于绘制精灵(称为皮肤或网格)的表面表示
     2)一套分层的相互连接的骨头(称为骨架)用于动态(姿势和关键帧)的网格


1.1.8 坐标系

1.1.8.1 UI坐标系


    UI坐标系的原点位于:左上角

1.1.8.2 OpenGL和Cocos2d坐标系



  OpenGL和Cocos2d坐标系的原点位于:左下角

1.1.8.3 纹理坐标系

   纹理坐标系以左上角为原点,向右为x轴正方向,向下为y轴正方向,如下图所示。在Cocos2d-x中,只有从纹理中截取部分矩形时才使用这个坐标系,如CCSprite的TextureRect属性。




1.1.8.3 父亲与孩子节点的关系

    • Anchor Point(锚点):

      CCNode有一个m_obAnchorPoint属性,可通过CCNode::setAnchorPoint进行设置。锚点同时用于定位和旋转对象。锚点坐标是一个相对坐标,通常对应Node对象中的一个点,如:ccp(0,0)位于Node对象左下角,ccp(0.5,0.5)位于Node对象中心,ccp(1,1)位于Node对象右上角。

      AnchorPoint用于设置一个锚点,以便精确地控制节点的位置和变换。AnchorPoint的两个参量x和y的取值通常都是0到1之间的实数,表示锚点相对于节点长宽的位置。例如,把节点左下角作为锚点,值为(0,0);把节点的中心作为锚点,值为(0.5,0.5);把节点右下角作为锚点,值为(1,0)。精灵的AnchorPoint默认值为(0.5,0.5),其他节点的默认值为(0,0)。

    • 设置对象位置(Position)

       m_obPosition用于设置本Node对象的锚点处在父亲节点中的位置。 

       Position用于设置节点的位置。由于Position指的是锚点在父节点中的坐标值,节点显示的位置通常与锚点有关。因此,如果层与场景保持默认的位置,只需把层中精灵位置设为窗口长宽的一半即可让它显示在屏幕中央。

     当多个节点嵌套时,每个节点的坐标系原点位于其内容的左下角。因此,锚点在父节点中的坐标实际上是它相对于父节点左下角的坐标值。

   •  旋转对象

       对象围绕Anchor Point(锚点)进行旋转。

   • getVisibleSize, getVisibleOrigin vs getWinSize

     其关系如下图及代码所示:

     

    m_fScaleX = (float)m_obScreenSize.width / m_obDesignResolutionSize.width; //值为2.0
    m_fScaleY = (float)m_obScreenSize.height / m_obDesignResolutionSize.height; //值为1.5
    
    if (resolutionPolicy == kResolutionNoBorder)
    {
        m_fScaleX = m_fScaleY = MAX(m_fScaleX, m_fScaleY); //值为2.0,为了保持不变形,有一部分看不见
    }
    
CCSize  CCEGLViewProtocol::getVisibleSize() const
{
    if (m_eResolutionPolicy == kResolutionNoBorder)
    {   //值为 (640,360)
        return CCSizeMake(m_obScreenSize.width/m_fScaleX, m_obScreenSize.height/m_fScaleY);
    }
    else 
    {
        return m_obDesignResolutionSize;
    }
}

CCPoint CCEGLViewProtocol::getVisibleOrigin() const
{
    if (m_eResolutionPolicy == kResolutionNoBorder)
    {   //值为(0,60)
        return CCPointMake((m_obDesignResolutionSize.width - m_obScreenSize.width/m_fScaleX)/2, 
                           (m_obDesignResolutionSize.height - m_obScreenSize.height/m_fScaleY)/2);
    }
    else 
    {
        return CCPointZero;
    }
}

   • 返回FrameBuffer分辨率

      CCEGLView::sharedOpenGLView()->getFrameSize()

   • designResolutionSize

     所有的游戏坐标都是基于设计分辨率(designResolutionSize),而不关心设备屏幕(framebuffer)分辨率。
      1) 如果游戏的UI设计资源只有一种,则通过以下函数来设置Design Resolution:
          CCEGLView::sharedOpenGLView()->setDesignResolutionSize(width, height, policy)
      2) 如果游戏的UI设计资源有多种,以更好地适合设备显示,则可以通过以下函数实现:
          searchPath.push_back(largeResource.directory);
          CCDirector::setContentScaleFactor(float scaleFactor);
          //scaleFactor为:(资源的高度/设计分辨率的高度)与(资源的宽度/设计分辨率的度)中较小的一个。

   • contentScaleFactor



    在上面的状态下,为了让画面可以全屏显示,有以下2中解决方案:

     1) 使用背景图片更宽
     2) 基于宽度计算contentScaleFactor

2.cocos2d-x 架构

    本要看的代码是Cocos2d-x v2.2.1。


3. 应用工作流程

3.1 Cocos2d-x主工作流程

   

3.2 应用初始化流程

    1) 初始化导演、EGLView和场景,由应用实现
    2) 导演管理EGLView和场景


3.3 创建CCScene


3.4 CCNode主要家族图谱


      从以上分析可知,应用程序的主要工作就是根据当前用户的操作,创建对应的CCScene并进行显示。

3.5 游戏主循环

      游戏主循环为:CCDirector::mainLoop()方法,当然它只是一个被调用者,真正的主循环为GLThread->run。

      这个方法负责调用定时器、绘图、发送全局通知、并处理内存回收池。该方法按帧调用,每帧调用一次,而帧间间隔取决于两个因素,一个是预设的帧率,默认为60帧每秒;另一个是每帧的计算量大小。当逻辑处理与绘图计算量过大时,设备无法完成每秒60次绘制,此时帧率就会降低。

void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
    {
	// 绘制当前场景并进行其他必要的处理
        drawScene();

        // 弹出自动回收池,使得这一帧被放入自动回收池的对象全部释放。
        CCPoolManager::sharedPoolManager()->pop();        
    }
}


4. 矩阵变换(平移、旋转、缩放)

     OpenGL维护了一个当前绘图矩阵,用于表示当前的绘图坐标系。这个矩阵被初始化为单位矩阵,此时绘图坐标系与世界坐标系相同,当我们不断地在绘图矩阵后乘上新的矩阵时,会相应地改变绘图坐标系。

     在OpenGL ES 1.0中有对应的操作函数,而在Cocos2d-x 2.0采用的OpenGL ES 2.0中,这些函数已经不可使用了。OpenGL ES 2.0已经放弃了固定的渲染流水线,取而代之的是自定义的各种着色器,在这种情况下变换操作通常需要由开发者来维护。所幸引擎也引入了一套第三方库Kazmath,它使得我们几乎可以按照原来OpenGL ES 1.0所采用的方式进行开发。其对应关系如下表所示:















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值