@rpgjs/tiledmap 5.0.0-alpha.4 → 5.0.0-alpha.40

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/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RpgMap } from '@rpgjs/server';
2
- import { MapClass } from '@rpgjs/tiled';
2
+ import { MapClass } from '@canvasengine/tiled';
3
3
  declare module "@rpgjs/server" {
4
4
  interface RpgMap {
5
5
  tiled?: MapClass;
@@ -18,14 +18,14 @@ export interface RpgTiledMap extends RpgMap {
18
18
  * Tiled Module for RPGJS
19
19
  *
20
20
  * @description This module extends RPGJS maps with Tiled functionality,
21
- * allowing TMX map parsing and automatic hitbox creation
22
- * based on collisions defined in Tiled
21
+ * allowing TMX map parsing and automatic tile-based collision detection
22
+ * using the physics engine's tile grid system
23
23
  *
24
24
  * ## Features
25
25
  *
26
26
  * - **Automatic parsing**: Parses TMX files from Tiled Map Editor
27
27
  * - **Collision detection**: Scans all tiles to detect collisions
28
- * - **Hitbox creation**: Automatically generates hitboxes for each collision tile
28
+ * - **Tile grid system**: Uses physics engine tile grid to block movement on collision tiles
29
29
  * - **RpgMap extension**: Adds the `tiled` property to all RpgMap instances
30
30
  *
31
31
  * ## Usage
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpgjs/tiledmap",
3
- "version": "5.0.0-alpha.4",
3
+ "version": "5.0.0-alpha.40",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -22,23 +22,25 @@
22
22
  "license": "MIT",
23
23
  "description": "RPGJS is a framework for creating RPG/MMORPG games",
24
24
  "peerDependencies": {
25
- "canvasengine": "2.0.0-beta.21",
26
- "@canvasengine/presets": "2.0.0-beta.21",
27
- "@rpgjs/common": "5.0.0-alpha.4",
28
- "@rpgjs/server": "5.0.0-alpha.4",
29
- "@rpgjs/client": "5.0.0-alpha.4",
30
- "@rpgjs/tiled": "5.0.0-alpha.4",
31
- "@rpgjs/vite": "5.0.0-alpha.4"
25
+ "@canvasengine/presets": "*",
26
+ "@rpgjs/client": "5.0.0-alpha.40",
27
+ "@rpgjs/common": "5.0.0-alpha.40",
28
+ "@rpgjs/server": "5.0.0-alpha.40",
29
+ "@rpgjs/vite": "5.0.0-alpha.40",
30
+ "canvasengine": "*"
32
31
  },
33
32
  "publishConfig": {
34
33
  "access": "public"
35
34
  },
36
35
  "devDependencies": {
37
- "@canvasengine/compiler": "2.0.0-beta.21",
38
- "vite": "^6.2.5",
39
- "vite-plugin-dts": "^4.5.3"
36
+ "@canvasengine/compiler": "^2.0.0-beta.44",
37
+ "vite": "^7.3.0",
38
+ "vite-plugin-dts": "^4.5.4"
40
39
  },
41
40
  "type": "module",
41
+ "dependencies": {
42
+ "@canvasengine/tiled": "^2.0.0-beta.44"
43
+ },
42
44
  "scripts": {
43
45
  "dev": "vite build --watch",
44
46
  "build": "vite build"
package/src/client.ts CHANGED
@@ -2,5 +2,5 @@ import { RpgClient, RpgClientEngine } from "@rpgjs/client";
2
2
  import { defineModule } from "@rpgjs/common";
3
3
 
4
4
  export default defineModule<RpgClient>({
5
- effects: []
5
+ componentAnimations: []
6
6
  })
package/src/index.ts CHANGED
@@ -2,11 +2,12 @@ import server from "./server";
2
2
  import client from "./client";
3
3
  import { createModule } from "@rpgjs/common";
4
4
  import { provideLoadMap } from "@rpgjs/client";
5
- import { TiledParser } from "@rpgjs/tiled";
5
+ import { TiledParser } from "@canvasengine/tiled";
6
6
  import Tiled from "./tiled.ce";
7
7
 
8
8
  export function provideTiledMap(options: {
9
9
  basePath: string;
10
+ onLoadMap?: (map: string) => Promise<void>;
10
11
  }) {
11
12
  return createModule("TiledMap", [
12
13
  {
@@ -14,33 +15,41 @@ export function provideTiledMap(options: {
14
15
  client,
15
16
  },
16
17
  provideLoadMap?.(async (map) => {
17
- 'use client';
18
- const response = await fetch(`${options.basePath}/${map}.tmx`)
19
- const mapData = (await response.text()).replace(/source="([^"]+)"/g, `source="${options.basePath}/$1"`)
18
+ "use client";
19
+ const response = await fetch(`${options.basePath}/${map}.tmx`);
20
+ const mapData = (await response.text());
20
21
  const parser = new TiledParser(mapData);
21
22
  const parsedMap = parser.parseMap();
22
- const tilesets: any = []
23
+ const tilesets: any = [];
23
24
  for (let tileset of parsedMap.tilesets) {
24
- const response = await fetch(`${tileset.source}`);
25
+ const response = await fetch(`${options.basePath}/${tileset.source}`);
25
26
  const tilesetData = await response.text();
26
27
  const parser = new TiledParser(tilesetData);
27
28
  const parsedTileset = parser.parseTileset();
28
- parsedTileset.image.source = `${options.basePath}/${parsedTileset.image.source}`
29
+ parsedTileset.image.source = `${options.basePath}/${parsedTileset.image.source}`;
29
30
  // Preserve firstgid from the original tileset reference
30
31
  tilesets.push({
31
- ...tileset, // Preserve original properties including firstgid
32
- ...parsedTileset // Merge with parsed tileset data
33
- })
32
+ ...tileset, // Preserve original properties including firstgid
33
+ ...parsedTileset, // Merge with parsed tileset data
34
+ });
34
35
  }
35
- parsedMap.tilesets = tilesets
36
-
36
+ parsedMap.tilesets = tilesets;
37
+
37
38
  const obj = {
38
- data: mapData,
39
- component: Tiled,
40
- parsedMap,
41
- id: map
42
- }
43
- return obj
39
+ data: mapData,
40
+ component: Tiled,
41
+ parsedMap,
42
+ id: map,
43
+ params: {
44
+ basePath: options.basePath,
45
+ },
46
+ };
47
+
48
+ if (options.onLoadMap) {
49
+ await options.onLoadMap(map);
50
+ }
51
+
52
+ return obj;
44
53
  }),
45
54
  ]);
46
55
  }
package/src/server.ts CHANGED
@@ -1,10 +1,7 @@
1
1
  import { RpgMap, RpgServer } from "@rpgjs/server";
2
- import { TiledParser, MapClass } from "@rpgjs/tiled";
2
+ import { MapClass } from "@canvasengine/tiled";
3
3
  import { defineModule } from "@rpgjs/common";
4
4
 
5
- // Import TileInfo depuis le bon endroit
6
- import type { TileInfo } from "@rpgjs/tiled/src/classes/Map";
7
-
8
5
  // Extend RpgMap interface to include tiled property
9
6
  declare module "@rpgjs/server" {
10
7
  interface RpgMap {
@@ -26,14 +23,14 @@ export interface RpgTiledMap extends RpgMap {
26
23
  * Tiled Module for RPGJS
27
24
  *
28
25
  * @description This module extends RPGJS maps with Tiled functionality,
29
- * allowing TMX map parsing and automatic hitbox creation
30
- * based on collisions defined in Tiled
26
+ * allowing TMX map parsing and automatic tile-based collision detection
27
+ * using the physics engine's tile grid system
31
28
  *
32
29
  * ## Features
33
30
  *
34
31
  * - **Automatic parsing**: Parses TMX files from Tiled Map Editor
35
32
  * - **Collision detection**: Scans all tiles to detect collisions
36
- * - **Hitbox creation**: Automatically generates hitboxes for each collision tile
33
+ * - **Tile grid system**: Uses physics engine tile grid to block movement on collision tiles
37
34
  * - **RpgMap extension**: Adds the `tiled` property to all RpgMap instances
38
35
  *
39
36
  * ## Usage
@@ -74,15 +71,20 @@ export default defineModule<RpgServer>({
74
71
  /**
75
72
  * Hook called before map update
76
73
  *
77
- * @description Parses Tiled data and creates collision hitboxes
78
- * automatically by iterating through all tiles on the map.
74
+ * @description Parses Tiled data and sets up tile-based collision detection
75
+ * using the physics engine's tile grid system instead of individual hitboxes.
79
76
  *
80
77
  * This method:
81
78
  * 1. Parses TMX data with TiledParser
82
79
  * 2. Creates a MapClass instance with parsed data
83
80
  * 3. Attaches the Tiled instance to the RpgMap
84
81
  * 4. Scans all tiles to detect collisions
85
- * 5. Automatically creates hitboxes for each collision tile
82
+ * 5. Stores blocked tiles in a Set for use with the physics engine tile grid
83
+ * 6. Configures tile dimensions for proper coordinate conversion
84
+ *
85
+ * The blocked tiles are used by the physics engine's `canEnterTile` hook
86
+ * to prevent entities from entering collision tiles, which is more efficient
87
+ * than creating individual hitboxes for each tile.
86
88
  *
87
89
  * @param mapData - Map data containing TMX information
88
90
  * @param map - RpgMap instance to extend
@@ -90,20 +92,9 @@ export default defineModule<RpgServer>({
90
92
  *
91
93
  * @example
92
94
  * ```ts
93
- * // Created hitboxes will have this structure:
94
- * {
95
- * id: 'collision_x_y', // Unique identifier
96
- * x: x * tileWidth, // X position in pixels
97
- * y: y * tileHeight, // Y position in pixels
98
- * width: tileWidth, // Tile width
99
- * height: tileHeight, // Tile height
100
- * properties: {
101
- * type: 'collision', // Hitbox type
102
- * tileX: x, // X position in tiles
103
- * tileY: y, // Y position in tiles
104
- * tileIndex: tileIndex // Tile index
105
- * }
106
- * }
95
+ * // Blocked tiles are stored as a Set with keys "x,y" (tile coordinates)
96
+ * // The physics engine will automatically check these tiles when entities
97
+ * // try to move, using the canEnterTile hook applied to all entities
107
98
  * ```
108
99
  */
109
100
  onBeforeUpdate<T = RpgMap>(mapData: any, map: T): T {
@@ -112,16 +103,24 @@ export default defineModule<RpgServer>({
112
103
  // Attach Tiled instance to the map
113
104
  (map as any).tiled = tiledMap;
114
105
 
115
- // Initialize hitboxes array
106
+ // Initialize hitboxes array (for backward compatibility, but we won't populate it)
116
107
  mapData.hitboxes = mapData.hitboxes || [];
117
108
  mapData.width = tiledMap.widthPx;
118
109
  mapData.height = tiledMap.heightPx;
119
110
 
111
+ // Store tile dimensions for coordinate conversion
112
+ const tileWidth = tiledMap.tilewidth;
113
+ const tileHeight = tiledMap.tileheight;
114
+ (map as any)._tiledTileWidth = tileWidth;
115
+ (map as any)._tiledTileHeight = tileHeight;
116
+
117
+ // Store blocked tiles in a Set for efficient lookup
118
+ // Key format: "x,y" where x and y are tile coordinates in Tiled's coordinate system
119
+ const blockedTiles = new Set<string>();
120
+
120
121
  // Iterate through all map tiles to detect collisions
121
122
  const mapWidth = tiledMap.width;
122
123
  const mapHeight = tiledMap.height;
123
- const tileWidth = tiledMap.tilewidth;
124
- const tileHeight = tiledMap.tileheight;
125
124
 
126
125
  // Iterate through each tile on the map
127
126
  for (let y = 0; y < mapHeight; y++) {
@@ -133,27 +132,16 @@ export default defineModule<RpgServer>({
133
132
  populateTiles: true,
134
133
  });
135
134
 
136
- // If tile has collision, create a hitbox
135
+ // If tile has collision, add it to the blocked tiles set
137
136
  if (tileInfo.hasCollision) {
138
- const hitbox = {
139
- id: `collision_${x}_${y}`,
140
- x: pixelX,
141
- y: pixelY,
142
- width: tileWidth,
143
- height: tileHeight,
144
- properties: {
145
- type: "collision",
146
- tileX: x,
147
- tileY: y,
148
- tileIndex: tileInfo.tileIndex,
149
- },
150
- };
151
-
152
- mapData.hitboxes.push(hitbox);
137
+ blockedTiles.add(`${x},${y}`);
153
138
  }
154
139
  }
155
140
  }
156
141
 
142
+ // Store blocked tiles on the map instance
143
+ (map as any)._blockedTiles = blockedTiles;
144
+
157
145
  for (let obj of mapData.parsedMap.objects) {
158
146
  if (obj.point) {
159
147
  mapData.events = mapData.events
@@ -170,7 +158,131 @@ export default defineModule<RpgServer>({
170
158
  .filter((e) => e !== null);
171
159
  }
172
160
  }
161
+
162
+ // Apply tile collision to all existing entities after a short delay
163
+ // to ensure physics entities are created
164
+ setTimeout(() => {
165
+ applyTileCollisionToEntities(map as any);
166
+ }, 0);
167
+
173
168
  return map;
174
169
  },
175
170
  },
171
+ player: {
172
+ /**
173
+ * Hook called when a player joins a map
174
+ *
175
+ * @description Applies tile-based collision detection to the player's physics entity
176
+ * using the blocked tiles stored on the map
177
+ *
178
+ * @param player - The player instance
179
+ * @param map - The map instance
180
+ */
181
+ onJoinMap(player: any, map: any) {
182
+ // Apply tile collision after a short delay to ensure physics entity is created
183
+ setTimeout(() => {
184
+ applyTileCollisionToEntity(player, map);
185
+ }, 0);
186
+ },
187
+ },
176
188
  });
189
+
190
+ /**
191
+ * Applies tile-based collision detection to a single entity
192
+ *
193
+ * @description This function sets up the `canEnterTile` hook on an entity's physics body
194
+ * to prevent movement into blocked tiles. It converts tile coordinates from the physics
195
+ * engine's coordinate system (based on default 32x32 tiles) to Tiled's coordinate system.
196
+ *
197
+ * @param owner - The owner object (player or event) that has a physics entity
198
+ * @param map - The map instance containing blocked tiles
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * // This is called automatically when a player joins a map or an event is created
203
+ * // The function checks if the tile the entity is trying to enter is in the
204
+ * // blocked tiles set, converting coordinates as needed
205
+ * ```
206
+ */
207
+ function applyTileCollisionToEntity(owner: any, map: any) {
208
+ if (!owner?.id || !map?._blockedTiles) {
209
+ return;
210
+ }
211
+
212
+ const entity = map.physic?.getEntityByUUID(owner.id);
213
+ if (!entity) {
214
+ return;
215
+ }
216
+
217
+ const blockedTiles = map._blockedTiles as Set<string>;
218
+ const tiledTileWidth = map._tiledTileWidth ?? 32;
219
+ const tiledTileHeight = map._tiledTileHeight ?? 32;
220
+
221
+ // Physics engine uses default 32x32 tiles, but Tiled may have different dimensions
222
+ // We need to convert physics engine tile coordinates to Tiled tile coordinates
223
+ const physicsTileWidth = 32; // Default physics engine tile width
224
+ const physicsTileHeight = 32; // Default physics engine tile height
225
+
226
+ // Apply canEnterTile hook to the entity
227
+ entity.canEnterTile(({ x, y }) => {
228
+ // x, y are tile coordinates from the physics engine (based on 32x32 tiles)
229
+ // Convert to Tiled tile coordinates
230
+ const tiledX = Math.floor((x * physicsTileWidth) / tiledTileWidth);
231
+ const tiledY = Math.floor((y * physicsTileHeight) / tiledTileHeight);
232
+
233
+ // Check if this tile is blocked
234
+ const tileKey = `${tiledX},${tiledY}`;
235
+ if (blockedTiles.has(tileKey)) {
236
+ return false; // Block movement into this tile
237
+ }
238
+
239
+ return true; // Allow movement
240
+ });
241
+ }
242
+
243
+ /**
244
+ * Applies tile-based collision detection to all existing entities on a map
245
+ *
246
+ * @description This function iterates through all players and events on the map
247
+ * and applies the tile collision hook to each one's physics entity. This is useful
248
+ * when setting up the map for the first time or when entities already exist before
249
+ * the map is loaded.
250
+ *
251
+ * @param map - The map instance containing blocked tiles
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * // Called automatically in onBeforeUpdate to apply collision to existing entities
256
+ * ```
257
+ */
258
+ function applyTileCollisionToEntities(map: any) {
259
+ if (!map?._blockedTiles) {
260
+ return;
261
+ }
262
+
263
+ // Apply to all players
264
+ if (map.players && typeof map.players === 'function') {
265
+ const players = map.players();
266
+ if (players && typeof players === 'object') {
267
+ for (const playerId in players) {
268
+ const player = players[playerId];
269
+ if (player) {
270
+ applyTileCollisionToEntity(player, map);
271
+ }
272
+ }
273
+ }
274
+ }
275
+
276
+ // Apply to all events
277
+ if (map.events && typeof map.events === 'function') {
278
+ const events = map.events();
279
+ if (events && typeof events === 'object') {
280
+ for (const eventId in events) {
281
+ const event = events[eventId];
282
+ if (event) {
283
+ applyTileCollisionToEntity(event, map);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ }
package/src/tiled.ce CHANGED
@@ -1,13 +1,18 @@
1
1
  <Container>
2
- <TiledMap map basePath="map" createLayersPerTilesZ={true} objectLayer={() => <EventLayerComponent />} />
2
+ <TiledMap map basePath createLayersPerTilesZ={true} objectLayer={() => <EventLayerComponent />} />
3
3
  </Container>
4
4
 
5
5
  <script>
6
6
  import { EventLayerComponent } from "@rpgjs/client";
7
7
  import { TiledMap } from "@canvasengine/presets";
8
- import { signal } from "canvasengine";
8
+ import { signal, effect } from "canvasengine";
9
9
 
10
- const { data, children } = defineProps()
10
+ const { data, params } = defineProps()
11
11
 
12
12
  const map = signal(data())
13
+ const basePath = signal(params().basePath)
14
+
15
+ effect(() => {
16
+ map.set(data())
17
+ })
13
18
  </script>