Cocos Creator 微信小游戏完整启动流程解析

概述

Cocos Creator 微信小游戏的启动流程是一个多阶段、跨平台的复杂过程,涉及从原生代码到 JavaScript 的执行环境搭建。整个流程可以分为以下几个主要阶段:

  1. 原生引擎初始化
  2. JavaScript 运行时环境准备
  3. 游戏脚本加载与执行
  4. 微信小游戏平台适配

这个流程在 Cocos Creator 引擎的多个文件中都有体现,特别是在 native 目录下的 Game.cppRuntimeJsImpl.h 等文件中。

1. 原生引擎初始化

1.1 Game 类的初始化

native/tools/simulator/frameworks/runtime-src/Classes/Game.cpp 文件中,Game 类的 init() 方法负责初始化游戏窗口和引擎核心组件。

int Game::init() {
    cc::pipeline::GlobalDSManager::setDescriptorSetLayout();
    SimulatorApp::getInstance()->init();
    std::call_once(_windowCreateFlag, [&]() {
        cc::ISystemWindowInfo info;
        info.width= SimulatorApp::getInstance()->getWidth();
        info.height = SimulatorApp::getInstance()->getHeight();
    #if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
        info.x = (GetSystemMetrics(SM_CXSCREEN) - info.width) / 2;
        info.y = (GetSystemMetrics(SM_CYSCREEN) - info.height) / 2;
    #elif (CC_PLATFORM == CC_PLATFORM_MACOS)
        auto mainDisplayId = CGMainDisplayID();
        info.x = (CGDisplayPixelsWide(mainDisplayId) - info.width) / 2;
        info.y = (CGDisplayPixelsHigh(mainDisplayId) - info.height) / 2;
    #endif
        info.title = "My Game";
        info.flags = cc::ISystemWindow::CC_WINDOW_SHOWN |
                    cc::ISystemWindow::CC_WINDOW_RESIZABLE |
                    cc::ISystemWindow::CC_WINDOW_INPUT_FOCUS;
        
        cc::ISystemWindowManager* windowMgr = CC_GET_PLATFORM_INTERFACE(cc::ISystemWindowManager);
        windowMgr->createWindow(info);
    });
    SimulatorApp::getInstance()->run();
    auto parser = ConfigParser::getInstance();
    setDebugIpAndPort("0.0.0.0", 5086, parser->isWaitForConnect());

    int ret = cc::CocosApplication::init();
    if (ret != 0) {
        return ret;
    }

    auto runtimeEngine = RuntimeEngine::getInstance();
    runtimeEngine->setEventTrackingEnable(true);
    
    auto jsRuntime = RuntimeJsImpl::create();
    runtimeEngine->addRuntime(jsRuntime, kRuntimeEngineJs);
    runtimeEngine->start();

    setXXTeaKey("");

    runScript("jsb-adapter/web-adapter.js");
    runScript("main.js");

    // Runtime end
    CC_LOG_DEBUG("iShow!");
    return 0;
}

这段代码主要完成了以下工作:

  • 初始化全局描述符集管理器
  • 创建游戏窗口并设置窗口属性
  • 初始化 Cocos 应用程序
  • 创建并启动 JavaScript 运行时
  • 设置脚本加密密钥
  • 加载并运行适配器脚本和主脚本

Game.cpp:61:120

1.2 窗口创建

窗口创建部分代码展示了如何根据平台创建游戏窗口:

cc::ISystemWindowInfo info;
info.width= SimulatorApp::getInstance()->getWidth();
info.height = SimulatorApp::getInstance()->getHeight();
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
    info.x = (GetSystemMetrics(SM_CXSCREEN) - info.width) / 2;
    info.y = (GetSystemMetrics(SM_CYSCREEN) - info.height) / 2;
#elif (CC_PLATFORM == CC_PLATFORM_MACOS)
    auto mainDisplayId = CGMainDisplayID();
    info.x = (CGDisplayPixelsWide(mainDisplayId) - info.width) / 2;
    info.y = (CGDisplayPixelsHigh(mainDisplayId) - info.height) / 2;
