Cocos2dx源码赏析(1)之启动流程与主循环

Cocos2dx源码赏析(1)之启动流程与主循环

我们知道Cocos2dx是一款开源的跨平台游戏引擎,而学习开源项目一个较实用的办法就是读源码。所谓,“源码之前,了无秘密”。而笔者从事的也是游戏开发工作,因此,通过梳理下源码的脉络,来加深对Cocos2dx游戏引擎的理解。

既然,Cocos2dx是跨平台的,那么,就有针对不同平台运行的入口以及维持引擎运转的“死循环”。下面,就分别从Windows、Android、iOS三个平台说明下Cocos2dx从启动到进入主循环的过程。

1、Windows

以引擎下的cpp-empty-test项目工程为例:

Windows工程的入口函数为cpp-empty-test/win32/main.cpp中的_tWinMain函数。

int WINAPI _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
<span class="hljs-comment">// create the application instance</span>
AppDelegate app;
<span class="hljs-keyword">return</span> Application::getInstance()-&gt;run();

}

这里,定义了一个AppDelegate类型的栈对象app。而AppDelegate继承自Application,所以这里会先初始化父类Application。再看下Application的实现,注意是进到CCApplication-win32.h和CCApplication-win32.cpp里。当然,Application还继续继承自ApplicationProtocol(通过预处理宏来针对不同的平台执行不同的代码)。这里,并没有做什么特别的处理,只是作了下相应的初始化的工作。

而在CCApplication-win32.h和CCApplication-win32.cpp代码中都有这样的宏判断:

#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32

继续追踪下去,可以发现CC_PLATFORM_WIN32在定义了WIN32宏时定义。

接下来,便执行Application::getInstance()->run()代码,这里的Application为单例的实现,这也是Cocos2dx单例常用的实现方式,在2.x版本的引擎中,单例的实现为sharedApplication,这是仿照Objective-C的写法。继续看CCApplication-win32中的run方法:

int Application::run()
{
    PVRFrameEnableControlWindow(false);
<span class="hljs-comment">// Main message loop:</span>
LARGE_INTEGER nLast;
LARGE_INTEGER nNow;

QueryPerformanceCounter(&amp;nLast);

initGLContextAttrs();

<span class="hljs-comment">// Initialize instance and cocos2d.</span>
<span class="hljs-keyword">if</span> (!applicationDidFinishLaunching())
{
    <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
}

<span class="hljs-keyword">auto</span> director = Director::getInstance();
<span class="hljs-keyword">auto</span> glview = director-&gt;getOpenGLView();

<span class="hljs-comment">// Retain glview to avoid glview being released in the while loop</span>
glview-&gt;retain();

<span class="hljs-keyword">while</span>(!glview-&gt;windowShouldClose())
{
    QueryPerformanceCounter(&amp;nNow);
    <span class="hljs-keyword">if</span> (nNow.QuadPart - nLast.QuadPart &gt; _animationInterval.QuadPart)
    {
        nLast.QuadPart = nNow.QuadPart - (nNow.QuadPart % _animationInterval.QuadPart);

        director-&gt;mainLoop();
        glview-&gt;pollEvents();
    }
    <span class="hljs-keyword">else</span>
    {
        Sleep(<span class="hljs-number">1</span>);
    }
}

<span class="hljs-comment">// Director should still do a cleanup if the window was closed manually.</span>
<span class="hljs-keyword">if</span> (glview-&gt;isOpenGLReady())
{
    director-&gt;end();
    director-&gt;mainLoop();
    director = <span class="hljs-literal">nullptr</span>;
}
glview-&gt;release();
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;

}

这里主要先看下applicationDidFinishLaunching()的调用,applicationDidFinishLaunching是虚函数,这里会调到子类AppDelegate中的applicationDidFinishLaunching的实现:

bool AppDelegate::applicationDidFinishLaunching()
{
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
        glview = GLViewImpl::create("Cpp Empty Test");
        director->setOpenGLView(glview);
    }
