From d5fd8a3841df99a7466e720c3c88ebf7dd561e1e Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Wed, 15 Apr 2020 12:53:33 +0300 Subject: [PATCH] Split Web export guide into class reference and the guide itself (#3326) --- _static/css/custom.css | 60 ++- .../platform/customizing_html5_shell.rst | 452 ++++++------------ tutorials/platform/html5_shell_classref.rst | 266 +++++++++++ .../platform/img/html5_export_options.png | Bin 0 -> 42928 bytes tutorials/platform/index.rst | 2 +- tutorials/platform/platform_html5.rst | 11 + 6 files changed, 478 insertions(+), 313 deletions(-) create mode 100644 tutorials/platform/html5_shell_classref.rst create mode 100644 tutorials/platform/img/html5_export_options.png create mode 100644 tutorials/platform/platform_html5.rst diff --git a/_static/css/custom.css b/_static/css/custom.css index 392595c9a..9f0c4273c 100644 --- a/_static/css/custom.css +++ b/_static/css/custom.css @@ -248,32 +248,74 @@ hr, border-color: var(--hr-color); } -/* Doesn't seem to be used on Read the Docs online builds, but is present when building locally */ +/* JavaScript documentation directives */ .rst-content dl:not(.docutils) dt { background-color: var(--admonition-note-background-color); border-color: var(--admonition-note-title-background-color); color: var(--admonition-note-color); } - -/* JavaScript documentation directives */ +.rst-content dl:not(.docutils) dl dt { + background-color: var(--admonition-attention-background-color); + border-color: var(--admonition-attention-title-background-color); + color: var(--admonition-attention-color); +} +.rst-content dl:not(.docutils).class dt, +.rst-content dl:not(.docutils).function dt, +.rst-content dl:not(.docutils).method dt, +.rst-content dl:not(.docutils).attribute dt { + width: 100%; +} +.rst-content dl:not(.docutils).class > dt, +.rst-content dl:not(.docutils).function > dt, +.rst-content dl:not(.docutils).method > dt, +.rst-content dl:not(.docutils).attribute > dt { + font-size: 100%; + font-weight: normal; + margin-bottom: 16px; + padding: 6px 8px; +} .rst-content dl:not(.docutils) tt.descclassname, .rst-content dl:not(.docutils) code.descclassname { - color: var(--highlight-type2-color) + color: var(--highlight-type2-color); + font-weight: normal; } .rst-content dl:not(.docutils) tt.descname, .rst-content dl:not(.docutils) code.descname { - color: var(--highlight-function-color) + color: var(--highlight-function-color); + font-weight: normal; } .rst-content dl:not(.docutils) .sig-paren, .rst-content dl:not(.docutils) .optional { - color: var(--highlight-operator-color) + color: var(--highlight-operator-color); + font-weight: normal; + padding: 0 2px; } +.rst-content dl:not(.docutils) .optional { + font-style: italic; +} +.rst-content dl:not(.docutils) .sig-param, .rst-content dl:not(.docutils).class dt > em, -.rst-content dl:not(.docutils).function dt > em { - color: var(--code-literal-color) +.rst-content dl:not(.docutils).function dt > em, +.rst-content dl:not(.docutils).method dt > em { + color: var(--code-literal-color); + font-style: normal; + padding: 0 4px; +} +.rst-content dl:not(.docutils) .sig-param, +.rst-content dl:not(.docutils).class dt > .optional ~ em, +.rst-content dl:not(.docutils).function dt > .optional ~ em, +.rst-content dl:not(.docutils).method dt > .optional ~ em { + color: var(--highlight-number-color); + font-style: italic; } .rst-content dl:not(.docutils).class dt > em.property { - color: var(--highlight-keyword-color) + color: var(--highlight-keyword-color); +} +.rst-content dl:not(.docutils) dt a.headerlink { + color: var(--link-color); +} +.rst-content dl:not(.docutils) dt a.headerlink:visited { + color: var(--link-color-visited); } footer, diff --git a/tutorials/platform/customizing_html5_shell.rst b/tutorials/platform/customizing_html5_shell.rst index d87dc9561..47743f811 100644 --- a/tutorials/platform/customizing_html5_shell.rst +++ b/tutorials/platform/customizing_html5_shell.rst @@ -1,355 +1,201 @@ .. _doc_customizing_html5_shell: -Customizing the Web export HTML page +Custom HTML page for Web export ==================================== -Rather than the default HTML page that comes with the export templates, it is -also possible to use a custom HTML page. This allows drastic customization of -the final web presentation and behavior. The path to the custom HTML page is -specified in the export options as ``Html/Custom Html Shell``. +While Web export templates provide a default HTML page fully capable of launching +the project without any further customization, it may be beneficial to create a custom +HTML page. While the game itself cannot be directly controlled from the outside, +such page allows to customize the initialization process for the engine. + +Some use-cases where customizing the default page is useful include: + +- Loading files from a different directory than the page; +- Loading a ``.zip`` file instead of a ``.pck`` file as the main pack; +- Loading the engine from a different directory than the main pack file; +- Adding a click-to-play button so that games can be started in the fullscreen mode; +- Loading some extra files before the engine starts, making them available in + the project file system as soon as possible; +- Passing custom command line arguments, e.g. ``-s`` to start a ``MainLoop`` script. The default HTML page is available in the Godot Engine repository at -`/misc/dist/html/full-size.html `__. -Some simple use-cases where customizing the default page is useful include: +`/misc/dist/html/full-size.html `__ +and can be used as a reference implementation. Another sample HTML page is available at +`/misc/dist/html/fixed-size.html `__. +It differs from the default one by having a fixed size canvas area and an output widget below it. - - Loading files from a different directory - - Loading a ``.zip`` file instead of a ``.pck`` file as main pack - - Loading engine files from a different directory than the main pack file - - Adding a click-to-play button so games can be started in full-screen mode - - Loading some extra files before the engine starts, so they are available in - the file system later - - Passing custom "command line" arguments, e.g. ``-s`` to start a MainLoop script +.. note:: It is recommended to use developer tools provided by browser vendors to debug + exported projects. Output generated by the engine may be limited and does not + include WebGL errors. -Another sample HTML page is available at `/misc/dist/html/fixed-size.html `__. -This page uses a fixed size canvas with an output widget below. However, the -F12 browser console should be preferred as it can display additional -information, such as WebGL errors. +Setup +----- +As evident by the default HTML page, it is mostly a regular HTML document. To work with +Godot projects it needs to be fully realized, to have a control code that calls +the :js:class:`Engine` class, and to provide places for several placeholders, which are +replaced with their actual values during export. -Placeholder substitution ------------------------- +.. image:: img/html5_export_options.png -When exporting the game, several placeholders in the HTML page are replaced -with values depending on the export: +- ``$GODOT_BASENAME``: + The base name from the *Export Path*, as set up in the export options; suffixes are omitted + (e.g. ``game.html`` becomes ``game``). This variable can be used to generate a path + to the main JavaScript file ``$GODOT_BASENAME.js``, which provides the :js:class:`Engine` + class. A splash image shown during the booting process can be accessed using this variable + as well: ``$GODOT_BASENAME.png``. -+------------------------------+-----------------------------------------------+ -| Placeholder | Substituted by | -+==============================+===============================================+ -| ``$GODOT_BASENAME`` | Basename of exported files without suffixes, | -| | e.g. ``game`` when exporting ``game.html`` | -+------------------------------+-----------------------------------------------+ -| ``$GODOT_DEBUG_ENABLED`` | ``true`` if debugging, ``false`` otherwise | -+------------------------------+-----------------------------------------------+ -| ``$GODOT_HEAD_INCLUDE`` | Custom string to include just before the end | -| | of the HTML ```` element | -+------------------------------+-----------------------------------------------+ +- ``$GODOT_HEAD_INCLUDE``: + A custom string to include in the HTML document just before the end of the ```` tag. It + is customized in the export options under the *Html / Head Include* section. While you fully + control the HTML page you create, this variable can be useful for configuring parts of the + HTML ``head`` element from the Godot Editor, e.g. for different Web export presets. -The HTML file must evaluate the JavaScript file ``$GODOT_BASENAME.js``. This -file defines a global ``Engine`` object used to start the engine, :ref:`see -below ` for details. +- ``$GODOT_DEBUG_ENABLED``: + A flag that tells if this is a debug build, or not. This variable is substituted by strings + ``true`` and ``false``, and can be used to disable debug branches within your control code. -The boot splash image is exported as ``$GODOT_BASENAME.png`` and can be used -e.g. in ```` elements. +When the custom page is ready, it can be selected in the export options under the *Html / Custom Html Shell* +section. -``$GODOT_DEBUG_ENABLED`` can be useful to optionally display e.g. an output -console or other debug tools. +Starting the project +-------------------- +To be able to start the game, you need to write a script that initializes the engine — the control +code. This process consists of three steps, though some of them can be skipped and left for +a default behavior. -``$GODOT_HEAD_INCLUDE`` is replaced with the string specified by the export -option ``Html/Head Include``. - -.. _doc_javascript_engine_object: - -The ``Engine`` object ---------------------- - -The JavaScript global object ``Engine`` is defined by ``$GODOT_BASENAME.js`` -and serves as an interface to the engine start-up process. - -The API is based on and requires basic understanding of `Promises `__. - -The object itself has only the following methods: - -.. js:function:: Engine.load(basePath) - - Load the engine from the passed base path. - - :param string basePath: Base path of the engine to load. - :returns: Promise which resolves once the engine is loaded. - -.. js:function:: Engine.unload() - - Unload the engine to free memory. - - This is called automatically once the engine is started unless - explicitly disabled using :js:func:`engine.setUnloadAfterInit`. - -.. js:function:: Engine.isWebGLAvailable([majorVersion = 1]) - - Check whether WebGL is available. - - :param number majorVersion: - The major WebGL version to check for. Defaults to 1 for *WebGL 1.0*. - - :returns: - ``true`` if the given major version of WebGL is available, ``false`` - otherwise. - -.. js:function:: Engine.setWebAssemblyFilenameExtension(extension) - - When loading the engine, the filename extension of the WebAssembly module - is assumed to be ``wasm``. This function allows usage of an alternate - extension. - - .. code-block:: js - - Engine.setWebAssemblyFilenameExtension('dat'); - // Load 'mygame.dat' as WebAssembly module. - Engine.load('mygame'); - - This is useful for outdated hosts that only accept uploads of files with - certain filename extensions. - - :param string extension: - Filename extension without preceding dot. - - .. Note:: - Depending on the host, using an alternate filename extension can prevent - some start-up optimizations. This occurs when the file is delivered with a - MIME-type other than :mimetype:`application/wasm`. - -Starting an ``Engine`` instance -------------------------------- - -:js:class:`Engine` also acts a class: - -.. js:class:: Engine - - An instance of the engine that can be started, usually a game. - -Instantiate the class using the ``new`` operator: +First, the engine must be loaded, then it needs to be initialized, and after this the project +can finally be started. You can perform every of these steps manually and with great control. +However, in the simplest case all you need to do is to create an instance of the :js:class:`Engine` +class and then call the :js:meth:`engine.startGame` method. .. code-block:: js - var engine = new Engine(); + const execName = "path://to/executable" + const mainPack = "path://to/main_pack" -This yields an :js:class:`Engine` instance, referred to as ``engine`` with a -lower-case ``e`` from here. + const engine = new Engine(); + engine.startGame(execName, mainPack) -To start such an instance, the global ``Engine`` object must be loaded, -then the ``engine`` instance must be initialized and finally started. +This snippet of code automatically loads and initializes the engine before starting the game. +It uses the given path to the executable to deduce the path to load the engine. The :js:meth:`engine.startGame` +method is asynchronous and returns a ``Promise``. This allows your control code to track if +the game was loaded correctly without blocking execution or relying on polling. -.. js:function:: engine.init([basePath]) +In case your project needs to have special arguments passed to it by the start-up script, +:js:meth:`engine.startGame` can be replaced by :js:meth:`engine.start`. This method takes an +arbitrary list of string arguments. As it does not have a defined list of arguments, :js:meth:`engine.start` +cannot automatically load the engine. - Initialize the instance. The instance can then be started with one of the - ``start`` functions, usually :js:func:`engine.startGame`. +To load the engine manually the :js:meth:`Engine.load` static method must be called. As +this method is static, multiple engine instances can be spawned with the exact same ``basePath``. +If an instance requires a different ``basePath``, you can call the :js:meth:`engine.init` +method with that path before starting the game. - :param string basePath: - The base path to the engine, same as in :js:func:`Engine.load`. - Must be passed only if the engine hasn't been loaded yet. +.. note:: Multiple instances cannot be spawned by default, as the engine is immediately unloaded after it is initialized. + To prevent this from happening the :js:meth:`engine.setUnloadAfterInit` method can be called. It is still possible + to unload the engine manually afterwards by calling the :js:meth:`Engine.unload` static method. Unloading the engine + frees browser memory by unloading files that are no longer needed once the instance is initialized. - :returns: Promise that resolves once the engine is loaded and initialized. +To correctly load the engine on some hosting providers and network configurations you may +need to change the default filename extension by using :js:meth:`Engine.setWebAssemblyFilenameExtension`. +By default, the extension is assumed to be ``wasm``. If your hosting provider blocks this +extension, this static method can be used to change it to something that is supported. -.. js:function:: engine.preloadFile(file[, path]) +.. code-block:: js - Load a file so it is available in the file system once the instance runs. Must - be called **before** starting the instance. + Engine.setWebAssemblyFilenameExtension("dat"); + // Load mygame.dat as WebAssembly module. + Engine.load("mygame"); - :param file: - If type is string, the file will be loaded from that path. +.. warning:: If a different filename extension is used, some web servers may automatically + set the MIME-type of the file to something other than :mimetype:`application/wasm`. + In that case some start-up optimizations may be skipped. - If type is ``ArrayBuffer`` or a view on one, the buffer will used as - the content of the file. +Customizing the behavior +------------------------ +In the Web environment several methods can be used to guarantee that the game will work as intended. - :param string path: - Path by which the file will be available. Mandatory if ``file`` is not - a string. If not passed, the path is derived from the URL of the loaded - file. +If you target a specific version of WebGL, or just want to check if WebGL is available at all, +you can call the :js:meth:`Engine.isWebGLAvailable` method. It optionally takes an argument that +allows to test for a specific major version of WebGL. - :returns: Promise that resolves once the file is preloaded. +As the real executable file does not exist in the Web environment, the engine only stores a virtual +filename formed from the base name of loaded engine files. This value affects the output of the +:ref:`OS.get_executable_path() ` method and defines the name of +the automatically started main pack. The :js:meth:`engine.setExecutableName` method can be used +to override this value. -.. js:function:: engine.start([arg1, arg2, …]) - - Starts the instance of the engine, using the passed strings as - command line arguments. This allows great control over how the engine is - started, but usually the other methods starting with ``engine.start`` are - simpler and should be used instead. - - If the instance has not yet been initialized with :js:func:`engine.init`, - it will be. - - The engine must be loaded beforehand. - - Requires that the engine has been loaded, and that a canvas can be found on - the page. - - :param string variadic: Command line arguments. - - :returns: Promise that resolves once the engine started. - -.. js:function:: engine.startGame(execName, mainPack) - - Initializes the engine if not yet initialized, loads the executable, - and starts the game with the main pack loaded from the passed URL. - - If the engine isn't loaded yet, the base path of the passed executable name - will be used to load the engine. - - :param string execName: - Executable's name (URL) to start. Also used as base path to load the - engine if not loaded already. Should not contain the file's extension. - - :param string mainPack: - Path (URL) to the main pack to start. - - :returns: Promise that resolves once the game started. - -Configuring start-up behaviour ------------------------------- - -Beside starting the engine, other methods of the engine instance allow -configuring the behavior: - -.. js:function:: engine.setUnloadAfterInit(enabled) - - Specify whether the Engine will be unloaded automatically after the - instance is initialized. - - This frees browser memory by unloading files that are no longer needed once - the instance is initialized. However, if more instances of the engine will - be started, the Engine will have to be loaded again. - - Enabled by default. - - :param boolean enabled: - ``true`` if the engine shall be unloaded after initializing, - ``false`` otherwise. - -.. js:function:: engine.setCanvas(canvasElem) - - Specify a canvas to use. - - By default, the first canvas element on the page is used for rendering. - - :param HTMLCanvasElement canvasElem: The canvas to use. - -.. js:function:: engine.setCanvasResizedOnStart(enabled) - - Specifies whether the canvas will be resized to the width and height - specified in the project settings on start. - - Enabled by default. - - :param boolean enabled: - ``true`` if the canvas shall be resized on start, ``false`` otherwise. - -.. js:function:: engine.setLocale(locale) - - By default, the engine will try to guess the locale to use from the - JavaScript environment. It is usually preferable to use a server-side - user-specified locale, or at least use the locale requested in the HTTP - ``Accept-Language`` header. This method allows specifying such a custom - locale string. - - For example, with PHP: - - .. code-block:: php - - engine.setLocale(); - - :param string locale: - Locale. - - .. seealso:: List of :ref:`locales `. - -.. js:function:: engine.setExecutableName(execName) - - Specify the virtual filename of the executable. - - A real executable file doesn't exist for the HTML5 platform. However, - a virtual filename is stored by the engine for compatibility with other - platforms. - - By default, the base name of the loaded engine files is used. - This method allows specifying another name. - - This affects the output of :ref:`OS.get_executable_path() ` - and the automatically started main pack, :file:`{ExecutableName}.pck`. - - :param string execName: Executable name. +If your project requires some files to be available the moment it is loaded, you can preload +them by calling the :js:meth:`engine.preloadFile` method with a path to a file or by providing it +with an ``ArrayBuffer`` object. In case of the ``ArrayBuffer``, or one of its views, a second argument +must be specified to define an internal path for the loaded resource. Customizing the presentation ---------------------------- +Several methods can be used to further customize the look and behavior of the game on your page. -The following methods are used to implement the presentation: +By default, the first canvas element on the page is used for rendering. To use a different canvas +element the :js:meth:`engine.setCanvas` method can be used. It requires a reference to the DOM +element itself. -.. js:function:: engine.setProgressFunc(callback) +.. code-block:: js - Set the callback for displaying download progress. + const canvasElement = document.querySelector("#my-canvas-element"); + engine.setCanvas(canvasElement); - :param function callback: - Callback called once per frame with two number arguments: - bytes loaded so far, and total bytes to load. +If the width and height of this canvas element differ from values set in the project settings, it +will be resized on the project start. This behavior can be disabled by calling the :js:meth:`engine.setCanvasResizedOnStart` +method. - .. code-block:: js +If your game takes some time to load, it may be useful to display a custom loading UI which tracks +the progress. This can be achieved with the :js:meth:`engine.setProgressFunc` method which allows +to set up a callback function to be called regularly as the engine loads new bytes. - function printProgress(current, total) { - console.log("Loaded " + current + " of " + total + " bytes"); - } - engine.setProgressFunc(printProgress); +.. code-block:: js - If the total is 0, it couldn't be calculated. Possible reasons - include: + function printProgress(current, total) { + console.log("Loaded " + current + " of " + total + " bytes"); + } + engine.setProgressFunc(printProgress); - - Files are delivered with server-side chunked compression - - Files are delivered with server-side compression on Chromium - - Not all file downloads have started yet (usually on servers without - multi-threading) +Be aware that in some cases ``total`` can be ``0``. This means that it cannot be calculated. - .. Note:: - For ease of use, the callback is only called once per frame, so that usage - of ``requestAnimationFrame()`` is not necessary. +If your game supports multiple languages, the :js:meth:`engine.setLocale` method can be used to set +a specific locale, provided you have a valid language code string. It may be good to use server-side +logic to determine which languages a user may prefer. This way the language code can be taken from the +``Accept-Language`` HTTP header, or determined by a GeoIP service. -.. js:function:: engine.setStdoutFunc(callback) +Debugging +--------- +To debug exported projects, it may be useful to read the standard output and error streams generated +by the engine. This is similar to the output shown in the editor console window. By default, standard +``console.log`` and ``console.warn`` are used for the output and error streams respectively. This +behavior can be customized by setting your own functions to handle messages. - Specify the standard output stream callback. +Use the :js:meth:`engine.setStdoutFunc` method to set a callback function for the output stream. Default +behavior is similar to this: - :param function callback: - Callback function called with one argument, the string to print. +.. code-block:: js - .. code-block:: js + function printStdout(text) { + console.log(text); + } + engine.setStdoutFunc(printStdout); - function printStdout(text) { - console.log(text); - } - engine.setStdoutFunc(printStdout); +Use the :js:meth:`engine.setStderrFunc` method to set a callback function for the error stream. Default +behavior is similar to this: - This method should usually only be used in debug pages. The - ``$GODOT_DEBUG_ENABLED`` placeholder can be used to check for this. +.. code-block:: js - By default, ``console.log()`` is used. + function printStderr(text) { + console.warn("Error: " + text); + } + engine.setStderrFunc(printStderr); -.. js:function:: engine.setStderrFunc(callback) +When handling the engine output keep in mind, that it may not be desirable to print it out in the +finished product. To control whether or not the current execution is actually a debug build you can +use ``$GODOT_DEBUG_ENABLED`` placeholder. - Specify the standard error stream callback. - - :param function callback: - Callback function called with one argument, the string to print. - - .. code-block:: js - - function printStderr(text) { - console.warn("Error: " + text); - } - engine.setStderrFunc(printStderr); - - This method should usually only be used in debug pages. The - ``$GODOT_DEBUG_ENABLED`` placeholder can be used to check for this. - - By default, ``console.warn()`` is used. - -Accessing the Emscripten ``Module`` ------------------------------------ - -If you know what you're doing, you can access the runtime environment -(Emscripten's ``Module``) as ``engine.rtenv``. Check the official Emscripten -documentation for information on how to use it: -https://kripken.github.io/emscripten-site/docs/api_reference/module.html +Further debugging options and a low level access to the execution environment are available in a form +of Emscripten's ``Module`` object. It can be accessed using the :js:attr:`engine.rtenv` property on the +engine instance. diff --git a/tutorials/platform/html5_shell_classref.rst b/tutorials/platform/html5_shell_classref.rst new file mode 100644 index 000000000..1e1a4e90a --- /dev/null +++ b/tutorials/platform/html5_shell_classref.rst @@ -0,0 +1,266 @@ +.. _doc_html5_shell_classref: + +HTML5 shell class reference +=========================== + +Projects exported for the Web expose the ``Engine`` class to the JavaScript environment, that allows +fine control over the engine's start-up process. + +This API is built in an asynchronous manner and requires basic understanding +of `Promises `__. + +Engine +------ + +The ``Engine`` class provides methods for loading and starting exported projects on the Web. For default export +settings, this is already part of the exported HTML page. To understand practical use of the ``Engine`` class, +see :ref:`Custom HTML page for Web export `. + +Static Methods +^^^^^^^^^^^^^^ + ++---------+-------------------------------------------------------------------------------------------------------------------+ +| Promise | `Engine.load <#Engine.load>`__ **(** string basePath **)** | ++---------+-------------------------------------------------------------------------------------------------------------------+ +| void | `Engine.unload <#Engine.unload>`__ **(** **)** | ++---------+-------------------------------------------------------------------------------------------------------------------+ +| boolean | `Engine.isWebGLAvailable <#Engine.isWebGLAvailable>`__ **(** *[ number majorVersion=1 ]* **)** | ++---------+-------------------------------------------------------------------------------------------------------------------+ +| void | `Engine.setWebAssemblyFilenameExtension <#Engine.setWebAssemblyFilenameExtension>`__ **(** string extension **)** | ++---------+-------------------------------------------------------------------------------------------------------------------+ + +Instance Properties +^^^^^^^^^^^^^^^^^^^ + ++-----------------------+----------------------------------+ +| Emscripten ``Module`` | `engine.rtenv <#engine.rtenv>`__ | ++-----------------------+----------------------------------+ + +Instance Methods +^^^^^^^^^^^^^^^^ + ++---------+------------------------------------------------------------------------------------------------------+ +| Engine | `Engine <#Engine>`__ **(** **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| Promise | `engine.init <#engine.init>`__ **(** *[ string basePath ]* **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| Promise | `engine.preloadFile <#engine.preloadFile>`__ **(** string\|ArrayBuffer file *[, string path ]* **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| Promise | `engine.start <#engine.start>`__ **(** *[ string arg1, string arg2, … ]* **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| Promise | `engine.startGame <#engine.startGame>`__ **(** string execName, string mainPack **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| void | `engine.setUnloadAfterInit <#engine.setUnloadAfterInit>`__ **(** boolean enabled **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| void | `engine.setCanvas <#engine.setCanvas>`__ **(** HTMLCanvasElement canvasElem **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| void | `engine.setCanvasResizedOnStart <#engine.setCanvasResizedOnStart>`__ **(** boolean enabled **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| void | `engine.setLocale <#engine.setLocale>`__ **(** string locale **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| void | `engine.setExecutableName <#engine.setExecutableName>`__ **(** string execName **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| void | `engine.setProgressFunc <#engine.setProgressFunc>`__ **(** function callback **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| void | `engine.setStdoutFunc <#engine.setStdoutFunc>`__ **(** function callback **)** | ++---------+------------------------------------------------------------------------------------------------------+ +| void | `engine.setStderrFunc <#engine.setStderrFunc>`__ **(** function callback **)** | ++---------+------------------------------------------------------------------------------------------------------+ + + +Static Method Descriptions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. js:method:: Engine.load(basePath) + + Load the engine from the specified base path. + + :param string basePath: + Base path of the engine to load. + :returns: + Promise which resolves once the engine is loaded. + +.. js:method:: Engine.unload() + + Unload the engine to free memory. + + This method is called automatically once the engine is started unless + explicitly disabled using :js:meth:`engine.setUnloadAfterInit`. + +.. js:method:: Engine.isWebGLAvailable([majorVersion = 1]) + + Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for. + + :param number majorVersion: + The major WebGL version to check for. Defaults to ``1`` for *WebGL 1.0*. + :returns: + ``true`` if the given major version of WebGL is available, ``false`` + otherwise. + +.. js:method:: Engine.setWebAssemblyFilenameExtension(extension) + + Set an alternative filename extension for the WebAssembly module. By default + it is assumed to be ``wasm``. + + :param string extension: + Filename extension without preceding dot. + + +Instance Property Descriptions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. js:attribute:: engine.rtenv + + The runtime environment provided by Emscripten's ``Module``. For more information + refer to the `official documentation `__ on Emscripten. + +Instance Method Descriptions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. js:class:: Engine + + Create a new instance of the ``Engine`` class. + +.. js:method:: engine.init([basePath]) + + Initialize the engine instance. Optionally, pass the base path to the engine to load it, + if it hasn't been loaded yet. See :js:meth:`Engine.load`. + + :param string basePath: + Base path of the engine to load. + + :returns: + Promise that resolves once the engine is loaded and initialized. + +.. js:method:: engine.preloadFile(file[, path]) + + Load a file so it is available in the instance's file system once it runs. Must + be called **before** starting the instance. + + :param string|ArrayBuffer file: + If type is ``string``, the file will be loaded from that path. + + If type is ``ArrayBuffer`` or a view on one, the buffer will used as + the content of the file. + + :param string path: + Path by which the file will be accessible. Required, if ``file`` is not + a string. If not passed, the path is derived from the URL of the loaded + file. + + :returns: + Promise that resolves once the file is loaded. + +.. js:method:: engine.start([arg1, arg2, …]) + + Start the instance of the engine, using the passed strings as + command line arguments. :js:meth:`engine.startGame` can be used + in typical cases instead. + + This will initialize the instance if it is not initialized. For manual + initialization, see :js:meth:`engine.init`. The engine must be loaded beforehand. + + Fails if a canvas cannot be found on the page. + + :param string variadic: + Command line argument. + + :returns: + Promise that resolves once the engine started. + +.. js:method:: engine.startGame(execName, mainPack) + + Start the game instance using the given executable URL and main pack URL. + + This will initialize the instance if it is not initialized. For manual + initialization, see :js:meth:`engine.init`. + + This will load the engine if it is not loaded. The base path of the + executable URL will be used as the engine base path. + + :param string execName: + Executable name in a form of URL, omitting filename extension. + + :param string mainPack: + URL of the main pack to start the game. + + :returns: + Promise that resolves once the game started. + +.. js:method:: engine.setUnloadAfterInit(enabled) + + Specify whether the engine will be unloaded automatically after the + instance is initialized. Enabled by default. + + :param boolean enabled: + ``true`` if the engine shall be unloaded after initializing, + ``false`` otherwise. + +.. js:method:: engine.setCanvas(canvasElem) + + Specify a canvas HTML element to use. By default, the first canvas element + on the page is used for rendering. + + :param HTMLCanvasElement canvasElem: + The canvas element to use. + +.. js:method:: engine.setCanvasResizedOnStart(enabled) + + Specifies whether the canvas will be resized to the width and height + specified in the project settings on start. Enabled by default. + + :param boolean enabled: + ``true`` if the canvas shall be resized on start, ``false`` otherwise. + +.. js:method:: engine.setLocale(locale) + + Specify a language code to select the proper localization for the game. + + .. seealso:: Complete list of :ref:`supported locales `. + + :param string locale: + Language code. + +.. js:method:: engine.setExecutableName(execName) + + Specify the virtual filename of the executable. By default, the base name + of the loaded engine files is used. + + This affects the output of :ref:`OS.get_executable_path() ` + and sets the automatically started main pack to :file:`{ExecutableName}.pck`. + + :param string execName: + Executable name. + +.. js:method:: engine.setProgressFunc(callback) + + Specify a callback function for displaying download progress. The callback function is + called once per frame, so that the usage of ``requestAnimationFrame()`` is not necessary. + + If the callback function receives a total amount of bytes as 0, this means that + it is impossible to calculate. Possible reasons include: + + - Files are delivered with server-side chunked compression + - Files are delivered with server-side compression on Chromium + - Not all file downloads have started yet (usually on servers without + multi-threading) + + :param function callback: + The callback function must accept two numeric arguments: the amount of bytes + loaded so far, and the total number of bytes to load. + +.. js:method:: engine.setStdoutFunc(callback) + + Specify a callback function for handling the standard output stream. This method + should usually only be used in debug pages. By default, ``console.log()`` is used. + + :param function callback: + The callback function must accept one string argument: the message to print. + +.. js:method:: engine.setStderrFunc(callback) + + Specify a callback function for handling the standard error stream. This method + should usually only be used in debug pages. By default, ``console.warn()`` is used. + + :param function callback: + The callback function must accept one string argument: the message to print. diff --git a/tutorials/platform/img/html5_export_options.png b/tutorials/platform/img/html5_export_options.png new file mode 100644 index 0000000000000000000000000000000000000000..27eeebe15b3fbdef12579c781f74de6f4d90a9b4 GIT binary patch literal 42928 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 literal 0 HcmV?d00001 diff --git a/tutorials/platform/index.rst b/tutorials/platform/index.rst index cf661b5de..0683cabbf 100644 --- a/tutorials/platform/index.rst +++ b/tutorials/platform/index.rst @@ -7,5 +7,5 @@ Platform-specific android_in_app_purchases services_for_ios - customizing_html5_shell + platform_html5 consoles diff --git a/tutorials/platform/platform_html5.rst b/tutorials/platform/platform_html5.rst new file mode 100644 index 000000000..2bf6cdd67 --- /dev/null +++ b/tutorials/platform/platform_html5.rst @@ -0,0 +1,11 @@ +.. _doc_platform_html5: + +HTML5 +===== + +.. toctree:: + :maxdepth: 1 + :name: toc-learn-features-platform-html5 + + html5_shell_classref + customizing_html5_shell