导出自定义HTML页面

虽然Web导出模板提供了一个默认的HTML页面,完全能够在没有任何进一步定制的情况下启动项目,但创建一个自定义的HTML页面可能是有益的。虽然游戏本身还不能轻易地从外部直接控制,但这样的页面允许自定义引擎的初始化过程。

自定义默认页面有用的一些用例包括:

  • 从其他目录加载文件;

  • 加载 .zip 文件而不是 .pck 文件作为主资源包;

  • 从主资源包文件以外的目录中加载引擎文件;

  • 添加一个 "即点即玩" 按钮, 使游戏可以在全屏模式下启动;

  • 在引擎启动之前加载一些额外的文件, 以便稍后在文件系统中可用;

  • 传递自定义 "命令行" 参数, 例如 -s 启动 MainLoop 脚本.

默认的HTML页面可在Godot引擎仓库 /misc/dist/html/full-size.html 中找到, 但以下模板可作为一个更简单的例子:

<!DOCTYPE html>
<html>
    <head>
        <title>My Template</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <canvas id="canvas"></canvas>
        <script src="$GODOT_URL"></script>
        <script>
            var engine = new Engine($GODOT_CONFIG);
            engine.startGame();
        </script>
    </body>
</html>

场景布置

如上面的例子所示,它主要是一个普通的HTML文档,有几个占位符需要在导出时替换,一个html <canvas> 元素,和一些简单的JavaScript代码,调用 Engine() 类。

唯一需要的占位符是:

  • $GODOT_URL : 主JavaScript文件的名称,它提供了启动引擎所需的 Engine() 类,必须作为 <script> 包含在HTML中。这个名字是在导出过程中从*导出路径Export Path* 生成的。

  • $GODOT_CONFIG : 一个JavaScript对象,包含导出选项,以后可以被重写。参见 EngineConfig 获取完整的重写列表。

以下可选的占位符将在你的自定义HTML模板中实现一些额外的功能。

  • $GODOT_PROJECT_NAME: 在项目设置中定义的项目名称。在你的模板中使用它作为 <title> 是一个好主意。

  • $GODOT_HEAD_INCLUDE. 在HTML文件中, 在 <head> 标签结尾前包含的一个自定义字符串. 它是在*Html / Head Include*部分的导出选项中自定义的. 虽然你可以完全控制你所创建的HTML页面, 但这个变量对于从Godot Editor中配置HTML``head`` 元素的部分内容很有用, 例如, 对于不同的Web导出预设.

当自定义页面准备好后, 可以在 Html / Custom Html Shell 部分的导出选项中选择.

../../_images/html5_export_options.png

启动项目

为了能够启动游戏,你需要写一个脚本来初始化引擎 — 控制代码。这个过程包括三个步骤,不过如图所示,其中大部分步骤可以跳过,这取决于需要多少定制化的内容,或者留给默认行为。

参见 HTML5 shell类参考 ,以获得可用方法和选项的完整列表。

首先,引擎必须被加载,然后需要被初始化,在这之后,项目最终可以被启动。你可以手动执行这些步骤中的每一步,而且控制得很好。然而,在最简单的情况下,你需要做的是用导出的配置创建一个 Engine() 类的实例,然后调用 engine.startGame 方法,可选择重写任何 EngineConfig 参数。

const engine = new Engine($GODOT_CONFIG);
engine.startGame({
    /* optional override configuration, eg. */
    // unloadAfterInit: false,
    // canvasResizePolicy: 0,
    // ...
});

这段代码在启动游戏前自动加载和初始化引擎。它使用给定的配置来加载引擎。 engine.startGame 方法是异步的,返回一个 Promise 。这允许你的控制代码跟踪游戏是否被正确加载,而无需阻塞执行或依赖轮询。

如果你的项目需要对启动参数和依赖文件进行特别控制,可以使用 engine.start 方法来代替。注意,这个方法不会自动预加载 pck 文件,所以你可能要通过 engine.preloadFile 方法手动预加载它和任何其他额外的文件。

另外,你也可以手动 engine.init 在模块初始化之后,但在引擎启动之前,执行特定的动作。

这个过程有点复杂,但可以让你完全控制引擎的启动过程。

const myWasm = 'mygame.wasm';
const myPck = 'mygame.pck';
const engine = new Engine();
Promise.all([
    // Load and init the engine
    engine.init(myWasm),
    // And the pck concurrently
    engine.preloadFile(myPck),
]).then(() => {
    // Now start the engine.
    return engine.start({ args: ['--main-pack', myPck] });
}).then(() => {
    console.log('Engine has started!');
});

要手动加载引擎,必须调用 Engine.load() 静态方法。由于这个方法是静态的,如果多个引擎实例共享相同的 wasm ,就可以产生多个引擎实例。

注解

默认情况下不能生成多个实例,因为引擎被初始化后会立即卸载。为了防止这种情况发生,请看 unloadAfterInit 覆盖选项。之后仍然可以通过调用 Engine.unload() 静态方法来手动卸载引擎。卸载引擎可以通过卸载实例初始化后不再需要的文件来释放浏览器内存。

自定义行为

在Web环境下, 可以使用几种方法来保证游戏能够按照预期的方式运行.

如果您的目标是WebGL的特定版本, 或者只是想检查WebGL是否可用, 您可以调用 Engine.isWebGLAvailable() 方法. 它可以选择接受一个参数, 允许测试WebGL的特定主要版本.

由于真正的可执行文件不存在于Web环境中,引擎只存储一个由加载的引擎文件的基本名称形成的虚拟文件名。这个值会影响 OS.get_executable_path() 方法的输出,并定义自动启动的主包的名称。executable 选项可以用来重写这个值。

自定义演示文稿

有几个配置选项可以用来进一步定制你页面上游戏的外观和行为。

默认情况下,页面上的第一个canvas元素被用于渲染。要使用一个不同的画布元素,可以使用 canvas 选项重写。它需要一个对DOM元素本身的引用。

const canvasElement = document.querySelector("#my-canvas-element");
engine.startGame({ canvas: canvasElement });

引擎调整画布大小的方式可以通过 canvasResizePolicy 覆盖选项进行配置。

如果你的游戏需要一些时间来加载,显示一个跟踪进度的自定义加载UI可能是有用的。这可以通过 onProgress 回调选项来实现,它允许设置一个回调函数,当引擎加载新字节时,该函数将被定期调用。

function printProgress(current, total) {
    console.log("Loaded " + current + " of " + total + " bytes");
}
engine.startGame({ onProgress: printProgress });

请注意, 在某些情况下, total 可能是 0 . 这意味着无法计算.

如果你的游戏支持多种语言, locale 覆盖选项可以用来强制使用一个特定的语言,只要你有一个有效的语言代码字符串。使用服务器端的逻辑来确定用户可能喜欢哪种语言可能较好。这样,语言代码可以从 Accept-Language HTTP头中获取,或者由GeoIP服务决定。

调试

要调试导出的项目, 可能需要读取引擎生成的标准输出和错误流. 这与编辑器控制台窗口中显示的输出类似. 默认情况下, 标准的 console.logconsole.warning 分别用于输出和错误流. 这种行为可以通过设置自己的函数来处理消息来定制.

使用 onPrint 覆盖选项为输出流设置一个回调函数,以及 onPrintError 覆盖选项为错误流设置一个回调函数。

function print(text) {
    console.log(text);
}
function printError(text) {
    console.warn(text);
}
engine.startGame({ onPrint: print, onPrintError: printError });

在处理引擎输出时,请记住,在成品中打印出来可能是不可取的。