@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/client/index.js +12 -7
- package/dist/client/index2.js +1 -1
- package/dist/client/index3.js +4638 -12
- package/dist/client/index4.js +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/server/index.js +2 -2
- package/dist/server/index2.js +88 -34
- package/dist/server/index3.js +4943 -0
- package/dist/server.d.ts +4 -4
- package/package.json +13 -11
- package/src/client.ts +1 -1
- package/src/index.ts +27 -18
- package/src/server.ts +155 -43
- package/src/tiled.ce +8 -3
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RpgMap } from '@rpgjs/server';
|
|
2
|
-
import { MapClass } from '@
|
|
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
|
|
22
|
-
*
|
|
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
|
-
* - **
|
|
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.
|
|
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": "
|
|
26
|
-
"@
|
|
27
|
-
"@rpgjs/common": "5.0.0-alpha.
|
|
28
|
-
"@rpgjs/server": "5.0.0-alpha.
|
|
29
|
-
"@rpgjs/
|
|
30
|
-
"
|
|
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.
|
|
38
|
-
"vite": "^
|
|
39
|
-
"vite-plugin-dts": "^4.5.
|
|
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
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 "@
|
|
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
|
-
|
|
18
|
-
const response = await fetch(`${options.basePath}/${map}.tmx`)
|
|
19
|
-
const mapData = (await response.text())
|
|
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,
|
|
32
|
-
...parsedTileset
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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 {
|
|
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
|
|
30
|
-
*
|
|
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
|
-
* - **
|
|
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
|
|
78
|
-
*
|
|
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.
|
|
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
|
-
* //
|
|
94
|
-
*
|
|
95
|
-
*
|
|
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,
|
|
135
|
+
// If tile has collision, add it to the blocked tiles set
|
|
137
136
|
if (tileInfo.hasCollision) {
|
|
138
|
-
|
|
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
|
|
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,
|
|
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>
|