Cocos2d-x3.0源码分析之跨平台适配层

下面这张图(Google图片第一张)清晰的描绘了Cocos2d-x的总体架构,一共可以跨6个平台,非常强大。再等过一个多月我就要离职去做手游项目了,当然手游主要关注Android和iOS平台,在写游戏之前这段空闲时间,正好可以梳理一下框架逻辑。

如果你刚从Android或者iOS开发转向Cocos2d-x,尤其是从Api如shit一般的Android平台解脱的同学,一定不要太激动,虽然看几个demo就可以写一些小游戏了,但还是要回过头来扎扎实实的全面学习一遍。毫无疑问最好办法就是阅读源代码,强烈建议从平台适配层代码开始入手,剥离层层代码封装,一步步进入Cocos的世界,学习框架代码如何做到与各平台代码的完美衔接,同时欣赏下这些跨平台代码设计的优雅和美妙之处,哈哈。接下来我要分析的就是衔接Android和iOS平台的Platform LayerAdaptor部分源码,如图:




总所周知,不同移动平台客户端框架(这里不讨论桌面操作系统)中的功能模块,特性,机制非常相似,甚至接口参数基本上都存在一一映射关系,只要做过2个以上平台开发必定深有体会。

以Android和iOS为例,需要适配的有Event模块(Touch,Keyboard,Mouse,Acceleration...),窗口渲染载体(OpenGLView,Render),消息循环机制(逐帧回调),Application生命周期(Launch,Pause,Resume...),存储模块(文件,资源),声音模块等等。

适配层只占所有框架代码的一小部分,剩下的大部分由C++一套代码实现,不需要适配,这些基础模块有网络库,xml/Json解析库,cocos2d核心功能代码(引用计数内存管理机制,Ref与AutoReleasePool体系,OC风格容器,Node体系等等,cocos2d-iphone带过来的,很有特色很有味道哈哈!),标准std库,物理引擎等等。

适配层的代码由各种语言组成,分别属于各个平台,且分散在不同文件夹下面,为了清晰直观的表明所有这些类之间的关系,我画了一个类图将他们放到了一起:




可以根据不同的语言将该图分为3个部分,也可以代表3个平台,但不完全代表不同平台编译时所需要的代码,如iOS平台编译时需要左上部分Objective-C代码外加下面的C++代码,但要除去适配Android的C++代码。

新建Cocos2d-x的iOS工程中,/ios文件夹下面包含了自动生成的iOS适配代码,AppControl和RootViewControl,而Android工程中对应的则是自动生成的AppActivity,开发者无需理会任何适配(除非有特殊的调用需求)即可以开始直接编写Cocos代码了!(一般在XCode里面开发好后,再用脚本编译出Android工程,基本也不用做任何修改即可完美运行,Cocos的确是神一般的跨平台体验!)

接下来整理这些适配代码之间的调用关系,学习之后就会发现,Oh,原来如此!


对于iOS,App启动和普通应用没什么区别,

1、入口从UIApplicationDelegate中的【application: didFinishLaunchingWithOptions】开始,这里首先创建了一个GLView(iOS版本,wrap了一个CCEAGLView),然后直接调用cocos2d::Application(iOS版)的run方法。

2、run()方法首先调用了cocos2d::Application的applicationDidFinishLaunching方法,然后是CCDirectorCaller->startMainLoop()。

int Application::run()
{
    if (applicationDidFinishLaunching()) 
    {
        [[CCDirectorCaller sharedDirectorCaller] startMainLoop];
    }
    return 0;
}

3、startMainLoop()向CADisplayLink注册了一个每帧回调的selector(doCaller:),doCaller里面则调用了cocos2d::Director的mainLoop()方法。