director-&gt;setOpenGLView(glview);

glview-&gt;setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);

director-&gt;setAnimationInterval(<span class="hljs-number">1.0f</span> / <span class="hljs-number">60</span>);

<span class="hljs-keyword">auto</span> scene = HelloWorld::scene();

director-&gt;runWithScene(scene);

<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;

}

这里对代码做了适当的删减。可以看到在AppDelegate的applicationDidFinishLaunching主要做了些跟游戏初始化相关的处理。例如,初始化导演类,设置OpenGL视图,设置适配方式,设置帧率以及初始化场景和运行该场景等。基本这个方法,也可以当作我们游戏代码初始化的入口。

再回到CCApplication的run方法,继续往下看。这里,有个while循环,至此,就找到了引擎的“死循环”了。在这个循环中,调用了导演类的mainLoop主循环方法,而在mainLoop中,主要控制渲染,定时器,动画,事件循环等处理。后续会分析这相关的部分,这里就不过多介绍了。至此,就是Cocos2dx在Windows平台从启动到主循环,代码执行的流程,简单的梳理,可以知道引擎代码是如何架构的。

2、Android

在Android平台的应用,一般由多个Activity组成,一个Activity代表一个“窗口”,Activity根据应用前后台切换有对应的声明周期状态。在配置清单文件中声明了

<action android:name="android.intent.action.MAIN" />

即代表该Acitivity为应用的入口Activity。而入口Activity也一般称为闪屏页(Splash)或启动页,用来呈现公司的或运营的合作伙伴Logo,之后再切换到主Activity。在Cocos2dx游戏中,主Activity一般是继承Cocos2dx引擎封装的Cocos2dxActivity类。先看onCreate()方法:

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

    sContext = <span class="hljs-keyword">this</span>;

    Cocos2dxHelper.init(<span class="hljs-keyword">this</span>);

    <span class="hljs-keyword">this</span>.init();
}

对onCreate里的代码做了精简,只列举了比较重要的几个方法。首先onLoadNativeLibraries方法:

