无限灰

CocosCreator3.x打包后嵌入到uniapp

首先Cocos本身就支持跨平台,可以打包成APP,但由于项目中游戏只是APP的一部分,还有其他界面要开发,用游戏引擎来写APP的一些交互逻辑页面确实不太合适,所以在技术选型上又选择uniapp。

uniapp的web-view是可以嵌入在线的地址和本地的html页面,但嵌入本地的html仅支持APP端,如果你开发需要兼容多端,那么直接使用在线地址的形式嵌入即可,下面的内容也不必阅读了。

需求上因为只需要支持APP端即可,并且使用在线地址嵌入在等待游戏加载的时间有点久,所以我选用了将游戏打包成h5并以本地html页面嵌入到uniapp形式,可以省去了请求游戏引擎js等文件的所需要消耗的时间。

但在嵌入发生一些意外,游戏页面加载时会报 file 协议跨域,从而加载游戏失败,在Cocos论坛找到了解决方案:

在游戏项目的构建模板中修改 fetch 函数:

(构建模板在游戏引擎编辑器左上角菜单栏-项目-创建项目构建模板(选择对应的构建平台)-web手机端即可创建)

function fetchLocal(url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
      resolve(
        new Response(xhr.response, {
          status: 200
        })
      );
    };
    xhr.onerror = function () {
      reject(new TypeError("Local request failed"));
    };
    xhr.open("GET", url);
    xhr.responseType = "arraybuffer";
    xhr.send(null);
  });
}

window.fetch = fetchLocal;

如果你的游戏需要使用到 fetch 来进行网络请求,你可以额外使用 window 全局属性来替代原来的 fetch:

// 在window.fetch被修改为fetchLocal之前赋值
window.fetchRemote = window.fetch;

function fetchLocal(url) {
  ...
}

window.fetch = fetchLocal;

还有,如果你游戏还用到远程服务器上的图片作为背景啥的,那么在运行时也应该会发生跨域问题的;

所以你还需要在修改Image对象:

let OldImage = Image;
let oldProto = Image.prototype;
Image = function (width, height) {
  let image = new OldImage(width, height);
  image.crossOrigin = "anonymous";
  Object.defineProperty(image, "crossOrigin", {
    set() {
      return true;
    }
  });
  return image;
};

Image.prototype = oldProto;

完整的构建模板代码如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title><%= projectName %></title>
    <!--http://www.html5rocks.com/en/mobile/mobifying/-->
    <meta
      name="viewport"
      content="width=device-width,user-scalable=no,initial-scale=1,minimum-scale=1,maximum-scale=1,minimal-ui=true"
    />
    <!--https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html-->
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
    <meta name="format-detection" content="telephone=no" />
    <!-- force webkit on 360 -->
    <meta name="renderer" content="webkit" />
    <meta name="force-rendering" content="webkit" />
    <!-- force edge on IE -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="msapplication-tap-highlight" content="no" />
    <!-- force full screen on some browser -->
    <meta name="full-screen" content="yes" />
    <meta name="x5-fullscreen" content="true" />
    <meta name="360-fullscreen" content="true" />
    <!--fix fireball/issues/3568 -->
    <!--<meta name="browsermode" content="application">-->
    <meta name="x5-page-mode" content="app" />
    <!--<link rel="apple-touch-icon" href=".png" />-->
    <!--<link rel="apple-touch-icon-precomposed" href=".png" />-->
    <link rel="stylesheet" type="text/css" href="<%= cssUrl %>" />
  </head>
  <body>
    <div id="GameDiv" cc_exact_fit_screen="true">
      <div id="Cocos3dGameContainer">
        <canvas
          id="GameCanvas"
          oncontextmenu="event.preventDefault()"
          tabindex="99"
        ></canvas>
      </div>
    </div>
    <script>
      window.fetchRemote = window.fetch;

      let OldImage = Image;
      let oldProto = Image.prototype;
      Image = function (width, height) {
        let image = new OldImage(width, height);
        image.crossOrigin = "anonymous";
        Object.defineProperty(image, "crossOrigin", {
          set() {
            return true;
          }
        });
        return image;
      };

      Image.prototype = oldProto;

      function fetchLocal(url) {
        return new Promise(function (resolve, reject) {
          var xhr = new XMLHttpRequest();
          xhr.onload = function () {
            resolve(
              new Response(xhr.response, {
                status: 200
              })
            );
          };
          xhr.onerror = function () {
            reject(new TypeError("Local request failed"));
          };
          xhr.open("GET", url);
          xhr.responseType = "arraybuffer";
          xhr.send(null);
        });
      }

      window.fetch = fetchLocal;
    </script>
    <!-- 注意上面的脚本排在 include(cocosTemplate, {}) 前面 -->
    <%- include(cocosTemplate, {}) %>
    <!-- 用于和uniapp通信 -->
    <script src="src/uni.webview.1.5.4.js"></script>
  </body>
</html>