#endif
info.title = "My Game";
info.flags = cc::ISystemWindow::CC_WINDOW_SHOWN |
            cc::ISystemWindow::CC_WINDOW_RESIZABLE |
            cc::ISystemWindow::CC_WINDOW_INPUT_FOCUS;

cc::ISystemWindowManager* windowMgr = CC_GET_PLATFORM_INTERFACE(cc::ISystemWindowManager);
windowMgr->createWindow(info);

这段代码根据平台创建了游戏窗口,并设置了窗口的位置、大小和属性。

Game.cpp:61:90

2. JavaScript 运行时环境准备

2.1 RuntimeJsImpl 类

native/tools/simulator/frameworks/runtime-src/Classes/ide-support/RuntimeJsImpl.h 文件中定义了 RuntimeJsImpl 类,负责创建和管理 JavaScript 运行时环境。

class RuntimeJsImpl : public RuntimeProtocol
{
public:
    static RuntimeJsImpl* create();
    
    void startScript(const std::string& file);
    void onStartDebuger(const rapidjson::Document& dArgParse, rapidjson::Document& dReplyParse);
    void onClearCompile(const rapidjson::Document& dArgParse, rapidjson::Document& dReplyParse);
    void onPrecompile(const rapidjson::Document& dArgParse, rapidjson::Document& dReplyParse);
    void onReload(const rapidjson::Document& dArgParse, rapidjson::Document& dReplyParse);
    void onRemove(const std::string &filename);
    void end();
    
    bool startWithDebugger();
private:
    RuntimeJsImpl();
    bool initJsEnv();
    bool loadScriptFile(const std::string& file);
};

这个类实现了 JavaScript 运行时的基本功能,包括脚本加载、调试支持等。

RuntimeJsImpl.h:31:60

2.2 JavaScript 环境初始化

Game.cpp 中,创建并启动 JavaScript 运行时:

auto runtimeEngine = RuntimeEngine::getInstance();
runtimeEngine->setEventTrackingEnable(true);

auto jsRuntime = RuntimeJsImpl::create();
runtimeEngine->addRuntime(jsRuntime, kRuntimeEngineJs);
runtimeEngine->start();

这段代码创建了 JavaScript 运行时实例,并将其添加到运行时引擎中,然后启动运行时。

Game.cpp:91:105

3. 游戏脚本加载与执行

3.1 脚本加载

Game.cpp 中,加载并执行了两个关键的 JavaScript 脚本:

setXXTeaKey("");

runScript("jsb-adapter/web-adapter.js");
runScript("main.js");
  • web-adapter.js:这是微信小游戏平台的适配器脚本,负责处理平台特定的功能
  • main.js:这是游戏的主脚本,包含游戏的初始化和启动代码

Game.cpp:106:115

3.2 脚本执行流程

微信小游戏的启动流程中,脚本执行主要涉及以下几个步骤:

  1. 加载适配器脚本web-adapter.js 负责初始化微信小游戏平台的特定功能,如网络请求、本地存储等
  2. 加载主脚本main.js 包含游戏的初始化代码,通常会调用 Cocos Creator 的启动函数
  3. 游戏初始化:在 JavaScript 中完成游戏场景、资源等的加载和初始化
  4. 游戏循环启动:开始游戏的主循环,处理用户输入、更新游戏状态和渲染画面

4. 微信小游戏平台适配

4.1 平台适配机制

Cocos Creator 通过适配器模式实现了对微信小游戏平台的支持。在 templates/harmonyos-next/entry/src/main/ets/workers/cocos_worker.ts 文件中,可以看到平台适配的相关代码:

import worker from '@ohos.worker';
import cocos from 'libcocos.so';
import hilog from '@ohos.hilog';

import { ContextType } from '../common/Constants';

<% if(!useV8) { %>
import { launchEngine } from '../cocos/game';
<% } %>
import { PortProxy } from '../common/PortProxy';

<% if(useV8) { %>
  globalThis.importPolyfill = async function () {
    await import('../cocos/oh-adapter/sys-ability-polyfill.js');
  }
  globalThis.importPolyfill();
  globalThis.oh = {};
<% } %>

if (!(console as any).assert) {
    (console as any).assert = function(cond, msg) {
        if (!cond) {
            throw new Error(msg);
        }
    };
}

