diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c1056df296..68e95d9f67 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,7 +6,7 @@ { "name": "melonjs", "source": "./", - "description": "Build games with melonJS — 23 guides to the 2D, 2.5D and 3D HTML5 game engine, its conventions and idioms, so generated code runs the first time." + "description": "Build games with melonJS — 25 guides to the 2D, 2.5D and 3D HTML5 game engine, its conventions and idioms, so generated code runs the first time." } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 86b37901c7..09a891c1a7 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "melonjs", "version": "20.11.0", - "description": "Build games with melonJS — 24 guides to the 2D, 2.5D and 3D HTML5 game engine, its conventions and idioms, so generated code runs the first time.", + "description": "Build games with melonJS — 25 guides to the 2D, 2.5D and 3D HTML5 game engine, its conventions and idioms, so generated code runs the first time.", "author": { "name": "melonJS", "url": "https://melonjs.org" diff --git a/README.md b/README.md index edb3651227..bbab0664ab 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ melonJS 2 ========= -![melonJS Logo](https://github.com/melonjs/melonJS/raw/master/media/Banner/Banner%20-%20Billboard%20-%20Original%20Logo%20-%20horizontal.png) +![melonJS: fast & light 2D & 3D games for the web, running on WebGPU, WebGL 2 or Canvas](https://github.com/melonjs/melonJS/raw/master/media/readme/hero.webp) [![Build Status](https://github.com/melonjs/melonJS/actions/workflows/main.yml/badge.svg)](https://github.com/melonjs/melonJS/actions/workflows/main.yml) [![NPM Package](https://img.shields.io/npm/v/melonjs)](https://www.npmjs.com/package/melonjs) @@ -14,8 +14,6 @@ melonJS 2 A modern & fully featured HTML5 game engine ------------------------------------------------------------------------------- -![melonJS](https://melonjs.org/img/alex4-github.png) - [melonJS](https://melonjs.org/) is an open-source 2D & 3D game engine designed for indie developers — perspective and orthogonal cameras, GPU-accelerated tilemap rendering, post-processing effects, custom shaders, 3D mesh support, polygon-accurate physics, modern Tiled workflows, and high performance. Runs on WebGPU, WebGL 2 or Canvas2D with automatic fallback. The whole engine is ~250 KB minzipped of vanilla JS/TS, with no dependencies and no toolchain lock-in. Built with modern class syntax and bundled with [esbuild](https://esbuild.github.io). [melonJS](https://melonjs.org/) is licensed under the [MIT License](LICENSE.md) and actively maintained by the team at AltByte in Singapore. @@ -180,7 +178,7 @@ Examples ------------------------------------------------------------------------------- * [Platformer](https://melonjs.github.io/melonJS/examples/#/platformer) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/platformer)) -* [Isometric RPG](https://melonjs.github.io/melonJS/examples/#/isometric-rpg) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/isometricRpg)) +* [Isometric Dungeon](https://melonjs.github.io/melonJS/examples/#/isometric-dungeon) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/isometricDungeon)) * [SVG Shapes](https://melonjs.github.io/melonJS/examples/#/svg-shapes) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/svgShapes)) * [Graphics](https://melonjs.github.io/melonJS/examples/#/graphics) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/graphics)) * [Hello World](https://melonjs.github.io/melonJS/examples/#/hello-world) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/helloWorld)) diff --git a/media/readme/hero.webp b/media/readme/hero.webp new file mode 100644 index 0000000000..454f565b2b Binary files /dev/null and b/media/readme/hero.webp differ diff --git a/packages/examples/LICENSE.md b/packages/examples/LICENSE.md index 2b8cfe345c..28e518ac6c 100644 --- a/packages/examples/LICENSE.md +++ b/packages/examples/LICENSE.md @@ -47,6 +47,40 @@ These assets are released under **CC0 1.0 Universal (Public Domain Dedication)** — no attribution legally required, but provided here as a courtesy to the original creator. +### `isometricDungeon` example + +The dungeon tileset (`public/assets/isometricDungeon/dungeon.png`) and the hero +atlas (`hero.png` / `hero.json`) are built from **"Isometric Miniature +Dungeon"** published by Kenney: + + + +The pieces and the character frames are scaled to half size and packed into +one tileset image and one trimmed atlas; the map (`dungeon.tmx`) was generated +for this example and can be edited in Tiled. Released under **CC0 1.0 +Universal (Public Domain Dedication)** — no attribution legally required, +credited here as a courtesy. + +The wall torch (the last two pieces of the tileset) is not Kenney's: it was +modelled and rendered for this example in the same projection and light as +the pack, and is released under the same CC0 terms. Its flame is particles, +drawn by the example. + +The sound effects in `public/assets/isometricDungeon/sfx/` (footsteps, the +door, the chest's latch and coins, the gate) are from Kenney's **"RPG +Audio"** and **"Impact Sounds"** packs, also CC0: + + · + + +The dungeon's ambience (`sfx/dungeonAmbience`, the draught and the water +drips) was synthesised for this example rather than recorded, and is released +under the same CC0 terms. + +The Kenney logo (`kenney-logo.png`), shown in the corner of the game to credit +the assets, is Kenney's own () and is not covered by +the CC0 terms above. + ### `multiMaterialMesh` example Spacecraft 3D models (`craft_speederA`, `craft_speederB`, `craft_racer`, diff --git a/packages/examples/public/assets/isometricDungeon/dungeon.png b/packages/examples/public/assets/isometricDungeon/dungeon.png new file mode 100644 index 0000000000..2eae0e68bd Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/dungeon.png differ diff --git a/packages/examples/public/assets/isometricDungeon/dungeon.tmx b/packages/examples/public/assets/isometricDungeon/dungeon.tmx new file mode 100644 index 0000000000..0eb73a34fb --- /dev/null +++ b/packages/examples/public/assets/isometricDungeon/dungeon.tmx @@ -0,0 +1,1018 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,3,2,1,6,3,6,2,4,0,0,0,0, +0,0,0,0,2,1,2,3,5,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,5,1,1,1,2,4,2,1,1,0,0,0,0, +0,0,0,0,6,3,5,6,5,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,6,1,4,1,2,1,1,1,1,0,0,0,0, +0,0,0,0,6,2,1,4,2,6,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,6,2,1,2,1,5,6,1,4,4,0,0,0,0, +0,0,0,0,5,1,5,1,2,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,2,1,2,6,4,6,2,2,0,0,0,0, +0,0,0,0,2,1,6,1,1,2,2,2,0,0,0,0,0,0,1,3,2,4,3,2,4,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,1,1,4,2,2,2,4,1,1,0,0,0,0, +0,0,0,0,1,1,1,1,1,1,3,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,3,0,0,0,0,0,0,0,0,0,0,2,6,1,4,1,2,0,0,0,0,0,0,2,2,1,1,1,1,2,5,1,1,1,0,0,0,0, +0,0,0,0,2,2,3,3,4,2,1,3,0,0,0,0,0,0,2,2,1,3,3,6,5,2,1,0,0,0,0,0,0,0,0,0,0,4,1,4,6,1,3,0,0,0,0,0,0,5,1,6,2,1,2,1,4,1,6,2,0,0,0,0, +0,0,0,0,2,1,1,3,5,1,5,1,3,7,7,2,7,1,1,1,5,6,1,3,3,1,4,1,0,0,0,0,0,0,0,0,0,4,1,3,2,2,2,0,0,0,0,0,0,0,0,0,0,0,6,7,0,0,0,0,0,0,0,0, +0,0,0,0,4,2,5,1,6,1,3,3,7,6,7,5,2,7,1,3,5,2,6,2,2,2,1,7,3,7,2,2,1,1,4,1,2,4,2,2,1,1,2,4,1,4,2,7,1,2,8,2,0,0,4,2,0,0,0,0,0,0,0,0, +0,0,0,0,1,1,5,4,3,2,1,4,0,0,0,0,0,0,2,4,1,2,4,5,5,1,4,7,7,4,1,2,7,2,7,5,7,5,6,1,3,6,3,5,1,6,3,7,2,4,1,4,0,0,3,7,0,0,0,0,0,0,0,0, +0,0,0,0,1,2,2,4,6,2,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,2,5,6,2,1,3,0,0,0,10,10,10,11,9,10,10,9,4,7,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,10,10,11,9,10,2,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,3,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,10,10,11,9,11,9,1,3,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,9,11,9,10,9,11,2,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,9,10,9,11,11,9,9,5,8,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,7,3,0,0,0,0,0,0,0,0,0,0,0,8,8,7,7,7,7,7,7,7,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,9,9,9,10,9,10,9,7,5,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,6,6,2,2,4,3,4,2,6,0,0,0,0,7,7,7,7,8,8,7,7,7,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,9,10,10,9,11,9,4,8,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,6,1,4,2,1,1,1,2,5,0,0,0,0,7,8,7,7,7,7,8,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,9,9,10,9,10,11,10,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,1,4,1,3,1,4,1,5,0,0,0,0,7,7,8,8,8,7,7,8,7,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,9,9,10,9,9,9,10,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,1,1,1,2,1,1,5,1,2,1,3,7,7,7,8,8,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,11,9,10,11,9,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,3,3,5,5,1,1,1,3,1,8,4,2,7,7,7,7,7,7,7,7,8,7,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5,2,1,3,2,2,4,1,1,0,0,0,0,0,0,0,0,0,1,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,8,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,10,11,11,10,11,10,10,11,0,0,0,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,10,10,9,10,10,9,9,0,0,0,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,9,10,11,11,9,10,9,0,0,0,7,7,7,8,7,7,7,7,0,0,0,0,6,3,5,5,1,4,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,10,10,11,9,9,11,10,9,1,2,7,7,7,7,7,7,7,7,7,8,1,3,2,2,5,1,5,3,3,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,9,10,10,10,9,10,9,9,7,2,1,7,7,7,7,8,7,8,7,7,3,2,1,3,1,3,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,9,10,9,10,10,10,9,9,0,0,0,7,7,7,7,7,7,8,7,0,0,0,0,3,5,5,1,1,6,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,2,6,5,2,1,2,2,0,0,0,0,0,0,0,10,10,10,9,9,9,10,10,10,9,0,0,0,7,7,7,7,8,7,7,8,0,0,0,0,1,6,2,1,3,4,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,4,2,2,1,1,1,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,2,1,1,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,2,1,2,1,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,6,4,1,6,3,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,2,5,1,1,4,5,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,1,3,6,5,6,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,3,4,1,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,5,5,6,1,6,6,1,2,2,7,1,2,7,1,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6,2,5,1,3,1,1,1,1,5,7,1,4,6,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,1,4,4,6,1,4,1,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,4,6,1,3,6,1,3,2,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,8,7,7,1,4,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5,1,1,1,2,2,6,1,0,0,0,0,0,0,7,1,0,0,0,0,0,0,6,1,4,2,1,2,1,2,0,0,0,0,0,0,0,7,7,7,7,7,7,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,6,2,0,0,0,0,0,0,0,0,0,4,2,2,2,1,6,2,0,0,0,0,5,2,4,2,6,3,5,6,0,0,0,0,0,0,0,7,8,8,7,7,7,2,6,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8,8,0,0,0,0,0,0,0,0,0,1,3,1,2,3,6,3,0,0,0,0,5,2,2,2,2,1,1,1,0,0,0,0,0,0,0,7,7,8,7,8,8,1,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7,3,0,0,0,0,0,0,0,0,0,3,1,2,2,6,1,5,1,1,4,7,5,4,1,4,2,1,4,5,8,3,3,6,8,7,5,8,8,7,7,7,7,4,7,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,3,2,4,1,1,1,2,8,6,6,8,6,6,4,4,5,3,1,3,1,7,7,4,6,6,7,7,8,8,8,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7,3,0,0,0,0,0,0,0,0,0,3,3,1,2,6,1,1,0,0,0,0,2,3,6,4,1,6,2,6,0,0,0,0,0,0,0,7,7,8,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,8,0,0,0,0,0,0,0,0,0,1,1,2,6,1,2,4,0,0,0,0,1,1,1,2,1,5,6,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,4,1,1,1,6,6,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,1,3,1,2,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,1,2,1,1,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5,4,1,1,2,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5,1,2,2,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,3,1,5,2,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5,1,2,4,2,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/examples/public/assets/isometricDungeon/hero.json b/packages/examples/public/assets/isometricDungeon/hero.json new file mode 100644 index 0000000000..9119c1b5fb --- /dev/null +++ b/packages/examples/public/assets/isometricDungeon/hero.json @@ -0,0 +1 @@ +{"frames":[{"filename":"idle_ne_0","frame":{"x":548,"y":0,"w":19,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":55,"y":176,"w":19,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"idle_e_0","frame":{"x":187,"y":0,"w":11,"h":56},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":59,"y":176,"w":11,"h":56},"sourceSize":{"w":128,"h":256}},{"filename":"idle_se_0","frame":{"x":308,"y":0,"w":20,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":177,"w":20,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"idle_s_0","frame":{"x":27,"y":61,"w":24,"h":53},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":177,"w":24,"h":53},"sourceSize":{"w":128,"h":256}},{"filename":"idle_sw_0","frame":{"x":569,"y":0,"w":19,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":177,"w":19,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"idle_w_0","frame":{"x":330,"y":0,"w":11,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":58,"y":176,"w":11,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"idle_nw_0","frame":{"x":590,"y":0,"w":20,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":176,"w":20,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"idle_n_0","frame":{"x":612,"y":0,"w":24,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":175,"w":24,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_0","frame":{"x":53,"y":61,"w":24,"h":53},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":177,"w":24,"h":53},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_1","frame":{"x":343,"y":0,"w":32,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":173,"w":32,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_2","frame":{"x":191,"y":61,"w":35,"h":52},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":44,"y":172,"w":35,"h":52},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_3","frame":{"x":921,"y":61,"w":29,"h":48},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":175,"w":29,"h":48},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_4","frame":{"x":380,"y":61,"w":27,"h":51},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":178,"w":27,"h":51},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_5","frame":{"x":377,"y":0,"w":26,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":175,"w":26,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_6","frame":{"x":200,"y":0,"w":31,"h":56},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":172,"w":31,"h":56},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_7","frame":{"x":79,"y":61,"w":33,"h":53},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":172,"w":33,"h":53},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_8","frame":{"x":128,"y":117,"w":28,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":175,"w":28,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_ne_9","frame":{"x":681,"y":61,"w":23,"h":49},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":55,"y":178,"w":23,"h":49},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_0","frame":{"x":529,"y":117,"w":31,"h":46},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":50,"y":180,"w":31,"h":46},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_1","frame":{"x":632,"y":117,"w":43,"h":45},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":41,"y":177,"w":43,"h":45},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_2","frame":{"x":64,"y":167,"w":45,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":39,"y":175,"w":45,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_3","frame":{"x":706,"y":61,"w":39,"h":49},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":45,"y":178,"w":39,"h":49},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_4","frame":{"x":158,"y":117,"w":28,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":182,"w":28,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_5","frame":{"x":580,"y":61,"w":32,"h":50},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":179,"w":32,"h":50},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_6","frame":{"x":188,"y":117,"w":42,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":41,"y":175,"w":42,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_7","frame":{"x":111,"y":167,"w":43,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":39,"y":175,"w":43,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_8","frame":{"x":232,"y":117,"w":37,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":45,"y":178,"w":37,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_e_9","frame":{"x":803,"y":117,"w":28,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":182,"w":28,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_0","frame":{"x":497,"y":167,"w":26,"h":41},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":184,"w":26,"h":41},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_1","frame":{"x":142,"y":213,"w":32,"h":36},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":180,"w":32,"h":36},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_2","frame":{"x":409,"y":167,"w":34,"h":42},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":179,"w":34,"h":42},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_3","frame":{"x":952,"y":61,"w":30,"h":48},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":183,"w":30,"h":48},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_4","frame":{"x":833,"y":117,"w":26,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":55,"y":186,"w":26,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_5","frame":{"x":156,"y":167,"w":26,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":183,"w":26,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_6","frame":{"x":69,"y":213,"w":32,"h":37},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":179,"w":32,"h":37},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_7","frame":{"x":184,"y":167,"w":36,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":43,"y":179,"w":36,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_8","frame":{"x":984,"y":61,"w":31,"h":48},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":182,"w":31,"h":48},"sourceSize":{"w":128,"h":256}},{"filename":"run_se_9","frame":{"x":222,"y":167,"w":27,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":186,"w":27,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_0","frame":{"x":775,"y":167,"w":23,"h":39},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":186,"w":23,"h":39},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_1","frame":{"x":513,"y":213,"w":22,"h":32},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":182,"w":22,"h":32},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_2","frame":{"x":525,"y":167,"w":23,"h":41},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":181,"w":23,"h":41},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_3","frame":{"x":0,"y":117,"w":23,"h":48},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":185,"w":23,"h":48},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_4","frame":{"x":445,"y":167,"w":24,"h":42},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":188,"w":24,"h":42},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_5","frame":{"x":687,"y":167,"w":24,"h":40},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":185,"w":24,"h":40},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_6","frame":{"x":352,"y":213,"w":24,"h":34},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":182,"w":24,"h":34},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_7","frame":{"x":251,"y":167,"w":24,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":182,"w":24,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_8","frame":{"x":747,"y":61,"w":24,"h":49},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":184,"w":24,"h":49},"sourceSize":{"w":128,"h":256}},{"filename":"run_s_9","frame":{"x":471,"y":167,"w":24,"h":42},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":188,"w":24,"h":42},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_0","frame":{"x":277,"y":167,"w":24,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":183,"w":24,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_1","frame":{"x":925,"y":167,"w":32,"h":38},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":50,"y":180,"w":32,"h":38},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_2","frame":{"x":550,"y":167,"w":35,"h":41},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":179,"w":35,"h":41},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_3","frame":{"x":271,"y":117,"w":29,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":50,"y":183,"w":29,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_4","frame":{"x":861,"y":117,"w":27,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":186,"w":27,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_5","frame":{"x":587,"y":167,"w":26,"h":41},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":184,"w":26,"h":41},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_6","frame":{"x":176,"y":213,"w":31,"h":36},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":181,"w":31,"h":36},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_7","frame":{"x":890,"y":117,"w":33,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":180,"w":33,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_8","frame":{"x":614,"y":61,"w":27,"h":50},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":182,"w":27,"h":50},"sourceSize":{"w":128,"h":256}},{"filename":"run_sw_9","frame":{"x":925,"y":117,"w":23,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":50,"y":186,"w":23,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_0","frame":{"x":409,"y":61,"w":31,"h":51},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":179,"w":31,"h":51},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_1","frame":{"x":302,"y":117,"w":43,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":44,"y":176,"w":43,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_2","frame":{"x":615,"y":167,"w":45,"h":41},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":44,"y":175,"w":45,"h":41},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_3","frame":{"x":562,"y":117,"w":39,"h":46},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":44,"y":179,"w":39,"h":46},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_4","frame":{"x":677,"y":117,"w":28,"h":45},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":182,"w":28,"h":45},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_5","frame":{"x":347,"y":117,"w":32,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":45,"y":180,"w":32,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_6","frame":{"x":950,"y":117,"w":42,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":45,"y":177,"w":42,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_7","frame":{"x":303,"y":167,"w":43,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":177,"w":43,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_8","frame":{"x":773,"y":61,"w":36,"h":49},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":179,"w":36,"h":49},"sourceSize":{"w":128,"h":256}},{"filename":"run_w_9","frame":{"x":381,"y":117,"w":28,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":183,"w":28,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_0","frame":{"x":100,"y":0,"w":26,"h":57},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":176,"w":26,"h":57},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_1","frame":{"x":128,"y":0,"w":31,"h":57},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":172,"w":31,"h":57},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_2","frame":{"x":638,"y":0,"w":34,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":171,"w":34,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_3","frame":{"x":411,"y":117,"w":30,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":175,"w":30,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_4","frame":{"x":811,"y":61,"w":26,"h":49},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":178,"w":26,"h":49},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_5","frame":{"x":674,"y":0,"w":26,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":50,"y":176,"w":26,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_6","frame":{"x":702,"y":0,"w":32,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":50,"y":173,"w":32,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_7","frame":{"x":643,"y":61,"w":36,"h":50},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":173,"w":36,"h":50},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_8","frame":{"x":839,"y":61,"w":31,"h":49},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":175,"w":31,"h":49},"sourceSize":{"w":128,"h":256}},{"filename":"run_nw_9","frame":{"x":442,"y":61,"w":27,"h":51},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":179,"w":27,"h":51},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_0","frame":{"x":0,"y":0,"w":23,"h":59},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":175,"w":23,"h":59},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_1","frame":{"x":25,"y":0,"w":22,"h":59},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":171,"w":22,"h":59},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_2","frame":{"x":75,"y":0,"w":23,"h":58},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":170,"w":23,"h":58},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_3","frame":{"x":25,"y":117,"w":23,"h":48},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":174,"w":23,"h":48},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_4","frame":{"x":471,"y":61,"w":24,"h":51},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":177,"w":24,"h":51},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_5","frame":{"x":233,"y":0,"w":24,"h":56},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":174,"w":24,"h":56},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_6","frame":{"x":49,"y":0,"w":24,"h":59},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":171,"w":24,"h":59},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_7","frame":{"x":161,"y":0,"w":24,"h":57},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":170,"w":24,"h":57},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_8","frame":{"x":50,"y":117,"w":24,"h":48},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":174,"w":24,"h":48},"sourceSize":{"w":128,"h":256}},{"filename":"run_n_9","frame":{"x":497,"y":61,"w":24,"h":51},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":178,"w":24,"h":51},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_0","frame":{"x":736,"y":0,"w":19,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":55,"y":176,"w":19,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_1","frame":{"x":443,"y":117,"w":22,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":57,"y":183,"w":22,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_2","frame":{"x":537,"y":213,"w":26,"h":32},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":56,"y":198,"w":26,"h":32},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_3","frame":{"x":625,"y":213,"w":26,"h":31},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":56,"y":199,"w":26,"h":31},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_4","frame":{"x":565,"y":213,"w":26,"h":32},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":56,"y":198,"w":26,"h":32},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_5","frame":{"x":662,"y":167,"w":23,"h":41},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":58,"y":189,"w":23,"h":41},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_6","frame":{"x":76,"y":117,"w":20,"h":48},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":58,"y":182,"w":20,"h":48},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_7","frame":{"x":228,"y":61,"w":19,"h":52},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":55,"y":178,"w":19,"h":52},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_8","frame":{"x":114,"y":61,"w":22,"h":53},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":177,"w":22,"h":53},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_ne_9","frame":{"x":757,"y":0,"w":21,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":176,"w":21,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_0","frame":{"x":259,"y":0,"w":11,"h":56},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":59,"y":176,"w":11,"h":56},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_1","frame":{"x":348,"y":167,"w":27,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":188,"w":27,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_2","frame":{"x":653,"y":213,"w":33,"h":31},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":201,"w":33,"h":31},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_3","frame":{"x":722,"y":213,"w":33,"h":30},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":202,"w":33,"h":30},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_4","frame":{"x":688,"y":213,"w":32,"h":31},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":50,"y":201,"w":32,"h":31},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_5","frame":{"x":959,"y":167,"w":30,"h":38},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":194,"w":30,"h":38},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_6","frame":{"x":467,"y":117,"w":26,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":185,"w":26,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_7","frame":{"x":249,"y":61,"w":20,"h":52},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":180,"w":20,"h":52},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_8","frame":{"x":780,"y":0,"w":16,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":55,"y":178,"w":16,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_e_9","frame":{"x":272,"y":0,"w":13,"h":56},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":57,"y":176,"w":13,"h":56},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_0","frame":{"x":405,"y":0,"w":20,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":177,"w":20,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_1","frame":{"x":800,"y":167,"w":32,"h":39},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":192,"w":32,"h":39},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_2","frame":{"x":757,"y":213,"w":38,"h":30},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":42,"y":202,"w":38,"h":30},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_3","frame":{"x":837,"y":213,"w":38,"h":29},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":42,"y":203,"w":38,"h":29},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_4","frame":{"x":797,"y":213,"w":38,"h":30},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":42,"y":202,"w":38,"h":30},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_5","frame":{"x":103,"y":213,"w":37,"h":37},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":43,"y":195,"w":37,"h":37},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_6","frame":{"x":707,"y":117,"w":32,"h":45},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":45,"y":187,"w":32,"h":45},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_7","frame":{"x":523,"y":61,"w":25,"h":51},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":181,"w":25,"h":51},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_8","frame":{"x":798,"y":0,"w":20,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":178,"w":20,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_se_9","frame":{"x":287,"y":0,"w":19,"h":56},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":176,"w":19,"h":56},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_0","frame":{"x":138,"y":61,"w":24,"h":53},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":177,"w":24,"h":53},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_1","frame":{"x":713,"y":167,"w":28,"h":40},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":192,"w":28,"h":40},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_2","frame":{"x":209,"y":213,"w":29,"h":35},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":198,"w":29,"h":35},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_3","frame":{"x":378,"y":213,"w":30,"h":34},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":199,"w":30,"h":34},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_4","frame":{"x":240,"y":213,"w":30,"h":35},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":198,"w":30,"h":35},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_5","frame":{"x":743,"y":167,"w":30,"h":40},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":193,"w":30,"h":40},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_6","frame":{"x":741,"y":117,"w":28,"h":45},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":186,"w":28,"h":45},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_7","frame":{"x":872,"y":61,"w":24,"h":49},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":181,"w":24,"h":49},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_8","frame":{"x":271,"y":61,"w":24,"h":52},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":53,"y":178,"w":24,"h":52},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_s_9","frame":{"x":164,"y":61,"w":25,"h":53},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":177,"w":25,"h":53},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_0","frame":{"x":820,"y":0,"w":19,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":177,"w":19,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_1","frame":{"x":994,"y":117,"w":22,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":190,"w":22,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_2","frame":{"x":834,"y":167,"w":26,"h":39},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":195,"w":26,"h":39},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_3","frame":{"x":991,"y":167,"w":26,"h":38},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":196,"w":26,"h":38},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_4","frame":{"x":862,"y":167,"w":26,"h":39},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":195,"w":26,"h":39},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_5","frame":{"x":0,"y":167,"w":23,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":190,"w":23,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_6","frame":{"x":898,"y":61,"w":21,"h":49},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":50,"y":184,"w":21,"h":49},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_7","frame":{"x":297,"y":61,"w":19,"h":52},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":179,"w":19,"h":52},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_8","frame":{"x":841,"y":0,"w":22,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":177,"w":22,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_sw_9","frame":{"x":427,"y":0,"w":21,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":55,"y":176,"w":21,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_0","frame":{"x":450,"y":0,"w":11,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":58,"y":176,"w":11,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_1","frame":{"x":603,"y":117,"w":27,"h":46},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":186,"w":27,"h":46},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_2","frame":{"x":0,"y":213,"w":33,"h":38},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":194,"w":33,"h":38},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_3","frame":{"x":890,"y":167,"w":33,"h":39},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":194,"w":33,"h":39},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_4","frame":{"x":35,"y":213,"w":32,"h":38},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":46,"y":194,"w":32,"h":38},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_5","frame":{"x":771,"y":117,"w":30,"h":45},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":47,"y":188,"w":30,"h":45},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_6","frame":{"x":318,"y":61,"w":26,"h":52},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":180,"w":26,"h":52},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_7","frame":{"x":865,"y":0,"w":20,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":55,"y":177,"w":20,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_8","frame":{"x":463,"y":0,"w":16,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":57,"y":176,"w":16,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_w_9","frame":{"x":481,"y":0,"w":13,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":58,"y":176,"w":13,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_0","frame":{"x":887,"y":0,"w":20,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":176,"w":20,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_1","frame":{"x":495,"y":117,"w":32,"h":47},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":49,"y":182,"w":32,"h":47},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_2","frame":{"x":272,"y":213,"w":38,"h":35},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":194,"w":38,"h":35},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_3","frame":{"x":410,"y":213,"w":38,"h":34},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":195,"w":38,"h":34},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_4","frame":{"x":312,"y":213,"w":38,"h":35},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":194,"w":38,"h":35},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_5","frame":{"x":25,"y":167,"w":37,"h":44},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":48,"y":185,"w":37,"h":44},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_6","frame":{"x":346,"y":61,"w":32,"h":52},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":178,"w":32,"h":52},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_7","frame":{"x":909,"y":0,"w":25,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":54,"y":176,"w":25,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_8","frame":{"x":936,"y":0,"w":20,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":56,"y":176,"w":20,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_nw_9","frame":{"x":958,"y":0,"w":19,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":56,"y":176,"w":19,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_0","frame":{"x":496,"y":0,"w":24,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":175,"w":24,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_1","frame":{"x":98,"y":117,"w":28,"h":48},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":181,"w":28,"h":48},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_2","frame":{"x":450,"y":213,"w":29,"h":34},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":195,"w":29,"h":34},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_3","frame":{"x":593,"y":213,"w":30,"h":32},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":197,"w":30,"h":32},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_4","frame":{"x":481,"y":213,"w":30,"h":34},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":195,"w":30,"h":34},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_5","frame":{"x":377,"y":167,"w":30,"h":43},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":186,"w":30,"h":43},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_6","frame":{"x":550,"y":61,"w":28,"h":51},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":178,"w":28,"h":51},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_7","frame":{"x":979,"y":0,"w":24,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":52,"y":176,"w":24,"h":54},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_8","frame":{"x":522,"y":0,"w":24,"h":55},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":175,"w":24,"h":55},"sourceSize":{"w":128,"h":256}},{"filename":"pickup_n_9","frame":{"x":0,"y":61,"w":25,"h":54},"rotated":false,"trimmed":true,"spriteSourceSize":{"x":51,"y":176,"w":25,"h":54},"sourceSize":{"w":128,"h":256}}],"meta":{"app":"melonJS","image":"default","size":{"w":1024,"h":251},"scale":"1"}} \ No newline at end of file diff --git a/packages/examples/public/assets/isometricDungeon/hero.png b/packages/examples/public/assets/isometricDungeon/hero.png new file mode 100644 index 0000000000..ee92436878 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/hero.png differ diff --git a/packages/examples/public/assets/isometricDungeon/kenney-logo.png b/packages/examples/public/assets/isometricDungeon/kenney-logo.png new file mode 100644 index 0000000000..0602b4505e Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/kenney-logo.png differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonAmbience.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonAmbience.mp3 new file mode 100644 index 0000000000..065051b11d Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonAmbience.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonAmbience.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonAmbience.ogg new file mode 100644 index 0000000000..adfb4042a0 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonAmbience.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonCoins.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonCoins.mp3 new file mode 100644 index 0000000000..dcc80a8078 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonCoins.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonCoins.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonCoins.ogg new file mode 100644 index 0000000000..c84e27d4a9 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonCoins.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonCreak.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonCreak.mp3 new file mode 100644 index 0000000000..d244fe80e3 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonCreak.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonCreak.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonCreak.ogg new file mode 100644 index 0000000000..4f6de6f87f Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonCreak.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonDoor.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonDoor.mp3 new file mode 100644 index 0000000000..cefd3ea456 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonDoor.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonDoor.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonDoor.ogg new file mode 100644 index 0000000000..7df2826531 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonDoor.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonGate.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonGate.mp3 new file mode 100644 index 0000000000..6140b7b82b Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonGate.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonGate.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonGate.ogg new file mode 100644 index 0000000000..d4432f48b6 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonGate.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonGateCreak.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonGateCreak.mp3 new file mode 100644 index 0000000000..e0d94aa251 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonGateCreak.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonGateCreak.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonGateCreak.ogg new file mode 100644 index 0000000000..0673845d05 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonGateCreak.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonLatch.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonLatch.mp3 new file mode 100644 index 0000000000..300972b4da Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonLatch.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonLatch.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonLatch.ogg new file mode 100644 index 0000000000..509c68f75a Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonLatch.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonLocked.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonLocked.mp3 new file mode 100644 index 0000000000..d9ba358bf5 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonLocked.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonLocked.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonLocked.ogg new file mode 100644 index 0000000000..1a9ee4bd73 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonLocked.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep0.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep0.mp3 new file mode 100644 index 0000000000..59a83c665b Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep0.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep0.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep0.ogg new file mode 100644 index 0000000000..163cc56468 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep0.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep1.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep1.mp3 new file mode 100644 index 0000000000..52957bd82c Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep1.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep1.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep1.ogg new file mode 100644 index 0000000000..4c62bac1fb Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep1.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep2.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep2.mp3 new file mode 100644 index 0000000000..f58f59bce4 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep2.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep2.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep2.ogg new file mode 100644 index 0000000000..99aec9818d Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep2.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep3.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep3.mp3 new file mode 100644 index 0000000000..d8dba5387c Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep3.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep3.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep3.ogg new file mode 100644 index 0000000000..bfb613ab88 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep3.ogg differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep4.mp3 b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep4.mp3 new file mode 100644 index 0000000000..ab684f8bb7 Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep4.mp3 differ diff --git a/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep4.ogg b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep4.ogg new file mode 100644 index 0000000000..fe8d18d13d Binary files /dev/null and b/packages/examples/public/assets/isometricDungeon/sfx/dungeonStep4.ogg differ diff --git a/packages/examples/public/assets/isometricRpg/Blank_Sprite_Sheet_4_2_by_KnightYamato.png b/packages/examples/public/assets/isometricRpg/Blank_Sprite_Sheet_4_2_by_KnightYamato.png deleted file mode 100644 index 6ae08858f9..0000000000 Binary files a/packages/examples/public/assets/isometricRpg/Blank_Sprite_Sheet_4_2_by_KnightYamato.png and /dev/null differ diff --git a/packages/examples/public/assets/isometricRpg/forest.png b/packages/examples/public/assets/isometricRpg/forest.png deleted file mode 100644 index fee2ac7cd6..0000000000 Binary files a/packages/examples/public/assets/isometricRpg/forest.png and /dev/null differ diff --git a/packages/examples/public/assets/isometricRpg/isometric.tmx b/packages/examples/public/assets/isometricRpg/isometric.tmx deleted file mode 100644 index 6199e99154..0000000000 --- a/packages/examples/public/assets/isometricRpg/isometric.tmx +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - DAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAA== - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALIAAACwAAAArwAAAK8AAACvAAAArwAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsgAAAK8AAACvAAAA1QAAAK8AAACwAAAArwAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAlwAAAAAAAAAAAAAAAAAAAAAAAACyAAAArwAAAK8AAACvAAAArwAAAK8AAACvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4AAADHAAAArwAAAK8AAACvAAAAsAAAAK8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4AAACsAAAArAAAAKwAAACsAAAAxwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAA+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwEAAAAAAAAiAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYBAAAAAAAAIQEAAAAAAAAsAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACABAAAAAAAAKwEAAAAAAAA2AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACoBAAAAAAAANQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcBAAAAAAAAAAAAAAAAAAAPAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - - - - - - - - - - - - - - - - - diff --git a/packages/examples/public/assets/isometricRpg/isometric_grass_and_water.png b/packages/examples/public/assets/isometricRpg/isometric_grass_and_water.png deleted file mode 100644 index b342a0cb43..0000000000 Binary files a/packages/examples/public/assets/isometricRpg/isometric_grass_and_water.png and /dev/null differ diff --git a/packages/examples/public/assets/isometricRpg/isometric_staggered.tmx b/packages/examples/public/assets/isometricRpg/isometric_staggered.tmx deleted file mode 100644 index 04d616b692..0000000000 --- a/packages/examples/public/assets/isometricRpg/isometric_staggered.tmx +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GAAAABgAAAAXAAAACwAAABMAAAATAAAADAAAABcAAAAYAAAAGAAAABcAAAAHAAAAAgAAAAIAAAABAAAABAAAAAIAAAACAAAAAwAAAAQAAAAEAAAAAQAAAAMAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAFwAAABcAAAAOAAAAAwAAAAMAAAAIAAAADAAAABgAAAAYAAAAEgAAAAQAAAABAAAAAwAAAAEAAAADAAAAAwAAAAQAAAAEAAAAAQAAAAMAAAABAAAABAAAAAMAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAAPAAAADwAAAAcAAAABAAAABAAAAAMAAAAUAAAAFwAAAAsAAAAHAAAAAgAAAAEAAAADAAAAAQAAAAMAAAAEAAAABAAAAAMAAAACAAAAAgAAAAMAAAAEAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgAAAAQAAAADAAAAAgAAAAIAAAAEAAAAAwAAAAgAAAAMAAAAEgAAAAUAAAAFAAAABQAAAAMAAAADAAAAAQAAAAQAAAACAAAAAQAAAAQAAAAEAAAABAAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAgAAAAEAAAAEAAAAAgAAAAEAAAAEAAAAAgAAAAgAAAAHAAAACAAAAAkAAAAJAAAABgAAAAMAAAADAAAABAAAAAMAAAAEAAAABAAAAAMAAAACAAAABAAAAAMAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAGAAAAAgAAAAEAAAAEAAAABAAAAAEAAAADAAAABAAAAAMAAAAPAAAAGAAAAA4AAAACAAAAAgAAAAEAAAABAAAAAQAAAAEAAAACAAAAAQAAAAQAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAABIAAAABAAAAAgAAAAQAAAADAAAAAwAAAAUAAAAGAAAABQAAAA0AAAAMAAAAEgAAAAQAAAADAAAABAAAAAUAAAADAAAAAQAAAAMAAAAEAAAAAgAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAADgAAAAQAAAACAAAABQAAAAYAAAACAAAACAAAABYAAAAJAAAACQAAAAoAAAAEAAAAAgAAAAUAAAAUAAAAEQAAAAEAAAAFAAAABgAAAAIAAAADAAAABAAAAAQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAAAHAAAAAwAAAAEAAAAIAAAABwAAAAQAAAABAAAACAAAAAwAAAAYAAAAFwAAAAYAAAAEAAAACAAAAAkAAAAYAAAAEQAAAAgAAAAHAAAAAQAAAAMAAAADAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAEAAAAAgAAAAIAAAADAAAAAwAAAAQAAAADAAAAFAAAABgAAAAKAAAAAwAAAAIAAAAPAAAAGAAAABcAAAAGAAAAAwAAAAEAAAAEAAAABAAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAUAAAANAAAABgAAAAEAAAACAAAAAgAAAAEAAAAQAAAAFwAAABcAAAANAAAABAAAAAMAAAAPAAAAFwAAAAoAAAABAAAAAQAAAAMAAAAFAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAEAAAAEAAAABgAAAAKAAAABgAAAAIAAAAEAAAACAAAAAsAAAAXAAAAGAAAABEAAAAFAAAAAwAAAA8AAAAYAAAADQAAAAIAAAAEAAAAEAAAAAYAAAAEAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAEAAAAUAAAAFwAAABgAAAAKAAAABgAAAAMAAAAEAAAABwAAAAwAAAAXAAAAFwAAAAkAAAANAAAABAAAAAwAAAAXAAAABgAAAAEAAAAQAAAACgAAAAQAAAADAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAgAAAAgAAAAMAAAAFwAAABgAAAASAAAABAAAAAMAAAAIAAAACwAAABgAAAAYAAAAFwAAAAYAAAAIAAAAFwAAAAoAAAABAAAACAAAAAsAAAAGAAAAAQAAAAMAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAABAAAAAgAAAAgAAAAMAAAAGAAAAA4AAAAEAAAAAQAAAAIAAAAHAAAAEwAAAAsAAAALAAAACgAAAAMAAAAMAAAAGAAAAAYAAAADAAAABwAAAAcAAAABAAAABAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAQAAAADAAAAAgAAAAgAAAATAAAABwAAAAIAAAACAAAAAwAAAAMAAAAHAAAAFgAAAAwAAAANAAAAFAAAABcAAAAOAAAABAAAAAQAAAAEAAAAAwAAAAEAAAACAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAAAAEAAAAEAAAABAAAAAEAAAACAAAABQAAAAYAAAACAAAAAgAAAAUAAAAQAAAACQAAABgAAAAJAAAAGAAAAAoAAAACAAAAAwAAAAMAAAACAAAABAAAAAMAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAQAAAAMAAAACAAAABQAAAA0AAAAJAAAADgAAAAMAAAAUAAAACQAAABgAAAAYAAAAFwAAABcAAAALAAAABgAAAAIAAAAFAAAAAQAAAAMAAAABAAAAAgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAEAAAADAAAAAwAAAAMAAAAUAAAAFwAAABgAAAASAAAABAAAABQAAAAYAAAAGAAAABcAAAALAAAAGAAAAAoAAAAHAAAAAQAAABQAAAAGAAAAAQAAAAMAAAAEAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAgAAAAQAAAABAAAAAgAAAAgAAAAPAAAAEwAAAAcAAAAIAAAAFwAAABcAAAALAAAAEgAAAA8AAAAYAAAABgAAAAMAAAAQAAAADgAAAAQAAAAEAAAAAwAAAAQAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAAAAgAAAAQAAAAEAAAAAwAAAAIAAAABAAAABAAAAAIAAAATAAAAGAAAABIAAAAHAAAAAwAAAAwAAAAOAAAAAwAAAAgAAAASAAAAAwAAAAIAAAAFAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAABAAAAAgAAAAMAAAADAAAAAwAAAAQAAAACAAAAAQAAABMAAAASAAAAAwAAAAQAAAAIAAAAEgAAAAEAAAABAAAABwAAAAQAAAACAAAAFAAAABEAAAADAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAQAAAAMAAAAEAAAABAAAAAQAAAABAAAABAAAAAcAAAADAAAAAgAAAAMAAAAHAAAAAQAAAAQAAAADAAAAAgAAAAEAAAAIAAAAGAAAAAYAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAACAAAAAwAAAAIAAAABAAAABAAAAAIAAAACAAAAAQAAAAIAAAACAAAAAgAAAAQAAAADAAAAAwAAAAIAAAADAAAAAwAAAAIAAAADAAAAAgAAAA8AAAAKAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAABAAAAAEAAAADAAAAAwAAAAIAAAABAAAABAAAAAIAAAABAAAAAwAAAAEAAAADAAAAAwAAAAQAAAADAAAABAAAAAIAAAABAAAAAgAAAAwAAAAGAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAABAAAAAwAAAAMAAAACAAAAAQAAAAQAAAAIAAAACgAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAADAAAAAQAAAAIAAAAEAAAABAAAAAQAAAABAAAAAwAAAAQAAAAMAAAABgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAABQAAAAEAAAAFAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAEAAAAAwAAAAQAAAABAAAAAgAAAAUAAAACAAAAEAAAAA4AAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAAIAAAADQAAABQAAAAGAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAEAAAABAAAAAEAAAAEAAAAAQAAAAQAAAACAAAAEAAAABEAAAAIAAAACgAAAAIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAABMAAAAJAAAAEgAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAMAAAABAAAAAwAAAAIAAAAEAAAAAwAAABAAAAAXAAAAEQAAAAwAAAAGAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAwAAAASAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAABAAAAAEAAAABAAAAAgAAAAEAAAAEAAAAEAAAABgAAAAXAAAACQAAAA4AAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAAIAAAADgAAAAEAAAAFAAAAAQAAAAEAAAABAAAAAQAAAAQAAAABAAAABQAAAAUAAAAFAAAAAgAAABQAAAAYAAAAFwAAABgAAAASAAAABAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAHAAAAAQAAABQAAAANAAAAAQAAAAEAAAABAAAAAwAAAAUAAAAQAAAACQAAAAkAAAANAAAACAAAABgAAAALAAAAGAAAAAoAAAAEAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAQAAAAFwAAABEAAAABAAAAAQAAAAEAAAAUAAAACQAAABgAAAAXAAAAFwAAAA0AAAATAAAADgAAAAwAAAAXAAAABgAAAAMAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAABQAAAAXAAAAGAAAAA0AAAABAAAAAQAAABAAAAAYAAAAFwAAABcAAAAYAAAAFwAAAA0AAAAHAAAACAAAABgAAAAKAAAAAwAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAQAAAAGAAAABcAAAAYAAAABgAAAAEAAAAUAAAAFwAAABcAAAAYAAAAGAAAABcAAAAXAAAAEQAAAAMAAAAMAAAAFwAAABEAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAgAAAAXAAAAFwAAABcAAAAKAAAAAQAAABAAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAYAAAABgAAAAgAAAAYAAAAFwAAAA0AAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAEwAAABcAAAAYAAAAFwAAAA0AAAAIAAAAFwAAABcAAAAYAAAAGAAAABcAAAAYAAAAGAAAAAoAAAABAAAAEwAAABcAAAAYAAAABgAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAADAAAABgAAAAYAAAAGAAAAA0AAAAMAAAAGAAAABcAAAAXAAAAFwAAABgAAAAXAAAAGAAAAAYAAAABAAAADwAAABgAAAAKAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAgAAAAXAAAAFwAAABgAAAAXAAAACQAAABcAAAAYAAAAGAAAABcAAAAYAAAAFwAAABcAAAASAAAAAwAAAAEAAAAPAAAAGAAAAAYAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAA8AAAAXAAAAGAAAABgAAAAYAAAAFwAAABcAAAAYAAAAGAAAABgAAAAYAAAAGAAAAA4AAAABAAAABAAAAAIAAAAPAAAADgAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAEwAAABcAAAALAAAAFwAAABcAAAAYAAAAFwAAABcAAAAXAAAAFwAAABgAAAAOAAAAAQAAAAMAAAACAAAAAQAAAAcAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAADwAAABIAAAAMAAAAGAAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAA4AAAACAAAAAQAAAAMAAAACAAAABAAAAAEAAAABAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAHAAAACAAAABcAAAAOAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAAAQAAAAEAAAABAAAAAQAAAAQAAAABAAAAAQAAABQAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAEwAAABIAAAABAAAAAgAAAAQAAAADAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAACAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAHAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAMAAAAFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAABQAAAAUAAAABAAAAAQAAAAEAAAABAAAAAQAAABAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAFAAAABQAAABAAAAAJAAAADQAAAAUAAAABAAAAAQAAAAEAAAAQAAAAGAAAABcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAABQAAAAJAAAACQAAABcAAAAXAAAACQAAAA0AAAABAAAAAQAAABAAAAAYAAAAFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAIAAAAGAAAABgAAAAYAAAAGAAAABcAAAAXAAAABgAAAAEAAAAIAAAAGAAAABcAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - - - diff --git a/packages/examples/src/examples/isometricRpg/ExampleIsometricRPG.tsx b/packages/examples/src/examples/isometricDungeon/ExampleIsometricDungeon.tsx similarity index 65% rename from packages/examples/src/examples/isometricRpg/ExampleIsometricRPG.tsx rename to packages/examples/src/examples/isometricDungeon/ExampleIsometricDungeon.tsx index f957babc14..ae6079af01 100644 --- a/packages/examples/src/examples/isometricRpg/ExampleIsometricRPG.tsx +++ b/packages/examples/src/examples/isometricDungeon/ExampleIsometricDungeon.tsx @@ -1,9 +1,9 @@ /** - * melonJS — isometric Tiled-map RPG demo example. + * melonJS — Isometric Dungeon example. * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. * See `packages/examples/LICENSE.md` for full license + asset credits. */ import { createExampleComponent } from "../utils"; import { createGame } from "./createGame"; -export const ExampleIsometricRPG = createExampleComponent(createGame); +export const ExampleIsometricDungeon = createExampleComponent(createGame); diff --git a/packages/examples/src/examples/isometricDungeon/constants.ts b/packages/examples/src/examples/isometricDungeon/constants.ts new file mode 100644 index 0000000000..53a2831eec --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/constants.ts @@ -0,0 +1,60 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ + +export const VIEW_W = 1280; +export const VIEW_H = 720; + +// size of every tileset cell and hero frame +export const FRAME_W = 128; +export const FRAME_H = 256; + +// height of a map cell +export const TILE_H = 64; + +// position of the hero's feet in a frame +export const FEET_X = 64; +export const FEET_Y = 234; + +// pixels per frame, along the map axes +export const HERO_SPEED = 3.2; + +// ms per animation frame +export const RUN_FRAME_MS = 55; +export const PICKUP_FRAME_MS = 45; + +// how close (in pixels) the hero needs to be to open something +export const REACH = 64; + +export const TORCH_RADIUS = 200; +export const TORCH_COLOR = "#ffb46a"; +export const TORCH_INTENSITY = 0.2; + +// the torches on the walls +export const WALL_TORCH_RADIUS = 120; +export const WALL_TORCH_COLOR = "#ff9a40"; +export const WALL_TORCH_INTENSITY = 0.35; + +// the darkness over the dungeon: the stage's ambient light, which every +// light cuts a soft hole in +export const DARKNESS = "rgba(4, 3, 10, 0.9)"; + +// how far the darkness opens around the hero and around a wall torch, in +// pixels, out to where it is fully dark again. The openings are ellipses, +// wider than tall +export const SIGHT_RADIUS = 380; +export const TORCH_SIGHT_RADIUS = 240; +export const SIGHT_SQUASH = 0.5625; + +// light over the chests that are still closed +export const CHEST_GLOW_RADIUS = 130; +export const CHEST_GLOW_COLOR = "#ffd75e"; +export const CHEST_GLOW_INTENSITY = 0.5; + +// the dust motes float over the world (the darkness is drawn after it) +export const MOTES_Z = 1e9; + +// the visibility layer of every particle emitter: the minimap leaves it out +export const PARTICLES = 2; diff --git a/packages/examples/src/examples/isometricDungeon/createGame.ts b/packages/examples/src/examples/isometricDungeon/createGame.ts new file mode 100644 index 0000000000..51de1f51a5 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/createGame.ts @@ -0,0 +1,63 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { DebugPanelPlugin } from "@melonjs/debug-plugin"; +import { + Application, + audio, + loader, + plugin, + registerTiledObjectClass, + state, +} from "melonjs"; +import { VIEW_H, VIEW_W } from "./constants"; +import { Hero } from "./entities/Hero"; +import { Chest, Door, Exit, Gate } from "./entities/usables"; +import { WallTorch } from "./entities/WallTorch"; +import { resources } from "./resources"; +import { EscapedStage } from "./scenes/EscapedStage"; +import { PlayStage } from "./scenes/PlayStage"; + +export const createGame = async () => { + const app = new Application(VIEW_W, VIEW_H, { + parent: "screen", + scale: "auto", + // the same number of updates every frame, so the scrolling is steady + syncToDisplay: true, + // and the world and the hero move by fractions of a pixel together, + // rather than each snapping to whole pixels on its own + subPixel: true, + // rendered art, not pixel art: textures sampled smoothly, so a + // fraction of a pixel shows as such instead of a whole pixel jump, + // while the canvas is still scaled up to the window crisp. And from + // the mipmaps where it is drawn small: the minimap, at a tenth of + // its size, would shimmer as the hero walks + textureFilter: "linear-mipmap", + }); + await app.init(); + + // debug panel, toggled with S + plugin.register(DebugPanelPlugin, "debugPanel"); + + // the classes in the map: the hero's spawn point has its own, the rest + // are set on tiles in the tileset, and every object placed from one of + // those tiles is built by its class + registerTiledObjectClass("Hero", Hero); + registerTiledObjectClass("Chest", Chest); + registerTiledObjectClass("Door", Door); + registerTiledObjectClass("Gate", Gate); + registerTiledObjectClass("Exit", Exit); + registerTiledObjectClass("WallTorch", WallTorch); + + // must be called before loading any audio + audio.init("mp3,ogg"); + + await loader.preload(resources); + + state.set(state.PLAY, new PlayStage()); + state.set(state.GAME_END, new EscapedStage()); + state.transition("fade", "#000000", 400); + state.change(state.PLAY); +}; diff --git a/packages/examples/src/examples/isometricDungeon/entities/Hero.ts b/packages/examples/src/examples/isometricDungeon/entities/Hero.ts new file mode 100644 index 0000000000..e85fcbb323 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/entities/Hero.ts @@ -0,0 +1,301 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { + collision, + input, + type Light2d, + loader, + type ParticleEmitter, + Polygon, + type Renderer, + Sprite, + TextureAtlas, + timer, + Vector2d, +} from "melonjs"; +import { + FEET_X, + FEET_Y, + HERO_SPEED, + PICKUP_FRAME_MS, + REACH, + RUN_FRAME_MS, + SIGHT_RADIUS, + SIGHT_SQUASH, + TORCH_INTENSITY, +} from "../constants"; +import { footstep } from "../fx/sound"; +import { breath, flicker } from "../fx/torch"; +import type { Dungeon } from "./dungeon"; +import { Exit, type Usable } from "./usables"; + +// heading for each map direction (sign of x, sign of y) +const HEADING: Record = { + "-1,-1": "n", + "0,-1": "ne", + "1,-1": "e", + "1,0": "se", + "1,1": "s", + "0,1": "sw", + "-1,1": "w", + "-1,0": "nw", +}; +const HEADINGS = Object.values(HEADING); + +// a direction, projected between the map and the screen +const direction = new Vector2d(); + +// frames per animation in the atlas +const FRAMES = { idle: 1, run: 10, pickup: 10 }; + +let atlas: TextureAtlas | undefined; +function heroAtlas() { + if (!atlas) { + const image = loader.getImage("hero"); + if (!image) { + throw new Error("hero image not loaded"); + } + atlas = new TextureAtlas(loader.getJSON("hero"), image); + } + return atlas; +} + +let shadow: HTMLCanvasElement | undefined; +// soft dark ellipse, wider than tall like the footprint +function shadowTexture() { + if (!shadow) { + shadow = document.createElement("canvas"); + shadow.width = 32; + shadow.height = 16; + const context = shadow.getContext("2d"); + if (!context) { + throw new Error("could not create a 2d context"); + } + context.scale(1, 0.5); + const fill = context.createRadialGradient(16, 16, 0, 16, 16, 16); + fill.addColorStop(0, "rgba(0, 0, 0, 0.45)"); + fill.addColorStop(0.6, "rgba(0, 0, 0, 0.25)"); + fill.addColorStop(1, "rgba(0, 0, 0, 0)"); + context.fillStyle = fill; + context.fillRect(0, 0, 32, 32); + } + return shadow; +} + +export class Hero extends Sprite { + dungeon: Dungeon | undefined; + torch?: Light2d; + // how far the darkness opens around the hero + sight?: Light2d; + dust?: ParticleEmitter; + // called every frame with whatever is in reach + onReach?: (target: Usable | undefined) => void; + // camera target: the feet, on a whole pixel (the camera's offset looks + // a bit above them) + readonly focus = new Vector2d(); + // where the feet are, to find what is in reach + private readonly feet = new Vector2d(); + private heading = "s"; + // true during the pickup animation + private busy = false; + + constructor(x: number, y: number) { + // (x, y) is where the feet are; the atlas holds the hero's frames only + super(x - FEET_X, y - FEET_Y, heroAtlas().getAnimationSettings()); + this.name = "hero"; + // the camera follows `focus`, which is only moved by update() + this.alwaysUpdate = true; + // same anchor as the wall sprites, so they sort the same way + this.anchorPoint.set(0, 0); + + for (const heading of HEADINGS) { + this.addAnimation(`idle_${heading}`, frames("idle", heading)); + this.addAnimation(`run_${heading}`, frames("run", heading), RUN_FRAME_MS); + this.addAnimation( + `pickup_${heading}`, + frames("pickup", heading), + PICKUP_FRAME_MS, + ); + } + this.setCurrentAnimation("idle_s"); + + // diamond shaped footprint, about as wide as the feet: 10 Tiled units + // across, well inside the 38 unit doorways + this.bodyDef = { + type: "dynamic", + shapes: [ + new Polygon(0, 0, [ + new Vector2d(FEET_X, FEET_Y - 5), + new Vector2d(FEET_X + 10, FEET_Y), + new Vector2d(FEET_X, FEET_Y + 5), + new Vector2d(FEET_X - 10, FEET_Y), + ]), + ], + collisionType: collision.types.PLAYER_OBJECT, + // only the collision layer and the doors: every wall and prop tile + // object has a body of its own over its whole image + collisionMask: collision.types.WORLD_SHAPE, + frictionAir: 0, + gravityScale: 0, + }; + this.focus.set(x, y); + } + + get feetX() { + return this.pos.x + FEET_X; + } + get feetY() { + return this.pos.y + FEET_Y; + } + + override update(dt: number) { + // keys to map direction: up is (-1, -1), right is (1, -1) + let mx = 0; + let my = 0; + if (!this.busy) { + if (input.isKeyPressed("up")) { + mx -= 1; + my -= 1; + } + if (input.isKeyPressed("down")) { + mx += 1; + my += 1; + } + if (input.isKeyPressed("right")) { + mx += 1; + my -= 1; + } + if (input.isKeyPressed("left")) { + mx -= 1; + my += 1; + } + } + const sx = Math.sign(mx); + const sy = Math.sign(my); + if (sx !== 0 || sy !== 0) { + // normalize, then project the map direction on the screen + direction.set(sx, sy).normalize().toIso().scale(HERO_SPEED); + this.body.setVelocity(direction.x, direction.y); + this.heading = HEADING[`${sx},${sy}`]; + this.setCurrentAnimation(`run_${this.heading}`, { + onFrame: this.footfall, + }); + } else { + this.body.setVelocity(0, 0); + if (!this.busy) { + this.setCurrentAnimation(`idle_${this.heading}`); + } + } + + const target = this.busy + ? undefined + : this.dungeon?.nearest(this.feet.set(this.feetX, this.feetY), REACH); + this.onReach?.(target); + if (target && input.isKeyPressed("action")) { + this.use(target); + } + + const seconds = timer.getTime() / 1000; + if (this.torch) { + this.torch.setPosition(this.feetX, this.feetY - 50); + this.torch.intensity = TORCH_INTENSITY * flicker(seconds); + } + if (this.sight) { + this.sight.setPosition(this.feetX, this.feetY - 70); + // the opening breathes with the torch + const radius = SIGHT_RADIUS * breath(seconds); + this.sight.setRadii(radius, radius * SIGHT_SQUASH); + } + + // on a whole pixel, so the camera comes to rest on one and the + // scene is sharp; it still glides between them while following + this.focus.set(Math.round(this.feetX), Math.round(this.feetY)); + super.update(dt); + return true; + } + + override draw(renderer: Renderer) { + // a shadow under the feet, about the size of the footprint: the + // footprint never reaches a wall, so neither does the shadow + renderer.drawImage( + shadowTexture(), + 0, + 0, + 32, + 16, + this.pos.x + FEET_X - 12, + this.pos.y + FEET_Y - 6, + 24, + 12, + ); + super.draw(renderer); + } + + // the feet touch the ground on frames 3 and 8 of the run + private readonly footfall = (frame: number) => { + if (frame === 3 || frame === 8) { + this.step(); + } + }; + + private step() { + footstep(); + if (this.dust) { + this.dust.setPosition(this.feetX - 11, this.feetY - 6); + this.dust.burstParticles(); + } + } + + private use(target: Usable) { + const dungeon = this.dungeon; + if (!dungeon) { + return; + } + if (target instanceof Exit) { + target.use(dungeon); + return; + } + // face the target and play the pickup animation before opening it + this.heading = facing( + target.spot.x - this.feetX, + target.spot.y - this.feetY, + ); + this.busy = true; + this.setCurrentAnimation(`pickup_${this.heading}`, { + // then stand still, facing it + next: `idle_${this.heading}`, + onComplete: () => { + target.use(dungeon); + this.busy = false; + }, + }); + } +} + +function frames(anim: keyof typeof FRAMES, heading: string) { + return Array.from( + { length: FRAMES[anim] }, + (_, f) => `${anim}_${heading}_${f}`, + ); +} + +// closest heading for a screen offset +function facing(dx: number, dy: number) { + // screen to map + const { x: mx, y: my } = direction.set(dx, dy).to2d(); + const step = Math.round(Math.atan2(my, mx) / (Math.PI / 4)); + const dir = [ + [1, 0], + [1, 1], + [0, 1], + [-1, 1], + [-1, 0], + [-1, -1], + [0, -1], + [1, -1], + ][(step + 8) % 8]; + return HEADING[`${dir[0]},${dir[1]}`]; +} diff --git a/packages/examples/src/examples/isometricDungeon/entities/WallTorch.ts b/packages/examples/src/examples/isometricDungeon/entities/WallTorch.ts new file mode 100644 index 0000000000..a3894ec1cf --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/entities/WallTorch.ts @@ -0,0 +1,97 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { + Container, + Light2d, + math, + type TMXObject, + timer, + Vector2d, +} from "melonjs"; +import { + FRAME_H, + FRAME_W, + SIGHT_SQUASH, + TORCH_SIGHT_RADIUS, + WALL_TORCH_COLOR, + WALL_TORCH_INTENSITY, + WALL_TORCH_RADIUS, +} from "../constants"; +import { torchFlame } from "../fx/flame"; +import { breath, flicker } from "../fx/torch"; +import { tileOf } from "./usables"; + +// where the embers are in the torch tile, by the wall it hangs on +const EMBERS = { + N: [21.7, 180.4], + W: [106.3, 180.4], +} as const; + +// a torch on a wall: its tile, a flame, and a light of its own +export class WallTorch extends Container { + // where the flame burns, in world coordinates + readonly flame = new Vector2d(); + // so the torches don't all flicker together + readonly phase = math.randomFloat(0, 100); + private light?: Light2d; + // how far the darkness opens around it, wider than its glow + private opening?: Light2d; + + constructor(x: number, y: number, settings: TMXObject) { + // the size of the tile, so it sorts with the wall it hangs on + super(x, y, FRAME_W, FRAME_H); + // lined up a pixel lower than that wall, so it is drawn over it + this.sortOffset = 1; + // the wall it hangs on, set on its tile in the tileset + const facing = settings.resolvedProperty("facing") === "W" ? "W" : "N"; + const [ex, ey] = EMBERS[facing]; + this.addChild(tileOf(settings).getRenderable(settings)); + this.addChild(torchFlame(ex, ey)); + this.flame.set(x + ex, y + ey); + } + + override onActivateEvent() { + const app = this.parentApp; + if (!app) { + return; + } + this.light = new Light2d( + this.flame.x, + this.flame.y - 8, + WALL_TORCH_RADIUS, + WALL_TORCH_RADIUS * 0.65, + WALL_TORCH_COLOR, + WALL_TORCH_INTENSITY, + ); + // its glow fades into the darkness rather than cutting a hard hole + this.light.softness = 1; + // over the walls and props, not under them like the hero's torch, so + // it lights the stone on both sides and whoever walks by + app.world.addChild(this.light, this.depth + 0.5); + // a light that only cuts the darkness, and draws nothing + this.opening = new Light2d( + this.flame.x, + this.flame.y, + TORCH_SIGHT_RADIUS, + TORCH_SIGHT_RADIUS * SIGHT_SQUASH, + ); + this.opening.illuminationOnly = true; + this.opening.softness = 1; + app.world.addChild(this.opening, this.depth + 0.5); + } + + override update(dt: number) { + const seconds = timer.getTime() / 1000 + this.phase; + if (this.light) { + this.light.intensity = WALL_TORCH_INTENSITY * flicker(seconds); + } + if (this.opening) { + const radius = TORCH_SIGHT_RADIUS * breath(seconds); + this.opening.setRadii(radius, radius * SIGHT_SQUASH); + } + return super.update(dt); + } +} diff --git a/packages/examples/src/examples/isometricDungeon/entities/dungeon.ts b/packages/examples/src/examples/isometricDungeon/entities/dungeon.ts new file mode 100644 index 0000000000..17aef5163d --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/entities/dungeon.ts @@ -0,0 +1,78 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import type { Container, Vector2d } from "melonjs"; +import { Chest, Door, Gate, Usable } from "./usables"; + +export interface DungeonEvents { + chest: (opened: number, total: number) => void; + locked: () => void; + gate: () => void; + exit: () => void; +} + +// how far the other half of a double door can be: it stands in the next +// cell along the wall, 72 pixels away on screen +const DOUBLE_DOOR = 80; + +// the rules: open every chest and the gate to the stairs opens +export class Dungeon { + readonly usables: Usable[]; + readonly chests: number; + private opened = 0; + + constructor( + world: Container, + readonly events: DungeonEvents, + ) { + this.usables = world + .getChildren() + .filter((child): child is Usable => child instanceof Usable); + this.chests = this.usables.filter((u) => u instanceof Chest).length; + } + + // the closest unused thing within `reach` of `at`, of those `accept` takes + nearest( + at: Vector2d, + reach: number, + accept: (usable: Usable) => boolean = () => true, + ) { + let best: Usable | undefined; + let bestDist = reach; + for (const usable of this.usables) { + if (usable.used || !accept(usable)) { + continue; + } + const dist = usable.spot.distance(at); + if (dist < bestDist) { + best = usable; + bestDist = dist; + } + } + return best; + } + + chestOpened() { + this.opened++; + this.events.chest(this.opened, this.chests); + if (this.opened === this.chests) { + for (const usable of this.usables) { + if (usable instanceof Gate) { + usable.open(); + } + } + this.events.gate(); + } + } + + // double doors are two tiles: open the one next to it as well + openDoorway(door: Door) { + door.open(); + const other = this.nearest(door.spot, DOUBLE_DOOR, (usable) => { + return usable instanceof Door && !(usable instanceof Gate); + }); + other?.open(); + } +} diff --git a/packages/examples/src/examples/isometricDungeon/entities/usables.ts b/packages/examples/src/examples/isometricDungeon/entities/usables.ts new file mode 100644 index 0000000000..c080185abb --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/entities/usables.ts @@ -0,0 +1,185 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { + Container, + collision, + Polygon, + type Renderable, + type Sprite, + type Tile, + type TMXObject, + type TMXTileset, + Tween, + Vector2d, +} from "melonjs"; +import { FRAME_H, FRAME_W, TILE_H } from "../constants"; +import { type ChestFx, highlightChest, openChest } from "../fx/chests"; +import { doorDust } from "../fx/doors"; +import { playAt, SFX } from "../fx/sound"; +import type { Dungeon } from "./dungeon"; + +// The classes of the dungeon are set on its tiles in the tileset, so every +// object placed from those tiles is a tile object +export function tileOf(settings: TMXObject): Tile { + if (settings.tile === undefined) { + throw new Error("expected a tile object, from a tile with this class"); + } + return settings.tile; +} + +// a tile object the hero can walk up to and use +export abstract class Usable extends Container { + abstract readonly hint: string; + used = false; + // center of the cell it stands on + readonly spot = new Vector2d(); + // its tile, drawn as Tiled draws it (flipped, resized or animated) + readonly sprite: Sprite; + protected readonly tileset: TMXTileset; + // the tile it turns into once used, set on its tile in the tileset + private readonly opens: number | undefined; + + constructor(x: number, y: number, settings: TMXObject) { + super(x, y, settings.width, settings.height); + const tile = tileOf(settings); + // a tile's renderable is always a sprite + this.sprite = tile.getRenderable(settings) as Sprite; + this.addChild(this.sprite); + this.tileset = tile.tileset; + this.opens = settings.resolvedProperty("opens"); + // Tiled anchors the tile at the bottom corner of its cell + this.spot.set(x + FRAME_W / 2, y + FRAME_H - TILE_H / 2); + } + + abstract use(dungeon: Dungeon): void; + + // switch to the "open" tile + open() { + this.used = true; + if (this.opens !== undefined) { + this.sprite.setRegion(this.tileset.texture.getRegion(this.opens)); + } + } +} + +export class Chest extends Usable { + readonly hint = "Space: open the chest"; + private fx?: ChestFx; + + override onActivateEvent() { + const app = this.parentApp; + if (app) { + this.fx = highlightChest(app, this, this.spot); + } + } + + use(dungeon: Dungeon) { + this.open(); + playAt(SFX.latch, this.spot.x, this.spot.y, 0.8); + playAt(SFX.coins, this.spot.x, this.spot.y, 0.9); + const app = this.parentApp; + if (app && this.fx) { + openChest(app, this, this.spot, this.fx); + } + dungeon.chestOpened(); + } +} + +// a doorway: the part of the cell edge that opens, in Tiled units out of 64 +// (the gate's arch, and a unit to spare), and how far its collision reaches +// behind and in front of the edge. That is well past the stone, so the hero +// never stands close enough to the frame to be drawn over it or under it. +// The stone on either side is in the map's collision layer, just as deep +const OPENING = [14, 52]; +const JAMB_BACK = 26; +const JAMB_FRONT = 20; + +export class Door extends Usable { + readonly hint: string = "Space: open the door"; + // the wall it stands in, set on its tile in the tileset + private readonly facing: "N" | "W"; + // the closed door, laid over the open frame as it fades out + private readonly closed: Renderable; + + constructor(x: number, y: number, settings: TMXObject) { + super(x, y, settings); + this.facing = settings.resolvedProperty("facing") === "N" ? "N" : "W"; + this.closed = tileOf(settings).getRenderable(settings); + // block the doorway until it opens. Points are offsets from the + // cell's bottom corner in Tiled units, projected on screen + const [a, b] = OPENING; + const [dx, dy] = + this.facing === "N" + ? [ + [a - 64, b - 64], + [-JAMB_BACK, JAMB_FRONT], + ] + : [ + [-JAMB_BACK, JAMB_FRONT], + [a - 64, b - 64], + ]; + // the cell's bottom corner, where Tiled anchors the tile + const origin = new Vector2d(FRAME_W / 2, FRAME_H); + const corner = (x: number, y: number) => { + return new Vector2d(x, y).toIso().add(origin); + }; + this.bodyDef = { + type: "static", + shapes: [ + new Polygon(0, 0, [ + corner(dx[0], dy[0]), + corner(dx[1], dy[0]), + corner(dx[1], dy[1]), + corner(dx[0], dy[1]), + ]), + ], + collisionType: collision.types.WORLD_SHAPE, + }; + } + + use(dungeon: Dungeon) { + playAt(SFX.door, this.spot.x, this.spot.y, 0.9); + playAt(SFX.creak, this.spot.x, this.spot.y, 0.6); + this.parentApp?.viewport.shake(2, 220); + dungeon.openDoorway(this); + } + + override open() { + super.open(); + this.body.setCollisionMask(collision.types.NO_OBJECT); + // the doors fade out of the frame in a cloud of dust + const app = this.parentApp; + if (app) { + const closed = this.closed; + this.addChild(closed); + new Tween(closed) + .to({ alpha: 0 }, { duration: 450, easing: Tween.Easing.Quadratic.In }) + .onComplete(() => { + this.removeChild(closed); + }) + .start(); + doorDust(app, this.pos.x, this.pos.y, this.depth, this.facing, OPENING); + } + } +} + +// locked until every chest is open, then opened by the dungeon +export class Gate extends Door { + override readonly hint = "The gate is locked"; + + override use(dungeon: Dungeon) { + playAt(SFX.locked, this.spot.x, this.spot.y, 0.8); + dungeon.events.locked(); + } +} + +export class Exit extends Usable { + readonly hint = "Space: climb the stairs"; + + use(dungeon: Dungeon) { + dungeon.events.exit(); + } +} diff --git a/packages/examples/src/examples/isometricDungeon/fx/chests.ts b/packages/examples/src/examples/isometricDungeon/fx/chests.ts new file mode 100644 index 0000000000..ae36022780 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/fx/chests.ts @@ -0,0 +1,179 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { + type Application, + GlowEffect, + Light2d, + ParticleEmitter, + Tween, + type Vector2d, +} from "melonjs"; +import { + CHEST_GLOW_COLOR, + CHEST_GLOW_INTENSITY, + CHEST_GLOW_RADIUS, + PARTICLES, +} from "../constants"; +import type { Usable } from "../entities/usables"; + +const GOLD = [1.0, 0.72, 0.18]; + +let spark: HTMLCanvasElement | undefined; +// soft white dot, tinted by each emitter +export function sparkTexture() { + if (!spark) { + spark = document.createElement("canvas"); + spark.width = spark.height = 16; + const context = spark.getContext("2d"); + if (!context) { + throw new Error("could not create a 2d context"); + } + const fill = context.createRadialGradient(8, 8, 0, 8, 8, 8); + fill.addColorStop(0, "rgba(255, 255, 255, 1)"); + fill.addColorStop(0.35, "rgba(255, 255, 255, 0.8)"); + fill.addColorStop(1, "rgba(255, 255, 255, 0)"); + context.fillStyle = fill; + context.fillRect(0, 0, 16, 16); + } + return spark; +} + +export interface ChestFx { + light: Light2d; + glow: GlowEffect | undefined; + pulse: Tween | undefined; + sparkles: ParticleEmitter; +} + +// a light, a pulsing outline and a few sparks on a closed chest +export function highlightChest( + app: Application, + chest: Usable, + at: Vector2d, +): ChestFx { + const z = chest.depth; + const light = new Light2d( + at.x, + at.y - 16, + CHEST_GLOW_RADIUS, + CHEST_GLOW_RADIUS * 0.6, + CHEST_GLOW_COLOR, + CHEST_GLOW_INTENSITY, + ); + // its soft hole in the darkness shows the chest from afar + light.softness = 1; + // below the sprites, like the torch + app.world.addChild(light, z - 0.5); + + // shader effect, WebGL/WebGPU only; its width is in pixels of the chest's art + let glow: GlowEffect | undefined; + let pulse: Tween | undefined; + if (app.renderer.shaderLanguage !== null) { + const halo = new GlowEffect(app.renderer, { + color: GOLD, + width: 4, + intensity: 0.8, + }); + chest.sprite.addPostEffect(halo); + const level = { value: 0.7 }; + pulse = new Tween(level) + .to( + { value: 1.8 }, + { + duration: 900, + easing: Tween.Easing.Sinusoidal.InOut, + yoyo: true, + repeat: Infinity, + }, + ) + .onUpdate(() => { + halo.setIntensity(level.value); + }) + .start(); + glow = halo; + } + + // emitter position is the top left of the spawn area + const sparkles = new ParticleEmitter(at.x - 18, at.y - 28, { + image: sparkTexture(), + width: 36, + height: 10, + tint: "#ffc23a", + totalParticles: 14, + angle: Math.PI / 2, + angleVariation: 0.4, + speed: 0.6, + speedVariation: 0.25, + minLife: 900, + maxLife: 1600, + minStartScale: 0.7, + maxStartScale: 1.2, + minEndScale: 0, + maxEndScale: 0.1, + textureAdditive: true, + frequency: 110, + // once stopped, it removes itself when the last spark is gone + autoDestroyOnComplete: true, + }); + // lined up with the chest, not with the bottom of the sparks' spawn area, + // so the hero standing in front of it covers them + sparkles.sortOffset = chest.pos.y + 1 - sparkles.pos.y; + sparkles.visibilityLayer = PARTICLES; + app.world.addChild(sparkles, z); + sparkles.streamParticles(); + + return { light, glow, pulse, sparkles }; +} + +// remove the highlight, burst some gold and flare the chest light +export function openChest( + app: Application, + chest: Usable, + at: Vector2d, + fx: ChestFx, +) { + fx.pulse?.stop(); + if (fx.glow) { + chest.sprite.removePostEffect(fx.glow); + } + fx.sparkles.stopStream(); + + const burst = new ParticleEmitter(at.x - 20, at.y - 26, { + image: sparkTexture(), + width: 40, + height: 14, + tint: "#ffb020", + totalParticles: 70, + angle: Math.PI / 2, + angleVariation: 1.0, + speed: 3.4, + speedVariation: 1.2, + gravity: 0.12, + minLife: 650, + maxLife: 1250, + minStartScale: 1.0, + maxStartScale: 1.8, + minEndScale: 0, + maxEndScale: 0.3, + textureAdditive: true, + autoDestroyOnComplete: true, + }); + burst.sortOffset = chest.pos.y + 1 - burst.pos.y; + burst.visibilityLayer = PARTICLES; + app.world.addChild(burst, chest.depth); + burst.burstParticles(); + + const light = fx.light; + const fade = new Tween(light) + .to({ intensity: 0 }, { duration: 700 }) + .onComplete(() => { + app.world.removeChild(light); + }); + new Tween(light) + .to({ intensity: 1.3 }, { duration: 140 }) + .chain(fade) + .start(); +} diff --git a/packages/examples/src/examples/isometricDungeon/fx/doors.ts b/packages/examples/src/examples/isometricDungeon/fx/doors.ts new file mode 100644 index 0000000000..92f4009693 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/fx/doors.ts @@ -0,0 +1,93 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { type Application, ParticleEmitter } from "melonjs"; +import { FRAME_W, PARTICLES } from "../constants"; +import { sparkTexture } from "./chests"; + +// how high the doorway's lintel is above its floor, in pixels +const LINTEL = 66; +// a wall's outward side on screen, below the horizontal: atan(1 / 2) +const OUTWARD = Math.atan2(1, 2); +const DUST = "#d9ccb2"; + +// The dust of a door swinging open, for a door tile at (x, y) whose doorway +// runs from `a` to `b` along its cell edge (Tiled units out of 64) +export function doorDust( + app: Application, + x: number, + y: number, + depth: number, + facing: "N" | "W", + [a, b]: readonly number[], +) { + // the doorway's floor line, a diagonal on screen: from u = a to u = b, + // (u, 224 + u / 2) on a north wall and mirrored on a west one + const left = x + (facing === "N" ? a : FRAME_W - b); + const width = b - a; + const top = y + 224 + a / 2 - 1; + const height = (b - a) / 2; + + // pushed out of the front of the doorway, rising slowly and thinning as + // it spreads. A north wall faces down left on screen, a west one down + // right (a particle's angle is counterclockwise, from the right) + const cloud = new ParticleEmitter(left, top, { + image: sparkTexture(), + width, + height, + tint: DUST, + totalParticles: 30, + angle: facing === "N" ? Math.PI + OUTWARD : -OUTWARD, + angleVariation: 0.8, + speed: 0.6, + speedVariation: 0.35, + gravity: -0.004, + minLife: 700, + maxLife: 1300, + minStartScale: 0.7, + maxStartScale: 1.1, + minEndScale: 2.0, + maxEndScale: 2.8, + autoDestroyOnComplete: true, + }); + + // and grit falling from the lintel, across the part of the arch that is + // open all the way up (the near jamb hides the rest) + const from = a + width / 3; + const grit = new ParticleEmitter( + x + (facing === "N" ? from : FRAME_W - b), + y + 224 + from / 2 - LINTEL, + { + image: sparkTexture(), + width: b - from, + height: (b - from) / 2, + tint: "#8f8170", + totalParticles: 12, + angle: -Math.PI / 2, + angleVariation: 0.3, + speed: 0.2, + speedVariation: 0.15, + gravity: 0.07, + minLife: 450, + maxLife: 750, + minStartScale: 0.2, + maxStartScale: 0.35, + minEndScale: 0.15, + maxEndScale: 0.25, + autoDestroyOnComplete: true, + }, + ); + + for (const emitter of [cloud, grit]) { + // an emitter sorts by the top of its spawn area, down on the floor, + // far below the top of the door's image that everything here sorts + // by: line it up with its door instead, a pixel lower so it is + // drawn over it + emitter.sortOffset = y + 1 - emitter.pos.y; + emitter.visibilityLayer = PARTICLES; + app.world.addChild(emitter, depth); + emitter.burstParticles(); + } +} diff --git a/packages/examples/src/examples/isometricDungeon/fx/dust.ts b/packages/examples/src/examples/isometricDungeon/fx/dust.ts new file mode 100644 index 0000000000..d187f4f30a --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/fx/dust.ts @@ -0,0 +1,76 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { ParticleEmitter } from "melonjs"; +import { PARTICLES } from "../constants"; +import type { Hero } from "../entities/Hero"; +import { sparkTexture } from "./chests"; + +// area around the hero where motes spawn +const MOTES_W = 520; +const MOTES_H = 340; + +// dust floating in the air around the hero. Both emitters use world space, +// so particles stay where they spawned when the hero moves +export class Motes extends ParticleEmitter { + constructor(private readonly hero: Hero) { + super(hero.feetX - MOTES_W / 2, hero.feetY - MOTES_H * 0.75, { + image: sparkTexture(), + width: MOTES_W, + height: MOTES_H, + referenceSpace: "world", + tint: "#ffe6bf", + totalParticles: 40, + angle: 0, + angleVariation: Math.PI, + speed: 0.12, + speedVariation: 0.08, + wind: 0.004, + minLife: 3500, + maxLife: 6500, + minStartScale: 0.15, + maxStartScale: 0.35, + minEndScale: 0, + maxEndScale: 0.05, + textureAdditive: true, + frequency: 120, + }); + this.visibilityLayer = PARTICLES; + this.streamParticles(); + } + + override update(dt: number) { + this.setPosition( + this.hero.feetX - MOTES_W / 2, + this.hero.feetY - MOTES_H * 0.75, + ); + return super.update(dt); + } +} + +// small puff of dust, burst on every footstep +export function footDust() { + const dust = new ParticleEmitter(0, 0, { + image: sparkTexture(), + width: 22, + height: 8, + referenceSpace: "world", + tint: "#b9a07a", + totalParticles: 6, + angle: Math.PI / 2, + angleVariation: 1.2, + speed: 0.45, + speedVariation: 0.25, + gravity: 0.008, + minLife: 350, + maxLife: 650, + minStartScale: 0.6, + maxStartScale: 1.0, + minEndScale: 0.1, + maxEndScale: 0.3, + }); + dust.visibilityLayer = PARTICLES; + return dust; +} diff --git a/packages/examples/src/examples/isometricDungeon/fx/flame.ts b/packages/examples/src/examples/isometricDungeon/fx/flame.ts new file mode 100644 index 0000000000..a3e71df36e --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/fx/flame.ts @@ -0,0 +1,72 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { ParticleEmitter } from "melonjs"; +import { PARTICLES } from "../constants"; + +// a flame particle goes from white hot to deep red as it rises. The flame +// is additive, so a darker tint is a dimmer particle: each color carries +// how faint that stage of the flame is +const COLORS = [ + "#b2a57e", + "#a67f37", + "#995317", + "#722a0a", + "#330d04", + "#0c0301", +]; +const SIZE = 16; + +let dot: HTMLCanvasElement | undefined; +// a soft white dot: the emitter tints it +function flameDot() { + if (!dot) { + dot = document.createElement("canvas"); + dot.width = SIZE; + dot.height = SIZE; + const context = dot.getContext("2d"); + if (!context) { + throw new Error("could not create a 2d context"); + } + const half = SIZE / 2; + const fill = context.createRadialGradient(half, half, 0, half, half, half); + fill.addColorStop(0, "rgba(255, 255, 255, 1)"); + fill.addColorStop(0.45, "rgba(255, 255, 255, 0.6)"); + fill.addColorStop(1, "rgba(255, 255, 255, 0)"); + context.fillStyle = fill; + context.fillRect(0, 0, SIZE, SIZE); + } + return dot; +} + +// a small fire burning at (x, y), streaming as long as it lives +export function torchFlame(x: number, y: number) { + const width = 5; + const height = 2; + const flame = new ParticleEmitter(x - width / 2, y - height / 2, { + image: flameDot(), + tintOverLife: COLORS, + width, + height, + totalParticles: 28, + angle: Math.PI / 2, + angleVariation: 0.22, + speed: 0.55, + speedVariation: 0.15, + // hot air rises faster and faster + gravity: -0.018, + minLife: 380, + maxLife: 700, + minStartScale: 0.5, + maxStartScale: 0.75, + minEndScale: 0.1, + maxEndScale: 0.2, + textureAdditive: true, + frequency: 16, + }); + flame.visibilityLayer = PARTICLES; + flame.streamParticles(); + return flame; +} diff --git a/packages/examples/src/examples/isometricDungeon/fx/sound.ts b/packages/examples/src/examples/isometricDungeon/fx/sound.ts new file mode 100644 index 0000000000..02bbdd49e4 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/fx/sound.ts @@ -0,0 +1,64 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { audio, math } from "melonjs"; + +export const SFX = { + steps: [ + "dungeonStep0", + "dungeonStep1", + "dungeonStep2", + "dungeonStep3", + "dungeonStep4", + ], + door: "dungeonDoor", + creak: "dungeonCreak", + latch: "dungeonLatch", + coins: "dungeonCoins", + locked: "dungeonLocked", + gate: "dungeonGate", + gateCreak: "dungeonGateCreak", + ambience: "dungeonAmbience", +}; + +// The ambience is a 12 s loop. The track loops on a region of exactly that +// length, so it has no gap even where the decoded MP3 is longer: an MP3 +// encoder pads the end of its file, and Safari plays that padding +const AMBIENCE: Record = { loop: [0, 12000] }; + +export function soundResources(base: string) { + const clips = [ + ...SFX.steps, + SFX.door, + SFX.creak, + SFX.latch, + SFX.coins, + SFX.locked, + SFX.gate, + SFX.gateCreak, + ]; + return [ + ...clips.map((name) => { + return { name, type: "audio" as const, src: base }; + }), + { name: SFX.ambience, type: "audio" as const, src: base, sprite: AMBIENCE }, + ]; +} + +// positional sound, see audio.setListener() in PlayStage +export function playAt(name: string, x: number, y: number, volume = 1) { + return audio.play(name, { at: { x, y }, volume }); +} + +let lastStep = -1; +// random footstep, never the same one twice in a row +export function footstep() { + let next = math.random(0, SFX.steps.length); + if (next === lastStep) { + next = (next + 1) % SFX.steps.length; + } + lastStep = next; + audio.play(SFX.steps[next], false, null, 0.28); +} diff --git a/packages/examples/src/examples/isometricDungeon/fx/torch.ts b/packages/examples/src/examples/isometricDungeon/fx/torch.ts new file mode 100644 index 0000000000..8c4b9cebb4 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/fx/torch.ts @@ -0,0 +1,21 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { Noise } from "melonjs"; + +// a smooth random wobble, seeded so it is the same every game +const noise = new Noise({ seed: 7, frequency: 1.5, octaves: 3 }); + +// torch brightness factor, around 1 (about 0.8 to 1.2). Each torch samples +// its own stretch of the noise, from its own start time +export function flicker(seconds: number) { + return 1 + 0.23 * noise.getNoise1d(seconds); +} + +// how much an opening in the darkness grows and shrinks with that flicker, +// around 1 +export function breath(seconds: number) { + return 1 + (flicker(seconds) - 1) * 0.12; +} diff --git a/packages/examples/src/examples/isometricDungeon/resources.ts b/packages/examples/src/examples/isometricDungeon/resources.ts new file mode 100644 index 0000000000..2ebc166202 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/resources.ts @@ -0,0 +1,17 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { soundResources } from "./fx/sound"; + +const base = `${import.meta.env.BASE_URL}assets/isometricDungeon/`; + +export const resources = [ + { name: "dungeon", type: "image", src: `${base}dungeon.png` }, + { name: "dungeon", type: "tmx", src: `${base}dungeon.tmx` }, + { name: "hero", type: "image", src: `${base}hero.png` }, + { name: "hero", type: "json", src: `${base}hero.json` }, + { name: "kenneyLogo", type: "image", src: `${base}kenney-logo.png` }, + ...soundResources(`${base}sfx/`), +]; diff --git a/packages/examples/src/examples/isometricDungeon/scenes/EscapedStage.ts b/packages/examples/src/examples/isometricDungeon/scenes/EscapedStage.ts new file mode 100644 index 0000000000..4e26b20877 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/scenes/EscapedStage.ts @@ -0,0 +1,62 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { + type Application, + audio, + ColorLayer, + event, + input, + Stage, + state, + Text, +} from "melonjs"; +import { VIEW_H, VIEW_W } from "../constants"; + +const STYLE = { + font: "Arial", + bold: true, + fillStyle: "#f6e7c8", + textAlign: "center", +} as const; + +// a separate stage, since state.change() to the current state does nothing +export class EscapedStage extends Stage { + private onKey?: (action: string | undefined) => void; + + override onResetEvent(app: Application, seconds = 0) { + audio.stopTrack(); + audio.tone({ freq: 392, duration: 0.12, gain: 0.2 }); + audio.tone({ freq: 587, duration: 0.18, delay: 0.11, gain: 0.2 }); + audio.tone({ freq: [784, 1176], duration: 0.75, delay: 0.23, gain: 0.2 }); + + app.world.addChild(new ColorLayer("background", "#06040c"), 0); + const minutes = Math.floor(seconds / 60); + const rest = String(Math.floor(seconds % 60)).padStart(2, "0"); + const lines: [string, number, number][] = [ + ["You escaped the dungeon!", 42, VIEW_H / 2 - 70], + [`in ${minutes}:${rest}`, 28, VIEW_H / 2], + ["Press space to go back in", 22, VIEW_H / 2 + 90], + ]; + for (const [text, size, y] of lines) { + app.world.addChild(new Text(VIEW_W / 2, y, { ...STYLE, size, text }), 1); + } + + input.bindKey(input.KEY.SPACE, "again", true); + this.onKey = (action: string | undefined) => { + if (action === "again") { + state.change(state.PLAY); + } + }; + event.on(event.KEYDOWN, this.onKey); + } + + override onDestroyEvent() { + if (this.onKey) { + event.off(event.KEYDOWN, this.onKey); + } + input.unbindKey(input.KEY.SPACE); + } +} diff --git a/packages/examples/src/examples/isometricDungeon/scenes/PlayStage.ts b/packages/examples/src/examples/isometricDungeon/scenes/PlayStage.ts new file mode 100644 index 0000000000..17c44a1e1b --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/scenes/PlayStage.ts @@ -0,0 +1,203 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { + type Application, + audio, + BloomEffect, + type Camera2d, + input, + Light2d, + level, + type Renderer, + Stage, + state, + type TMXLayer, + timer, + type World, +} from "melonjs"; +import { + DARKNESS, + MOTES_Z, + SIGHT_RADIUS, + SIGHT_SQUASH, + TORCH_COLOR, + TORCH_INTENSITY, + TORCH_RADIUS, +} from "../constants"; +import { Dungeon } from "../entities/dungeon"; +import type { Hero } from "../entities/Hero"; +import type { Usable } from "../entities/usables"; +import { footDust, Motes } from "../fx/dust"; +import { SFX } from "../fx/sound"; +import { Hud } from "../ui/Hud"; +import { Minimap } from "../ui/Minimap"; + +const KEYS: [number, string][] = [ + [input.KEY.UP, "up"], + [input.KEY.DOWN, "down"], + [input.KEY.LEFT, "left"], + [input.KEY.RIGHT, "right"], +]; +const ACTION_KEYS = [input.KEY.SPACE, input.KEY.E, input.KEY.ENTER]; + +export class PlayStage extends Stage { + private started = 0; + private bloom: BloomEffect | undefined; + private hud: Hud | undefined; + + override onResetEvent(app: Application) { + this.started = timer.getTime(); + app.world.gravity.set(0, 0); + audio.playTrack(SFX.ambience, { sprite: "loop", volume: 0.6 }); + + for (const [key, action] of KEYS) { + input.bindKey(key, action); + } + for (const key of ACTION_KEYS) { + // locked, so holding the key doesn't repeat the action + input.bindKey(key, "action", true); + } + + level.load("dungeon", { + container: app.world, + onLoaded: () => { + this.build(app); + }, + }); + } + + private build(app: Application) { + // isometric depth: lower on screen means in front + app.world.sortOn = "y"; + // the darkness every light cuts a soft hole in + this.ambientLight.parseCSS(DARKNESS); + + const hero = app.world.getChildByName("hero")[0] as Hero; + const hud = new Hud(); + this.hud = hud; + const dungeon = new Dungeon(app.world, { + chest: (opened, total) => { + hud.chests(opened, total); + if (opened < total) { + hud.say(`${total - opened} to go`, 1200); + } + }, + locked: () => { + hud.say("Locked. Open every chest first."); + }, + gate: () => { + hud.say("Somewhere, a gate creaks open..."); + app.viewport.shake(7, 800); + app.viewport.fadeOut("rgba(255, 214, 150, 0.4)", 700); + audio.play(SFX.gate, false, null, 0.9); + const creak = audio.play(SFX.gateCreak, false, null, 0.8); + audio.rate(SFX.gateCreak, 0.6, creak); + }, + exit: () => { + // nothing else to use while fading out + hero.dungeon = undefined; + const seconds = (timer.getTime() - this.started) / 1000; + state.change(state.GAME_END, false, seconds); + }, + }); + hud.chests(0, dungeon.chests); + hud.say(`Find the ${dungeon.chests} chests`); + + hero.dungeon = dungeon; + // a second camera, drawing the map in a corner + const floor = app.world.getChildByName("floor")[0] as TMXLayer; + this.cameras.set("minimap", new Minimap(hero, dungeon, floor)); + hero.onReach = (target: Usable | undefined) => { + hud.hint(target?.hint ?? ""); + }; + hero.torch = new Light2d( + hero.feetX, + hero.feetY - 50, + TORCH_RADIUS, + TORCH_RADIUS * 0.75, + TORCH_COLOR, + TORCH_INTENSITY, + ); + hero.torch.softness = 1; + // below the sprites, so the light falls on the floor and not on the hero + app.world.addChild(hero.torch, hero.depth - 0.5); + // the darkness opens wider than the torch's glow: a light that only + // cuts it, and draws nothing + hero.sight = new Light2d( + hero.feetX, + hero.feetY - 70, + SIGHT_RADIUS, + SIGHT_RADIUS * SIGHT_SQUASH, + ); + hero.sight.illuminationOnly = true; + hero.sight.softness = 1; + app.world.addChild(hero.sight, hero.depth - 0.5); + app.world.addChild(new Motes(hero), MOTES_Z); + hero.dust = footDust(); + app.world.addChild(hero.dust, hero.depth - 0.25); + + // positional sounds are heard from the hero + audio.setListener(hero); + + // shader effect, WebGL/WebGPU only. The threshold is high enough that + // only the sparks bloom, not the lit floor + if (app.renderer.shaderLanguage !== null) { + this.bloom = new BloomEffect(app.renderer, { + threshold: 0.94, + intensity: 0.6, + radius: 3, + }); + app.viewport.addPostEffect(this.bloom); + } + + app.viewport.follow(hero.focus, app.viewport.AXIS.BOTH, 0.12); + // frame the hero's body rather than the feet + app.viewport.offset.set(0, -70); + } + + // the HUD is not in the world, so the darkness drawn over the world does + // not cover it: it is drawn here, over everything the cameras drew + override draw(renderer: Renderer, world: World) { + super.draw(renderer, world); + const camera = this.cameras.get("default"); + if (this.hud && camera) { + this.hud.preDraw(renderer); + this.hud.draw(renderer, camera); + this.hud.postDraw(renderer); + } + } + + // every camera draws the stage's darkness: keep it off the minimap, so + // the map is lit + override drawLighting( + renderer: Renderer, + camera: Camera2d, + translateX: number, + translateY: number, + ) { + if (!(camera instanceof Minimap)) { + super.drawLighting(renderer, camera, translateX, translateY); + } + } + + override onDestroyEvent(app: Application) { + audio.setListener(null); + // it is in no container, so nothing else frees its texts and logo + this.hud?.reset(); + this.hud = undefined; + // the viewport outlives the stage (removing the effect also destroys it) + if (this.bloom) { + app.viewport.removePostEffect(this.bloom); + this.bloom = undefined; + } + for (const [key] of KEYS) { + input.unbindKey(key); + } + for (const key of ACTION_KEYS) { + input.unbindKey(key); + } + } +} diff --git a/packages/examples/src/examples/isometricDungeon/ui/Hud.ts b/packages/examples/src/examples/isometricDungeon/ui/Hud.ts new file mode 100644 index 0000000000..35c018a410 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/ui/Hud.ts @@ -0,0 +1,96 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { Container, Sprite, Text, Tween, Vector2d } from "melonjs"; +import { VIEW_H, VIEW_W } from "../constants"; + +const STYLE = { + font: "Arial", + bold: true, + fillStyle: "#f6e7c8", + // the stroke is drawn over the fill, so keep it thin + strokeStyle: "#120c06", + lineWidth: 1, +}; + +// the credit in the bottom right corner: the logo, drawn at half the size of +// its image so it stays sharp when the canvas is scaled up +const MARGIN = 16; +const LOGO_SCALE = 0.45; +const LOGO_W = 288 * LOGO_SCALE; +const LOGO_H = 112 * LOGO_SCALE; + +// The HUD, in screen space. It is not in the world: the stage's darkness is +// drawn over the whole world, so the play stage draws the HUD itself, over +// everything the cameras drew. +export class Hud extends Container { + private readonly count: Text; + private readonly prompt: Text; + private readonly message: Text; + private fade?: Tween; + + constructor() { + super(0, 0, VIEW_W, VIEW_H); + this.name = "hud"; + this.count = new Text(24, 20, { ...STYLE, size: 26, text: " " }); + this.prompt = new Text(VIEW_W / 2, VIEW_H - 56, { + ...STYLE, + size: 22, + textAlign: "center", + text: " ", + }); + this.message = new Text(VIEW_W / 2, VIEW_H / 2 - 120, { + ...STYLE, + size: 34, + textAlign: "center", + text: " ", + }); + for (const text of [this.count, this.prompt, this.message]) { + text.floating = true; + this.addChild(text); + } + + // the dungeon and the hero are Kenney's + const logo = new Sprite(VIEW_W - MARGIN, VIEW_H - MARGIN, { + image: "kenneyLogo", + anchorPoint: new Vector2d(1, 1), + }); + logo.scale(LOGO_SCALE); + const credit = new Text( + VIEW_W - MARGIN - LOGO_W / 2, + VIEW_H - MARGIN - LOGO_H - 2, + { + ...STYLE, + size: 14, + textAlign: "center", + textBaseline: "bottom", + text: "assets by", + }, + ); + for (const part of [logo, credit]) { + part.floating = true; + part.setOpacity(0.85); + this.addChild(part); + } + } + + chests(opened: number, total: number) { + this.count.setText(`Chests ${opened} / ${total}`); + } + + hint(text: string) { + this.prompt.setText(text || " "); + } + + // show a message, then fade it out + say(text: string, hold = 2500) { + this.fade?.stop(); + this.message.setText(text); + this.message.setOpacity(1); + this.fade = new Tween(this.message) + .to({ alpha: 0 }, { duration: 800, delay: hold }) + .start(); + } +} diff --git a/packages/examples/src/examples/isometricDungeon/ui/Minimap.ts b/packages/examples/src/examples/isometricDungeon/ui/Minimap.ts new file mode 100644 index 0000000000..bfeee0d010 --- /dev/null +++ b/packages/examples/src/examples/isometricDungeon/ui/Minimap.ts @@ -0,0 +1,241 @@ +/** + * melonJS — Isometric Dungeon example. + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits. + */ +import { + Camera2d, + CanvasRenderTarget, + math, + type Renderer, + type TMXLayer, + type TMXRenderer, + timer, + Vector2d, +} from "melonjs"; +import { PARTICLES, VIEW_W } from "../constants"; +import type { Dungeon } from "../entities/dungeon"; +import type { Hero } from "../entities/Hero"; +import { Chest, Exit, Gate } from "../entities/usables"; + +// size on screen, top right +const W = 260; +const H = 150; +const MARGIN = 16; +// the map at a tenth of its size +const ZOOM = 0.1; +// how far around the hero the map is revealed, in cells +const SIGHT = 5; +// how far the walls rise above their cell, in pixels +const WALL_UP = 110; +// the colour of the map's background: unexplored parts look like nothing +const FOG = "#0b0a10"; +// world pixels per pixel of fog +const FOG_SCALE = 8; + +// the fog over the whole dungeon. A render target's texture stays on the GPU +// until the renderer goes (a new stage does not free it, and nothing public +// does), so there is one, cleared for every game +let fogTarget: CanvasRenderTarget | undefined; +function fogCanvas(width: number, height: number) { + fogTarget ??= new CanvasRenderTarget(width, height, { transparent: true }); + fogTarget.resize(width, height); + return fogTarget; +} + +// A second camera, drawing the dungeon around the hero, small, in a corner +// of the screen. The play stage keeps the darkness off it, so the map is +// lit; a fog hides what the hero has not seen yet. +export class Minimap extends Camera2d { + private readonly fog: CanvasRenderTarget; + private readonly context: CanvasRenderingContext2D; + private fogChanged = true; + private readonly seen = new Set(); + private heroCell = -1; + // the floor layer's renderer, which turns cells into world pixels and + // back, as when it draws the layer + private readonly grid: TMXRenderer; + private readonly cols: number; + private readonly rows: number; + // the whole map, in world pixels + private readonly area: { + left: number; + top: number; + width: number; + height: number; + }; + private readonly point = new Vector2d(); + + constructor( + private readonly hero: Hero, + private readonly dungeon: Dungeon, + floor: TMXLayer, + ) { + super(0, 0, W, H); + this.name = "minimap"; + this.screenX = VIEW_W - W - MARGIN; + this.screenY = MARGIN; + this.autoResize = false; + this.zoom = ZOOM; + // every layer but the particles': at this size they are only noise + this.cullMask = ~PARTICLES; + + this.grid = floor.getRenderer(); + this.cols = floor.cols; + this.rows = floor.rows; + // the map's bounds, as level.load gave the main camera + const { left, top, width, height } = floor.getBounds(); + this.area = { left, top, width, height }; + + this.fog = fogCanvas( + Math.ceil(width / FOG_SCALE), + Math.ceil(height / FOG_SCALE), + ); + this.context = this.fog.context as CanvasRenderingContext2D; + this.context.globalCompositeOperation = "copy"; + this.context.fillStyle = FOG; + this.context.fillRect(0, 0, this.fog.width, this.fog.height); + + // last: these update the camera, which reveals around the hero + this.setBounds(left, top, width, height); + this.setDeadzone(0, 0); + this.follow(hero.focus, this.AXIS.BOTH); + } + + override update(dt: number) { + // the cell under the hero's feet + const at = this.grid.pixelToTileCoords( + this.hero.feetX, + this.hero.feetY, + this.point, + ); + const c = Math.floor(at.x); + const r = Math.floor(at.y); + const cell = r * this.cols + c; + if (cell !== this.heroCell) { + this.heroCell = cell; + this.reveal(c, r); + } + super.update(dt); + // redrawn every frame, for the hero and the markers + return true; + } + + // clear the fog off every cell around (c, r) + private reveal(c: number, r: number) { + const context = this.context; + context.globalCompositeOperation = "destination-out"; + context.fillStyle = "#000"; + for (let dr = -SIGHT; dr <= SIGHT; dr++) { + for (let dc = -SIGHT; dc <= SIGHT; dc++) { + const cc = c + dc; + const rr = r + dr; + const key = rr * this.cols + cc; + if ( + dc * dc + dr * dr > SIGHT * SIGHT || + cc < 0 || + rr < 0 || + cc >= this.cols || + rr >= this.rows || + this.seen.has(key) + ) { + continue; + } + this.seen.add(key); + // the cell's diamond, and the walls standing on it + context.beginPath(); + this.fogPoint(cc, rr + 1, 0, true); + this.fogPoint(cc + 1, rr + 1); + this.fogPoint(cc + 1, rr); + this.fogPoint(cc + 1, rr, WALL_UP); + this.fogPoint(cc, rr, WALL_UP); + this.fogPoint(cc, rr + 1, WALL_UP); + context.closePath(); + context.fill(); + this.fogChanged = true; + } + } + } + + // a cell corner, `up` pixels above the floor, on the fog canvas + private fogPoint(c: number, r: number, up = 0, first = false) { + const p = this.grid.tileToPixelCoords(c, r, this.point); + const fx = (p.x - this.area.left) / FOG_SCALE; + const fy = (p.y - up - this.area.top) / FOG_SCALE; + if (first) { + this.context.moveTo(fx, fy); + } else { + this.context.lineTo(fx, fy); + } + } + + override postDraw(renderer: Renderer) { + // still in the minimap's projection: one unit is one world pixel, from + // its top left corner + const px = 1 / ZOOM; + const ox = this.pos.x; + const oy = this.pos.y; + if (this.fogChanged) { + this.fog.invalidate(renderer); + this.fogChanged = false; + } + renderer.drawImage( + this.fog.canvas, + 0, + 0, + this.fog.width, + this.fog.height, + this.area.left - ox, + this.area.top - oy, + this.area.width, + this.area.height, + ); + + // the chests still closed, seen or not: they are what the hero is + // after. Those out of view sit on the edge, in their direction + const pulse = 1 + 0.25 * Math.sin(timer.getTime() / 180); + renderer.setColor("#ffcf4a"); + for (const usable of this.dungeon.usables) { + if (usable instanceof Chest && !usable.used) { + this.marker( + renderer, + usable.spot.x - ox, + usable.spot.y - oy, + 3 * px * pulse, + ); + } + } + // and the way out, once the gate is open + if (this.dungeon.usables.some((u) => u instanceof Gate && u.used)) { + const exit = this.dungeon.usables.find((u) => u instanceof Exit); + if (exit) { + renderer.setColor("#7dff9a"); + this.marker(renderer, exit.spot.x - ox, exit.spot.y - oy, 4 * px); + } + } + // the hero + const hx = this.hero.feetX - ox; + const hy = this.hero.feetY - oy; + renderer.setColor("#1a120a"); + renderer.fillEllipse(hx, hy, 4 * px, 4 * px); + renderer.setColor("#ffffff"); + renderer.fillEllipse(hx, hy, 2.5 * px, 2.5 * px); + + // frame + renderer.setColor("#c9a86a"); + renderer.lineWidth = 2 * px; + renderer.strokeRect(0, 0, W * px, H * px); + renderer.lineWidth = 1; + + super.postDraw(renderer); + } + + // a dot at (x, y), or on the edge of the minimap when it is out of view + private marker(renderer: Renderer, x: number, y: number, radius: number) { + const inset = 5 / ZOOM; + const cx = math.clamp(x, inset, W / ZOOM - inset); + const cy = math.clamp(y, inset, H / ZOOM - inset); + const r = cx === x && cy === y ? radius : radius * 0.7; + renderer.fillEllipse(cx, cy, r, r); + } +} diff --git a/packages/examples/src/examples/isometricRpg/PlayerEntity.ts b/packages/examples/src/examples/isometricRpg/PlayerEntity.ts deleted file mode 100644 index 4b03b56d3d..0000000000 --- a/packages/examples/src/examples/isometricRpg/PlayerEntity.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * melonJS — isometric Tiled-map RPG demo example. - * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. - * See `packages/examples/LICENSE.md` for full license + asset credits. - */ -import { Body, game, input, Rect, Sprite } from "melonjs"; - -export class PlayerEntity extends Sprite { - constructor(x, y, settings) { - // call the constructor - super( - x, - y, - Object.assign( - { - image: "Blank_Sprite_Sheet", - framewidth: 32, - frameheight: 32, - }, - settings, - ), - ); - - // add a physic body with a diamond as a body shape - this.body = new Body(this, new Rect(16, 16, 16, 16).toIso()); - // walking & jumping speed - this.body.setMaxVelocity(2.5, 2.5); - this.body.setFriction(0.4, 0.4); - - // set the display around our position - game.viewport.follow(this, game.viewport.AXIS.BOTH); - - // enable keyboard - input.bindKey(input.KEY.LEFT, "left"); - input.bindKey(input.KEY.RIGHT, "right"); - input.bindKey(input.KEY.UP, "up"); - input.bindKey(input.KEY.DOWN, "down"); - - // define an additional basic walking animation - this.addAnimation( - "walk_left", - [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], - ); - this.addAnimation( - "walk_right", - [24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35], - ); - this.addAnimation( - "walk_up", - [36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47], - ); - this.addAnimation("walk_down", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - // set default one - this.setCurrentAnimation("walk_down"); - } - - /** - * update the player pos - */ - update(dt) { - if (input.isKeyPressed("left")) { - // update the entity velocity - this.body.force.x = -this.body.maxVel.x; - if (!this.isCurrentAnimation("walk_left")) { - this.setCurrentAnimation("walk_left"); - } - } else if (input.isKeyPressed("right")) { - // update the entity velocity - this.body.force.x = this.body.maxVel.x; - if (!this.isCurrentAnimation("walk_right")) { - this.setCurrentAnimation("walk_right"); - } - } else { - this.body.force.x = 0; - } - if (input.isKeyPressed("up")) { - // update the entity velocity - this.body.force.y = -this.body.maxVel.y; - if (!this.isCurrentAnimation("walk_up") && this.body.vel.x === 0) { - this.setCurrentAnimation("walk_up"); - } - } else if (input.isKeyPressed("down")) { - // update the entity velocity - this.body.force.y = this.body.maxVel.y; - if (!this.isCurrentAnimation("walk_down") && this.body.vel.x === 0) { - this.setCurrentAnimation("walk_down"); - } - } else { - this.body.force.y = 0; - } - - // check if we moved (an "idle" animation would definitely be cleaner) - if (this.body.vel.x !== 0 || this.body.vel.y !== 0) { - super.update(dt); - return true; - } - } - - /** - * colision handler - * (called when colliding with other objects) - */ - onCollision(/*response, other*/) { - // Make all other objects solid - return true; - } -} diff --git a/packages/examples/src/examples/isometricRpg/assets/Blank_Sprite_Sheet_4_2_by_KnightYamato.png b/packages/examples/src/examples/isometricRpg/assets/Blank_Sprite_Sheet_4_2_by_KnightYamato.png deleted file mode 100644 index 6ae08858f9..0000000000 Binary files a/packages/examples/src/examples/isometricRpg/assets/Blank_Sprite_Sheet_4_2_by_KnightYamato.png and /dev/null differ diff --git a/packages/examples/src/examples/isometricRpg/assets/forest.png b/packages/examples/src/examples/isometricRpg/assets/forest.png deleted file mode 100644 index fee2ac7cd6..0000000000 Binary files a/packages/examples/src/examples/isometricRpg/assets/forest.png and /dev/null differ diff --git a/packages/examples/src/examples/isometricRpg/assets/isometric.tmx b/packages/examples/src/examples/isometricRpg/assets/isometric.tmx deleted file mode 100644 index 6199e99154..0000000000 --- a/packages/examples/src/examples/isometricRpg/assets/isometric.tmx +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - DAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAA== - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALIAAACwAAAArwAAAK8AAACvAAAArwAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsgAAAK8AAACvAAAA1QAAAK8AAACwAAAArwAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAlwAAAAAAAAAAAAAAAAAAAAAAAACyAAAArwAAAK8AAACvAAAArwAAAK8AAACvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4AAADHAAAArwAAAK8AAACvAAAAsAAAAK8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4AAACsAAAArAAAAKwAAACsAAAAxwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAA+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwEAAAAAAAAiAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYBAAAAAAAAIQEAAAAAAAAsAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACABAAAAAAAAKwEAAAAAAAA2AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACoBAAAAAAAANQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcBAAAAAAAAAAAAAAAAAAAPAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - - - - - - - - - - - - - - - - - diff --git a/packages/examples/src/examples/isometricRpg/assets/isometric_grass_and_water.png b/packages/examples/src/examples/isometricRpg/assets/isometric_grass_and_water.png deleted file mode 100644 index b342a0cb43..0000000000 Binary files a/packages/examples/src/examples/isometricRpg/assets/isometric_grass_and_water.png and /dev/null differ diff --git a/packages/examples/src/examples/isometricRpg/assets/isometric_staggered.tmx b/packages/examples/src/examples/isometricRpg/assets/isometric_staggered.tmx deleted file mode 100644 index 04d616b692..0000000000 --- a/packages/examples/src/examples/isometricRpg/assets/isometric_staggered.tmx +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GAAAABgAAAAXAAAACwAAABMAAAATAAAADAAAABcAAAAYAAAAGAAAABcAAAAHAAAAAgAAAAIAAAABAAAABAAAAAIAAAACAAAAAwAAAAQAAAAEAAAAAQAAAAMAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAFwAAABcAAAAOAAAAAwAAAAMAAAAIAAAADAAAABgAAAAYAAAAEgAAAAQAAAABAAAAAwAAAAEAAAADAAAAAwAAAAQAAAAEAAAAAQAAAAMAAAABAAAABAAAAAMAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAAPAAAADwAAAAcAAAABAAAABAAAAAMAAAAUAAAAFwAAAAsAAAAHAAAAAgAAAAEAAAADAAAAAQAAAAMAAAAEAAAABAAAAAMAAAACAAAAAgAAAAMAAAAEAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgAAAAQAAAADAAAAAgAAAAIAAAAEAAAAAwAAAAgAAAAMAAAAEgAAAAUAAAAFAAAABQAAAAMAAAADAAAAAQAAAAQAAAACAAAAAQAAAAQAAAAEAAAABAAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAgAAAAEAAAAEAAAAAgAAAAEAAAAEAAAAAgAAAAgAAAAHAAAACAAAAAkAAAAJAAAABgAAAAMAAAADAAAABAAAAAMAAAAEAAAABAAAAAMAAAACAAAABAAAAAMAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAGAAAAAgAAAAEAAAAEAAAABAAAAAEAAAADAAAABAAAAAMAAAAPAAAAGAAAAA4AAAACAAAAAgAAAAEAAAABAAAAAQAAAAEAAAACAAAAAQAAAAQAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAABIAAAABAAAAAgAAAAQAAAADAAAAAwAAAAUAAAAGAAAABQAAAA0AAAAMAAAAEgAAAAQAAAADAAAABAAAAAUAAAADAAAAAQAAAAMAAAAEAAAAAgAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAADgAAAAQAAAACAAAABQAAAAYAAAACAAAACAAAABYAAAAJAAAACQAAAAoAAAAEAAAAAgAAAAUAAAAUAAAAEQAAAAEAAAAFAAAABgAAAAIAAAADAAAABAAAAAQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAAAHAAAAAwAAAAEAAAAIAAAABwAAAAQAAAABAAAACAAAAAwAAAAYAAAAFwAAAAYAAAAEAAAACAAAAAkAAAAYAAAAEQAAAAgAAAAHAAAAAQAAAAMAAAADAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAEAAAAAgAAAAIAAAADAAAAAwAAAAQAAAADAAAAFAAAABgAAAAKAAAAAwAAAAIAAAAPAAAAGAAAABcAAAAGAAAAAwAAAAEAAAAEAAAABAAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAUAAAANAAAABgAAAAEAAAACAAAAAgAAAAEAAAAQAAAAFwAAABcAAAANAAAABAAAAAMAAAAPAAAAFwAAAAoAAAABAAAAAQAAAAMAAAAFAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAEAAAAEAAAABgAAAAKAAAABgAAAAIAAAAEAAAACAAAAAsAAAAXAAAAGAAAABEAAAAFAAAAAwAAAA8AAAAYAAAADQAAAAIAAAAEAAAAEAAAAAYAAAAEAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAEAAAAUAAAAFwAAABgAAAAKAAAABgAAAAMAAAAEAAAABwAAAAwAAAAXAAAAFwAAAAkAAAANAAAABAAAAAwAAAAXAAAABgAAAAEAAAAQAAAACgAAAAQAAAADAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAgAAAAgAAAAMAAAAFwAAABgAAAASAAAABAAAAAMAAAAIAAAACwAAABgAAAAYAAAAFwAAAAYAAAAIAAAAFwAAAAoAAAABAAAACAAAAAsAAAAGAAAAAQAAAAMAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAABAAAAAgAAAAgAAAAMAAAAGAAAAA4AAAAEAAAAAQAAAAIAAAAHAAAAEwAAAAsAAAALAAAACgAAAAMAAAAMAAAAGAAAAAYAAAADAAAABwAAAAcAAAABAAAABAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAQAAAADAAAAAgAAAAgAAAATAAAABwAAAAIAAAACAAAAAwAAAAMAAAAHAAAAFgAAAAwAAAANAAAAFAAAABcAAAAOAAAABAAAAAQAAAAEAAAAAwAAAAEAAAACAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAAAAEAAAAEAAAABAAAAAEAAAACAAAABQAAAAYAAAACAAAAAgAAAAUAAAAQAAAACQAAABgAAAAJAAAAGAAAAAoAAAACAAAAAwAAAAMAAAACAAAABAAAAAMAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAQAAAAMAAAACAAAABQAAAA0AAAAJAAAADgAAAAMAAAAUAAAACQAAABgAAAAYAAAAFwAAABcAAAALAAAABgAAAAIAAAAFAAAAAQAAAAMAAAABAAAAAgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAEAAAADAAAAAwAAAAMAAAAUAAAAFwAAABgAAAASAAAABAAAABQAAAAYAAAAGAAAABcAAAALAAAAGAAAAAoAAAAHAAAAAQAAABQAAAAGAAAAAQAAAAMAAAAEAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAgAAAAQAAAABAAAAAgAAAAgAAAAPAAAAEwAAAAcAAAAIAAAAFwAAABcAAAALAAAAEgAAAA8AAAAYAAAABgAAAAMAAAAQAAAADgAAAAQAAAAEAAAAAwAAAAQAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAAAAgAAAAQAAAAEAAAAAwAAAAIAAAABAAAABAAAAAIAAAATAAAAGAAAABIAAAAHAAAAAwAAAAwAAAAOAAAAAwAAAAgAAAASAAAAAwAAAAIAAAAFAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAABAAAAAgAAAAMAAAADAAAAAwAAAAQAAAACAAAAAQAAABMAAAASAAAAAwAAAAQAAAAIAAAAEgAAAAEAAAABAAAABwAAAAQAAAACAAAAFAAAABEAAAADAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAQAAAAMAAAAEAAAABAAAAAQAAAABAAAABAAAAAcAAAADAAAAAgAAAAMAAAAHAAAAAQAAAAQAAAADAAAAAgAAAAEAAAAIAAAAGAAAAAYAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAACAAAAAwAAAAIAAAABAAAABAAAAAIAAAACAAAAAQAAAAIAAAACAAAAAgAAAAQAAAADAAAAAwAAAAIAAAADAAAAAwAAAAIAAAADAAAAAgAAAA8AAAAKAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAABAAAAAEAAAADAAAAAwAAAAIAAAABAAAABAAAAAIAAAABAAAAAwAAAAEAAAADAAAAAwAAAAQAAAADAAAABAAAAAIAAAABAAAAAgAAAAwAAAAGAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAABAAAAAwAAAAMAAAACAAAAAQAAAAQAAAAIAAAACgAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAADAAAAAQAAAAIAAAAEAAAABAAAAAQAAAABAAAAAwAAAAQAAAAMAAAABgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAABQAAAAEAAAAFAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAEAAAAAwAAAAQAAAABAAAAAgAAAAUAAAACAAAAEAAAAA4AAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAAIAAAADQAAABQAAAAGAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAEAAAABAAAAAEAAAAEAAAAAQAAAAQAAAACAAAAEAAAABEAAAAIAAAACgAAAAIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAABMAAAAJAAAAEgAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAMAAAABAAAAAwAAAAIAAAAEAAAAAwAAABAAAAAXAAAAEQAAAAwAAAAGAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAwAAAASAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAABAAAAAEAAAABAAAAAgAAAAEAAAAEAAAAEAAAABgAAAAXAAAACQAAAA4AAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAAIAAAADgAAAAEAAAAFAAAAAQAAAAEAAAABAAAAAQAAAAQAAAABAAAABQAAAAUAAAAFAAAAAgAAABQAAAAYAAAAFwAAABgAAAASAAAABAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAHAAAAAQAAABQAAAANAAAAAQAAAAEAAAABAAAAAwAAAAUAAAAQAAAACQAAAAkAAAANAAAACAAAABgAAAALAAAAGAAAAAoAAAAEAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAQAAAAFwAAABEAAAABAAAAAQAAAAEAAAAUAAAACQAAABgAAAAXAAAAFwAAAA0AAAATAAAADgAAAAwAAAAXAAAABgAAAAMAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAABQAAAAXAAAAGAAAAA0AAAABAAAAAQAAABAAAAAYAAAAFwAAABcAAAAYAAAAFwAAAA0AAAAHAAAACAAAABgAAAAKAAAAAwAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAQAAAAGAAAABcAAAAYAAAABgAAAAEAAAAUAAAAFwAAABcAAAAYAAAAGAAAABcAAAAXAAAAEQAAAAMAAAAMAAAAFwAAABEAAAACAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAgAAAAXAAAAFwAAABcAAAAKAAAAAQAAABAAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAYAAAABgAAAAgAAAAYAAAAFwAAAA0AAAADAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAEwAAABcAAAAYAAAAFwAAAA0AAAAIAAAAFwAAABcAAAAYAAAAGAAAABcAAAAYAAAAGAAAAAoAAAABAAAAEwAAABcAAAAYAAAABgAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAADAAAABgAAAAYAAAAGAAAAA0AAAAMAAAAGAAAABcAAAAXAAAAFwAAABgAAAAXAAAAGAAAAAYAAAABAAAADwAAABgAAAAKAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAgAAAAXAAAAFwAAABgAAAAXAAAACQAAABcAAAAYAAAAGAAAABcAAAAYAAAAFwAAABcAAAASAAAAAwAAAAEAAAAPAAAAGAAAAAYAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAA8AAAAXAAAAGAAAABgAAAAYAAAAFwAAABcAAAAYAAAAGAAAABgAAAAYAAAAGAAAAA4AAAABAAAABAAAAAIAAAAPAAAADgAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAEwAAABcAAAALAAAAFwAAABcAAAAYAAAAFwAAABcAAAAXAAAAFwAAABgAAAAOAAAAAQAAAAMAAAACAAAAAQAAAAcAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAADwAAABIAAAAMAAAAGAAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAA4AAAACAAAAAQAAAAMAAAACAAAABAAAAAEAAAABAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAHAAAACAAAABcAAAAOAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAAAQAAAAEAAAABAAAAAQAAAAQAAAABAAAAAQAAABQAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAEwAAABIAAAABAAAAAgAAAAQAAAADAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAACAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAHAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAMAAAAFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAABQAAAAUAAAABAAAAAQAAAAEAAAABAAAAAQAAABAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAFAAAABQAAABAAAAAJAAAADQAAAAUAAAABAAAAAQAAAAEAAAAQAAAAGAAAABcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAABQAAAAJAAAACQAAABcAAAAXAAAACQAAAA0AAAABAAAAAQAAABAAAAAYAAAAFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAIAAAAGAAAABgAAAAYAAAAGAAAABcAAAAXAAAABgAAAAEAAAAIAAAAGAAAABcAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - - - diff --git a/packages/examples/src/examples/isometricRpg/createGame.ts b/packages/examples/src/examples/isometricRpg/createGame.ts deleted file mode 100644 index 68c1b0e031..0000000000 --- a/packages/examples/src/examples/isometricRpg/createGame.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * melonJS — isometric Tiled-map RPG demo example. - * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. - * See `packages/examples/LICENSE.md` for full license + asset credits. - */ -import { DebugPanelPlugin } from "@melonjs/debug-plugin"; -import { - Application, - loader, - plugin, - registerTiledObjectClass, - state, -} from "melonjs"; -import { PlayerEntity } from "./PlayerEntity.js"; -import { PlayScreen } from "./play.js"; -import { resources } from "./resources.js"; - -export const createGame = async () => { - const app = new Application(800, 600, { - parent: "screen", - scale: "auto", - }); - await app.init(); - - // register the debug plugin - plugin.register(DebugPanelPlugin, "debugPanel"); - - // set all ressources to be loaded - loader.preload(resources, () => { - // set the "Play/Ingame" Screen Object - state.set(state.PLAY, new PlayScreen()); - - // set the fade transition effect - state.transition("fade", "#FFFFFF", 250); - - // name the player class so the Tiled map can place it - registerTiledObjectClass("mainPlayer", PlayerEntity); - - // switch to PLAY state - state.change(state.PLAY); - }); -}; diff --git a/packages/examples/src/examples/isometricRpg/play.ts b/packages/examples/src/examples/isometricRpg/play.ts deleted file mode 100644 index 0af10208ed..0000000000 --- a/packages/examples/src/examples/isometricRpg/play.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * melonJS — isometric Tiled-map RPG demo example. - * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. - * See `packages/examples/LICENSE.md` for full license + asset credits. - */ -import { event, game, level, Renderable, Stage, Text } from "melonjs"; - -class Selector extends Renderable { - constructor() { - // reference to the main layer - const levelLayer = game.world.getChildByName("level 1")[0]; - - // call the parent constructor using the tile size - super(0, 0, levelLayer.tilewidth / 2, levelLayer.tileheight); - - this.refLayer = levelLayer; - - this.anchorPoint.set(0, 0); - - // configure it as floating - this.floating = true; - - // create a corresponding diamond polygon shape with an isometric projection - this.diamondShape = this.clone().toPolygon().toIso(); - - // currently selected tile - this.currentTile = null; - - // simple system font to display tile coordinates - this.font = new Text(0, 0, { - font: "Arial", - fillStyle: "#FFFFFF", - size: 10, - textAlign: "center", - }); - - // dirty flag to enable/disable redraw - this.dirty = false; - - this.isKinematic = false; - - // subscribe to pointer and viewport move event - this.pointerEvent = event.on(event.POINTERMOVE, this.pointerMove, this); - this.viewportEvent = event.on( - event.VIEWPORT_ONCHANGE, - this.viewportMove, - this, - ); - } - - /** pointer move event callback */ - pointerMove(event) { - const tile = this.refLayer.getTile(event.gameWorldX, event.gameWorldY); - if (tile && tile !== this.currentTile) { - // get the tile x/y world isometric coordinates - this.refLayer - .getRenderer() - .tileToPixelCoords(tile.col, tile.row, this.diamondShape.pos); - // convert thhe diamon shape pos to floating coordinates - game.viewport.worldToLocal( - this.diamondShape.pos.x, - this.diamondShape.pos.y, - this.diamondShape.pos, - ); - // store the current tile - this.currentTile = tile; - } - } - - /** viewport move event callback */ - viewportMove(_pos) { - // invalidate the current tile when the viewport is moved - this.currentTile = null; - } - - /** Update function */ - update(_dt) { - return typeof this.currentTile === "object"; - } - - /** draw function */ - draw(renderer) { - if (this.currentTile) { - // draw our diamond shape - renderer.save(); - renderer.setColor("#FF0000"); - renderer.stroke(this.diamondShape); - - renderer.setColor("#FFFFFF"); - // draw the tile col/row in the middle - this.font.draw( - renderer, - `( ${this.currentTile.col}/${this.currentTile.row} )`, - this.diamondShape.pos.x, - this.diamondShape.pos.y + this.currentTile.height / 2 - 8, - ); - renderer.restore(); - } - } -} - -export class PlayScreen extends Stage { - /** - * action to perform on state change - */ - onResetEvent() { - // disable gravity - game.world.gravity.set(0, 0); - // load a level - level.load("isometric"); - // enable Y-based depth sorting for proper isometric rendering - game.world.sortOn = "y"; - - // display a basic tile selector - game.world.addChild(new Selector()); - - // register on mouse event - // input.registerPointerEvent( - // "pointermove", - // game.viewport, - // (ev) => { - // event.emit(event.POINTERMOVE, ev); - // }, - // false, - // ); - } -} diff --git a/packages/examples/src/examples/isometricRpg/resources.ts b/packages/examples/src/examples/isometricRpg/resources.ts deleted file mode 100644 index 64d6551071..0000000000 --- a/packages/examples/src/examples/isometricRpg/resources.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * melonJS — isometric Tiled-map RPG demo example. - * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. - * See `packages/examples/LICENSE.md` for full license + asset credits. - */ -const base = `${import.meta.env.BASE_URL}assets/isometricRpg/`; - -export const resources = [ - { name: "forest", type: "image", src: `${base}forest.png` }, - { - name: "isometric_grass_and_water", - type: "image", - src: `${base}isometric_grass_and_water.png`, - }, - { - name: "Blank_Sprite_Sheet", - type: "image", - src: `${base}Blank_Sprite_Sheet_4_2_by_KnightYamato.png`, - }, - { name: "isometric", type: "tmx", src: `${base}isometric.tmx` }, - { - name: "isometric_staggered", - type: "tmx", - src: `${base}isometric_staggered.tmx`, - }, -]; diff --git a/packages/examples/src/main.tsx b/packages/examples/src/main.tsx index fc416c190a..2a99115f98 100644 --- a/packages/examples/src/main.tsx +++ b/packages/examples/src/main.tsx @@ -93,9 +93,9 @@ const ExampleWebGPU = lazy(() => default: m.ExampleWebGPU, })), ); -const ExampleIsometricRPG = lazy(() => - import("./examples/isometricRpg/ExampleIsometricRPG").then((m) => ({ - default: m.ExampleIsometricRPG, +const ExampleIsometricDungeon = lazy(() => + import("./examples/isometricDungeon/ExampleIsometricDungeon").then((m) => ({ + default: m.ExampleIsometricDungeon, })), ); const ExampleLights = lazy(() => @@ -417,12 +417,12 @@ const examples: { "The WebGPU backend: sprites, text, blend modes, primitives, clipping and stencil masks.", }, { - component: , - label: "Isometric RPG", - path: "isometric-rpg", - sourceDir: "isometricRpg", + component: , + label: "Isometric Dungeon", + path: "isometric-dungeon", + sourceDir: "isometricDungeon", description: - "Isometric tile map rendering with Tiled map support, camera follow, and NPC entities.", + "A big isometric Tiled dungeon: depth-sorted walls, flickering wall torches, chests, doors and a locked gate.", }, { component: , diff --git a/packages/melonjs/CHANGELOG.md b/packages/melonjs/CHANGELOG.md index b318acad3b..b97b04f807 100644 --- a/packages/melonjs/CHANGELOG.md +++ b/packages/melonjs/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [20.11.0] (melonJS 2) - _unreleased_ +## [20.11.0] (melonJS 2) - _2026-10-27_ ### Added - Effects: colors take an alpha. Every color option and `setColor` takes `[r, g, b]` or `[r, g, b, a]`, the alpha being the color's opacity (`[r, g, b]` stays opaque), `RadialGradientEffect` honours the alpha of its `Color`, and `DropShadowEffect` gains the `setColor` the others have. Four values used to break an effect (WebGL rejected them, and on WebGPU the fourth overwrote the next setting, such as an outline's width); a color of any other length now throws a `TypeError` saying so