Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Upgrade Tiled plugin & replace flawed XML parser #477

Merged
merged 101 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
6d3cb4e
Much progress on parsing and upgrade
eonarheim Dec 11, 2023
9a889bc
Read tile layer data into tilemap
eonarheim Dec 13, 2023
f4514d8
More progress on objects
eonarheim Dec 15, 2023
aaf00fe
Objects are starting to work!
eonarheim Dec 15, 2023
6a5c5d0
Progress on objects
eonarheim Dec 15, 2023
9196e44
Missing number prop
eonarheim Dec 16, 2023
21e393f
Fix missing tintcolor
eonarheim Dec 16, 2023
e4847a7
Progress on object layers!
eonarheim Dec 16, 2023
3d280d4
More progress on object layers
eonarheim Dec 16, 2023
21221f6
Object layers so close
eonarheim Dec 16, 2023
23726e1
Clean up some todos
eonarheim Dec 19, 2023
e829fbe
Add entity factories, remove lots of missing implementations
eonarheim Dec 20, 2023
0756d1c
Image layers implemented
eonarheim Dec 20, 2023
b9a2040
Add todos
eonarheim Dec 22, 2023
8c3134c
Rev excalibur
eonarheim Dec 22, 2023
83a20c0
Lots of progress filling out implemenations and cleaning up TODOs
eonarheim Dec 23, 2023
eca2757
Implement excalibur wiring
eonarheim Dec 23, 2023
5e7ac06
Fix tile colliders positioning
eonarheim Dec 23, 2023
5609100
Add objectalignment
eonarheim Dec 23, 2023
772b30e
Add missing components, start on fileloader
eonarheim Dec 23, 2023
c8070ab
Templates nearly complete
eonarheim Dec 24, 2023
d261cd0
Templates totally work!
eonarheim Dec 24, 2023
873b017
remove console.log
eonarheim Dec 24, 2023
5c1ddf5
Templates now work with object factories
eonarheim Dec 25, 2023
32b1f44
Allow factories to not produce an entity
eonarheim Dec 25, 2023
9d8cd1e
Implement pathMap
eonarheim Dec 25, 2023
2c04ab5
External resources refactored with plugable loaders
eonarheim Dec 27, 2023
5545e5f
Implement strict v non-strict mode parsing
eonarheim Dec 27, 2023
7764165
Add tilemap camera strategy
eonarheim Dec 27, 2023
32199d3
Implement headless
eonarheim Dec 27, 2023
75c7b6f
Move old types to deprecated import site
eonarheim Dec 27, 2023
6fa55ab
Add new tilemap for tests
eonarheim Dec 27, 2023
972cc2e
Basic isometric working
eonarheim Dec 28, 2023
f0115ac
rev deps
eonarheim Dec 28, 2023
5f22da1
Isometric is mostly working!
eonarheim Dec 30, 2023
6ab18d8
fix ordering in isometric maps
eonarheim Dec 30, 2023
086d113
Refactor to separate files
eonarheim Dec 30, 2023
9ae66c3
rename
eonarheim Dec 30, 2023
36c6ed3
rename
eonarheim Dec 30, 2023
c5f5d19
Visibility and opacity implemented
eonarheim Dec 30, 2023
3181226
Very close on isometric
eonarheim Dec 30, 2023
8a85d1c
fix parser bug include orthogonal
eonarheim Dec 30, 2023
6880e50
fix template visibility
eonarheim Dec 30, 2023
5f2fdd0
Add simple orthogonal
eonarheim Dec 30, 2023
aa32629
add simple orthogonal
eonarheim Dec 30, 2023
6056b4b
more orthogonal
eonarheim Dec 30, 2023
1e79359
Implement tileset draw offset!
eonarheim Dec 30, 2023
93b5d55
remove todo
eonarheim Dec 30, 2023
d4643e0
Refactor triangulation
eonarheim Dec 30, 2023
2d3b0df
Target alpha build
eonarheim Dec 31, 2023
56d0fac
Update tsconfig
eonarheim Dec 31, 2023
269dfa4
Update readme
eonarheim Dec 31, 2023
4778618
Add query by name
eonarheim Dec 31, 2023
fce625c
Rev to alpha
eonarheim Dec 31, 2023
0cade8e
Map tweaks
eonarheim Dec 31, 2023
6ffd35f
Update readme
eonarheim Dec 31, 2023
30f6cdf
remove integration tests for now
eonarheim Dec 31, 2023
77202e8
Tests are working
eonarheim Jan 1, 2024
c4e496f
Update playwright
eonarheim Jan 1, 2024
6468942
Run unit on CI build
eonarheim Jan 1, 2024
75bfd15
Update snapshots
eonarheim Jan 1, 2024
27ff57e
pin deps
eonarheim Jan 1, 2024
ba09af6
Rev to latest alpha
eonarheim Jan 1, 2024
6491901
inherit props and class
eonarheim Jan 1, 2024
058d045
update snapshots
eonarheim Jan 1, 2024
db4ad17
Rev excalibur to include scene background color
eonarheim Jan 1, 2024
40d676a
A whole mess of tests
eonarheim Jan 2, 2024
c12f31a
Fix test
eonarheim Jan 2, 2024
03fd731
Support infinite tile maps
eonarheim Jan 2, 2024
f1517a9
Implement infinite orthogonal tests
eonarheim Jan 2, 2024
14c1829
Infinite isometric implemented
eonarheim Jan 2, 2024
7aae568
Add missing webkit snapshot
eonarheim Jan 2, 2024
a2d4469
Implement start z-index
eonarheim Jan 2, 2024
93722c0
Update test case
eonarheim Jan 2, 2024
58c3e4c
Reset tilemap
eonarheim Jan 2, 2024
0822850
Remove todo
eonarheim Jan 3, 2024
645e7e3
Remove errant public
eonarheim Jan 3, 2024
f54102d
Remove spec
eonarheim Jan 3, 2024
d55fe5f
Unit tests
eonarheim Jan 3, 2024
986e0f4
fix tests
eonarheim Jan 3, 2024
e818d81
All the tests
eonarheim Jan 3, 2024
b6b1300
Integration test refactoring
eonarheim Jan 3, 2024
1416d89
fix csv parsing bug
eonarheim Jan 3, 2024
10aca79
More integration tests
eonarheim Jan 4, 2024
255f5de
Fix linux snap shots
eonarheim Jan 4, 2024
92d700f
remove only
eonarheim Jan 4, 2024
6aa01aa
fix snapshots
eonarheim Jan 4, 2024
5173763
Fix with solid layers
eonarheim Jan 4, 2024
da3a9b5
fix elevation bug
eonarheim Jan 4, 2024
b98170d
fix orientation bug
eonarheim Jan 4, 2024
b6cda8d
fix solid layers
eonarheim Jan 4, 2024
45ddcaa
theoretically de-flake the tests by not using webpack dev server
eonarheim Jan 4, 2024
059a053
rev excalibur
eonarheim Jan 4, 2024
b01fca9
fix solid
eonarheim Jan 4, 2024
54602f0
fix solid
eonarheim Jan 4, 2024
364b19b
update snapshots
eonarheim Jan 4, 2024
7095abe
update snaps
eonarheim Jan 4, 2024
e1e7386
Mark deprecated
eonarheim Jan 6, 2024
c510327
update npm ignore and readme
eonarheim Jan 6, 2024
c3248f4
update lock
eonarheim Jan 6, 2024
161f019
update npm ignore
eonarheim Jan 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ jobs:
${{ runner.os }}-node-
- run: npm ci
- run: npm run build
- run: npx playwright install
- run: npm run test
27 changes: 27 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Playwright Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ actual-*.png
diff-*.png
coverage/
!files.d.ts
!karma.conf.js
!karma.conf.js
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
*.tiled-session
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ coverage
example
node_cache/*
test
test-results
playwright-report
readme/*
!dist/*
**/webpack.config.js
**/webpack.config.test.js
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
16.20.2
18.14.1
298 changes: 34 additions & 264 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,291 +1,41 @@
# Tiled Plugin for Excalibur.js