const nativeContext = cocos.getContext(ContextType.WORKER_INIT);
nativeContext.workerInit();

const nativeEditBox = cocos.getContext(ContextType.EDITBOX_UTILS);
const nativeWebView = cocos.getContext(ContextType.WEBVIEW_UTILS);
const appLifecycle = cocos.getContext(ContextType.APP_LIFECYCLE);
const nativeVideo = cocos.getContext(ContextType.VIDEO_UTILS);

let uiPort = new PortProxy(worker.workerPort);

nativeContext.postMessage = function (msgType: string, msgData: string): void {
  uiPort.postMessage(msgType, msgData);
}

nativeContext.postSyncMessage = async function (msgType: string, msgData: string): Promise<boolean | string | number> {
  const result = await uiPort.postSyncMessage(msgType, msgData) as boolean | string | number;
  return result;
}

// The purpose of this is to avoid being GC
nativeContext.setPostMessageFunction.call(nativeContext, nativeContext.postMessage)
nativeContext.setPostSyncMessageFunction.call(nativeContext, nativeContext.postSyncMessage)

globalThis.terminateProcess = function () {
  uiPort.postMessage("exitGame",0);
}

uiPort._messageHandle = function (e) {
  var data = e.data;
  var msg = data.data;

  switch (msg.name) {
    case "onXCLoad":
      const renderContext = cocos.getContext(ContextType.NATIVE_RENDER_API);
      renderContext.nativeEngineInit();
      <% if(!useV8) { %>
        launchEngine().then(() => {
          console.info('launch CC engine finished');
        }).catch(e => {
          console.error('launch CC engine failed');
        });
      <% } %>
      // @ts-ignore
      globalThis.oh.postMessage = nativeContext.postMessage;
      // @ts-ignore
      globalThis.oh.postSyncMessage = nativeContext.postSyncMessage;
      renderContext.nativeEngineStart();
      break;
    case "onTextInput":
      nativeEditBox.onTextChange(msg.param);
      break;
    case "onComplete":
      nativeEditBox.onComplete(msg.param);
      break;
    case "onConfirm":
      nativeEditBox.onConfirm(msg.param);
      break;
    case "onPageBegin":
      nativeWebView.shouldStartLoading(msg.param.viewTag, msg.param.url);
      break;
    case "onPageEnd":
      nativeWebView.finishLoading(msg.param.viewTag, msg.param.url);
      break;
    case "onErrorReceive":
      nativeWebView.failLoading(msg.param.viewTag, msg.param.url);
      break;
    case "onVideoEvent":
      nativeVideo.onVideoEvent(JSON.stringify(msg.param));
      break;
    case "backPress":
      appLifecycle.onBackPress();
      break;
    default:
      console.error("cocos worker: message type unknown");
      break;
  }
}

这段代码展示了如何在微信小游戏平台上初始化 Cocos 引擎,并处理平台特定的事件和消息。

cocos_worker.ts:1:129

启动流程图解

1. 整体启动流程图

游戏启动
原生引擎初始化
创建游戏窗口
初始化JavaScript运行时
加载适配器脚本
加载主游戏脚本
游戏初始化
游戏循环启动

2. JavaScript 运行时初始化流程

创建RuntimeJsImpl实例
初始化JavaScript环境
加载脚本文件
执行脚本

3. 微信小游戏平台适配流程

初始化平台上下文
设置消息处理
注册平台事件回调
启动引擎

总结

Cocos Creator 微信小游戏的完整启动流程是一个多阶段、跨平台的过程,涉及从原生代码到 JavaScript 的执行环境搭建。整个流程可以总结为以下几个关键步骤:

  1. 原生引擎初始化:创建游戏窗口并初始化引擎核心组件
  2. JavaScript 运行时环境准备:创建并启动 JavaScript 运行时
  3. 游戏脚本加载与执行:加载适配器脚本和主游戏脚本
  4. 微信小游戏平台适配:处理平台特定的事件和消息

这个流程在 Cocos Creator 引擎的多个文件中都有体现,特别是在 native 目录下的 Game.cppRuntimeJsImpl.h 等文件中。通过理解这些文件的内容和交互,可以更好地掌握 Cocos Creator 微信小游戏的启动机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值