C73bded4e01fdb613cf8d9f31697cf04
cocos2dx项目启动流程与跨平台原理

最近大部分空余时间都在开始学习cocos2d游戏开发相关技术,作为一个游戏行业菜鸟级别的选手,我觉得基础很重要,所以我决定从cocos2dx启动流程和快平台原理开始!

注:这里只针对Lua做相关介绍,为什么是Lua,有过cocos2dx经验的朋友应该都知道Lua小,快,简单,而且比较大众化,而C++就不多多说了,大部分程序员都有畏惧心理,当然也有一些C++比较好,或者有独特爱好和专研朋友!

关于其他平台基本上的流程和原理其实是一样的,可以直接参考!

什么是Lua

  • 百科:
    • Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
    • Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。
    • Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。

很多应用程序、游戏使用LUA作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。这其中包括魔兽世界、博德之门、愤怒的小鸟、QQ三国、VOCALOID3、太阳神三国杀、游戏王ygocore等。

Cocos2d-lua,其实只是Cocos2d引擎添加了Lua绑定的版本。

Cocos2d这里就不解释了,懒得拷贝,只是关于cocos创建和使用的时候需要注意的是

cocos new TestProj -d Desktop/ -l lua,这里的引擎其实是同一套,只是创建工程时提供了不同语言的桥接层

  • 使用C++语言和Cocos2d-x引擎进行开发时,我们写的代码是直接调用引擎的API的,因为引擎也是用C++语言编写,不需要进行语言转换
  • 使用Lua语言和Cocos2d-x引擎进行开发时,我们写的代码通过LuaEngine执行,而LuaEngine封装了Cocos2d-x引擎的API,所以就相当于使用Lua脚本在调用Cocos2d-x的API了

各个平台的入口

iOS
#import <UIKit/UIKit.h>

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, @"AppController");
    [pool release];
    return retVal;
}
Mac OS
#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[]) {
    return NSApplicationMain(argc, (const char **)argv);
}
Linux
int main(int argc,char *argv)
{
    AppDelegate app;
    return Application::getInstance()->run();
}
Android
void cocos_android_app_init(JNIENV* env)
{
    appDelegate.reset(new AppDelegate());
    //新版本 : AppDelegate *pAppDelegate = new AppDelegate();
}
  • Android启动流程概述
    • 1. 配置文件Manifest
    • 2. AppActivity
    • 3. onCreate
    • 4. super.onCreate
    • 5. onLoadNativeLibraries
    • 6. System.loadLibrary(libName);
    • 7. 触发cocos_android_app_init(在main.cpp)中
    • 8. 再由库执行调用对应的Lua代码
    • ......
由于笔者一直着力于iOS平台,所以这里以iOS为例,其他平台可参考:

1. main函数入口

iOSApp中打开程序,加载完动态库,和一些必备的初始化和准备(rebase, bind,SetUp)之后,会回到main函数开始执行真正的程序代码

int main(int argc, char *argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, @"AppController");
    }
}

Main函数里面会调用UIApplicationMain, UIApplicationMain会一路走完这些流程

+ 根据principalClassName传递的类名创建UIApplication对象
+ 创建UIApplication代理对象,给UIApplication对象设置代理
+ 开启主运行时间循环,处理事件,保持程序一直运行
+ 加载info.plist,判断下是否指定了main,如果指定了,就会去加载

我们可以看到这里设置的代理是AppController,然后看看AppController

2. 代理对象AppController

AppController里面有个 didFinishLaunchingWithOptions,这个是程序加载完毕的监听方法

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

cocos2d::Application *app = cocos2d::Application::getInstance();

// Initialize the GLView attributes
app->initGLContextAttrs();
cocos2d::GLViewImpl::convertAttrs();

// Override point for customization after application launch.

// Add the view controller's view to the window and display.
window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];

// Use RootViewController to manage CCEAGLView
_viewController = [[RootViewController alloc]init];
_viewController.wantsFullScreenLayout = YES;


// Set RootViewController to window
if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
// warning: addSubView doesn't work on iOS6
[window addSubview: _viewController.view];
}
else
{
// use this method on ios6
[window setRootViewController:_viewController];
}

[window makeKeyAndVisible];

[[UIApplication sharedApplication] setStatusBarHidden:true];

// IMPORTANT: Setting the GLView should be done after creating the RootViewController
cocos2d::GLView *glview = cocos2d::GLViewImpl::createWithEAGLView((__bridge void *)_viewController.view);
cocos2d::Director::getInstance()->setOpenGLView(glview);

//run the cocos2d-x game scene
app->run();

return YES;
}

前面分别:获取Director,GLView设置GLView,最后执行run

3. run cocos2d delegate

我们看看run方法里面,这里的run方法很关键,

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

有个applicationDidFinishLaunching,其实这里cocos2d默认的代理,在class中

4. cocos2d配置,lua加载

这里在coco2d的代理中,可以看到一堆的初始化配置和加载,然后就开始获取并执行脚本lua(通过lua脚本显示并处理Scene逻辑)

bool AppDelegate::applicationDidFinishLaunching()
{
    // set default FPS
    Director::getInstance()->setAnimationInterval(1.0 / 60.0f);

    // register lua module
    auto engine = LuaEngine::getInstance();
    ScriptEngineManager::getInstance()->setScriptEngine(engine);
    lua_State* L = engine->getLuaStack()->getLuaState();
    lua_module_register(L);

    register_all_packages();

    LuaStack* stack = engine->getLuaStack();
    stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));

    //register custom function
    //LuaStack* stack = engine->getLuaStack();
    //register_custom_function(stack->getLuaState());

    #if CC_64BITS
        FileUtils::getInstance()->addSearchPath("src/64bit");
    #endif
        FileUtils::getInstance()->addSearchPath("src");
        FileUtils::getInstance()->addSearchPath("res");
        if (engine->executeScriptFile("main.lua"))
        {
            return false;
        }

    return true;
}
原生集成补充

如果你是直接在原生嵌入而不是夸平台会看到这里其实是创建并返回Scene

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
        glview = GLViewImpl::create("Fiction_Single");
        director->setOpenGLView(glview);
    }

    // turn on display FPS
    // director->setDisplayStats(true);
    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0f / 60);
    register_all_packages();

    //SpriteFrameCache::getInstance()->removeSpriteFrames();
    //SpriteFrameCache::getInstance()->removeUnusedSpriteFrames();

    // create a scene. it's an autorelease object
    // 初始化与运行主场景
    auto scene = GameMainLayer::MainScene();    // 初始化与运行主场景
    director->runWithScene(scene);
    return true;
}

游戏逻辑就可以从这个Scene中的init函数开始,添加UI层,添加事件监听器,添加游戏层等等...如果我们有一些统计、资源管理器等,也可以在AppDelegate的applicationDidFinishLaunching函数中来进行。

5. Lua脚本初始化

在这之前,我们先先看看,项目的配置文件config.json

{
    "init_cfg":{
       "isLandscape": true,
       "isWindowTop": false,
       "name": "MyDemo",
       "width": 960,
       "height": 640,
       "entry": "src/main.lua",
       "consolePort": 6050,
       "uploadPort": 6060
top Created with Sketch.