protected void onLoadNativeLibraries() {
        try {
            ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
            Bundle bundle = ai.metaData;
            String libName = bundle.getString("android.app.lib_name");
            System.loadLibrary(libName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

该方法会读取配置在Manifest里中的meta-data标签的字段为android.app.lib_name的值,来加载动态库。即为:

    <meta-data android:name="android.app.lib_name"
                android:value="cpp_empty_test" />

同样,以cpp_empty_test的项目为例,可知这里要加载名字为libcpp_empty_test.so动态库。由于Cocos2dx引擎核心部分是C++实现,在Android平台通过jni的方式来调用和启动引擎。

再回到Cocos2dxActivity中的onCreate,继续往下进行。可以看到:

sContext = this;

sContext是Cocos2dxActivity的实例,被声明为静态的,通过这种实现了单例的效果。在需要Context实例的地方以及需要调用Cocos2dxActivity方法的地方,可以直接用该实例。

Cocos2dxHelper.init(this);

Cocos2dxHelper的init中主要是一些对象的初始化,例如:声音,音效,重力感应,Asset管理等。

接下来,调用了Cocos2dxActivity的init方法里:

public void init() {
    ViewGroup.LayoutParams framelayout_params =
        <span class="hljs-keyword">new</span> ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                   ViewGroup.LayoutParams.MATCH_PARENT);

    mFrameLayout = <span class="hljs-keyword">new</span> ResizeLayout(<span class="hljs-keyword">this</span>);

    mFrameLayout.setLayoutParams(framelayout_params);

    ViewGroup.LayoutParams edittext_layout_params =
        <span class="hljs-keyword">new</span> ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                   ViewGroup.LayoutParams.WRAP_CONTENT);
    Cocos2dxEditBox edittext = <span class="hljs-keyword">new</span> Cocos2dxEditBox(<span class="hljs-keyword">this</span>);
    edittext.setLayoutParams(edittext_layout_params);

    mFrameLayout.addView(edittext);

    <span class="hljs-keyword">this</span>.mGLSurfaceView = <span class="hljs-keyword">this</span>.onCreateView();

    mFrameLayout.addView(<span class="hljs-keyword">this</span>.mGLSurfaceView);

    <span class="hljs-comment">// Switch to supported OpenGL (ARGB888) mode on emulator</span>
    <span class="hljs-keyword">if</span> (isAndroidEmulator())
       <span class="hljs-keyword">this</span>.mGLSurfaceView.setEGLConfigChooser(<span class="hljs-number">8</span>, <span class="hljs-number">8</span>, <span class="hljs-number">8</span>, <span class="hljs-number">8</span>, <span class="hljs-number">16</span>, <span class="hljs-number">0</span>);

    <span class="hljs-keyword">this</span>.mGLSurfaceView.setCocos2dxRenderer(<span class="hljs-keyword">new</span> Cocos2dxRenderer());
    <span class="hljs-keyword">this</span>.mGLSurfaceView.setCocos2dxEditText(edittext);

    setContentView(mFrameLayout);
}

该方法主要设置要显示的视图界面,即mFrameLayout。重点关注这几行代码:

        this.mGLSurfaceView = this.onCreateView();
        mFrameLayout.addView(this.mGLSurfaceView);
    <span class="hljs-keyword">this</span>.mGLSurfaceView.setCocos2dxRenderer(<span class="hljs-keyword">new</span> Cocos2dxRenderer());

在onCreateView方法中,返回了一个Cocos2dxGLSurfaceView对象,并将该对象添加到了帧布局的容器对象(mFrameLayout)中。首先,了解下Cocos2dxGLSurfaceView类的实现:

public class Cocos2dxGLSurfaceView extends GLSurfaceView {
<span class="hljs-keyword">private</span> Cocos2dxRenderer mCocos2dxRenderer;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCocos2dxRenderer</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Cocos2dxRenderer renderer)</span> </span>{
    <span class="hljs-keyword">this</span>.mCocos2dxRenderer = renderer;
    <span class="hljs-keyword">this</span>.setRenderer(<span class="hljs-keyword">this</span>.mCocos2dxRenderer);
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onResume</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">super</span>.onResume();
    <span class="hljs-keyword">this</span>.setRenderMode(RENDERMODE_CONTINUOUSLY);
    <span class="hljs-keyword">this</span>.queueEvent(<span class="hljs-keyword">new</span> Runnable() {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
            Cocos2dxGLSurfaceView.<span class="hljs-keyword">this</span>.mCocos2dxRenderer.handleOnResume();
        }
    });
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onPause</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">this</span>.queueEvent(<span class="hljs-keyword">new</span> Runnable() {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
            Cocos2dxGLSurfaceView.<span class="hljs-keyword">this</span>.mCocos2dxRenderer.handleOnPause();
        }
    });
    <span class="hljs-keyword">this</span>.setRenderMode(RENDERMODE_WHEN_DIRTY);
    <span class="hljs-comment">//super.onPause();</span>
}

}

(上述代码有做删减,只保留需要说明的地方)

Cocos2dxGLSurfaceView继承自GLSurfaceView,通过阅读GLSurfaceView文档可知,GLSurfaceView又继承自SurfaceView,而SurfaceView又进一步继承自View。GLSurfaceView封装了OpenGL ES所需的运行环境,同时能让OpenGL ES渲染的内容直接生成在Android的View视图上。绘制渲染时,用户可以自定义渲染器(GLSurfaceView.Renderer),该渲染器运行在单独的线程里,独立于UI线程。GLSurfaceView还能适应于Activity的声明周期的变化做相应的处理(例如:onPause、onResume等)。

GLSurfaceView的初始化过程中,需要设置渲染器。即调用setRenderer方法。