-(void) startMainLoop
{
        // Director::setAnimationInterval() is called, we should invalidate it first
        [displayLink invalidate];
        displayLink = nil;
        
        displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
        [displayLink setFrameInterval: self.interval];
        [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
                      
-(void) doCaller: (id) sender
{
    cocos2d::Director* director = cocos2d::Director::getInstance();
    [EAGLContext setCurrentContext: [(CCEAGLView*)director->getOpenGLView()->getEAGLView() context]];
    director->mainLoop();
}

4、从mainLoop()方法开始,正式进入Cocos的世界,这里做了2件事情,首先调用drawScene(),然后执行PoolManager::getInstance()->getCurrentPool()->clear(); 清理自动管理内存池。

void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (! _invalid)
    {
        drawScene();
     
        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}
5、drawScene()里面依次执行下列逻辑(代码实在太清晰了),计算每帧时间,_scheduler->update一次(scheduler里面包括了Action,Selector,Timer等),EventDispatcher派发beforeVisit事件,绘制当前帧,EventDispatcher派发afterVisit事件,绘制NotificationNode,执行Render指令栈(3.0优化),EventDispatcher派发afterDraw事件,交换OpenGLView双缓冲(Android里面啥也没干...晕)。

// Draw the Scene
void Director::drawScene()
{
    // calculate "global" dt
    calculateDeltaTime();

    if (_openGLView)
    {
        _openGLView->pollInputEvents();
    }

    //tick before glClear: issue #533
    if (! _paused)
    {
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

    ...

    // draw the scene
    if (_runningScene)
    {
        _runningScene->visit(_renderer, identity, false);
        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    }

    // draw the notifications node
    if (_notificationNode)
    {
        _notificationNode->visit(_renderer, identity, false);
    }

    if (_displayStats)
    {
        showStats();
    }

    _renderer->render();
    _eventDispatcher->dispatchEvent(_eventAfterDraw);

    kmGLPopMatrix();

    _totalFrames++;

    // swap buffers
    if (_openGLView)
    {
        _openGLView->swapBuffers();
    }

    if (_displayStats)
    {
        calculateMPF();
    }
}

注意这里的Event派发只是Cocos体系内的CustomEvent,系统的Event派发并没有在Cocos的逐帧回调中执行(除非是没有提供消息回调的平台,如按需实现GLViewProtocol的pollInputEvents()方法,Android和iOS并不需要),而使用平台系统的消息循环队列,可以理解为系统Event派发与逐帧回调的执行是排队分先后的。看到这里,Cocos2d框架的运行逻辑已经非常清晰,程序启动后就可以循环绘制起来了,事件派发也搞定!

不得不提的是Cocos的Notification实现通过Callback同步调用,和iOS里面发送同步消息的NSNotificationCenter(Each process has a default notification center that you access with the NSNotificationCenter +defaultCenter )类似,而iOS里面发送异步消息的NSNotificationQueue(Every thread has a default notification queue, which is associated with the default notification center for the process.)则与Android中的Handler&MessageLoop机制类似。

Cocos编程主要集中在主线程,编程模型其实非常简单,此外框架还提供了很多的单例类,访问非常方便,而为了效率大多实现都没考虑线程安全。


对于Android,

1、启动后进入AppActivity的初始化,在基类Cocos2dxActivity的onCreate里面调用init().

	@Override
	protected void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		...

	    	this.init();
	}

2、init()中创建一个Cocos2dxGLSurfaceView,同时创建Coco2dxRender。

	public void init() {
		
    	// FrameLayout
        ViewGroup.LayoutParams framelayout_params =
            new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                       ViewGroup.LayoutParams.MATCH_PARENT);
        FrameLayout framelayout = new FrameLayout(this);
        framelayout.setLayoutParams(framelayout_params);

        ...

        // Cocos2dxGLSurfaceView
        this.mGLSurfaceView = this.onCreateView();

        // ...add to FrameLayout
        framelayout.addView(this.mGLSurfaceView);

        ...

        this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
        this.mGLSurfaceView.setCocos2dxEditText(edittext);

        // Set framelayout as the content view
		setContentView(framelayout);
	}

3、在Cocos2dxRender的重载方法onSurfaceCreated中调用了nativeInit()方法。

	@Override
	public void onSurfaceCreated(final GL10 pGL10, final EGLConfig pEGLConfig) {
		Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);
		this.mLastTickInNanoSeconds = System.nanoTime();
	}
	private static native void nativeInit(final int pWidth, final int pHeight);

4、nativeInit()方法中创建了GLView(Android版本),然后调用cocos2d::Application(Android版)的run()方法。
void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
{
    auto director = cocos2d::Director::getInstance();
    auto glview = director->getOpenGLView();
    if (!glview)
    {
        glview = cocos2d::GLView::create("Android app");
        glview->setFrameSize(w, h);
        director->setOpenGLView(glview);

        cocos_android_app_init(env, thiz);

        cocos2d::Application::getInstance()->run();
    }
    else
    {
       ...
    }

}
5、run()方法直接调用了cocos2d::Application的applicationDidFinishLaunching方法。

