@rpgjs/tiledmap 5.0.0-alpha.14 → 5.0.0-alpha.16

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.
@@ -1,4 +1,4 @@
1
- import { useProps, useDefineProps, signal, h, Container } from "canvasengine";
1
+ import { useProps, useDefineProps, signal, effect, h, Container } from "canvasengine";
2
2
  import { EventLayerComponent } from "@rpgjs/client";
3
3
  import { TiledMap } from "@canvasengine/presets";
4
4
  function component($$props) {
@@ -7,6 +7,9 @@ function component($$props) {
7
7
  var _a = defineProps(), data = _a.data, params = _a.params;
8
8
  var map = signal(data());
9
9
  var basePath = signal(params().basePath);
10
+ effect(function() {
11
+ map.set(data());
12
+ });
10
13
  let $this = h(Container, null, h(TiledMap, { map, basePath, createLayersPerTilesZ: true, objectLayer: () => h(EventLayerComponent) }));
11
14
  return $this;
12
15
  }
@@ -5,15 +5,20 @@ const server = defineModule({
5
5
  /**
6
6
  * Hook called before map update
7
7
  *
8
- * @description Parses Tiled data and creates collision hitboxes
9
- * automatically by iterating through all tiles on the map.
8
+ * @description Parses Tiled data and sets up tile-based collision detection
9
+ * using the physics engine's tile grid system instead of individual hitboxes.
10
10
  *
11
11
  * This method:
12
12
  * 1. Parses TMX data with TiledParser
13
13
  * 2. Creates a MapClass instance with parsed data
14
14
  * 3. Attaches the Tiled instance to the RpgMap
15
15
  * 4. Scans all tiles to detect collisions
16
- * 5. Automatically creates hitboxes for each collision tile
16
+ * 5. Stores blocked tiles in a Set for use with the physics engine tile grid
17
+ * 6. Configures tile dimensions for proper coordinate conversion
18
+ *
19
+ * The blocked tiles are used by the physics engine's `canEnterTile` hook
20
+ * to prevent entities from entering collision tiles, which is more efficient
21
+ * than creating individual hitboxes for each tile.
17
22
  *
18
23
  * @param mapData - Map data containing TMX information
19
24
  * @param map - RpgMap instance to extend
@@ -21,20 +26,9 @@ const server = defineModule({
21
26
  *
22
27
  * @example
23
28
  * ```ts
24
- * // Created hitboxes will have this structure:
25
- * {
26
- * id: 'collision_x_y', // Unique identifier
27
- * x: x * tileWidth, // X position in pixels
28
- * y: y * tileHeight, // Y position in pixels
29
- * width: tileWidth, // Tile width
30
- * height: tileHeight, // Tile height
31
- * properties: {
32
- * type: 'collision', // Hitbox type
33
- * tileX: x, // X position in tiles
34
- * tileY: y, // Y position in tiles
35
- * tileIndex: tileIndex // Tile index
36
- * }
37
- * }
29
+ * // Blocked tiles are stored as a Set with keys "x,y" (tile coordinates)
30
+ * // The physics engine will automatically check these tiles when entities
31
+ * // try to move, using the canEnterTile hook applied to all entities
38
32
  * ```
39
33
  */
40
34
  onBeforeUpdate(mapData, map) {
@@ -43,10 +37,13 @@ const server = defineModule({
43
37
  mapData.hitboxes = mapData.hitboxes || [];
44
38
  mapData.width = tiledMap.widthPx;
45
39
  mapData.height = tiledMap.heightPx;
46
- const mapWidth = tiledMap.width;
47
- const mapHeight = tiledMap.height;
48
40
  const tileWidth = tiledMap.tilewidth;
49
41
  const tileHeight = tiledMap.tileheight;
42
+ map._tiledTileWidth = tileWidth;
43
+ map._tiledTileHeight = tileHeight;
44
+ const blockedTiles = /* @__PURE__ */ new Set();
45
+ const mapWidth = tiledMap.width;
46
+ const mapHeight = tiledMap.height;
50
47
  for (let y = 0; y < mapHeight; y++) {
51
48
  for (let x = 0; x < mapWidth; x++) {
52
49
  const pixelX = x * tileWidth;
@@ -55,23 +52,11 @@ const server = defineModule({
55
52
  populateTiles: true
56
53
  });
57
54
  if (tileInfo.hasCollision) {
58
- const hitbox = {
59
- id: `collision_${x}_${y}`,
60
- x: pixelX,
61
- y: pixelY,
62
- width: tileWidth,
63
- height: tileHeight,
64
- properties: {
65
- type: "collision",
66
- tileX: x,
67
- tileY: y,
68
- tileIndex: tileInfo.tileIndex
69
- }
70
- };
71
- mapData.hitboxes.push(hitbox);
55
+ blockedTiles.add(`${x},${y}`);
72
56
  }
73
57
  }
74
58
  }
59
+ map._blockedTiles = blockedTiles;
75
60
  for (let obj of mapData.parsedMap.objects) {
76
61
  if (obj.point) {
77
62
  mapData.events = mapData.events.map((e) => {
@@ -86,10 +71,80 @@ const server = defineModule({
86
71
  }).filter((e) => e !== null);
87
72
  }
88
73
  }
74
+ setTimeout(() => {
75
+ applyTileCollisionToEntities(map);
76
+ }, 0);
89
77
  return map;
90
78
  }
79
+ },
80
+ player: {
81
+ /**
82
+ * Hook called when a player joins a map
83
+ *
84
+ * @description Applies tile-based collision detection to the player's physics entity
85
+ * using the blocked tiles stored on the map
86
+ *
87
+ * @param player - The player instance
88
+ * @param map - The map instance
89
+ */
90
+ onJoinMap(player, map) {
91
+ setTimeout(() => {
92
+ applyTileCollisionToEntity(player, map);
93
+ }, 0);
94
+ }
91
95
  }
92
96
  });
97
+ function applyTileCollisionToEntity(owner, map) {
98
+ var _a;
99
+ if (!(owner == null ? void 0 : owner.id) || !(map == null ? void 0 : map._blockedTiles)) {
100
+ return;
101
+ }
102
+ const entity = (_a = map.physic) == null ? void 0 : _a.getEntityByUUID(owner.id);
103
+ if (!entity) {
104
+ return;
105
+ }
106
+ const blockedTiles = map._blockedTiles;
107
+ const tiledTileWidth = map._tiledTileWidth ?? 32;
108
+ const tiledTileHeight = map._tiledTileHeight ?? 32;
109
+ const physicsTileWidth = 32;
110
+ const physicsTileHeight = 32;
111
+ entity.canEnterTile(({ x, y }) => {
112
+ const tiledX = Math.floor(x * physicsTileWidth / tiledTileWidth);
113
+ const tiledY = Math.floor(y * physicsTileHeight / tiledTileHeight);
114
+ const tileKey = `${tiledX},${tiledY}`;
115
+ if (blockedTiles.has(tileKey)) {
116
+ return false;
117
+ }
118
+ return true;
119
+ });
120
+ }
121
+ function applyTileCollisionToEntities(map) {
122
+ if (!(map == null ? void 0 : map._blockedTiles)) {
123
+ return;
124
+ }
125
+ if (map.players && typeof map.players === "function") {
126
+ const players = map.players();
127
+ if (players && typeof players === "object") {
128
+ for (const playerId in players) {
129
+ const player = players[playerId];
130
+ if (player) {
131
+ applyTileCollisionToEntity(player, map);
132
+ }
133
+ }
134
+ }
135
+ }
136
+ if (map.events && typeof map.events === "function") {
137
+ const events = map.events();
138
+ if (events && typeof events === "object") {
139
+ for (const eventId in events) {
140
+ const event = events[eventId];
141
+ if (event) {
142
+ applyTileCollisionToEntity(event, map);
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
93
148
  export {
94
149
  server as default
95
150
  };
package/dist/server.d.ts CHANGED
@@ -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.14",
3
+ "version": "5.0.0-alpha.16",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -22,24 +22,24 @@
22
22
  "license": "MIT",
23
23
  "description": "RPGJS is a framework for creating RPG/MMORPG games",
24
24
  "peerDependencies": {
25
- "@canvasengine/presets": "2.0.0-beta.25",
26
- "@rpgjs/client": "5.0.0-alpha.14",
27
- "@rpgjs/common": "5.0.0-alpha.14",
28
- "@rpgjs/server": "5.0.0-alpha.14",
29
- "@rpgjs/vite": "5.0.0-alpha.14",
30
- "canvasengine": "2.0.0-beta.25"
25
+ "@canvasengine/presets": "^2.0.0-beta.33",
26
+ "@rpgjs/client": "5.0.0-alpha.16",
27
+ "@rpgjs/common": "5.0.0-alpha.16",
28
+ "@rpgjs/server": "5.0.0-alpha.16",
29
+ "@rpgjs/vite": "5.0.0-alpha.16",
30
+ "canvasengine": "^2.0.0-beta.33"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
35
  "devDependencies": {
36
- "@canvasengine/compiler": "2.0.0-beta.21",
37
- "vite": "^6.2.5",
38
- "vite-plugin-dts": "^4.5.3"
36
+ "@canvasengine/compiler": "^2.0.0-beta.33",
37
+ "vite": "^7.2.4",
38
+ "vite-plugin-dts": "^4.5.4"
39
39
  },
40
40
  "type": "module",
41
41
  "dependencies": {
42
- "@canvasengine/tiled": "2.0.0-beta.25"
42
+ "@canvasengine/tiled": "^2.0.0-beta.33"
43
43
  },
44
44
  "scripts": {
45
45
  "dev": "vite build --watch",
package/src/server.ts CHANGED
@@ -23,14 +23,14 @@ export interface RpgTiledMap extends RpgMap {
23
23
  * Tiled Module for RPGJS
24
24
  *
25
25
  * @description This module extends RPGJS maps with Tiled functionality,
26
- * allowing TMX map parsing and automatic hitbox creation
27
- * 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
28
28
  *
29
29
  * ## Features
30
30
  *
31
31
  * - **Automatic parsing**: Parses TMX files from Tiled Map Editor
32
32
  * - **Collision detection**: Scans all tiles to detect collisions
33
- * - **Hitbox creation**: Automatically generates hitboxes for each collision tile
33
+ * - **Tile grid system**: Uses physics engine tile grid to block movement on collision tiles
34
34
  * - **RpgMap extension**: Adds the `tiled` property to all RpgMap instances
35
35
  *
36
36
  * ## Usage
@@ -71,15 +71,20 @@ export default defineModule<RpgServer>({
71
71
  /**
72
72
  * Hook called before map update
73
73
  *
74
- * @description Parses Tiled data and creates collision hitboxes
75
- * 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.
76
76
  *
77
77
  * This method:
78
78
  * 1. Parses TMX data with TiledParser
79
79
  * 2. Creates a MapClass instance with parsed data
80
80
  * 3. Attaches the Tiled instance to the RpgMap
81
81
  * 4. Scans all tiles to detect collisions
82
- * 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.
83
88
  *
84
89
  * @param mapData - Map data containing TMX information
85
90
  * @param map - RpgMap instance to extend
@@ -87,20 +92,9 @@ export default defineModule<RpgServer>({
87
92
  *
88
93
  * @example
89
94
  * ```ts
90
- * // Created hitboxes will have this structure:
91
- * {
92
- * id: 'collision_x_y', // Unique identifier
93
- * x: x * tileWidth, // X position in pixels
94
- * y: y * tileHeight, // Y position in pixels
95
- * width: tileWidth, // Tile width
96
- * height: tileHeight, // Tile height
97
- * properties: {
98
- * type: 'collision', // Hitbox type
99
- * tileX: x, // X position in tiles
100
- * tileY: y, // Y position in tiles
101
- * tileIndex: tileIndex // Tile index
102
- * }
103
- * }
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
104
98
  * ```
105
99
  */
106
100
  onBeforeUpdate<T = RpgMap>(mapData: any, map: T): T {
@@ -109,16 +103,24 @@ export default defineModule<RpgServer>({
109
103
  // Attach Tiled instance to the map
110
104
  (map as any).tiled = tiledMap;
111
105
 
112
- // Initialize hitboxes array
106
+ // Initialize hitboxes array (for backward compatibility, but we won't populate it)
113
107
  mapData.hitboxes = mapData.hitboxes || [];
114
108
  mapData.width = tiledMap.widthPx;
115
109
  mapData.height = tiledMap.heightPx;
116
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
+
117
121
  // Iterate through all map tiles to detect collisions
118
122
  const mapWidth = tiledMap.width;
119
123
  const mapHeight = tiledMap.height;
120
- const tileWidth = tiledMap.tilewidth;
121
- const tileHeight = tiledMap.tileheight;
122
124
 
123
125
  // Iterate through each tile on the map
124
126
  for (let y = 0; y < mapHeight; y++) {
@@ -130,27 +132,16 @@ export default defineModule<RpgServer>({
130
132
  populateTiles: true,
131
133
  });
132
134
 
133
- // If tile has collision, create a hitbox
135
+ // If tile has collision, add it to the blocked tiles set
134
136
  if (tileInfo.hasCollision) {
135
- const hitbox = {
136
- id: `collision_${x}_${y}`,
137
- x: pixelX,
138
- y: pixelY,
139
- width: tileWidth,
140
- height: tileHeight,
141
- properties: {
142
- type: "collision",
143
- tileX: x,
144
- tileY: y,
145
- tileIndex: tileInfo.tileIndex,
146
- },
147
- };
148
-
149
- mapData.hitboxes.push(hitbox);
137
+ blockedTiles.add(`${x},${y}`);
150
138
  }
151
139
  }
152
140
  }
153
141
 
142
+ // Store blocked tiles on the map instance
143
+ (map as any)._blockedTiles = blockedTiles;
144
+
154
145
  for (let obj of mapData.parsedMap.objects) {
155
146
  if (obj.point) {
156
147
  mapData.events = mapData.events
@@ -167,7 +158,131 @@ export default defineModule<RpgServer>({
167
158
  .filter((e) => e !== null);
168
159
  }
169
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
+
170
168
  return map;
171
169
  },
172
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
+ },
173
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
@@ -5,10 +5,14 @@
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
10
  const { data, params } = defineProps()
11
11
 
12
12
  const map = signal(data())
13
13
  const basePath = signal(params().basePath)
14
+
15
+ effect(() => {
16
+ map.set(data())
17
+ })
14
18
  </script>