Cocos2dxGLSurfaceView类中的onResume和onPause方法,这两个方法受Activity的相应的声明周期的方法影响, Activity窗口暂停(pause)或恢复(resume)时,GLSurfaceView都会收到通知,此时它的onPause方法和 onResume方法应该被调用。这样GLSurfaceView就会暂停或恢复它的渲染线程,以便它及时释放或重建OpenGL的资源。其中都分别调用了queueEvent的方法。这里,需要注意的是,Android的UI运行在主线程,而OpenGL的GLSurfaceView运行在一个单独的线程中,因此,需要调用queueEvent来给OpenGL线程分发调用,来达到两个线程间通信。最后都交给Cocos2dxRenderer处理。

最后,再重点看下渲染器类Cocos2dxRenderer的实现:

public class Cocos2dxRenderer implements GLSurfaceView.Renderer {
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onSurfaceCreated</span><span class="hljs-params">(<span class="hljs-keyword">final</span> GL10 GL10, <span class="hljs-keyword">final</span> EGLConfig EGLConfig)</span> </span>{
    Cocos2dxRenderer.nativeInit(<span class="hljs-keyword">this</span>.mScreenWidth, <span class="hljs-keyword">this</span>.mScreenHeight);
    <span class="hljs-keyword">this</span>.mLastTickInNanoSeconds = System.nanoTime();
    mNativeInitCompleted = <span class="hljs-keyword">true</span>;
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onSurfaceChanged</span><span class="hljs-params">(<span class="hljs-keyword">final</span> GL10 GL10, <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> width, <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> height)</span> </span>{
    Cocos2dxRenderer.nativeOnSurfaceChanged(width, height);
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDrawFrame</span><span class="hljs-params">(<span class="hljs-keyword">final</span> GL10 gl)</span> </span>{
    <span class="hljs-keyword">if</span> (sAnimationInterval &lt;= <span class="hljs-number">1.0</span> / <span class="hljs-number">60</span> * Cocos2dxRenderer.NANOSECONDSPERSECOND) {
        Cocos2dxRenderer.nativeRender();
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> now = System.nanoTime();
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> interval = now - <span class="hljs-keyword">this</span>.mLastTickInNanoSeconds;

        <span class="hljs-keyword">if</span> (interval &lt; Cocos2dxRenderer.sAnimationInterval) {
            <span class="hljs-keyword">try</span> {
                Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
            } <span class="hljs-keyword">catch</span> (<span class="hljs-keyword">final</span> Exception e) {
            }
        }

        <span class="hljs-keyword">this</span>.mLastTickInNanoSeconds = System.nanoTime();
        Cocos2dxRenderer.nativeRender();
    }
}

}

首先,Cocos2dxRenderer继承了渲染器类GLSurfaceView.Renderer,并重写了以下上个方法:

onSurfaceCreated

该方法是当Surface被创建的时候,会调用,即应用程序第一次运行的时候。当设备被唤醒或用户从其它Activity切换回来的时候,该方法也可能被调用。因此,该方法可能会被多次调用。一般会在该方法中,完成一些OpenGL ES的初始化工作。

onSurfaceChanged

该方法是在Surface被创建以后,每次Surface尺寸发生变化时(例如:横竖屏切换),该方法会被调用。

onDrawFrame

绘制的每一帧,该方法都会被调用。

其实,看到onDrawFrame中的代码,可以知道Cocos2dx引擎在Android平台的“死循环”在该方法中。最后,通过jni的方式调用nativeRender来启动导演类的主循环。

熟悉jni调用的可以知道,nativeRender是声明为native的方法,Cocos2dxRenderer.nativeRender最终会调到Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp类中:

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

可以看到,跟Windows平台的一样,最终调用到导演类的mainLoop方法,殊途同归。以上便是,Android平台Cocos2dx引擎从启动到进入死循环的过程。

3、iOS

同样,以引擎下的cpp-empty-test项目工程为例:

iOS工程的入口函数为cpp-empty-test/proj.ios/main.cpp中的main函数。

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, @"AppController");
    [pool release];
    return retVal;
}

