@codexo/exojs-tilemap 0.15.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -26
- package/dist/esm/ChunkSource.d.ts +33 -0
- package/dist/esm/ChunkSource.d.ts.map +1 -0
- package/dist/esm/ChunkStreamer.d.ts +98 -0
- package/dist/esm/ChunkStreamer.d.ts.map +1 -0
- package/dist/esm/ChunkStreamer.js +201 -0
- package/dist/esm/ChunkStreamer.js.map +1 -0
- package/dist/esm/ImageLayer.d.ts +11 -3
- package/dist/esm/ImageLayer.d.ts.map +1 -0
- package/dist/esm/ImageLayer.js +71 -64
- package/dist/esm/ImageLayer.js.map +1 -1
- package/dist/esm/ImageLayerNode.d.ts +89 -0
- package/dist/esm/ImageLayerNode.d.ts.map +1 -0
- package/dist/esm/ImageLayerNode.js +205 -0
- package/dist/esm/ImageLayerNode.js.map +1 -0
- package/dist/esm/MapObject.d.ts +70 -0
- package/dist/esm/MapObject.d.ts.map +1 -0
- package/dist/esm/MapObject.js +34 -0
- package/dist/esm/MapObject.js.map +1 -0
- package/dist/esm/MapObjectSpawner.d.ts +133 -0
- package/dist/esm/MapObjectSpawner.d.ts.map +1 -0
- package/dist/esm/MapObjectSpawner.js +147 -0
- package/dist/esm/MapObjectSpawner.js.map +1 -0
- package/dist/esm/MapSpawnSession.d.ts +53 -0
- package/dist/esm/MapSpawnSession.d.ts.map +1 -0
- package/dist/esm/MapSpawnSession.js +88 -0
- package/dist/esm/MapSpawnSession.js.map +1 -0
- package/dist/esm/MapWorld.d.ts +134 -0
- package/dist/esm/MapWorld.d.ts.map +1 -0
- package/dist/esm/MapWorld.js +150 -0
- package/dist/esm/MapWorld.js.map +1 -0
- package/dist/esm/MapWorldRuntime.d.ts +182 -0
- package/dist/esm/MapWorldRuntime.d.ts.map +1 -0
- package/dist/esm/MapWorldRuntime.js +261 -0
- package/dist/esm/MapWorldRuntime.js.map +1 -0
- package/dist/esm/ObjectLayer.d.ts +93 -7
- package/dist/esm/ObjectLayer.d.ts.map +1 -0
- package/dist/esm/ObjectLayer.js +184 -167
- package/dist/esm/ObjectLayer.js.map +1 -1
- package/dist/esm/SampledChunkSource.d.ts +43 -0
- package/dist/esm/SampledChunkSource.d.ts.map +1 -0
- package/dist/esm/SampledChunkSource.js +48 -0
- package/dist/esm/SampledChunkSource.js.map +1 -0
- package/dist/esm/TileAnimator.d.ts +12 -2
- package/dist/esm/TileAnimator.d.ts.map +1 -0
- package/dist/esm/TileAnimator.js +154 -165
- package/dist/esm/TileAnimator.js.map +1 -1
- package/dist/esm/TileChunk.d.ts +30 -5
- package/dist/esm/TileChunk.d.ts.map +1 -0
- package/dist/esm/TileChunk.js +190 -188
- package/dist/esm/TileChunk.js.map +1 -1
- package/dist/esm/TileChunkNode.d.ts +18 -3
- package/dist/esm/TileChunkNode.d.ts.map +1 -0
- package/dist/esm/TileChunkNode.js +104 -85
- package/dist/esm/TileChunkNode.js.map +1 -1
- package/dist/esm/TileLayer.d.ts +104 -24
- package/dist/esm/TileLayer.d.ts.map +1 -0
- package/dist/esm/TileLayer.js +511 -469
- package/dist/esm/TileLayer.js.map +1 -1
- package/dist/esm/TileLayerNode.d.ts +52 -11
- package/dist/esm/TileLayerNode.d.ts.map +1 -0
- package/dist/esm/TileLayerNode.js +224 -166
- package/dist/esm/TileLayerNode.js.map +1 -1
- package/dist/esm/TileMap.d.ts +96 -31
- package/dist/esm/TileMap.d.ts.map +1 -0
- package/dist/esm/TileMap.js +401 -357
- package/dist/esm/TileMap.js.map +1 -1
- package/dist/esm/TileMapBand.d.ts +42 -29
- package/dist/esm/TileMapBand.d.ts.map +1 -0
- package/dist/esm/TileMapBand.js +151 -158
- package/dist/esm/TileMapBand.js.map +1 -1
- package/dist/esm/TileMapNode.d.ts +34 -18
- package/dist/esm/TileMapNode.d.ts.map +1 -0
- package/dist/esm/TileMapNode.js +122 -109
- package/dist/esm/TileMapNode.js.map +1 -1
- package/dist/esm/TileMapView.d.ts +108 -47
- package/dist/esm/TileMapView.d.ts.map +1 -0
- package/dist/esm/TileMapView.js +385 -338
- package/dist/esm/TileMapView.js.map +1 -1
- package/dist/esm/TileSet.d.ts +15 -6
- package/dist/esm/TileSet.d.ts.map +1 -0
- package/dist/esm/TileSet.js +161 -181
- package/dist/esm/TileSet.js.map +1 -1
- package/dist/esm/WangSet.d.ts +5 -4
- package/dist/esm/WangSet.d.ts.map +1 -0
- package/dist/esm/WangSet.js +70 -78
- package/dist/esm/WangSet.js.map +1 -1
- package/dist/esm/WorkerSampledChunkSource.d.ts +95 -0
- package/dist/esm/WorkerSampledChunkSource.d.ts.map +1 -0
- package/dist/esm/WorkerSampledChunkSource.js +117 -0
- package/dist/esm/WorkerSampledChunkSource.js.map +1 -0
- package/dist/esm/autoTile.d.ts +10 -5
- package/dist/esm/autoTile.d.ts.map +1 -0
- package/dist/esm/autoTile.js +164 -205
- package/dist/esm/autoTile.js.map +1 -1
- package/dist/esm/chunkGeometry.d.ts +8 -7
- package/dist/esm/chunkGeometry.d.ts.map +1 -0
- package/dist/esm/chunkGeometry.js +83 -94
- package/dist/esm/chunkGeometry.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +26 -15
- package/dist/esm/nodeBounds.d.ts +20 -0
- package/dist/esm/nodeBounds.d.ts.map +1 -0
- package/dist/esm/nodeBounds.js +40 -0
- package/dist/esm/nodeBounds.js.map +1 -0
- package/dist/esm/pixelSnap.d.ts +4 -3
- package/dist/esm/pixelSnap.d.ts.map +1 -0
- package/dist/esm/pixelSnap.js +18 -16
- package/dist/esm/pixelSnap.js.map +1 -1
- package/dist/esm/public.d.ts +23 -3
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/tileCollision.d.ts +190 -0
- package/dist/esm/tileCollision.d.ts.map +1 -0
- package/dist/esm/tileCollision.js +367 -0
- package/dist/esm/tileCollision.js.map +1 -0
- package/dist/esm/tileWord.d.ts +12 -0
- package/dist/esm/tileWord.d.ts.map +1 -0
- package/dist/esm/tileWord.js +16 -0
- package/dist/esm/tileWord.js.map +1 -0
- package/dist/esm/tilemapExtension.d.ts +4 -3
- package/dist/esm/tilemapExtension.d.ts.map +1 -0
- package/dist/esm/tilemapExtension.js +47 -47
- package/dist/esm/tilemapExtension.js.map +1 -1
- package/dist/esm/tilemapSerializers.d.ts +5 -4
- package/dist/esm/tilemapSerializers.d.ts.map +1 -0
- package/dist/esm/tilemapSerializers.js +48 -41
- package/dist/esm/tilemapSerializers.js.map +1 -1
- package/dist/esm/types.d.ts +34 -18
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +111 -120
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts +50 -2
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts.map +1 -0
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.js +366 -332
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.js.map +1 -1
- package/dist/esm/webgl2/shaders/tile-chunk.frag.js +6 -0
- package/dist/esm/webgl2/shaders/tile-chunk.frag.js.map +1 -0
- package/dist/esm/webgl2/shaders/tile-chunk.vert.js +6 -0
- package/dist/esm/webgl2/shaders/tile-chunk.vert.js.map +1 -0
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts +70 -4
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts.map +1 -0
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.js +496 -383
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.js.map +1 -1
- package/dist/esm/webgpu/shaders/tile-chunk.wgsl.js +6 -0
- package/dist/esm/webgpu/shaders/tile-chunk.wgsl.js.map +1 -0
- package/package.json +8 -15
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/register.d.ts +0 -1
- package/dist/esm/register.js +0 -25
- package/dist/esm/register.js.map +0 -1
package/dist/esm/ImageLayer.js
CHANGED
|
@@ -1,67 +1,74 @@
|
|
|
1
|
+
//#region src/ImageLayer.ts
|
|
1
2
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
3
|
+
* A data-only image layer: a single image (texture + resolved URL) placed as a
|
|
4
|
+
* background or foreground layer. The layer itself holds no scene-graph state -
|
|
5
|
+
* it is rendered by an {@link import('./ImageLayerNode').ImageLayerNode}, which
|
|
6
|
+
* {@link import('./TileMapNode').TileMapNode} and
|
|
7
|
+
* {@link import('./TileMapView').TileMapView} generate and interleave with tile
|
|
8
|
+
* layers by the map's combined document order
|
|
9
|
+
* ({@link import('./TileMap').TileMap.renderableLayers}).
|
|
10
|
+
*
|
|
11
|
+
* Parallax, opacity, tint, offset, and repeat flags are carried from the
|
|
12
|
+
* source Tiled map.
|
|
13
|
+
*
|
|
14
|
+
* @advanced
|
|
15
|
+
*/
|
|
16
|
+
var ImageLayer = class {
|
|
17
|
+
/** Layer-kind discriminant. */
|
|
18
|
+
kind = "image";
|
|
19
|
+
/** Layer id (unique within the map). */
|
|
20
|
+
id;
|
|
21
|
+
/** Layer name. */
|
|
22
|
+
name;
|
|
23
|
+
/** Layer class string. */
|
|
24
|
+
class;
|
|
25
|
+
/** Resolved URL of the layer image. */
|
|
26
|
+
image;
|
|
27
|
+
/** Loaded texture for the image, or `null` if unavailable. */
|
|
28
|
+
texture;
|
|
29
|
+
/** Whether the layer is visible. */
|
|
30
|
+
visible;
|
|
31
|
+
/** Layer opacity in `[0, 1]`. */
|
|
32
|
+
opacity;
|
|
33
|
+
/** Layer pixel offset X. */
|
|
34
|
+
offsetX;
|
|
35
|
+
/** Layer pixel offset Y. */
|
|
36
|
+
offsetY;
|
|
37
|
+
/** Horizontal parallax factor. */
|
|
38
|
+
parallaxX;
|
|
39
|
+
/** Vertical parallax factor. */
|
|
40
|
+
parallaxY;
|
|
41
|
+
/** Uniform scale applied around the layer origin during parallax rendering. */
|
|
42
|
+
parallaxScale;
|
|
43
|
+
/** Tint colour as `0xRRGGBB`, or `null`. */
|
|
44
|
+
tintColor;
|
|
45
|
+
/** Whether the image repeats horizontally. */
|
|
46
|
+
repeatX;
|
|
47
|
+
/** Whether the image repeats vertically. */
|
|
48
|
+
repeatY;
|
|
49
|
+
/** Immutable layer properties. */
|
|
50
|
+
properties;
|
|
51
|
+
constructor(options) {
|
|
52
|
+
this.id = options.id;
|
|
53
|
+
this.name = options.name ?? "";
|
|
54
|
+
this.class = options.class ?? "";
|
|
55
|
+
this.image = options.image;
|
|
56
|
+
this.texture = options.texture ?? null;
|
|
57
|
+
this.visible = options.visible ?? true;
|
|
58
|
+
this.opacity = options.opacity ?? 1;
|
|
59
|
+
this.offsetX = options.offsetX ?? 0;
|
|
60
|
+
this.offsetY = options.offsetY ?? 0;
|
|
61
|
+
this.parallaxX = options.parallaxX ?? 1;
|
|
62
|
+
this.parallaxY = options.parallaxY ?? 1;
|
|
63
|
+
this.parallaxScale = options.parallaxScale ?? 1;
|
|
64
|
+
if (!Number.isFinite(this.parallaxScale) || this.parallaxScale <= 0) throw new Error("ImageLayer parallaxScale must be a positive finite number.");
|
|
65
|
+
this.tintColor = options.tintColor ?? null;
|
|
66
|
+
this.repeatX = options.repeatX ?? false;
|
|
67
|
+
this.repeatY = options.repeatY ?? false;
|
|
68
|
+
this.properties = options.properties ? Object.freeze({ ...options.properties }) : Object.freeze({});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
65
71
|
|
|
72
|
+
//#endregion
|
|
66
73
|
export { ImageLayer };
|
|
67
|
-
//# sourceMappingURL=ImageLayer.js.map
|
|
74
|
+
//# sourceMappingURL=ImageLayer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageLayer.js","sources":["
|
|
1
|
+
{"version":3,"file":"ImageLayer.js","names":[],"sources":["../../src/ImageLayer.ts"],"sourcesContent":["import type { Texture } from '@codexo/exojs';\n\nimport type { TileProperties } from './types';\n\n/** Construction options for an {@link ImageLayer}. */\nexport interface ImageLayerOptions {\n /** Layer id (unique within the map). */\n readonly id: number;\n /** Layer name. */\n readonly name?: string;\n /** Layer class string. */\n readonly class?: string;\n /** Resolved image URL. */\n readonly image: string;\n /** Loaded texture for the image, or `null` if unavailable. */\n readonly texture?: Texture | null;\n /** Whether the layer is visible. Default `true`. */\n readonly visible?: boolean;\n /** Layer opacity in `[0, 1]`. Default `1`. */\n readonly opacity?: number;\n /** Layer pixel offset X. Default `0`. */\n readonly offsetX?: number;\n /** Layer pixel offset Y. Default `0`. */\n readonly offsetY?: number;\n /** Horizontal parallax factor. Default `1`. */\n readonly parallaxX?: number;\n /** Vertical parallax factor. Default `1`. */\n readonly parallaxY?: number;\n /** Uniform parallax scale around the layer origin. Default `1`. */\n readonly parallaxScale?: number;\n /** Tint colour as `0xRRGGBB`, or `null`. Default `null`. */\n readonly tintColor?: number | null;\n /** Whether the image repeats horizontally. Default `false`. */\n readonly repeatX?: boolean;\n /** Whether the image repeats vertically. Default `false`. */\n readonly repeatY?: boolean;\n /** Layer properties (copied and frozen). */\n readonly properties?: TileProperties;\n}\n\n/**\n * A data-only image layer: a single image (texture + resolved URL) placed as a\n * background or foreground layer. The layer itself holds no scene-graph state -\n * it is rendered by an {@link import('./ImageLayerNode').ImageLayerNode}, which\n * {@link import('./TileMapNode').TileMapNode} and\n * {@link import('./TileMapView').TileMapView} generate and interleave with tile\n * layers by the map's combined document order\n * ({@link import('./TileMap').TileMap.renderableLayers}).\n *\n * Parallax, opacity, tint, offset, and repeat flags are carried from the\n * source Tiled map.\n *\n * @advanced\n */\nexport class ImageLayer {\n /** Layer-kind discriminant. */\n public readonly kind = 'image' as const;\n\n /** Layer id (unique within the map). */\n public readonly id: number;\n /** Layer name. */\n public readonly name: string;\n /** Layer class string. */\n public readonly class: string;\n /** Resolved URL of the layer image. */\n public readonly image: string;\n /** Loaded texture for the image, or `null` if unavailable. */\n public readonly texture: Texture | null;\n /** Whether the layer is visible. */\n public readonly visible: boolean;\n /** Layer opacity in `[0, 1]`. */\n public readonly opacity: number;\n /** Layer pixel offset X. */\n public readonly offsetX: number;\n /** Layer pixel offset Y. */\n public readonly offsetY: number;\n /** Horizontal parallax factor. */\n public readonly parallaxX: number;\n /** Vertical parallax factor. */\n public readonly parallaxY: number;\n /** Uniform scale applied around the layer origin during parallax rendering. */\n public readonly parallaxScale: number;\n /** Tint colour as `0xRRGGBB`, or `null`. */\n public readonly tintColor: number | null;\n /** Whether the image repeats horizontally. */\n public readonly repeatX: boolean;\n /** Whether the image repeats vertically. */\n public readonly repeatY: boolean;\n /** Immutable layer properties. */\n public readonly properties: TileProperties;\n\n public constructor(options: ImageLayerOptions) {\n this.id = options.id;\n this.name = options.name ?? '';\n this.class = options.class ?? '';\n this.image = options.image;\n this.texture = options.texture ?? null;\n this.visible = options.visible ?? true;\n this.opacity = options.opacity ?? 1;\n this.offsetX = options.offsetX ?? 0;\n this.offsetY = options.offsetY ?? 0;\n this.parallaxX = options.parallaxX ?? 1;\n this.parallaxY = options.parallaxY ?? 1;\n this.parallaxScale = options.parallaxScale ?? 1;\n if (!Number.isFinite(this.parallaxScale) || this.parallaxScale <= 0) {\n throw new Error('ImageLayer parallaxScale must be a positive finite number.');\n }\n this.tintColor = options.tintColor ?? null;\n this.repeatX = options.repeatX ?? false;\n this.repeatY = options.repeatY ?? false;\n this.properties = options.properties ? Object.freeze({ ...options.properties }) : Object.freeze({});\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsDA,IAAa,aAAb,MAAwB;;CAEtB,AAAgB,OAAO;;CAGvB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;;CAEhB,AAAgB;CAEhB,AAAO,YAAY,SAA4B;EAC7C,KAAK,KAAK,QAAQ;EAClB,KAAK,OAAO,QAAQ,QAAQ;EAC5B,KAAK,QAAQ,QAAQ,SAAS;EAC9B,KAAK,QAAQ,QAAQ;EACrB,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,YAAY,QAAQ,aAAa;EACtC,KAAK,YAAY,QAAQ,aAAa;EACtC,KAAK,gBAAgB,QAAQ,iBAAiB;EAC9C,IAAI,CAAC,OAAO,SAAS,KAAK,aAAa,KAAK,KAAK,iBAAiB,GAChE,MAAM,IAAI,MAAM,4DAA4D;EAE9E,KAAK,YAAY,QAAQ,aAAa;EACtC,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,aAAa,QAAQ,aAAa,OAAO,OAAO,EAAE,GAAG,QAAQ,WAAW,CAAC,IAAI,OAAO,OAAO,CAAC,CAAC;CACpG;AACF"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Container } from '@codexo/exojs';
|
|
2
|
+
import { PixelSnapMode, type RenderPlanBuilder } from '@codexo/exojs/renderer-sdk';
|
|
3
|
+
import type { ImageLayer } from './ImageLayer';
|
|
4
|
+
/**
|
|
5
|
+
* A scene node that renders one Tiled {@link ImageLayer} as a single
|
|
6
|
+
* {@link RepeatingSprite} wrapped in a {@link Container}.
|
|
7
|
+
*
|
|
8
|
+
* The layer's pixel `offset` becomes this node's position; `parallax` is
|
|
9
|
+
* resolved per frame in {@link _collectContent} by patching the node position
|
|
10
|
+
* against the camera centre (exactly like {@link import('./TileLayerNode').TileLayerNode}),
|
|
11
|
+
* then restoring it. When `repeatX`/`repeatY` is set the wrapped sprite is
|
|
12
|
+
* grown each frame to cover the visible view with a period-aligned origin, so
|
|
13
|
+
* the pattern tiles seamlessly and scrolls with parallax. A repeat axis or a
|
|
14
|
+
* `parallax` factor other than `1` both make this node's rendered position
|
|
15
|
+
* and/or size camera-dependent at collect time, so its static bounds cannot be
|
|
16
|
+
* culled against - such a node opts out of view culling (`cullable = false`).
|
|
17
|
+
*
|
|
18
|
+
* `visible`, `opacity`, and `tintColor` are applied **once at construction**:
|
|
19
|
+
* {@link ImageLayer} is immutable, so there is nothing to re-sync per frame.
|
|
20
|
+
* A layer whose `texture` is `null` (image failed to load / not yet available)
|
|
21
|
+
* produces an empty node with no children - it occupies its offset in the
|
|
22
|
+
* scene graph but draws nothing.
|
|
23
|
+
*
|
|
24
|
+
* The node references - but never owns - the {@link ImageLayer} and its
|
|
25
|
+
* Loader-owned texture: {@link destroy} frees the wrapped sprite but leaves the
|
|
26
|
+
* layer and texture intact. Image-layer nodes are band-selectable exactly like
|
|
27
|
+
* tile-layer nodes: list the image layer in a
|
|
28
|
+
* {@link import('./TileMapView').TileMapViewOptions.bands} definition and the
|
|
29
|
+
* band stacks it by the map's combined document order, or parent an unbanded
|
|
30
|
+
* node into the scene directly wherever the image belongs in draw order.
|
|
31
|
+
*
|
|
32
|
+
* For a `repeatX`/`repeatY` node, bounds and hit-test queries reflect the
|
|
33
|
+
* coverage geometry computed by the last {@link _collectContent} call (since
|
|
34
|
+
* that geometry is recomputed per collect, not maintained incrementally),
|
|
35
|
+
* which is also why such a node force-disables `cullable`. A repeating or
|
|
36
|
+
* parallax node also re-sizes its wrapped sprite as the camera crosses period
|
|
37
|
+
* boundaries, which content-dirties an enclosing retained group the same way
|
|
38
|
+
* a streamed layer does - give it the same treatment (its own
|
|
39
|
+
* `RetainedContainer`, or none).
|
|
40
|
+
*
|
|
41
|
+
* @advanced
|
|
42
|
+
*/
|
|
43
|
+
export declare class ImageLayerNode extends Container {
|
|
44
|
+
private readonly _layer;
|
|
45
|
+
private readonly _sprite;
|
|
46
|
+
private readonly _baseOffsetX;
|
|
47
|
+
private readonly _baseOffsetY;
|
|
48
|
+
private readonly _imageWidth;
|
|
49
|
+
private readonly _imageHeight;
|
|
50
|
+
private _pixelSnapMode;
|
|
51
|
+
private _covViewX;
|
|
52
|
+
private _covViewY;
|
|
53
|
+
private _covViewWidth;
|
|
54
|
+
private _covViewHeight;
|
|
55
|
+
private _covOriginX;
|
|
56
|
+
private _covOriginY;
|
|
57
|
+
constructor(layer: ImageLayer);
|
|
58
|
+
/** The runtime layer this node renders. */
|
|
59
|
+
get layer(): ImageLayer;
|
|
60
|
+
/**
|
|
61
|
+
* Render-only pixel-snap mode forwarded to the wrapped {@link RepeatingSprite}
|
|
62
|
+
* (which snaps its rendered origin, and - in `PixelSnapMode.Geometry` mode for
|
|
63
|
+
* axis-aligned transforms - its repeat-segment boundaries, to the render
|
|
64
|
+
* target's device-pixel grid). Purely visual: the layer offset, parallax, and
|
|
65
|
+
* repeat coverage are never changed.
|
|
66
|
+
*
|
|
67
|
+
* For a `null`-texture layer there is no drawable to forward to, so the mode
|
|
68
|
+
* is simply stored. Setting the current value is a no-op; an invalid value
|
|
69
|
+
* throws and leaves the prior mode unchanged.
|
|
70
|
+
*
|
|
71
|
+
* @default PixelSnapMode.None
|
|
72
|
+
* @stable
|
|
73
|
+
*/
|
|
74
|
+
get pixelSnapMode(): PixelSnapMode;
|
|
75
|
+
set pixelSnapMode(mode: PixelSnapMode);
|
|
76
|
+
/** @internal */
|
|
77
|
+
protected _collectContent(builder: RenderPlanBuilder): void;
|
|
78
|
+
destroy(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Grow the wrapped sprite along each repeating axis so it covers the visible
|
|
81
|
+
* view, with a period-aligned local origin so the pattern offset stays zero
|
|
82
|
+
* (seamless tiling). Non-repeating axes keep the natural image size at local
|
|
83
|
+
* `0`. Reads `this.x`/`this.y` after the parallax patch, so the pattern anchor
|
|
84
|
+
* follows the parallax-shifted origin. Skips the rebuild when neither the view
|
|
85
|
+
* span nor the patched origin changed since the last frame.
|
|
86
|
+
*/
|
|
87
|
+
private _updateRepeatCoverage;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=ImageLayerNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageLayerNode.d.ts","sourceRoot":"","sources":["../../src/ImageLayerNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,cAAc,CAAqC;IAI3D,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,WAAW,CAAc;gBAEd,KAAK,EAAE,UAAU;IAgDpC,2CAA2C;IAC3C,IAAW,KAAK,IAAI,UAAU,CAE7B;IAED;;;;;;;;;;;;;OAaG;IACH,IAAW,aAAa,IAAI,aAAa,CAExC;IAED,IAAW,aAAa,CAAC,IAAI,EAAE,aAAa,EAW3C;IAED,gBAAgB;cACG,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IA8CpD,OAAO,IAAI,IAAI;IAQ/B;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;CA4D9B"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { assertPixelSnapMode } from "./pixelSnap.js";
|
|
2
|
+
import { PixelSnapMode } from "@codexo/exojs/renderer-sdk";
|
|
3
|
+
import { Container, RepeatingSprite } from "@codexo/exojs";
|
|
4
|
+
|
|
5
|
+
//#region src/ImageLayerNode.ts
|
|
6
|
+
/**
|
|
7
|
+
* A scene node that renders one Tiled {@link ImageLayer} as a single
|
|
8
|
+
* {@link RepeatingSprite} wrapped in a {@link Container}.
|
|
9
|
+
*
|
|
10
|
+
* The layer's pixel `offset` becomes this node's position; `parallax` is
|
|
11
|
+
* resolved per frame in {@link _collectContent} by patching the node position
|
|
12
|
+
* against the camera centre (exactly like {@link import('./TileLayerNode').TileLayerNode}),
|
|
13
|
+
* then restoring it. When `repeatX`/`repeatY` is set the wrapped sprite is
|
|
14
|
+
* grown each frame to cover the visible view with a period-aligned origin, so
|
|
15
|
+
* the pattern tiles seamlessly and scrolls with parallax. A repeat axis or a
|
|
16
|
+
* `parallax` factor other than `1` both make this node's rendered position
|
|
17
|
+
* and/or size camera-dependent at collect time, so its static bounds cannot be
|
|
18
|
+
* culled against - such a node opts out of view culling (`cullable = false`).
|
|
19
|
+
*
|
|
20
|
+
* `visible`, `opacity`, and `tintColor` are applied **once at construction**:
|
|
21
|
+
* {@link ImageLayer} is immutable, so there is nothing to re-sync per frame.
|
|
22
|
+
* A layer whose `texture` is `null` (image failed to load / not yet available)
|
|
23
|
+
* produces an empty node with no children - it occupies its offset in the
|
|
24
|
+
* scene graph but draws nothing.
|
|
25
|
+
*
|
|
26
|
+
* The node references - but never owns - the {@link ImageLayer} and its
|
|
27
|
+
* Loader-owned texture: {@link destroy} frees the wrapped sprite but leaves the
|
|
28
|
+
* layer and texture intact. Image-layer nodes are band-selectable exactly like
|
|
29
|
+
* tile-layer nodes: list the image layer in a
|
|
30
|
+
* {@link import('./TileMapView').TileMapViewOptions.bands} definition and the
|
|
31
|
+
* band stacks it by the map's combined document order, or parent an unbanded
|
|
32
|
+
* node into the scene directly wherever the image belongs in draw order.
|
|
33
|
+
*
|
|
34
|
+
* For a `repeatX`/`repeatY` node, bounds and hit-test queries reflect the
|
|
35
|
+
* coverage geometry computed by the last {@link _collectContent} call (since
|
|
36
|
+
* that geometry is recomputed per collect, not maintained incrementally),
|
|
37
|
+
* which is also why such a node force-disables `cullable`. A repeating or
|
|
38
|
+
* parallax node also re-sizes its wrapped sprite as the camera crosses period
|
|
39
|
+
* boundaries, which content-dirties an enclosing retained group the same way
|
|
40
|
+
* a streamed layer does - give it the same treatment (its own
|
|
41
|
+
* `RetainedContainer`, or none).
|
|
42
|
+
*
|
|
43
|
+
* @advanced
|
|
44
|
+
*/
|
|
45
|
+
var ImageLayerNode = class extends Container {
|
|
46
|
+
_layer;
|
|
47
|
+
_sprite;
|
|
48
|
+
_baseOffsetX;
|
|
49
|
+
_baseOffsetY;
|
|
50
|
+
_imageWidth;
|
|
51
|
+
_imageHeight;
|
|
52
|
+
_pixelSnapMode = PixelSnapMode.None;
|
|
53
|
+
_covViewX = NaN;
|
|
54
|
+
_covViewY = NaN;
|
|
55
|
+
_covViewWidth = NaN;
|
|
56
|
+
_covViewHeight = NaN;
|
|
57
|
+
_covOriginX = NaN;
|
|
58
|
+
_covOriginY = NaN;
|
|
59
|
+
constructor(layer) {
|
|
60
|
+
super();
|
|
61
|
+
this._layer = layer;
|
|
62
|
+
this._baseOffsetX = layer.offsetX;
|
|
63
|
+
this._baseOffsetY = layer.offsetY;
|
|
64
|
+
this.setPosition(layer.offsetX, layer.offsetY);
|
|
65
|
+
this.visible = layer.visible;
|
|
66
|
+
const texture = layer.texture;
|
|
67
|
+
if (texture === null) {
|
|
68
|
+
this._sprite = null;
|
|
69
|
+
this._imageWidth = 0;
|
|
70
|
+
this._imageHeight = 0;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this._imageWidth = texture.width;
|
|
74
|
+
this._imageHeight = texture.height;
|
|
75
|
+
const sprite = new RepeatingSprite(texture, {
|
|
76
|
+
modeX: layer.repeatX ? "repeat" : "stretch",
|
|
77
|
+
modeY: layer.repeatY ? "repeat" : "stretch",
|
|
78
|
+
width: texture.width,
|
|
79
|
+
height: texture.height
|
|
80
|
+
});
|
|
81
|
+
const tintColor = layer.tintColor;
|
|
82
|
+
const r = tintColor === null ? 255 : tintColor >> 16 & 255;
|
|
83
|
+
const g = tintColor === null ? 255 : tintColor >> 8 & 255;
|
|
84
|
+
const b = tintColor === null ? 255 : tintColor & 255;
|
|
85
|
+
sprite.tint.set(r, g, b, layer.opacity);
|
|
86
|
+
this._sprite = sprite;
|
|
87
|
+
this.addChild(sprite);
|
|
88
|
+
if (layer.repeatX || layer.repeatY || layer.parallaxX !== 1 || layer.parallaxY !== 1 || layer.parallaxScale !== 1) this.cullable = false;
|
|
89
|
+
}
|
|
90
|
+
/** The runtime layer this node renders. */
|
|
91
|
+
get layer() {
|
|
92
|
+
return this._layer;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Render-only pixel-snap mode forwarded to the wrapped {@link RepeatingSprite}
|
|
96
|
+
* (which snaps its rendered origin, and - in `PixelSnapMode.Geometry` mode for
|
|
97
|
+
* axis-aligned transforms - its repeat-segment boundaries, to the render
|
|
98
|
+
* target's device-pixel grid). Purely visual: the layer offset, parallax, and
|
|
99
|
+
* repeat coverage are never changed.
|
|
100
|
+
*
|
|
101
|
+
* For a `null`-texture layer there is no drawable to forward to, so the mode
|
|
102
|
+
* is simply stored. Setting the current value is a no-op; an invalid value
|
|
103
|
+
* throws and leaves the prior mode unchanged.
|
|
104
|
+
*
|
|
105
|
+
* @default PixelSnapMode.None
|
|
106
|
+
* @stable
|
|
107
|
+
*/
|
|
108
|
+
get pixelSnapMode() {
|
|
109
|
+
return this._pixelSnapMode;
|
|
110
|
+
}
|
|
111
|
+
set pixelSnapMode(mode) {
|
|
112
|
+
if (mode === this._pixelSnapMode) return;
|
|
113
|
+
assertPixelSnapMode(mode);
|
|
114
|
+
this._pixelSnapMode = mode;
|
|
115
|
+
if (this._sprite !== null) this._sprite.pixelSnapMode = mode;
|
|
116
|
+
}
|
|
117
|
+
/** @internal */
|
|
118
|
+
_collectContent(builder) {
|
|
119
|
+
if (this._sprite === null) {
|
|
120
|
+
super._collectContent(builder);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const layer = this._layer;
|
|
124
|
+
const patchesPosition = layer.parallaxX !== 1 || layer.parallaxY !== 1;
|
|
125
|
+
const patchesScale = layer.parallaxScale !== 1;
|
|
126
|
+
if (patchesPosition || patchesScale) {
|
|
127
|
+
const camCenter = builder.view.center;
|
|
128
|
+
const prevX = this.x;
|
|
129
|
+
const prevY = this.y;
|
|
130
|
+
const prevScaleX = this.scale.x;
|
|
131
|
+
const prevScaleY = this.scale.y;
|
|
132
|
+
if (patchesPosition) {
|
|
133
|
+
this.x = this._baseOffsetX + camCenter.x * (1 - layer.parallaxX);
|
|
134
|
+
this.y = this._baseOffsetY + camCenter.y * (1 - layer.parallaxY);
|
|
135
|
+
}
|
|
136
|
+
if (patchesScale) this.setScale(prevScaleX * layer.parallaxScale, prevScaleY * layer.parallaxScale);
|
|
137
|
+
try {
|
|
138
|
+
this._updateRepeatCoverage(builder, layer.parallaxScale);
|
|
139
|
+
super._collectContent(builder);
|
|
140
|
+
} finally {
|
|
141
|
+
if (patchesPosition) this.setPosition(prevX, prevY);
|
|
142
|
+
if (patchesScale) this.setScale(prevScaleX, prevScaleY);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
this._updateRepeatCoverage(builder);
|
|
146
|
+
super._collectContent(builder);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
destroy() {
|
|
150
|
+
const sprite = this._sprite;
|
|
151
|
+
super.destroy();
|
|
152
|
+
sprite?.destroy();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Grow the wrapped sprite along each repeating axis so it covers the visible
|
|
156
|
+
* view, with a period-aligned local origin so the pattern offset stays zero
|
|
157
|
+
* (seamless tiling). Non-repeating axes keep the natural image size at local
|
|
158
|
+
* `0`. Reads `this.x`/`this.y` after the parallax patch, so the pattern anchor
|
|
159
|
+
* follows the parallax-shifted origin. Skips the rebuild when neither the view
|
|
160
|
+
* span nor the patched origin changed since the last frame.
|
|
161
|
+
*/
|
|
162
|
+
_updateRepeatCoverage(builder, parallaxScale = 1) {
|
|
163
|
+
const layer = this._layer;
|
|
164
|
+
const sprite = this._sprite;
|
|
165
|
+
if (sprite === null || !layer.repeatX && !layer.repeatY) return;
|
|
166
|
+
const bounds = builder.view.getBounds();
|
|
167
|
+
const originX = this.x;
|
|
168
|
+
const originY = this.y;
|
|
169
|
+
if (bounds.x === this._covViewX && bounds.y === this._covViewY && bounds.width === this._covViewWidth && bounds.height === this._covViewHeight && originX === this._covOriginX && originY === this._covOriginY) return;
|
|
170
|
+
this._covViewX = bounds.x;
|
|
171
|
+
this._covViewY = bounds.y;
|
|
172
|
+
this._covViewWidth = bounds.width;
|
|
173
|
+
this._covViewHeight = bounds.height;
|
|
174
|
+
this._covOriginX = originX;
|
|
175
|
+
this._covOriginY = originY;
|
|
176
|
+
let childX = 0;
|
|
177
|
+
let childY = 0;
|
|
178
|
+
let childWidth = this._imageWidth;
|
|
179
|
+
let childHeight = this._imageHeight;
|
|
180
|
+
if (layer.repeatX) {
|
|
181
|
+
const imgW = this._imageWidth;
|
|
182
|
+
const localViewMin = (bounds.x - originX) / parallaxScale;
|
|
183
|
+
const localViewWidth = bounds.width / parallaxScale;
|
|
184
|
+
const startLocal = Math.floor(localViewMin / imgW) * imgW;
|
|
185
|
+
const periods = Math.ceil((localViewMin + localViewWidth - startLocal) / imgW);
|
|
186
|
+
childX = startLocal;
|
|
187
|
+
childWidth = periods * imgW;
|
|
188
|
+
}
|
|
189
|
+
if (layer.repeatY) {
|
|
190
|
+
const imgH = this._imageHeight;
|
|
191
|
+
const localViewMin = (bounds.y - originY) / parallaxScale;
|
|
192
|
+
const localViewHeight = bounds.height / parallaxScale;
|
|
193
|
+
const startLocal = Math.floor(localViewMin / imgH) * imgH;
|
|
194
|
+
const periods = Math.ceil((localViewMin + localViewHeight - startLocal) / imgH);
|
|
195
|
+
childY = startLocal;
|
|
196
|
+
childHeight = periods * imgH;
|
|
197
|
+
}
|
|
198
|
+
sprite.setPosition(childX, childY);
|
|
199
|
+
sprite.setSize(childWidth, childHeight);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
//#endregion
|
|
204
|
+
export { ImageLayerNode };
|
|
205
|
+
//# sourceMappingURL=ImageLayerNode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageLayerNode.js","names":[],"sources":["../../src/ImageLayerNode.ts"],"sourcesContent":["import { Container, RepeatingSprite } from '@codexo/exojs';\nimport { PixelSnapMode, type RenderPlanBuilder } from '@codexo/exojs/renderer-sdk';\n\nimport type { ImageLayer } from './ImageLayer';\nimport { assertPixelSnapMode } from './pixelSnap';\n\n/**\n * A scene node that renders one Tiled {@link ImageLayer} as a single\n * {@link RepeatingSprite} wrapped in a {@link Container}.\n *\n * The layer's pixel `offset` becomes this node's position; `parallax` is\n * resolved per frame in {@link _collectContent} by patching the node position\n * against the camera centre (exactly like {@link import('./TileLayerNode').TileLayerNode}),\n * then restoring it. When `repeatX`/`repeatY` is set the wrapped sprite is\n * grown each frame to cover the visible view with a period-aligned origin, so\n * the pattern tiles seamlessly and scrolls with parallax. A repeat axis or a\n * `parallax` factor other than `1` both make this node's rendered position\n * and/or size camera-dependent at collect time, so its static bounds cannot be\n * culled against - such a node opts out of view culling (`cullable = false`).\n *\n * `visible`, `opacity`, and `tintColor` are applied **once at construction**:\n * {@link ImageLayer} is immutable, so there is nothing to re-sync per frame.\n * A layer whose `texture` is `null` (image failed to load / not yet available)\n * produces an empty node with no children - it occupies its offset in the\n * scene graph but draws nothing.\n *\n * The node references - but never owns - the {@link ImageLayer} and its\n * Loader-owned texture: {@link destroy} frees the wrapped sprite but leaves the\n * layer and texture intact. Image-layer nodes are band-selectable exactly like\n * tile-layer nodes: list the image layer in a\n * {@link import('./TileMapView').TileMapViewOptions.bands} definition and the\n * band stacks it by the map's combined document order, or parent an unbanded\n * node into the scene directly wherever the image belongs in draw order.\n *\n * For a `repeatX`/`repeatY` node, bounds and hit-test queries reflect the\n * coverage geometry computed by the last {@link _collectContent} call (since\n * that geometry is recomputed per collect, not maintained incrementally),\n * which is also why such a node force-disables `cullable`. A repeating or\n * parallax node also re-sizes its wrapped sprite as the camera crosses period\n * boundaries, which content-dirties an enclosing retained group the same way\n * a streamed layer does - give it the same treatment (its own\n * `RetainedContainer`, or none).\n *\n * @advanced\n */\nexport class ImageLayerNode extends Container {\n private readonly _layer: ImageLayer;\n private readonly _sprite: RepeatingSprite | null;\n private readonly _baseOffsetX: number;\n private readonly _baseOffsetY: number;\n private readonly _imageWidth: number;\n private readonly _imageHeight: number;\n private _pixelSnapMode: PixelSnapMode = PixelSnapMode.None;\n\n // Repeat-coverage cache: the view span and patched origin that last drove a\n // resize. A static camera pays one comparison per frame and skips the rebuild.\n private _covViewX = Number.NaN;\n private _covViewY = Number.NaN;\n private _covViewWidth = Number.NaN;\n private _covViewHeight = Number.NaN;\n private _covOriginX = Number.NaN;\n private _covOriginY = Number.NaN;\n\n public constructor(layer: ImageLayer) {\n super();\n\n this._layer = layer;\n this._baseOffsetX = layer.offsetX;\n this._baseOffsetY = layer.offsetY;\n\n this.setPosition(layer.offsetX, layer.offsetY);\n this.visible = layer.visible;\n\n const texture = layer.texture;\n\n if (texture === null) {\n this._sprite = null;\n this._imageWidth = 0;\n this._imageHeight = 0;\n\n return;\n }\n\n this._imageWidth = texture.width;\n this._imageHeight = texture.height;\n\n // Non-repeating axes use `'stretch'` so the axis shows exactly one un-tiled\n // copy at its natural size; repeating axes use `'repeat'` and are grown to\n // cover the view in `_collectContent`.\n const sprite = new RepeatingSprite(texture, {\n modeX: layer.repeatX ? 'repeat' : 'stretch',\n modeY: layer.repeatY ? 'repeat' : 'stretch',\n width: texture.width,\n height: texture.height,\n });\n\n const tintColor = layer.tintColor;\n const r = tintColor === null ? 255 : (tintColor >> 16) & 0xff;\n const g = tintColor === null ? 255 : (tintColor >> 8) & 0xff;\n const b = tintColor === null ? 255 : tintColor & 0xff;\n\n sprite.tint.set(r, g, b, layer.opacity);\n\n this._sprite = sprite;\n this.addChild(sprite);\n\n if (layer.repeatX || layer.repeatY || layer.parallaxX !== 1 || layer.parallaxY !== 1 || layer.parallaxScale !== 1) {\n this.cullable = false;\n }\n }\n\n /** The runtime layer this node renders. */\n public get layer(): ImageLayer {\n return this._layer;\n }\n\n /**\n * Render-only pixel-snap mode forwarded to the wrapped {@link RepeatingSprite}\n * (which snaps its rendered origin, and - in `PixelSnapMode.Geometry` mode for\n * axis-aligned transforms - its repeat-segment boundaries, to the render\n * target's device-pixel grid). Purely visual: the layer offset, parallax, and\n * repeat coverage are never changed.\n *\n * For a `null`-texture layer there is no drawable to forward to, so the mode\n * is simply stored. Setting the current value is a no-op; an invalid value\n * throws and leaves the prior mode unchanged.\n *\n * @default PixelSnapMode.None\n * @stable\n */\n public get pixelSnapMode(): PixelSnapMode {\n return this._pixelSnapMode;\n }\n\n public set pixelSnapMode(mode: PixelSnapMode) {\n if (mode === this._pixelSnapMode) {\n return;\n }\n\n assertPixelSnapMode(mode);\n this._pixelSnapMode = mode;\n\n if (this._sprite !== null) {\n this._sprite.pixelSnapMode = mode;\n }\n }\n\n /** @internal */\n protected override _collectContent(builder: RenderPlanBuilder): void {\n if (this._sprite === null) {\n super._collectContent(builder);\n\n return;\n }\n\n const layer = this._layer;\n\n const patchesPosition = layer.parallaxX !== 1 || layer.parallaxY !== 1;\n const patchesScale = layer.parallaxScale !== 1;\n\n if (patchesPosition || patchesScale) {\n const camCenter = builder.view.center;\n const prevX = this.x;\n const prevY = this.y;\n const prevScaleX = this.scale.x;\n const prevScaleY = this.scale.y;\n\n if (patchesPosition) {\n this.x = this._baseOffsetX + camCenter.x * (1 - layer.parallaxX);\n this.y = this._baseOffsetY + camCenter.y * (1 - layer.parallaxY);\n }\n\n if (patchesScale) {\n this.setScale(prevScaleX * layer.parallaxScale, prevScaleY * layer.parallaxScale);\n }\n\n try {\n this._updateRepeatCoverage(builder, layer.parallaxScale);\n super._collectContent(builder);\n } finally {\n if (patchesPosition) {\n this.setPosition(prevX, prevY);\n }\n\n if (patchesScale) {\n this.setScale(prevScaleX, prevScaleY);\n }\n }\n } else {\n this._updateRepeatCoverage(builder);\n super._collectContent(builder);\n }\n }\n\n public override destroy(): void {\n const sprite = this._sprite;\n\n super.destroy();\n\n sprite?.destroy();\n }\n\n /**\n * Grow the wrapped sprite along each repeating axis so it covers the visible\n * view, with a period-aligned local origin so the pattern offset stays zero\n * (seamless tiling). Non-repeating axes keep the natural image size at local\n * `0`. Reads `this.x`/`this.y` after the parallax patch, so the pattern anchor\n * follows the parallax-shifted origin. Skips the rebuild when neither the view\n * span nor the patched origin changed since the last frame.\n */\n private _updateRepeatCoverage(builder: RenderPlanBuilder, parallaxScale = 1): void {\n const layer = this._layer;\n const sprite = this._sprite;\n\n if (sprite === null || (!layer.repeatX && !layer.repeatY)) {\n return;\n }\n\n const bounds = builder.view.getBounds();\n const originX = this.x;\n const originY = this.y;\n\n if (\n bounds.x === this._covViewX &&\n bounds.y === this._covViewY &&\n bounds.width === this._covViewWidth &&\n bounds.height === this._covViewHeight &&\n originX === this._covOriginX &&\n originY === this._covOriginY\n ) {\n return;\n }\n\n this._covViewX = bounds.x;\n this._covViewY = bounds.y;\n this._covViewWidth = bounds.width;\n this._covViewHeight = bounds.height;\n this._covOriginX = originX;\n this._covOriginY = originY;\n\n let childX = 0;\n let childY = 0;\n let childWidth = this._imageWidth;\n let childHeight = this._imageHeight;\n\n if (layer.repeatX) {\n const imgW = this._imageWidth;\n const localViewMin = (bounds.x - originX) / parallaxScale;\n const localViewWidth = bounds.width / parallaxScale;\n const startLocal = Math.floor(localViewMin / imgW) * imgW;\n const periods = Math.ceil((localViewMin + localViewWidth - startLocal) / imgW);\n\n childX = startLocal;\n childWidth = periods * imgW;\n }\n\n if (layer.repeatY) {\n const imgH = this._imageHeight;\n const localViewMin = (bounds.y - originY) / parallaxScale;\n const localViewHeight = bounds.height / parallaxScale;\n const startLocal = Math.floor(localViewMin / imgH) * imgH;\n const periods = Math.ceil((localViewMin + localViewHeight - startLocal) / imgH);\n\n childY = startLocal;\n childHeight = periods * imgH;\n }\n\n sprite.setPosition(childX, childY);\n sprite.setSize(childWidth, childHeight);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ObjectLayer, TileMapObject } from './ObjectLayer';
|
|
2
|
+
import type { TileMap } from './TileMap';
|
|
3
|
+
import type { TileProperties } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* A format-neutral view of one map object, as handed to a
|
|
6
|
+
* {@link import('./MapObjectSpawner').MapObjectFactory}.
|
|
7
|
+
*
|
|
8
|
+
* The descriptor is what game code writes against: it resolves the two things
|
|
9
|
+
* that differ between authoring formats - stable identity and the dispatch
|
|
10
|
+
* key - and pairs the object with the layer it came from. The parsed object
|
|
11
|
+
* itself stays reachable through {@link object}, and the untouched source
|
|
12
|
+
* record through {@link TileMapObject.source}.
|
|
13
|
+
*/
|
|
14
|
+
export interface MapObjectDescriptor {
|
|
15
|
+
/**
|
|
16
|
+
* Stable identity for this object.
|
|
17
|
+
*
|
|
18
|
+
* Uniqueness domain is **one {@link TileMap}**: Tiled object ids are unique
|
|
19
|
+
* within a map, and LDtk entity iids are unique globally, so an LDtk-sourced
|
|
20
|
+
* id keeps that stronger guarantee. Combine with the owning level's id if a
|
|
21
|
+
* world-wide key is needed.
|
|
22
|
+
*
|
|
23
|
+
* Derived from {@link TileMapObject.sourceId} when the format supplies one,
|
|
24
|
+
* otherwise from the numeric {@link TileMapObject.id}.
|
|
25
|
+
*/
|
|
26
|
+
readonly id: string;
|
|
27
|
+
/**
|
|
28
|
+
* Canonical dispatch key - the object's class/type string, or `null` when the
|
|
29
|
+
* source declares none.
|
|
30
|
+
*
|
|
31
|
+
* LDtk fills it with the entity definition identifier, Tiled with the
|
|
32
|
+
* object's class. A {@link import('./MapObjectSpawner').MapObjectSpawner}
|
|
33
|
+
* uses it to pick a factory unless an `identify` strategy overrides that.
|
|
34
|
+
*/
|
|
35
|
+
readonly kind: string | null;
|
|
36
|
+
/** Object name; may be empty and is not unique. */
|
|
37
|
+
readonly name: string;
|
|
38
|
+
/** X of the object origin in object-layer pixel space. */
|
|
39
|
+
readonly x: number;
|
|
40
|
+
/** Y of the object origin in object-layer pixel space. */
|
|
41
|
+
readonly y: number;
|
|
42
|
+
/** Bounding width in px (0 for points). */
|
|
43
|
+
readonly width: number;
|
|
44
|
+
/** Bounding height in px (0 for points). */
|
|
45
|
+
readonly height: number;
|
|
46
|
+
/** Rotation in degrees, clockwise. See {@link TileMapObject} for the pivot. */
|
|
47
|
+
readonly rotation: number;
|
|
48
|
+
/** The object's immutable custom properties. */
|
|
49
|
+
readonly properties: TileProperties;
|
|
50
|
+
/** The object layer this object was authored on, with its placement metadata. */
|
|
51
|
+
readonly layer: ObjectLayer;
|
|
52
|
+
/** The parsed object itself - narrow on `object.kind` to read geometry. */
|
|
53
|
+
readonly object: TileMapObject;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build the {@link MapObjectDescriptor} for one object of `layer`.
|
|
57
|
+
*
|
|
58
|
+
* Exposed for code that spawns from a hand-picked object rather than from a
|
|
59
|
+
* whole map; {@link mapObjectDescriptors} covers the common case.
|
|
60
|
+
*/
|
|
61
|
+
export declare const mapObjectDescriptor: (object: TileMapObject, layer: ObjectLayer) => MapObjectDescriptor;
|
|
62
|
+
/**
|
|
63
|
+
* Every object of `map`, in a defined order: object-layer order first, then
|
|
64
|
+
* object order within each layer.
|
|
65
|
+
*
|
|
66
|
+
* This is the order a {@link import('./MapObjectSpawner').MapObjectSpawner}
|
|
67
|
+
* spawns in, so the same map always produces the same sequence.
|
|
68
|
+
*/
|
|
69
|
+
export declare const mapObjectDescriptors: (map: TileMap) => Generator<MapObjectDescriptor>;
|
|
70
|
+
//# sourceMappingURL=MapObject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapObject.d.ts","sourceRoot":"","sources":["../../src/MapObject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;CAChC;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,QAAQ,aAAa,EAAE,OAAO,WAAW,KAAG,mBAY9E,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAc,KAAK,OAAO,KAAG,SAAS,CAAC,mBAAmB,CAM1F,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/MapObject.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build the {@link MapObjectDescriptor} for one object of `layer`.
|
|
4
|
+
*
|
|
5
|
+
* Exposed for code that spawns from a hand-picked object rather than from a
|
|
6
|
+
* whole map; {@link mapObjectDescriptors} covers the common case.
|
|
7
|
+
*/
|
|
8
|
+
const mapObjectDescriptor = (object, layer) => ({
|
|
9
|
+
id: object.sourceId ?? String(object.id),
|
|
10
|
+
kind: object.type === "" ? null : object.type,
|
|
11
|
+
name: object.name,
|
|
12
|
+
x: object.x,
|
|
13
|
+
y: object.y,
|
|
14
|
+
width: object.width,
|
|
15
|
+
height: object.height,
|
|
16
|
+
rotation: object.rotation,
|
|
17
|
+
properties: object.properties,
|
|
18
|
+
layer,
|
|
19
|
+
object
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Every object of `map`, in a defined order: object-layer order first, then
|
|
23
|
+
* object order within each layer.
|
|
24
|
+
*
|
|
25
|
+
* This is the order a {@link import('./MapObjectSpawner').MapObjectSpawner}
|
|
26
|
+
* spawns in, so the same map always produces the same sequence.
|
|
27
|
+
*/
|
|
28
|
+
const mapObjectDescriptors = function* (map) {
|
|
29
|
+
for (const layer of map.objectLayers) for (const object of layer.objects) yield mapObjectDescriptor(object, layer);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { mapObjectDescriptor, mapObjectDescriptors };
|
|
34
|
+
//# sourceMappingURL=MapObject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapObject.js","names":[],"sources":["../../src/MapObject.ts"],"sourcesContent":["import type { ObjectLayer, TileMapObject } from './ObjectLayer';\nimport type { TileMap } from './TileMap';\nimport type { TileProperties } from './types';\n\n/**\n * A format-neutral view of one map object, as handed to a\n * {@link import('./MapObjectSpawner').MapObjectFactory}.\n *\n * The descriptor is what game code writes against: it resolves the two things\n * that differ between authoring formats - stable identity and the dispatch\n * key - and pairs the object with the layer it came from. The parsed object\n * itself stays reachable through {@link object}, and the untouched source\n * record through {@link TileMapObject.source}.\n */\nexport interface MapObjectDescriptor {\n /**\n * Stable identity for this object.\n *\n * Uniqueness domain is **one {@link TileMap}**: Tiled object ids are unique\n * within a map, and LDtk entity iids are unique globally, so an LDtk-sourced\n * id keeps that stronger guarantee. Combine with the owning level's id if a\n * world-wide key is needed.\n *\n * Derived from {@link TileMapObject.sourceId} when the format supplies one,\n * otherwise from the numeric {@link TileMapObject.id}.\n */\n readonly id: string;\n /**\n * Canonical dispatch key - the object's class/type string, or `null` when the\n * source declares none.\n *\n * LDtk fills it with the entity definition identifier, Tiled with the\n * object's class. A {@link import('./MapObjectSpawner').MapObjectSpawner}\n * uses it to pick a factory unless an `identify` strategy overrides that.\n */\n readonly kind: string | null;\n /** Object name; may be empty and is not unique. */\n readonly name: string;\n /** X of the object origin in object-layer pixel space. */\n readonly x: number;\n /** Y of the object origin in object-layer pixel space. */\n readonly y: number;\n /** Bounding width in px (0 for points). */\n readonly width: number;\n /** Bounding height in px (0 for points). */\n readonly height: number;\n /** Rotation in degrees, clockwise. See {@link TileMapObject} for the pivot. */\n readonly rotation: number;\n /** The object's immutable custom properties. */\n readonly properties: TileProperties;\n /** The object layer this object was authored on, with its placement metadata. */\n readonly layer: ObjectLayer;\n /** The parsed object itself - narrow on `object.kind` to read geometry. */\n readonly object: TileMapObject;\n}\n\n/**\n * Build the {@link MapObjectDescriptor} for one object of `layer`.\n *\n * Exposed for code that spawns from a hand-picked object rather than from a\n * whole map; {@link mapObjectDescriptors} covers the common case.\n */\nexport const mapObjectDescriptor = (object: TileMapObject, layer: ObjectLayer): MapObjectDescriptor => ({\n id: object.sourceId ?? String(object.id),\n kind: object.type === '' ? null : object.type,\n name: object.name,\n x: object.x,\n y: object.y,\n width: object.width,\n height: object.height,\n rotation: object.rotation,\n properties: object.properties,\n layer,\n object,\n});\n\n/**\n * Every object of `map`, in a defined order: object-layer order first, then\n * object order within each layer.\n *\n * This is the order a {@link import('./MapObjectSpawner').MapObjectSpawner}\n * spawns in, so the same map always produces the same sequence.\n */\nexport const mapObjectDescriptors = function* (map: TileMap): Generator<MapObjectDescriptor> {\n for (const layer of map.objectLayers) {\n for (const object of layer.objects) {\n yield mapObjectDescriptor(object, layer);\n }\n }\n};\n"],"mappings":";;;;;;;AA8DA,MAAa,uBAAuB,QAAuB,WAA6C;CACtG,IAAI,OAAO,YAAY,OAAO,OAAO,EAAE;CACvC,MAAM,OAAO,SAAS,KAAK,OAAO,OAAO;CACzC,MAAM,OAAO;CACb,GAAG,OAAO;CACV,GAAG,OAAO;CACV,OAAO,OAAO;CACd,QAAQ,OAAO;CACf,UAAU,OAAO;CACjB,YAAY,OAAO;CACnB;CACA;AACF;;;;;;;;AASA,MAAa,uBAAuB,WAAW,KAA8C;CAC3F,KAAK,MAAM,SAAS,IAAI,cACtB,KAAK,MAAM,UAAU,MAAM,SACzB,MAAM,oBAAoB,QAAQ,KAAK;AAG7C"}
|