Skip to content

Commit

Permalink
fix: [#2574] Isometric text positioning and other isometric positioni…
Browse files Browse the repository at this point in the history
…ng (#448)

Fixes: excaliburjs/Excalibur#2574

Tiled in isometric mode represents objects in a different coordinate space
* Fixes text placement
* Fixes object placement
* Fixes placed tiles
  • Loading branch information
eonarheim authored Aug 9, 2023
1 parent 1e77eef commit 276310f
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 16 deletions.
9 changes: 8 additions & 1 deletion example/cubes.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.8" tiledversion="1.8.0" name="cubes" tilewidth="111" tileheight="128" tilecount="168" columns="21">
<tileset version="1.10" tiledversion="1.10.1" name="cubes" tilewidth="111" tileheight="128" tilecount="168" columns="21">
<image source="assets/isometric-blocks/Tilesheet/tilesheet_complete.png" width="2331" height="1025"/>
<tile id="0" type="Grass"/>
<tile id="1">
<objectgroup draworder="index" id="2">
<object id="1" x="0.666667" y="95">
<polygon points="0,0 54.6667,-29.3333 109.667,0.666667 55.3333,32.3333"/>
</object>
</objectgroup>
</tile>
<tile id="36">
<objectgroup draworder="index" id="2">
<object id="1" x="1.52937" y="95.4681">
Expand Down
34 changes: 32 additions & 2 deletions example/example-isometric.tmx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.8" tiledversion="1.8.0" orientation="isometric" renderorder="right-down" width="20" height="20" tilewidth="111" tileheight="64" infinite="0" backgroundcolor="#176baa" nextlayerid="5" nextobjectid="1">
<map version="1.10" tiledversion="1.10.1" orientation="isometric" renderorder="right-down" width="20" height="20" tilewidth="111" tileheight="64" infinite="0" backgroundcolor="#176baa" nextlayerid="6" nextobjectid="23">
<tileset firstgid="1" source="cubes.tsx"/>
<layer id="1" name="Ground" width="20" height="20">
<data encoding="csv">
Expand All @@ -17,7 +17,7 @@
0,0,1,1,1,1,1,1,1,1,6,6,6,6,1,1,1,1,1,1,
0,0,1,1,1,1,1,1,1,6,6,6,6,6,6,1,1,1,1,1,
0,0,1,1,1,1,1,1,1,6,6,6,6,6,6,1,1,1,1,1,
0,0,1,1,1,1,1,1,1,1,1,6,6,6,6,1,1,1,1,1,
0,0,1,1,1,1,1,1,1,1,1,6,6,6,1,1,1,1,1,1,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
Expand Down Expand Up @@ -103,4 +103,34 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
<objectgroup id="5" name="Object">
<properties>
<property name="excalibur" type="bool" value="true"/>
</properties>
<object id="1" name="helloworld" x="66.4859" y="110.306" width="80" height="17.875">
<text wrap="1">Hello World</text>
</object>
<object id="3" name="3" x="64.5069" y="178.285" width="80" height="17.875">
<text wrap="1">Hello World</text>
</object>
<object id="4" name="b" x="129.044" y="111.747" width="80" height="17.875">
<text wrap="1">Hello World</text>
</object>
<object id="5" name="a" x="127.474" y="173.984" width="80" height="17.875">
<text wrap="1">Hello World</text>
</object>
<object id="13" name="Box" type="BoxCollider" x="380.943" y="63.0571" width="253.583" height="251.949">
<properties>
<property name="CollisionType" value="Fixed"/>
</properties>
</object>
<object id="19" name="Object" gid="2" x="385.514" y="195.82" width="111" height="128">
<properties>
<property name="CollisionType" value="Fixed"/>
</properties>
</object>
<object id="22" name="Camera" type="Camera" x="1176.18" y="1178.49">
<point/>
</object>
</objectgroup>
</map>
97 changes: 85 additions & 12 deletions src/tiled-map-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import {
IsometricEntityComponent,
Animation,
ParallaxComponent,
Tile
Tile,
Text,
BoundingBox
} from 'excalibur';
import { ExcaliburData } from './tiled-types';
import { RawTiledTileset } from "./raw-tiled-tileset";
Expand Down Expand Up @@ -128,8 +130,12 @@ export class TiledMapResource implements Loadable<TiledMap> {
private _addTiledCamera(scene: Scene) {
const camera = this.ex.camera;
if (camera) {
scene.camera.x = camera.x;
scene.camera.y = camera.y;
let cameraPos = vec(camera.x, camera.y);
if (this.isIsometric()) {
cameraPos = this._isoTileToScreenCoords(camera.x, camera.y);
}
scene.camera.x = cameraPos.x;
scene.camera.y = cameraPos.y;
scene.camera.zoom = camera.zoom;
}
}
Expand All @@ -149,9 +155,22 @@ export class TiledMapResource implements Loadable<TiledMap> {
}

if (collider.type === 'box') {
actor.collider.useBoxCollider(collider.width, collider.height, Vector.Zero);
if (this.isIsometric()) {
actor.pos = this._isoTileToScreenCoords(collider.x, collider.y);
const bb = new BoundingBox({
left: 0,
top: 0,
right: collider.width,
bottom: collider.height
});
const points = bb.getPoints().map(p => this._isoTileToScreenCoords(p.x, p.y));
actor.collider.usePolygonCollider(points, Vector.Zero);
} else {
actor.collider.useBoxCollider(collider.width, collider.height, Vector.Zero);
}
}
if (collider.type === 'circle') {
// FIXME no ellipse support yet for colliders in isometric
actor.collider.useCircleCollider(collider.radius);
}
actor.addComponent(new TiledObjectComponent(collider.tiled));
Expand All @@ -163,15 +182,37 @@ export class TiledMapResource implements Loadable<TiledMap> {
}
}

private _isoTileToScreenCoords(x: number, y: number) {
// Transformation sourced from:
// https://discourse.mapeditor.org/t/how-to-get-cartesian-coords-of-objects-from-tileds-isometric-map/4623/3
if (this.isIsometric()) {
const map = this.isoLayers[0];
const tileWidth = map.tileWidth;
const tileHeight = map.tileHeight;
const originX = 0;
const tileY = y / tileHeight;
const tileX = x / tileHeight;
return vec(
(tileX - tileY) * tileWidth / 2 + originX,
(tileX + tileY) * tileHeight / 2);
}
return vec(x, y);
}

private _addTiledText(scene: Scene) {
const excaliburObjectLayers = this.data?.getExcaliburObjects();
if (excaliburObjectLayers.length > 0) {
for (const objectLayer of excaliburObjectLayers) {
const textObjects = objectLayer.getText();
for (const text of textObjects) {
let worldPos = vec(text.x, text.y + ((text.height ?? 0) - (text.text?.pixelSize ?? 0)));
if (this.isIsometric()) {
worldPos = this._isoTileToScreenCoords(text.x, text.y);
}
const label = new Label({
x: text.x,
y: text.y + ((text.height ?? 0) - (text.text?.pixelSize ?? 0)),
anchor: Vector.Zero,
x: worldPos.x,
y: worldPos.y,
text: text.text?.text ?? '',
name: this._getEntityName(text),
font: new Font({
Expand All @@ -185,9 +226,18 @@ export class TiledMapResource implements Loadable<TiledMap> {
label.rotation = text.rotation;
label.color = Color.fromHex(text.text?.color ?? '#000000');
label.collider.set(Shape.Box(text.width ?? 0, text.height ?? 0));
label.body.collisionType = CollisionType.PreventCollision;
label.addComponent(new TiledObjectComponent(text));
scene.add(label);

label.z = this._calculateZIndex(text, objectLayer);
if (this.isIsometric()) {
// The component just needs the tile width/height and row/cols
// all the layers are the same so we can just use the first
const iso = new IsometricEntityComponent(this.isoLayers[0]);
label.addComponent(iso);
iso.elevation = objectLayer.order;
}
scene.add(label);
}
}
}
Expand All @@ -204,29 +254,47 @@ export class TiledMapResource implements Loadable<TiledMap> {
if (collisionTypeProp) {
collisionType = collisionTypeProp.value;
}
let worldPos = vec(tile.x, tile.y);
if (this.isIsometric()) {
worldPos = this._isoTileToScreenCoords(tile.x, tile.y);
}

if (tile.gid) {
const sprite = this.getSpriteForGid(tile.gid);
const colliders = this.getCollidersForGid(tile.gid);
const actor = new Actor({
x: tile.x,
y: tile.y,
x: worldPos.x,
y: worldPos.y,
width: tile.width,
height: tile.height,
anchor: vec(0, 1),
anchor: this.isIsometric() ? vec(.5, 1) : vec(0, 1),
rotation: tile.rotation,
collisionType,
name: this._getEntityName(tile)
});
if (this.isIsometric()) {
const map = this.isoLayers[0];
for (let c of colliders) {
c.offset = vec(-map.tileWidth / 2, -map.tileHeight * 2)
}
}
if (colliders.length) {
actor.collider.clear();
actor.collider.set(new CompositeCollider(colliders));
}
actor.addComponent(new TiledObjectComponent(tile));
actor.graphics.anchor = vec(0, 1);
actor.graphics.anchor = this.isIsometric() ? vec(.5, 1) : vec(0, 1);
// respect tile size on sprite
sprite.destSize.width = tile.width ?? sprite.destSize.width;
sprite.destSize.height = tile.height ?? sprite.destSize.height;
actor.graphics.use(sprite);
if (this.isIsometric()) {
// The component just needs the tile width/height and row/cols
// all the layers are the same so we can just use the first
const iso = new IsometricEntityComponent(this.isoLayers[0]);
actor.addComponent(iso);
iso.elevation = objectLayer.order;
}
scene.add(actor);
actor.z = this._calculateZIndex(tile, objectLayer);
}
Expand Down Expand Up @@ -265,10 +333,11 @@ export class TiledMapResource implements Loadable<TiledMap> {
scene.add(tm);
}

for (const iso of this.isoLayers){
for (const iso of this.isoLayers) {
scene.add(iso);
}

// TODO tiled uses different coordinates for iso and iso staggered
this._addTiledCamera(scene);
this._addTiledColliders(scene);
this._addTiledText(scene);
Expand Down Expand Up @@ -337,6 +406,10 @@ export class TiledMapResource implements Loadable<TiledMap> {
return !!this.data;
}

public isIsometric() {
return !!this.isoLayers.length;
}

public async load(): Promise<TiledMap> {
const mapData = await this._resource.load();
const tiledMap = await this._importMapData(mapData);
Expand Down
2 changes: 1 addition & 1 deletion src/tiled-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class TiledObjectGroup extends TiledEntity {
}

public getObjectsByName(name: string): TiledObject[] {
return this.objects.filter(o => o.name?.toLocaleLowerCase() === name.toLocaleLowerCase());
return this.objects.filter(o => o.name?.toString().toLocaleLowerCase() === name.toString().toLocaleLowerCase());
}

public getPoints(): TiledObject[] {
Expand Down
1 change: 1 addition & 0 deletions test/integration/ex-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ test('Tiled Tests', async (page) => {

const legacyMapTypes = [
'margin.tmx',
// 'isometric',
'v1',
'v1 external tileset',
'v0 gzip',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 276310f

Please sign in to comment.