在iOS应用中,都必须在函数main中调用UIApplicationMain方法来启动应用和设置相应的事件循环。UIApplicationMain函数原型如下:

UIKIT_EXTERN int UIApplicationMain(int argc, char *argv[], NSString * __nullable principalClassName, NSString * __nullable delegateClassName);

其中,argc是参数的个数,argv是可变的参数列表,principalClassName代表的是一个继承自UIApplication类的类名,delegateClassName是应用程序的代理类名称。在跟踪AppController类代码之前,有必要先了解下iOS应用的运行状态以及相应的生命周期方法:

  • Not Running(非运行状态):应用没有运行或被系统终止。
  • Inactive(前台非活动状态):应用正在进入前台状态,但是还不能接受事件处理。
  • Active(前台活动状态):应用进入前台,能接受事件处理。
  • Background(后台状态):应用进入后台后,依然能够执行代码。如果有可执行的代码,就会执行,如果没有可执行的代码或可执行的代码执行完毕,应用会马上进入挂起状态。
  • Suspended(挂起状态):处于挂起的应用进入一种“冷冻”状态,不能执行代码。如果系统内存不够,应用会被终止。

生命周期方法有:

application:didFinishLaunchingWithOptions:

应用程序启动并进行初始化时会调用该方法。

applicationDidBecomeActive:

应用程序进入前台并处于活动状态时调用该方法。

applicationWillResignActive:

应用程序从活动状态进入非活动状态时调用该方法。

applicationDidEnterBackground:

应用程序进入后台时调用该方法。

applicationWillEnterForeground:

应用程序进入前台,但还没有处于活动状态时调用该方法。

applicationWillTerminate:

应用程序被终止时调用该方法。

进入AppController类,AppController实现了UIApplicationDelegate,并重写了相应的生命周期的方法。那么,重点看application:didFinishLaunchingWithOptions:方法:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
cocos2d::Application *app = cocos2d::Application::getInstance();
app-&gt;initGLContextAttrs();
cocos2d::GLViewImpl::convertAttrs();

<span class="hljs-comment">// Override point for customization after application launch.</span>

<span class="hljs-comment">// Add the view controller‘s view to the window and display.</span>
window = [[<span class="hljs-built_in">UIWindow</span> alloc] initWithFrame: [[<span class="hljs-built_in">UIScreen</span> mainScreen] bounds]];

CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
                                     pixelFormat: (<span class="hljs-built_in">NSString</span>*)cocos2d::GLViewImpl::_pixelFormat
                                     depthFormat: cocos2d::GLViewImpl::_depthFormat
                             preserveBackbuffer: <span class="hljs-literal">NO</span>
                                     sharegroup: <span class="hljs-literal">nil</span>
                                  multiSampling: <span class="hljs-literal">NO</span>
                                numberOfSamples: <span class="hljs-number">0</span>];

<span class="hljs-comment">// Use RootViewController manage CCEAGLView</span>
viewController = [[RootViewController alloc] initWithNibName:<span class="hljs-literal">nil</span> bundle:<span class="hljs-literal">nil</span>];
viewController.wantsFullScreenLayout = <span class="hljs-literal">YES</span>;
viewController.view = eaglView;

<span class="hljs-comment">// Set RootViewController to window</span>
<span class="hljs-keyword">if</span> ( [[<span class="hljs-built_in">UIDevice</span> currentDevice].systemVersion floatValue] &lt; <span class="hljs-number">6.0</span>)
{
    <span class="hljs-comment">// warning: addSubView doesn‘t work on iOS6</span>
    [window addSubview: viewController.view];
}
<span class="hljs-keyword">else</span>
{
    <span class="hljs-comment">// use this method on ios6</span>
    [window setRootViewController:viewController];
}

[window makeKeyAndVisible];

