Переглянути джерело

Animations (easing part) manual improvements (#37)

* animation manuals fixes

* sorting fix

* Removed broken links

* Fixed part with the links to images

* insert Easing example

* Fix links for manual

* replace example

* Update animation.md

Review fixes
Alexey Gulev 6 роки тому
батько
коміт
82c03c58e2

+ 1 - 0
docs/assets/easier/archive/archive_files.json

@@ -0,0 +1 @@
+{"content":[{"name":"game.projectc","size":2310,"pieces":[{"name":"game.projectc0","offset":0}]},{"name":"game.arci","size":3088,"pieces":[{"name":"game.arci0","offset":0}]},{"name":"game.arcd","size":113454,"pieces":[{"name":"game.arcd0","offset":0}]},{"name":"game.dmanifest","size":5772,"pieces":[{"name":"game.dmanifest0","offset":0}]},{"name":"game.public.der","size":162,"pieces":[{"name":"game.public.der0","offset":0}]}]}

BIN
docs/assets/easier/archive/game.arcd0


BIN
docs/assets/easier/archive/game.arci0


BIN
docs/assets/easier/archive/game.dmanifest0


+ 144 - 0
docs/assets/easier/archive/game.projectc0

@@ -0,0 +1,144 @@
+[project]
+title = easier
+version = 1.0
+write_log = 0
+compress_archive = 1
+
+[display]
+width = 640
+height = 512
+high_dpi = 0
+samples = 0
+fullscreen = 0
+update_frequency = 0
+vsync = 1
+display_profiles = /builtins/render/default.display_profilesc
+dynamic_orientation = 0
+
+[physics]
+type = 2D
+gravity_y = -10
+debug = 0
+debug_alpha = 0.9
+world_count = 4
+gravity_x = 0
+gravity_z = 0
+scale = 1
+debug_scale = 30
+max_collisions = 64
+max_contacts = 128
+contact_impulse_limit = 0
+ray_cast_limit_2d = 64
+ray_cast_limit_3d = 128
+trigger_overlap_capacity = 16
+
+[bootstrap]
+main_collection = /main/main.collectionc
+render = /builtins/render/default.renderc
+
+[graphics]
+default_texture_min_filter = linear
+default_texture_mag_filter = linear
+max_draw_calls = 1024
+max_characters = 8192
+max_debug_vertices = 10000
+texture_profiles = /builtins/graphics/default.texture_profiles
+
+[sound]
+gain = 1
+max_sound_data = 128
+max_sound_buffers = 32
+max_sound_sources = 16
+max_sound_instances = 256
+
+[resource]
+http_cache = 0
+max_resources = 1024
+
+[input]
+repeat_delay = 0.5
+repeat_interval = 0.2
+gamepads = /builtins/input/default.gamepadsc
+game_binding = /input/game.input_bindingc
+use_accelerometer = 1
+
+[sprite]
+max_count = 128
+subpixels = 1
+
+[spine]
+max_count = 128
+
+[model]
+max_count = 128
+
+[gui]
+max_count = 64
+max_particlefx_count = 64
+max_particle_count = 1024
+
+[collection]
+max_instances = 1024
+
+[collection_proxy]
+max_count = 8
+
+[collectionfactory]
+max_count = 128
+
+[factory]
+max_count = 128
+
+[ios]
+pre_renderered_icons = 0
+bundle_identifier = example.unnamed
+infoplist = /builtins/manifests/ios/Info.plist
+
+[android]
+version_code = 1
+package = com.example.todo
+manifest = /builtins/manifests/android/AndroidManifest.xml
+iap_provider = GooglePlay
+input_method = KeyEvent
+immersive_mode = 0
+debuggable = 0
+
+[osx]
+infoplist = /builtins/manifests/osx/Info.plist
+bundle_identifier = example.unnamed
+
+[windows]
+
+[html5]
+custom_heap_size = 0
+include_dev_tool = 0
+htmlfile = /builtins/manifests/web/engine_template.html
+archive_location_prefix = archive
+
+[particle_fx]
+max_count = 64
+max_particle_count = 1024
+
+[iap]
+auto_finish_transactions = 1
+
+[network]
+http_timeout = 0
+
+[library]
+
+[script]
+shared_state = 1
+
+[tracking]
+
+[label]
+max_count = 64
+subpixels = 1
+
+[profiler]
+track_cpu = 0
+
+[liveupdate]
+settings = /liveupdate.settings
+

BIN
docs/assets/easier/archive/game.public.der0


BIN
docs/assets/easier/defold_sound.swf


+ 652 - 0
docs/assets/easier/dmloader.js

@@ -0,0 +1,652 @@
+/* ********************************************************************* */
+/* Load and combine data that is split into archives                     */
+/* ********************************************************************* */
+
+var Combine = {
+    _targets: [],
+    _targetIndex: 0,
+    // target: build target
+    //  name: intended filepath of built object
+    //  size: expected size of built object.
+    //  data: combined data
+    //  downloaded: total amount of data downloaded
+    //  pieces: array of name, offset and data objects
+    //  numExpectedFiles: total number of files expected in description
+    //  lastRequestedPiece: index of last data file requested (strictly ascending)
+    //  totalLoadedPieces: counts the number of data files received
+
+    //MAX_CONCURRENT_XHR: 6,    // remove comment if throttling of XHR is desired.
+
+    isCompleted: false,       // status of process
+
+    _onCombineCompleted: [],    // signature: name, data.
+    _onAllTargetsBuilt:[],      // signature: void
+    _onDownloadProgress: [],    // signature: downloaded, total
+
+    _totalDownloadBytes: 0,
+    _archiveLocationFilter: function(path) { return "split" + path; },
+
+    addProgressListener: function(callback) {
+        if (typeof callback !== 'function') {
+            throw "Invalid callback registration";
+        }
+        this._onDownloadProgress.push(callback);
+    },
+
+    addCombineCompletedListener: function(callback) {
+        if (typeof callback !== 'function') {
+            throw "Invalid callback registration";
+        }
+        this._onCombineCompleted.push(callback);
+    },
+
+    addAllTargetsBuiltListener: function(callback) {
+        if (typeof callback !== 'function') {
+            throw "Invalid callback registration";
+        }
+        this._onAllTargetsBuilt.push(callback);
+    },
+
+    // descriptUrl: location of text file describing files to be preloaded
+    process: function(descriptUrl) {
+        var xhr = new XMLHttpRequest();
+        xhr.open('GET', descriptUrl);
+        xhr.responseType = 'text';
+        xhr.onload = function(evt) {
+            Combine.onReceiveDescription(xhr);
+        };
+        xhr.send(null);
+    },
+
+    cleanUp: function() {
+        this._targets =  [];
+        this._targetIndex = 0;
+        this.isCompleted = false;
+        this._onCombineCompleted = [];
+        this._onAllTargetsBuilt = [];
+        this._onDownloadProgress = [];
+
+        this._totalDownloadBytes = 0;
+    },
+
+    onReceiveDescription: function(xhr) {
+        var json = JSON.parse(xhr.responseText);
+        this._targets = json.content;
+        this._totalDownloadBytes = 0;
+
+        var targets = this._targets;
+        for(var i=0; i<targets.length; ++i) {
+            this._totalDownloadBytes += targets[i].size;
+        }
+        this.requestContent();
+    },
+
+    requestContent: function() {
+        var target = this._targets[this._targetIndex];
+        if (1 < target.pieces.length) {
+            target.data = new Uint8Array(target.size);
+        }
+        var limit = target.pieces.length;
+        if (typeof this.MAX_CONCURRENT_XHR !== 'undefined') {
+            limit = Math.min(limit, this.MAX_CONCURRENT_XHR);
+        }
+        for (var i=0; i<limit; ++i) {
+            this.requestPiece(target, i);
+        }
+    },
+
+    requestPiece: function(target, index) {
+        if (index <  target.lastRequestedPiece) {
+            throw "Request out of order";
+        }
+
+        target.lastRequestedPiece = index;
+        target.progress = {};
+
+        var item = target.pieces[index];
+        var xhr = new XMLHttpRequest();
+        xhr.open('GET', this._archiveLocationFilter('/' + item.name), true);
+        xhr.responseType = 'arraybuffer';
+        xhr.onprogress = function(evt) {
+           target.progress[item.name] = {total: 0, downloaded: 0};
+            if (evt.total && evt.lengthComputable) {
+                target.progress[item.name].total = evt.total;
+            }
+            if (evt.loaded && evt.lengthComputable) {
+                target.progress[item.name].downloaded = evt.loaded;
+                Combine.updateProgress(target);
+            }
+        };
+        xhr.onload = function(evt) {
+            item.data = new Uint8Array(xhr.response);
+            item.dataLength = item.data.length;
+            target.progress[item.name].total = item.dataLength;
+            target.progress[item.name].downloaded = item.dataLength;
+            Combine.copyData(target, item);
+            Combine.onPieceLoaded(target, item);
+            Combine.updateProgress(target);
+            item.data = undefined;
+        };
+        xhr.send(null);
+    },
+
+    updateProgress: function(target) {
+        var total_downloaded = 0;
+        for (var p in target.progress) {
+            total_downloaded += target.progress[p].downloaded;
+        }
+        for(i = 0; i<this._onDownloadProgress.length; ++i) {
+            this._onDownloadProgress[i](total_downloaded, this._totalDownloadBytes);
+        }
+    },
+
+    copyData: function(target, item) {
+        if (1 == target.pieces.length) {
+            target.data = item.data;
+        } else {
+            var start = item.offset;
+            var end = start + item.data.length;
+            if (0 > start) {
+                throw "Buffer underflow";
+            }
+            if (end > target.data.length) {
+                throw "Buffer overflow";
+            }
+            target.data.set(item.data, item.offset);
+        }
+    },
+
+    onPieceLoaded: function(target, item) {
+        if (typeof target.totalLoadedPieces === 'undefined') {
+            target.totalLoadedPieces = 0;
+        }
+        ++target.totalLoadedPieces;
+        if (target.totalLoadedPieces == target.pieces.length) {
+            this.finalizeTarget(target);
+            ++this._targetIndex;
+            for (var i=0; i<this._onCombineCompleted.length; ++i) {
+                this._onCombineCompleted[i](target.name, target.data);
+            }
+            if (this._targetIndex < this._targets.length) {
+                this.requestContent();
+            } else {
+                this.isCompleted = true;
+                for (i=0; i<this._onAllTargetsBuilt.length; ++i) {
+                    this._onAllTargetsBuilt[i]();
+                }
+            }
+        } else {
+            var next = target.lastRequestedPiece + 1;
+            if (next < target.pieces.length) {
+                this.requestPiece(target, next);
+            }
+        }
+    },
+
+    finalizeTarget: function(target) {
+        var actualSize = 0;
+        for (var i=0;i<target.pieces.length; ++i) {
+            actualSize += target.pieces[i].dataLength;
+        }
+        if (actualSize != target.size) {
+            throw "Unexpected data size";
+        }
+
+        if (1 < target.pieces.length) {
+            var output = target.data;
+            var pieces = target.pieces;
+            for (i=0; i<pieces.length; ++i) {
+                var item = pieces[i];
+                // Bounds check
+                var start = item.offset;
+                var end = start + item.dataLength;
+                if (0 < i) {
+                    var previous = pieces[i - 1];
+                    if (previous.offset + previous.dataLength > start) {
+                        throw "Segment underflow";
+                    }
+                }
+                if (pieces.length - 2 > i) {
+                    var next = pieces[i + 1];
+                    if (end > next.offset) {
+                        throw "Segment overflow";
+                    }
+                }
+            }
+        }
+    }
+};
+
+/* ********************************************************************* */
+/* Default splash and progress visualisation                             */
+/* ********************************************************************* */
+
+var Progress = {
+    progress_id: "defold-progress",
+    bar_id: "defold-progress-bar",
+
+    addProgress : function (canvas) {
+        /* Insert default progress bar below canvas */
+        canvas.insertAdjacentHTML('afterend', '<div id="' + Progress.progress_id + '" class="canvas-app-progress"><div id="' + Progress.bar_id + '" class="canvas-app-progress-bar" style="width: 0%;">0%</div></div>');
+        Progress.bar = document.getElementById(Progress.bar_id);
+        Progress.progress = document.getElementById(Progress.progress_id);
+    },
+
+    updateProgress: function (percentage, text) {
+        Progress.bar.style.width = percentage + "%";
+
+        text = (typeof text === 'undefined') ? Math.round(percentage) + "%" : text;
+        Progress.bar.innerText = text;
+    },
+
+    removeProgress: function () {
+        if (Progress.progress.parentElement !== null) {
+            Progress.progress.parentElement.removeChild(Progress.progress);
+
+            // Remove any background/splash image that was set in runApp().
+            // Workaround for Safari bug DEF-3061.
+            Module.canvas.style.background = "";
+            Module["on_game_start"]();
+        }
+    }
+};
+
+/* ********************************************************************* */
+/* Default input override                                                */
+/* ********************************************************************* */
+
+var CanvasInput = {
+    arrowKeysHandler : function(e) {
+        switch(e.keyCode) {
+            case 37: case 38: case 39:  case 40: // Arrow keys
+            case 32: e.preventDefault(); e.stopPropagation(); // Space
+            default: break; // do not block other keys
+        }
+    },
+
+    onFocusIn : function(e) {
+        window.addEventListener("keydown", CanvasInput.arrowKeysHandler, false);
+    },
+
+    onFocusOut: function(e) {
+        window.removeEventListener("keydown", CanvasInput.arrowKeysHandler, false);
+    },
+
+    addToCanvas : function(canvas) {
+        canvas.addEventListener("focus", CanvasInput.onFocusIn, false);
+        canvas.addEventListener("blur", CanvasInput.onFocusOut, false);
+        canvas.focus();
+        CanvasInput.onFocusIn();
+    }
+};
+
+/* ********************************************************************* */
+/* Module is Emscripten namespace                                        */
+/* ********************************************************************* */
+
+var Module = {
+    noInitialRun: true,
+
+    _filesToPreload: [],
+    _archiveLoaded: false,
+    _preLoadDone: false,
+    _waitingForArchive: false,
+
+    // Persistent storage
+    persistentStorage: true,
+    _syncInProgress: false,
+    _syncNeeded: false,
+    _syncInitial: false,
+    _syncMaxTries: 3,
+    _syncTries: 0,
+
+    print: function(text) { console.log(text); },
+    printErr: function(text) { console.error(text); },
+
+    setStatus: function(text) { console.log(text); },
+
+    isWASMSupported: (function() {
+        try {
+            if (typeof WebAssembly === "object"
+                && typeof WebAssembly.instantiate === "function") {
+                const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
+                if (module instanceof WebAssembly.Module)
+                    return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
+            }
+        } catch (e) {
+        }
+        return false;
+    })(),
+
+    prepareErrorObject: function (err, url, line, column, errObj) {
+        line = typeof line == "undefined" ? 0 : line;
+        column = typeof column == "undefined" ? 0 : column;
+        url = typeof url == "undefined" ? "" : url;
+        var errorLine = url + ":" + line + ":" + column;
+
+        var error = errObj || (typeof window.event != "undefined" ? window.event.error : "" ) || err || "Undefined Error";
+        var message = "";
+        var stack = "";
+        var backtrace = "";
+
+        if (typeof error == "object" && typeof error.stack != "undefined" && typeof error.message != "undefined") {
+            stack = String(error.stack);
+            message = String(error.message);
+        } else {
+            stack = String(error).split("\n");
+            message = stack.shift();
+            stack = stack.join("\n");
+        }
+        stack = stack || errorLine;
+
+        var callLine = /at (\S+:\d*$)/.exec(message);
+        if (callLine) {
+            message = message.replace(/(at \S+:\d*$)/, "");
+            stack = callLine[1] + "\n" + stack;
+        }
+
+        message = message.replace(/(abort\(.+\)) at .+/, "$1");
+        stack = stack.replace(/\?{1}\S+(:\d+:\d+)/g, "$1");
+        stack = stack.replace(/ *at (\S+)$/gm, "@$1");
+        stack = stack.replace(/ *at (\S+)(?: \[as \S+\])? +\((.+)\)/g, "$1@$2");
+        stack = stack.replace(/^((?:Object|Array)\.)/gm, "");
+        stack = stack.split("\n");
+
+        return { stack:stack, message:message };
+    },
+
+    hasWebGLSupport: function() {
+        var webgl_support = false;
+        try {
+            var canvas = document.createElement("canvas");
+            var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
+            if (gl && gl instanceof WebGLRenderingContext) {
+                webgl_support = true;
+            }
+        } catch (error) {
+            console.log("An error occurred while detecting WebGL support: " + error);
+            webgl_support = false;
+        }
+
+        return webgl_support;
+    },
+
+    handleVisibilityChange: function () {
+        GLFW.onFocusChanged(document[Module.hiddenProperty] ? 0 : 1);
+    },
+
+    getHiddenProperty: function () {
+        if ('hidden' in document) return 'hidden';
+        var prefixes = ['webkit','moz','ms','o'];
+        for (var i = 0; i < prefixes.length; i++){
+            if ((prefixes[i] + 'Hidden') in document)
+                return prefixes[i] + 'Hidden';
+        }
+        return null;
+    },
+
+    setupVisibilityChangeListener: function() {
+        Module.hiddenProperty = Module.getHiddenProperty();
+        if( Module.hiddenProperty ) {
+            var eventName = Module.hiddenProperty.replace(/[H|h]idden/,'') + 'visibilitychange';
+            document.addEventListener(eventName, Module.handleVisibilityChange, false);
+        } else {
+            console.log("No document.hidden property found. The focus events won't be enabled.")
+        }
+    },
+
+    /**
+    * Module.runApp - Starts the application given a canvas element id
+    *
+    * 'extra_params' is an optional object that can have the following fields:
+    *
+    *     'splash_image':
+    *         Path to an image that should be used as a background image for
+    *         the canvas element.
+    *
+    *     'archive_location_filter':
+    *         Filter function that will run for each archive path.
+    *
+    *     'unsupported_webgl_callback':
+    *         Function that is called if WebGL is not supported.
+    *
+    *     'engine_arguments':
+    *         List of arguments (strings) that will be passed to the engine.
+    *
+    *     'persistent_storage':
+    *         Boolean toggling the usage of persistent storage.
+    *
+    *     'custom_heap_size':
+    *         Number of bytes specifying the memory heap size.
+    *
+    *     'disable_context_menu':
+    *         Disables the right-click context menu on the canvas element if true.
+    *
+    **/
+    runApp: function(app_canvas_id, extra_params) {
+        app_canvas_id = (typeof app_canvas_id === 'undefined') ?  'canvas' : app_canvas_id;
+
+        var params = {
+            splash_image: undefined,
+            archive_location_filter: function(path) { return 'split' + path; },
+            unsupported_webgl_callback: undefined,
+            engine_arguments: [],
+            persistent_storage: true,
+            custom_heap_size: undefined,
+            disable_context_menu: true
+        };
+
+        for (var k in extra_params) {
+            if (extra_params.hasOwnProperty(k)) {
+                params[k] = extra_params[k];
+            }
+        }
+
+        Module.canvas = document.getElementById(app_canvas_id);
+        if (typeof params["splash_image"] !== 'undefined') {
+            Module.canvas.style.background = 'no-repeat center url("' + params["splash_image"] + '")';
+        }
+        Module.arguments = params["engine_arguments"];
+        Module.persistentStorage = params["persistent_storage"];
+        Module["TOTAL_MEMORY"] = params["custom_heap_size"];
+        Module["on_game_start"] = params["game_start"];
+
+        if (Module.hasWebGLSupport()) {
+            // Override game keys
+            CanvasInput.addToCanvas(Module.canvas);
+
+            Module.setupVisibilityChangeListener();
+
+            // Add progress visuals
+            Progress.addProgress(Module.canvas);
+
+            // Add context menu hide-handler if requested
+            if (params["disable_context_menu"])
+            {
+                Module.canvas.oncontextmenu = function(e) {
+                    e.preventDefault();
+                };
+            }
+
+            // Load and assemble archive
+            Combine.addCombineCompletedListener(Module.onArchiveFileLoaded);
+            Combine.addAllTargetsBuiltListener(Module.onArchiveLoaded);
+            Combine.addProgressListener(Module.onArchiveLoadProgress);
+            Combine._archiveLocationFilter = params["archive_location_filter"];
+            Combine.process(Combine._archiveLocationFilter('/archive_files.json'));
+        } else {
+            Progress.addProgress(Module.canvas);
+            Progress.updateProgress(100, "Unable to start game, WebGL not supported");
+            Module.setStatus = function(text) {
+                if (text) Module.printErr('[missing WebGL] ' + text);
+            };
+
+            if (typeof params["unsupported_webgl_callback"] === "function") {
+                params["unsupported_webgl_callback"]();
+            }
+        }
+    },
+
+    onArchiveLoadProgress: function(downloaded, total) {
+        Progress.updateProgress(downloaded / total * 100);
+    },
+
+    onArchiveFileLoaded: function(name, data) {
+        Module._filesToPreload.push({path: name, data: data});
+    },
+
+    onArchiveLoaded: function() {
+        Combine.cleanUp();
+        Module._archiveLoaded = true;
+        Progress.updateProgress(100, "Starting...");
+
+        if (Module._waitingForArchive) {
+            Module._preloadAndCallMain();
+        }
+    },
+
+    toggleFullscreen: function() {
+        if (GLFW.isFullscreen) {
+            GLFW.cancelFullScreen();
+        } else {
+            GLFW.requestFullScreen();
+        }
+    },
+
+    preSync: function(done) {
+        // Initial persistent sync before main is called
+        FS.syncfs(true, function(err) {
+            if(err) {
+                Module._syncTries += 1;
+                console.error("FS syncfs error: " + err);
+                if (Module._syncMaxTries > Module._syncTries) {
+                    Module.preSync(done);
+                } else {
+                    Module._syncInitial = true;
+                    done();
+                }
+            } else {
+                Module._syncInitial = true;
+                if (done !== undefined) {
+                    done();
+                }
+            }
+        });
+    },
+
+    preloadAll: function() {
+        if (Module._preLoadDone) {
+            return;
+        }
+        Module._preLoadDone = true;
+        for (var i = 0; i < Module._filesToPreload.length; ++i) {
+            var item = Module._filesToPreload[i];
+            FS.createPreloadedFile("", item.path, item.data, true, true);
+        }
+    },
+
+    // Tries to do a MEM->IDB sync
+    // It will flag that another one is needed if there is already one sync running.
+    persistentSync: function() {
+
+        // Need to wait for the initial sync to finish since it
+        // will call close on all its file streams which will trigger
+        // new persistentSync for each.
+        if (Module._syncInitial) {
+            if (Module._syncInProgress) {
+                Module._syncNeeded = true;
+            } else {
+                Module._startSyncFS();
+            }
+        }
+    },
+
+    preInit: [function() {
+        /* Mount filesystem on preinit */
+        var dir = DMSYS.GetUserPersistentDataRoot();
+        FS.mkdir(dir);
+
+        // If IndexedDB is supported we mount the persistent data root as IDBFS,
+        // then try to do a IDB->MEM sync before we start the engine to get
+        // previously saved data before boot.
+        window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+        if (Module.persistentStorage && window.indexedDB) {
+            FS.mount(IDBFS, {}, dir);
+
+            // Patch FS.close so it will try to sync MEM->IDB
+            var _close = FS.close; FS.close = function(stream) { var r = _close(stream); Module.persistentSync(); return r; }
+
+            // Sync IDB->MEM before calling main()
+            Module.preSync(function() {
+                Module._preloadAndCallMain();
+            });
+        } else {
+            Module._preloadAndCallMain();
+        }
+    }],
+
+    preRun: [function() {
+        /* If archive is loaded, preload all its files */
+        if(Module._archiveLoaded) {
+            Module.preloadAll();
+        }
+    }],
+
+    postRun: [function() {
+        if(Module._archiveLoaded) {
+            Progress.removeProgress();
+        }
+    }],
+
+    _preloadAndCallMain: function() {
+        // If the archive isn't loaded,
+        // we will have to wait with calling main.
+        if (!Module._archiveLoaded) {
+            Module._waitingForArchive = true;
+        } else {
+
+            // Need to set heap size before calling main
+            TOTAL_MEMORY = Module["TOTAL_MEMORY"] || TOTAL_MEMORY;
+
+            Module.preloadAll();
+            Progress.removeProgress();
+            if (Module.callMain === undefined) {
+                Module.noInitialRun = false;
+            } else {
+                Module.callMain(Module.arguments);
+            }
+        }
+    },
+
+    // Wrap IDBFS syncfs call with logic to avoid multiple syncs
+    // running at the same time.
+    _startSyncFS: function() {
+        Module._syncInProgress = true;
+
+        if (Module._syncMaxTries > Module._syncTries) {
+            FS.syncfs(false, function(err) {
+                Module._syncInProgress = false;
+
+                if (err) {
+                    console.error("Module._startSyncFS error: " + err);
+                    Module._syncTries += 1;
+                }
+
+                if (Module._syncNeeded) {
+                    Module._syncNeeded = false;
+                    Module._startSyncFS();
+                }
+
+            });
+        }
+    },
+};
+
+window.onerror = function(err, url, line, column, errObj) {
+    var errorObject = Module.prepareErrorObject(err, url, line, column, errObj);
+    Module.ccall('JSWriteDump', 'null', ['string'], [JSON.stringify(errorObject.stack)]);
+    Module.setStatus('Exception thrown, see JavaScript console');
+    Module.setStatus = function(text) {
+        if (text) Module.printErr('[post-exception status] ' + text);
+    };
+};

BIN
docs/assets/easier/easier.wasm


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
docs/assets/easier/easier_asmjs.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
docs/assets/easier/easier_wasm.js


BIN
docs/assets/easier/preview.jpg


+ 99 - 15
docs/en/manuals/animation.md

@@ -329,20 +329,105 @@ The pingpong modes run the animation first forward, then backward. A set of corr
 
 Easing defines how the animated value changes over time. The images below describe the functions applied over time to create the easing.
 
-The corresponding value to use when calling `go.animate()` are `go.EASING_LINEAR`, `go.EASING_INBACK`, `go.EASING_OUTBACK` and so forth.
-
-For `gui.animate()` the values are named `gui.EASING_LINEAR`, `gui.EASING_INBACK`, `gui.EASING_OUTBACK` and so forth.
-
-```linechart
-{
-  "labels": [1, "Tuesday", "Wednesday", "Thursday", "Friday"],
-  "series": [
-    [12, 9, 7, 8, 5],
-    [2, 1, 3.5, 7, 3],
-    [1, 3, 4, 5, 6]
-  ]
-}
-```
+The following are valid easing values for `go.animate()`:
+| | |
+|---|---|
+| go.EASING_LINEAR | |
+| go.EASING_INBACK | go.EASING_OUTBACK |
+| go.EASING_INOUTBACK | go.EASING_OUTINBACK |
+| go.EASING_INBOUNCE | go.EASING_OUTBOUNCE |
+| go.EASING_INOUTBOUNCE | go.EASING_OUTINBOUNCE |
+| go.EASING_INELASTIC | go.EASING_OUTELASTIC |
+| go.EASING_INOUTELASTIC | go.EASING_OUTINELASTIC |
+| go.EASING_INSINE | go.EASING_OUTSINE |
+| go.EASING_INOUTSINE | go.EASING_OUTINSINE |
+| go.EASING_INEXPO | go.EASING_OUTEXPO |
+| go.EASING_INOUTEXPO | go.EASING_OUTINEXPO |
+| go.EASING_INCIRC | go.EASING_OUTCIRC |
+| go.EASING_INOUTCIRC | go.EASING_OUTINCIRC |
+| go.EASING_INQUAD | go.EASING_OUTQUAD |
+| go.EASING_INOUTQUAD | go.EASING_OUTINQUAD |
+| go.EASING_INCUBIC | go.EASING_OUTCUBIC |
+| go.EASING_INOUTCUBIC | go.EASING_OUTINCUBIC |
+| go.EASING_INQUART | go.EASING_OUTQUART |
+| go.EASING_INOUTQUART | go.EASING_OUTINQUART |
+| go.EASING_INQUINT | go.EASING_OUTQUINT |
+| go.EASING_INOUTQUINT | go.EASING_OUTINQUINT |
+
+The following are valid easing values for `gui.animate()`:
+| | |
+|---|---|
+| gui.EASING_LINEAR | |
+| gui.EASING_INBACK | gui.EASING_OUTBACK |
+| gui.EASING_INOUTBACK | gui.EASING_OUTINBACK |
+| gui.EASING_INBOUNCE | gui.EASING_OUTBOUNCE |
+| gui.EASING_INOUTBOUNCE | gui.EASING_OUTINBOUNCE |
+| gui.EASING_INELASTIC | gui.EASING_OUTELASTIC |
+| gui.EASING_INOUTELASTIC | gui.EASING_OUTINELASTIC |
+| gui.EASING_INSINE | gui.EASING_OUTSINE |
+| gui.EASING_INOUTSINE | gui.EASING_OUTINSINE |
+| gui.EASING_INEXPO | gui.EASING_OUTEXPO |
+| gui.EASING_INOUTEXPO | gui.EASING_OUTINEXPO |
+| gui.EASING_INCIRC | gui.EASING_OUTCIRC |
+| gui.EASING_INOUTCIRC | gui.EASING_OUTINCIRC |
+| gui.EASING_INQUAD | gui.EASING_OUTQUAD |
+| gui.EASING_INOUTQUAD | gui.EASING_OUTINQUAD |
+| gui.EASING_INCUBIC | gui.EASING_OUTCUBIC |
+| gui.EASING_INOUTCUBIC | gui.EASING_OUTINCUBIC |
+| gui.EASING_INQUART | gui.EASING_OUTQUART |
+| gui.EASING_INOUTQUART | gui.EASING_OUTINQUART |
+| gui.EASING_INQUINT | gui.EASING_OUTQUINT |
+| gui.EASING_INOUTQUINT | gui.EASING_OUTINQUINT |
+
+<div id="game-container" class="game-container">
+    <img id="game-preview" src="//storage.googleapis.com/defold-doc/assets/easier/preview.jpg"/>
+    <canvas id="game-canvas" tabindex="1" width="640" height="512">
+    </canvas>
+    <button id="game-button">
+        SHOW EXAMPLE <span class="icon"></span>
+    </button>
+    <script src="//storage.googleapis.com/defold-doc/assets/easier/dmloader.js">
+    </script>
+    <script>
+        document.getElementById("game-button").onclick = function (e) {
+            var extra_params = {
+              archive_location_filter: function( path ) {
+                return ('//storage.googleapis.com/defold-doc/assets/easier/archive' + path + '');
+              },
+              splash_image: '//storage.googleapis.com/defold-doc/assets/easier/preview.jpg',
+              custom_heap_size: 268435456,
+              disable_context_menu: true,
+              game_start: function() {
+                  var e = document.getElementById("game-preview");
+                  e.parentElement.removeChild(e);
+              }
+            };
+            Module['onRuntimeInitialized'] = function() {
+              Module.runApp("game-canvas", extra_params);
+            };
+            Module['locateFile'] = function(path, scriptDirectory)
+            {
+              if (path == "dmengine.wasm" || path == "dmengine_release.wasm" || path == "dmengine_headless.wasm") {
+                path = "easier.wasm";
+              }
+              return scriptDirectory + path;
+            };
+            function load_engine() {
+              var engineJS = document.createElement('script');
+              engineJS.type = 'text/javascript';
+              if (Module['isWASMSupported']) {
+                  engineJS.src = '//storage.googleapis.com/defold-doc/assets/easier/easier_wasm.js';
+              } else {
+                  engineJS.src = '//storage.googleapis.com/defold-doc/assets/easier/easier_asmjs.js';
+              }
+              document.head.appendChild(engineJS);
+            }
+            load_engine();
+            document.getElementById("game-button").style.display = 'none';
+            document.getElementById("game-button").onclick = null;
+        };
+    </script>
+</div>
 
 ![Linear interpolation](images/properties/easing_linear.png){.inline}
 ![In back](images/properties/easing_inback.png){.inline}
@@ -433,4 +518,3 @@ function init(self)
     go.animate(".", "position.y", go.PLAYBACK_ONCE_FORWARD, 100, go.EASING_OUTBOUNCE, 2, 0, done_bouncing)
 end
 ```
-

+ 5 - 2
docs/en/tutorials/car.md

@@ -90,9 +90,12 @@ Select the "car" game object, right-click and select <kbd>Add Component</kbd>, t
 Image
 : This requires an image source for the sprite. Create an atlas image file by marking "main" in the *Project Explorer* view, right-clicking and selecting <kbd>New ▸ Atlas File</kbd>. Name the new atlas file *sprites.atlas* and double click it to open it in the atlas editor. Save the following two image files to your computer and drag them into *main* in the *Project Explorer* view. Now you can mark the Atlas root node in the atlas editor, right click and select <kbd>Add Images</kbd>. Add the car and the tire image to the atlas and save. Now you can select *sprites.atlas* as the image source for the sprite component in the "car" game object in the "car" collection.
 
-![Car image](images/car/start_car.png) [Car image](images/car/start_car.png)
+Images for our game:
 
-![Tire image](images/car/start_tire.png) [Tire image](images/car/start_tire.png)
+![Car image](images/car/start_car.png)
+![Tire image](images/car/start_tire.png)
+
+Add these images to the atlas:
 
 ![Sprites atlas](images/car/start_sprites_atlas.png)
 

Деякі файли не було показано, через те що забагато файлів було змінено