This extension adds support for tile maps from all [Tiled map editor](http://mapeditor.org) files in Excalibur. Use the `TiledMapResource` to load and interact with Tiled based maps!
Tiled is a super useful tool for building game levels across the industry. The Tiled plugin for Excalibur offers support for both Orthogonal (standard) and Isometric maps!

![](./readme/example.gif)

## Quickstart

Install using [npm](http://npmjs.org):

```
> npm install @excaliburjs/plugin-tiled
```

## ES2015 (TS/JS)
The current Tiled plugin aims to support *parsing all data* in the Map (.tmx/.tmj), Tileset (.tsx, .tsj.) and Template files (.tx, tj). The plugin however does not support rendering all map types, currently hexagons and isometric staggered are not supported.

The ES2015 `import` syntax is the recommended way to use Excalibur with Excalibur Tiled and is supported through a module loader like [webpack](https://github.com/excaliburjs/example-ts-webpack) or [Parcel](https://parceljs.org) with TypeScript or Babel:
The plugin officially supports the latest version of Tiled that has been published and will warn if you are using an older version. This is because there have been many breaking changes to the Tiled map format over time that are difficult to reconcile.

```ts
import * as ex from 'excalibur';
import { TiledMapResource } from '@excaliburjs/plugin-tiled';

// Create tiled map resource, pointing to static asset path
const tiledMap = new TiledMapResource("/assets/map.tmx");

// Create a loader and reference the map
const loader = new ex.Loader([tiledMap]);

// Start the game (starts the loader)
game.start(loader).then(function() {

console.log("Game loaded");
tiledMap.addTiledMapToScene(game.currentScene);

});
```

For reference, see this [CodeSandbox sample](https://codesandbox.io/s/excalibur-tiled-example-4f83x?fontsize=14) for a Parcel-based game.

## Features

* Parse default Tiled tmx files
- Supports all Tiled compressions zlib, gzip, and zstd
* Parse Tiled exported json files
* Supports external tilesets `.tsx` and `json`
* New TypeScript based object model for working with Tiled data
* Query for layers by property
* Query for objects by property
* Easy helpers to locate Polygons, Polylines, and Text
* Automatic Excalibur wiring for certain Tiled properties and objects:
* Camera
* Colliders
* Solid TileMap Layers
* Tiled Text
* Inserted Tiled Tiles

### Excalibur Wiring
![](./readme/example.gif)

You may opt-in to the Excalibur wiring by calling `addTiledMapToScene(someScene)`
## Installation

```typescript
// After loading tiledMapResource
tiledMapResource.addTiledMapToScene(game.currentScene);
```sh
npm install --save-exact @excaliburjs/plugin-tiled@next
```

* **To exclude object layers `"excalibur-exclude"=true`**. - Objects are included by default and can be retrieved with the following

```typescript
const objects: TiledObjectGroup[] = tiledMapResource.getObjects();
```

* **Camera Object Position & Zoom** - You may set the starting camera position and zoom

![](./readme/camera.png)
- In an object layer with a custom property "excalibur"=true
- **Note** Only the first Camera in the first "excalibur"=true layer will be used
- Create a Tiled "Point" with the Tiled Class "Camera"
- Optionally, to set zoom other than the default of 1.0, create a custom property named "Zoom" with a numeric value

* **Solid layers** - You can mark a particular layers tiles as solid in Tiled

![](./readme/solid.png)
- In the Tiled layer properties, add a custom property named "Solid" with a boolean value `true`
- The presence of a tile in this layer indicates that space is solid, the absence of a tile means it is not solid

* **Colliders Objects** - You may position Excalibur colliders within Tiled
![](./readme/collider.png)
- In an object layer with a custom property "excalibur"=true
- Create a "Circle" (ellipses are not supported) or "Rectangle"
- Set the Tiled Class to "BoxCollider" or "CircleCollider"
- Optionally, to set an Excalibur collision type specify a custom property named "CollisionType" with the value
- "Fixed" (default for colliders) - non-movable object
- "Passive" - triggers events, does not participate in collision
- "Active" - participates in collision and can be pushed around
- "PreventCollision" - all collisions are ignored

* **Tile Custom Colliders** - You can leverage custom colliders in Tiled and they will be pulled into excalibur
![Add a tile collider](./readme/tile-collider.png)
- Must be in a layer marked with a custom property named "solid" with a value `true`
- Colliders are "Fixed"

* **Text** - You may insert excalibur labels within Tiled
![Example of Tiled text](./readme/text.png)
- In an object layer with a custom property "excalibur"=true
- Create a Tiled Text object
- Optionally, you can set the "ZIndex" as a float custom tiled property
- **⚠ A word of caution around fonts ⚠** - fonts are different on every operating system (some may not be available to your user unless you explicitly load them into the page with a font loader). See [here for some detail](https://erikonarheim.com/posts/dont-test-fonts/)

* **Inserted Tile Objects** - You may insert tiles on or off grid in Tiled with inserted tiles
![Example of an inserted Tile](./readme/insertedtile.png)
- In an object layer with a custom property "excalibur"=true
- Create a Tiled inserted Tile
- Optionally, you can set the "ZIndex" as a float custom tiled property
- Optionally, to set an Excalibur collision type specify a custom property named "CollisionType" with the value
- "Fixed" non-movable object
- "Passive" (default for inserted tiles) - triggers events, does not participate in collision
- "Active" - participates in collision and can be pushed around
- "PreventCollision" - all collisions are ignored


* **Tile Animations** - You can leverage tile animations in Tiled and they will be pulled into excalibur
![Tiled Tile Animation Editor](./readme/animations.png)

* **Isometric Tile Maps** - Tiled isometric maps now work without any additional configuration!
![Tiled Isometric](./readme/isometric.png)

* **Parallax Layers** - Tiled parallax layers are now supported
![Tiled Layer Parallax Factor](./readme/parallax.png)

* **Layer offsets** -
![Tiled Layer Offset](./readme/offset.png)

## Not Yet Supported Out of the Box

* Currently Hexagonal maps are not directly supported by Excalibur TileMaps, however the data is still parsed by this plugin and can be used manually by accessing the `RawTiledMap` in `TiledMapResource.data.rawMap` after loading.

* Excalibur Text is limited at the moment and doesn't support Tiled word wrapping or Tiled text alignment other than the default "Left" horizontal, "Top" vertical alignments.

* [Layer tinting](https://doc.mapeditor.org/en/latest/manual/layers/#tinting-layers) is not yet supported

* Image Layers - Tiled image layers are not yet fully supported, but do show up in the `RawTiledMap` so can be used that way. Using inserted [Tile Objects](https://doc.mapeditor.org/fr/latest/manual/layers/#image-layers) is a way to achieve the same effect in a fully supported way.

* Group Layers - Tiled group layers are not yet supported at all, currently layers in a group do not load. Maps with group layers will load all other layers fine.

* Infinite maps - Tiled infinite maps are not yet supported, but do show up in the `RawTiledMap`.

* `RawTiledMap` fully types the Tiled 1.4.3 api, this can be used to write custom code for anything this plugin doesn't yet support.
Create your resource, load it, then add it to your scene!

```typescript
const game = new ex.Engine({...});

import * as ex from 'excalibur';
import { TiledMapResource } from '@excaliburjs/plugin-tiled';

// Create tiled map resource, pointing to static asset path
const tiledMap = new TiledMapResource("/assets/map.tmx");
const tiledMap = new TiledResource('./path/to/map.tmx');

// Create a loader and reference the map
const loader = new ex.Loader([tiledMap]);

game.start(loader).then(function() {

// Access raw data
const rawMap = tiledMap.data.rawMap;

game.start(loader).then(() => {
tiledMap.addToScene(game.currentScene);
});

```



## Webpack Configuration

You will need to modify your webpack configuration to load Tiled JSON files using `file-loader` and then ensure any tilemap images are copied to the same output directory as your bundle, see [this example-ts-webpack branch](https://github.com/excaliburjs/example-ts-webpack/tree/feature/excalibur-tiled-with-webpack) for an example.

## Standalone Script File (JS)

In your HTML file, add a reference **dist/excalibur-tiled.min.js** in your page:

```html
<script type="text/javascript" src="node_modules/excalibur/dist/excalibur.min.js"></script>
<script type="text/javascript" src="node_modules/@excaliburjs/excalibur-tiled/dist/excalibur-tiled.min.js"></script>
```

and then you can use it like this:

```js

// New game
const game = new ex.Engine({ width: 500, height: 400, canvasElementId: "game" });

// Create a new TiledMapResource loadable
const tiledMap = new ex.Plugin.Tiled.TiledMapResource("test.tmx");

// Create a loader and reference the map
const loader = new ex.Loader([tiledMap]);

// Start the game (starts the loader)
game.start(loader).then(function() {

console.log("Game loaded");

tiledMap.addTiledMapToScene(game.currentScene);

});
```

The dist uses a UMD build and will attach itself to the `ex.Plugin.Tiled` global if running in the browser standalone.

## Documentation

The `TiledMapResource` loadable will load the map file you specify along with any referenced tile set assets (images).

### Handling Tiled Paths

The image paths and external tileset paths loaded will be relative to where the exported file was saved.

For example, let's say this is your source working directory structure when you make your Tiled map:

```
work/
- map.tmx
- map.png
- map.tsx
```

The tileset image and/or source are stored next to the TMX file.

So when you export to JSON, say to **map.json**, Tiled will save the paths like this:

```js
{
"tilesets": [
{
"image": "map.png"
},
{
"source": "map.tsx"
}
]
}
```

But for your game, your file structure looks like this:

```
assets/
- maps/map.json
- tx/map.png
- tsx/map.tsx
```

When your game loads and the extension loads your map file (`/assets/maps/map.tmx`), the paths will be loaded **relative** to the map tmx or any tsx file, so they will return 404 responses:

```
GET /assets/maps/map.png -> 404 Not Found
GET /assets/maps/map.tsx -> 404 Not Found
```

If you need to override this behavior, you can set `convertPath` to a custom function that takes two parameters: `originPath` and `relativePath` data.

`originPath` is the path of the original source file (for example the `map.tmx`), and `relativePath` is referenced external fil (for example the `map.tsx`)

```js
// Create a new TiledResource loadable
var map = new ex.Plugin.Tiled.TiledMapResource("map.tmx");

map.convertPath = function (originPath, relativePath) {
return "/assets/tx/" + path;
}
```

That will fix the paths:

```
GET /assets/tx/map.png -> 200 OK
GET /assets/tsx/map.tsx -> 200 OK
```

### Supported Formats

Supports all currently supported Tiled 1.4.3 formats!

* TMX - CSV, Base64 + Compressed (`zlib`, `gzip`, and `zstd`)
* JSON Tiled Export
For information on how to use the plugin visit https://beta.excaliburjs.com/docs/plugin/tiled-plugin

## Contributing

- Built with webpack 4
- Built with webpack 5
- Uses webpack-dev-server

To start development server:
Expand All @@ -302,4 +52,24 @@ To compile only:

To run tests:

npx playwright install
npm test

To update snapshots

* Windows

```powershell
npx playwright test --update-snapshots
```

* Linux for CI

```powershell
docker run --rm --network host -v C:\projects\excalibur-tiled:/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.40.0-jammy /bin/bash
npm install
npx playwright test --update-snapshots
```



Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/formats/assets/isometric/Spritesheet.png
File renamed without changes
Loading
Loading