[[<span class="hljs-built_in">UIApplication</span> sharedApplication] setStatusBarHidden: <span class="hljs-literal">YES</span>];

<span class="hljs-comment">// IMPORTANT: Setting the GLView should be done after creating the RootViewController</span>
cocos2d::GLViewImpl *glview = cocos2d::GLViewImpl::createWithEAGLView(eaglView);
cocos2d::Director::getInstance()-&gt;setOpenGLView(glview);

app-&gt;run();
<span class="hljs-keyword">return</span> <span class="hljs-literal">YES</span>;

}

这里主要是实例化一个UIWindow对象,每一个UIWindow对象上面都有一个根视图,它所对应的控制器为根视图控制器(ViewController),最后把根视图控制器放到UIWindow上。最后,app->run()会调用到CCApplication-ios.mm(这个也是根据项目中的预编译宏实现)中的run方法:

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

这里有个跟生命周期方法类似的名字applicationDidFinishLaunching,这个会调到ApAppDelegate的applicationDidFinishLaunching方法,这点跟Windows平台类似,一般是在这个方法做跟游戏内容相关的初始化。run方法接下来,就是调startMainLoop方法,看这个名字,知道跟要找的目标很接近了,再继续跟下去。这里会调到CCDirectorCaller-ios.mm中的startMainLoop方法:

-(void) startMainLoop
{
    // Director::setAnimationInterval() is called, we should invalidate it first
    [self stopMainLoop];
displayLink = [<span class="hljs-built_in">NSClassFromString</span>(<span class="hljs-string">@"CADisplayLink"</span>) displayLinkWithTarget:<span class="hljs-keyword">self</span> selector:<span class="hljs-keyword">@selector</span>(doCaller:)];
[displayLink setFrameInterval: <span class="hljs-keyword">self</span>.interval];
[displayLink addToRunLoop:[<span class="hljs-built_in">NSRunLoop</span> currentRunLoop] forMode:<span class="hljs-built_in">NSDefaultRunLoopMode</span>];

}

首先是通过NSClassFromString动态加载CADisplayLink类,然后调用了该类的displayLinkWithTarget方法,该方法类似定时器的功能,周期的调用该selector包装的方法(即:doCaller:方法):

-(void) doCaller: (id) sender
{
    if (isAppActive) {
        cocos2d::Director* director = cocos2d::Director::getInstance();
        [EAGLContext setCurrentContext: [(CCEAGLView*)director->getOpenGLView()->getEAGLView() context]];
        director->mainLoop();
    }
}

至此,我们就找到了导演类的mainLoop方法,开启了引擎的主循环。以上,便是Cocos2dx引擎在iOS平台从启动到进入主循环的过程。

通过以上简单的分析,我们知道,Cocos2dx引擎利用了相应的平台循环方式来调用导演类的主循环来进入引擎的内部工作。下一篇继续通过代码的方式来梳理下Cocos2dx的渲染过程。如果在本篇中,有你觉得不对的地方,也欢迎来和我讨论。

技术交流QQ群:528655025

作者:AlphaGL

出处:http://www.cnblogs.com/alphagl/

版权所有,欢迎保留原文链接进行转载 ??

原文地址:https://www.cnblogs.com/jaredz/p/12693348.html

时间: 2024-10-12 21:06:27

Cocos2dx源码赏析(1)之启动流程与主循环的相关文章

Cocos2dx源码赏析(2)之渲染