int Application::run()
{
    // Initialize instance and cocos2d.
    if (! applicationDidFinishLaunching())
    {
        return 0;
    }
    
    return -1;
}

6、上面才刚刚初始化完成,而Android的逐帧回调和iOS不一样,逐帧回调从Coco2dxRender的onDrawFrame(系统自己的逐帧回调,因此也没法精确控制帧率)开始, 调用nativeRender().

	@Override
	public void onDrawFrame(final GL10 gl) {
		...

		// should render a frame when onDrawFrame() is called or there is a
		// "ghost"
		Cocos2dxRenderer.nativeRender();

		...
	}

7、nativeRender()里面调用cocos2d::Director->mainLoop(),等同于上面的第4步,进入Cocos的世界,之后的逻辑几乎完全一样,不过Android版GLView基本是个空壳,双缓冲神马的更没有,不知道SurfaceView里面有没有实现了。

JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env) {
        cocos2d::Director::getInstance()->mainLoop();
    }

上面分析包含了App的初始化回调,渲染载体,逐帧回调方法,系统Event派发机制,至于其它的模块,生命周期方法调用等方式类似,以后在分析吧。


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: cocos2d-x是一款流行的开源游戏引擎,它可以用作开发本地移动游戏和桌面游戏。利用cocos2d-x游戏源码,开发人员可以快速构建流畅、高效、具有吸引力的游戏。该引擎使用C ++语言和脚本语言Lua来实现游戏开发,用户可以根据自己的需要进行选择。 cocos2d-x游戏源码包含许多强大的功能和工具,例如精灵、动画、场景管理、碰撞检测和音频控制等。通过这些功能,开发人员可以方便地创建各种类型的游戏,例如动作游戏、射击游戏、冒险游戏和益智游戏等。 此外,cocos2d-x游戏源码还具有高度定制化的特性,允许开发人员根据他们的需求自定义游戏元素。这种定制化可以通过改进游戏画面,添加新的模式和关卡,甚至整个新的游戏玩法来完成。 总之,cocos2d-x游戏源码是一款功能强大、易于使用和高度定制的游戏引擎,它可以帮助开发人员快速开发各种类型的游戏。无论你是专业开发人员还是无经验的游戏制作者,cocos2d-x都是一个极好的选择。 ### 回答2: Cocos2d-x游戏源码是使用Cocos2d-x引擎进行开发的游戏程序代码。Cocos2d-x引擎是一个开源的跨平台游戏引擎,可以支持iOS、Android、Windows Phone、Windows、MacOS和Linux等多个平台,并且提供了丰富的游戏开发API和工具集。Cocos2d-x游戏源码包含了游戏的核心逻辑、UI设计、动画效果、音频效果等各方面的代码,可以作为开发者学习和借鉴的重要资料。 由于Cocos2d-x引擎支持多种编程语言(如C++、Lua等),因此Cocos2d-x游戏源码也会有对应的编程语言版本。开发者可以通过阅读Cocos2d-x游戏源码,了解游戏开发过程中的技术细节,学习如何使用Cocos2d-x引擎进行游戏开发。同时,开发者也可以通过对Cocos2d-x游戏源码进行修改和优化,来满足游戏的特定需求,提升游戏的性能和用户体验。 总之,Cocos2d-x游戏源码是游戏开发者必备的重要资源,可以帮助开发者更好地了解游戏开发技术,提升游戏开发水平。 ### 回答3: cocos2d-x是一款优秀的游戏开发引擎,其源码包含了许多功能强大的游戏开发工具和API。使用cocos2d-x可以帮助开发者更快速更高效地开发出优秀的游戏作品。 cocos2d-x的游戏源码非常丰富,其包含了许多不同类型的游戏示例和模板,如平面射击、塔防、解谜等,这些示例可以帮助开发者更好地了解cocos2d-x的使用方法和原理cocos2d-x源码还包含了许多强大的API和工具,例如场景管理、动画控制、音频引擎等,这些工具和API能够帮助开发者更好地完成游戏的开发和调试。 此外,cocos2d-x源码还提供了完整的游戏开发框架,包括资源管理、内存管理、事件机制等,这些框架可以帮助开发者更好地组织代码和提高代码的可维护性。 总的来说,cocos2d-x游戏源码提供了丰富的工具和API,可以帮助开发者更高效地进行游戏开发,大大降低了开发难度和成本,是一款非常适合游戏开发者使用的引擎。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值