Cocos2dx源码赏析(2)之渲染 这篇,继续从源码的角度来跟踪下Cocos2dx引擎的渲染过程,以此来梳理下Cocos2dx引擎是如何将精灵等元素显示在屏幕上的. 从上一篇对Cocos2dx启动流程的梳理中可知,Cocos2dx依靠通过各平台的入口启动引擎,并在循环中调用Director::mainLoop方法来维持引擎的各种逻辑: void Director::mainLoop() { if (_purgeDirectorInNextLoop) { _purgeDirectorInNext

Fresco源码赏析 之 图片显示流程

转载请注明出处:http://blog.csdn.net/u014614038/article/details/51498068 上文大概将Fresco的基本流程通了一遍,就是Frosco是如何实现了只是简单调用了setImageUrl的方法后自动将所有事情做好,这里包含很多的流程,上文大概把这些流程过了一遍,这篇文章打算将接说一下上文没有说到的一些东西,就是拿到数据后它是如何处理的,是怎么显示出来的. 上文 Fresco源码赏析 之基本流程可以看这里: http://blog.csdn.net

Tomcat源码分析之—具体启动流程分析

从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息.Catalina.base信息,在initClassLoaders方法中初始化类加载器,然后通过反射初始化org.apache.catalina.startup.Catalina作为catalina守护进程: 一.load Bootstrap中load流程: 反射调用Catalina的load方法

spark core源码分析4 worker启动流程

源码位置:org.apache.spark.deploy.worker.Worker.scala 首先查看worker的main方法,与master类似,创建sparkConf,参数解析,以及构造worker对象并创建ActorRef用于对外或者本身的信息交互.这里masters参数可以设置多个 def main(argStrings: Array[String]) { SignalLogger.register(log) val conf = new SparkConf val args =

Spring源码分析2 — 容器启动流程

1 主要类 部署web应用时,web容器(比如Tomcat)会读取配置在web.xml中的监听器,从而启动spring容器.有了spring容器之后,我们才能使用spring的IOC AOP等特性.弄清spring容器启动流程,有利于理解spring IOC中的各种特性,比如BeanPostProcessor,MessageSource,ApplicationListener等.我们先来看下容器启动流程中涉及的主要类. ContextLoaderListener:注册在web.xml中,web应

Android -- Camera源码简析,启动流程

com.android.camera.Camera.java,主要的实现Activity,继承于ActivityBase. ActivityBase 在ActivityBase中执行流程: onCreate中进行判断是否是平板: onResume中判断是否锁屏,锁屏&camera不存在时候,mOnResumePending置为true,否则置为false并执行doOnResume: onWindowFocusChanged中判断是否获取到焦点&mOnResumePending,满足的话执行

spark源码分析之Executor启动与任务提交篇

任务提交流程 概述 在阐明了Spark的Master的启动流程与Worker启动流程.接下继续执行的就是Worker上的Executor进程了,本文继续分析整个Executor的启动与任务提交流程 Spark-submit 提交一个任务到集群通过的是Spark-submit 通过启动脚本的方式启动它的主类,这里以WordCount为例子 spark-submit --class cn.itcast.spark.WordCount bin/spark-clas -> org.apache.spar

[转帖]cocos2D-X源码分析之从cocos2D-X学习OpenGL(2)----QUAD_COMMAND

原文:cocos2D-X源码分析之从cocos2D-X学习OpenGL(2)----QUAD_COMMAND 上一篇文章介绍了cocos2d-x的基本渲染结构,这篇顺着之前的渲染结构介绍渲染命令QUAD_COMMAND命令的部分,通过这部分的函数,学习opengl处理图片渲染的方法,首先介绍这节需要涉及到的基本概念VAO和VBO. VAO和VBO: 顶点数组对象(Vertex Array Object  即VAO)是一个包含一个或数个顶点缓冲区对象(Vertex Buffer Object, 即

cocos2d-x 源码分析 : EventDispatcher、EventListener、Event 源码分析 (新触摸机制,新的NotificationCenter机制)

源码版本来自3.x,转载请注明 cocos2d-x 源码分析总目录 http://blog.csdn.net/u011225840/article/details/31743129 1.继承结构 1.1 结构 不详吐槽太多,也不贴图了,贴图要审核好久好久好久好久. 从小到大,先来看下Event的结构. 1.Event--------EventTouch,EventCustom,EventMouse,EventKeyboard,EventFocus,EventAcceleration 其中,Eve