emeraldengine 2.2.1 → 3.1.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 +2198 -968
- package/dist/types/index.d.ts +75 -32
- package/dist/types/src/Animator.d.ts +50 -0
- package/dist/types/{BitmapText.d.ts → src/BitmapText.d.ts} +19 -21
- package/dist/types/src/Camera.d.ts +122 -0
- package/dist/types/src/CameraController.d.ts +107 -0
- package/dist/types/src/CanvasText.d.ts +91 -0
- package/dist/types/src/CollisionLayers.d.ts +58 -0
- package/dist/types/{Color.d.ts → src/Color.d.ts} +6 -6
- package/dist/types/src/Coroutine.d.ts +111 -0
- package/dist/types/src/DebugOverlay.d.ts +86 -0
- package/dist/types/src/Drawable.d.ts +271 -0
- package/dist/types/src/Easing.d.ts +22 -0
- package/dist/types/src/Emerald.d.ts +420 -0
- package/dist/types/src/EmeraldDB.d.ts +159 -0
- package/dist/types/{FPSCounter.d.ts → src/FPSCounter.d.ts} +1 -3
- package/dist/types/src/GLUtils.d.ts +4 -0
- package/dist/types/{Instance.d.ts → src/Instance.d.ts} +37 -15
- package/dist/types/{InstancedTexture.d.ts → src/InstancedTexture.d.ts} +75 -23
- package/dist/types/src/Interpolator.d.ts +66 -0
- package/dist/types/src/Material.d.ts +87 -0
- package/dist/types/src/MathUtils.d.ts +81 -0
- package/dist/types/src/ParticleEmitter.d.ts +131 -0
- package/dist/types/src/Physics.d.ts +337 -0
- package/dist/types/src/Pool.d.ts +53 -0
- package/dist/types/src/PostEffects.d.ts +68 -0
- package/dist/types/src/PostProcessor.d.ts +124 -0
- package/dist/types/src/RenderTarget.d.ts +56 -0
- package/dist/types/src/Scene.d.ts +62 -0
- package/dist/types/src/ScreenEffects.d.ts +111 -0
- package/dist/types/src/Serializer.d.ts +86 -0
- package/dist/types/src/Shaders.d.ts +2 -0
- package/dist/types/{Shapes.d.ts → src/Shapes.d.ts} +4 -6
- package/dist/types/src/SpatialGrid.d.ts +50 -0
- package/dist/types/src/SpriteBatch.d.ts +89 -0
- package/dist/types/src/StateMachine.d.ts +59 -0
- package/dist/types/src/Storage.d.ts +89 -0
- package/dist/types/{Texture.d.ts → src/Texture.d.ts} +3 -4
- package/dist/types/src/TextureAtlas.d.ts +55 -0
- package/dist/types/src/Tilemap.d.ts +91 -0
- package/dist/types/src/Time.d.ts +53 -0
- package/dist/types/src/Timer.d.ts +54 -0
- package/dist/types/src/Transform.d.ts +94 -0
- package/dist/types/src/Tween.d.ts +81 -0
- package/dist/types/src/UI.d.ts +121 -0
- package/dist/types/src/components/Behaviour.d.ts +71 -0
- package/dist/types/{components → src/components}/BoxCollider.d.ts +14 -14
- package/dist/types/src/components/BoxColliderDebug.d.ts +15 -0
- package/dist/types/{components → src/components}/CircleCollider.d.ts +14 -12
- package/dist/types/src/components/CircleColliderDebug.d.ts +15 -0
- package/dist/types/src/components/Collider.d.ts +102 -0
- package/dist/types/src/components/GameObject.d.ts +135 -0
- package/dist/types/src/components/PolygonCollider.d.ts +33 -0
- package/dist/types/src/components/RigidBody.d.ts +456 -0
- package/dist/types/src/importers/Aseprite.d.ts +79 -0
- package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
- package/dist/types/src/importers/TiledMap.d.ts +62 -0
- package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
- package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
- package/dist/types/src/managers/AssetManager.d.ts +116 -0
- package/dist/types/src/managers/AudioManager.d.ts +259 -0
- package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
- package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
- package/dist/types/src/managers/GLManager.d.ts +84 -0
- package/dist/types/src/managers/GLState.d.ts +37 -0
- package/dist/types/src/managers/Gamepad.d.ts +102 -0
- package/dist/types/src/managers/IDManager.d.ts +31 -0
- package/dist/types/src/managers/InputManager.d.ts +381 -0
- package/dist/types/src/managers/NetworkManager.d.ts +93 -0
- package/dist/types/src/managers/RenderStats.d.ts +34 -0
- package/dist/types/src/managers/SceneManager.d.ts +44 -0
- package/dist/types/src/managers/ShaderManager.d.ts +55 -0
- package/dist/types/src/managers/TextureManager.d.ts +102 -0
- package/dist/types/src/particlesystem/Particle.d.ts +64 -0
- package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
- package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
- package/dist/types/src/physics/AABB.d.ts +92 -0
- package/dist/types/src/physics/Body.d.ts +435 -0
- package/dist/types/src/physics/BodyType.d.ts +6 -0
- package/dist/types/src/physics/BroadPhase.d.ts +210 -0
- package/dist/types/src/physics/Collision.d.ts +102 -0
- package/dist/types/src/physics/Contact.d.ts +206 -0
- package/dist/types/src/physics/ContactSolver.d.ts +108 -0
- package/dist/types/src/physics/Distance.d.ts +54 -0
- package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
- package/dist/types/src/physics/Fixture.d.ts +221 -0
- package/dist/types/src/physics/Island.d.ts +52 -0
- package/dist/types/src/physics/Joint.d.ts +59 -0
- package/dist/types/src/physics/Math2D.d.ts +371 -0
- package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
- package/dist/types/src/physics/Settings.d.ts +22 -0
- package/dist/types/src/physics/Shapes.d.ts +207 -0
- package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
- package/dist/types/src/physics/World.d.ts +274 -0
- package/dist/types/src/physics/index.d.ts +34 -0
- package/index.js +78 -0
- package/package.json +73 -60
- package/src/Animator.js +95 -0
- package/src/BitmapText.js +6 -5
- package/src/Camera.js +183 -0
- package/src/CameraController.js +192 -0
- package/src/CanvasText.js +281 -0
- package/src/CollisionLayers.js +86 -0
- package/src/Color.js +26 -18
- package/src/Coroutine.js +259 -0
- package/src/DebugOverlay.js +246 -0
- package/src/Drawable.js +842 -582
- package/src/Easing.js +57 -0
- package/src/Emerald.js +1150 -459
- package/src/EmeraldDB.js +328 -0
- package/src/FPSCounter.js +43 -43
- package/src/GLUtils.js +60 -67
- package/src/Instance.js +41 -5
- package/src/InstancedTexture.js +308 -127
- package/src/Interpolator.js +124 -0
- package/src/Material.js +202 -0
- package/src/MathUtils.js +134 -0
- package/src/ParticleEmitter.js +284 -0
- package/src/Physics.js +431 -40
- package/src/Pool.js +85 -0
- package/src/PostEffects.js +296 -0
- package/src/PostProcessor.js +304 -0
- package/src/RenderTarget.js +134 -0
- package/src/Scene.js +115 -83
- package/src/ScreenEffects.js +266 -0
- package/src/Serializer.js +131 -0
- package/src/Shaders.js +150 -165
- package/src/Shapes.js +118 -129
- package/src/SpatialGrid.js +111 -0
- package/src/SpriteBatch.js +299 -0
- package/src/StateMachine.js +82 -0
- package/src/Storage.js +175 -47
- package/src/Texture.js +58 -67
- package/src/TextureAtlas.js +96 -0
- package/src/Tilemap.js +274 -0
- package/src/Time.js +51 -6
- package/src/Timer.js +99 -0
- package/src/Transform.js +100 -7
- package/src/Tween.js +160 -0
- package/src/UI.js +394 -0
- package/src/components/Behaviour.js +90 -0
- package/src/components/BoxCollider.js +29 -12
- package/src/components/BoxColliderDebug.js +3 -4
- package/src/components/CircleCollider.js +26 -12
- package/src/components/CircleColliderDebug.js +25 -24
- package/src/components/Collider.js +150 -34
- package/src/components/GameObject.js +130 -21
- package/src/components/PolygonCollider.js +55 -0
- package/src/components/RigidBody.js +556 -8
- package/src/importers/Aseprite.js +142 -0
- package/src/importers/ForgeLevel.js +581 -0
- package/src/importers/TiledMap.js +158 -0
- package/src/lights/DirectionalLight.js +6 -15
- package/src/lights/PointLight.js +4 -4
- package/src/managers/AssetManager.js +239 -0
- package/src/managers/AudioManager.js +565 -146
- package/src/managers/EventManager.js +488 -477
- package/src/managers/GLManager.js +57 -0
- package/src/managers/GLState.js +70 -0
- package/src/managers/Gamepad.js +126 -0
- package/src/managers/IDManager.js +24 -2
- package/src/managers/InputManager.js +905 -0
- package/src/managers/NetworkManager.js +178 -0
- package/src/managers/RenderStats.js +34 -0
- package/src/managers/SceneManager.js +30 -0
- package/src/managers/ShaderManager.js +0 -2
- package/src/managers/TextureManager.js +218 -0
- package/src/particlesystem/Particle.js +82 -7
- package/src/particlesystem/ParticleSettings.js +21 -3
- package/src/particlesystem/Particles.js +80 -31
- package/src/physics/AABB.js +207 -0
- package/src/physics/Body.js +862 -0
- package/src/physics/BodyType.js +16 -0
- package/src/physics/BroadPhase.js +641 -0
- package/src/physics/Collision.js +534 -0
- package/src/physics/Contact.js +500 -0
- package/src/physics/ContactSolver.js +526 -0
- package/src/physics/Distance.js +403 -0
- package/src/physics/DistanceJoint.js +227 -0
- package/src/physics/Fixture.js +346 -0
- package/src/physics/Island.js +203 -0
- package/src/physics/Joint.js +78 -0
- package/src/physics/Math2D.js +573 -0
- package/src/physics/RevoluteJoint.js +278 -0
- package/src/physics/Settings.js +78 -0
- package/src/physics/Shapes.js +549 -0
- package/src/physics/TimeOfImpact.js +87 -0
- package/src/physics/World.js +731 -0
- package/src/physics/index.js +79 -0
- package/dist/types/Drawable.d.ts +0 -157
- package/dist/types/Emerald.d.ts +0 -73
- package/dist/types/GLUtils.d.ts +0 -6
- package/dist/types/Physics.d.ts +0 -139
- package/dist/types/Scene.d.ts +0 -39
- package/dist/types/Shaders.d.ts +0 -4
- package/dist/types/Storage.d.ts +0 -46
- package/dist/types/Time.d.ts +0 -22
- package/dist/types/Transform.d.ts +0 -41
- package/dist/types/components/BoxColliderDebug.d.ts +0 -19
- package/dist/types/components/CircleColliderDebug.d.ts +0 -19
- package/dist/types/components/Collider.d.ts +0 -53
- package/dist/types/components/GameObject.d.ts +0 -72
- package/dist/types/components/RigidBody.d.ts +0 -104
- package/dist/types/managers/AudioManager.d.ts +0 -60
- package/dist/types/managers/GLManager.d.ts +0 -47
- package/dist/types/managers/IDManager.d.ts +0 -21
- package/dist/types/managers/SceneManager.d.ts +0 -22
- package/dist/types/particlesystem/Particle.d.ts +0 -42
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import Tilemap from "../Tilemap.js";
|
|
2
|
+
|
|
3
|
+
const FLIP_FLAGS = 0xe0000000;
|
|
4
|
+
const GID_MASK = 0x1fffffff;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @class TiledMap
|
|
8
|
+
* @description Imports orthogonal maps exported from the Tiled editor
|
|
9
|
+
* (https://www.mapeditor.org) in JSON format into the engine's Tilemap, plus
|
|
10
|
+
* helpers to pull object layers (spawn points, triggers, etc.) out as plain
|
|
11
|
+
* data. Pure parsing: pass it the already-parsed JSON object (load it with
|
|
12
|
+
* AssetManager.json or fetch).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const map = TiledMap.toTilemap(mapJson, "tiles.png");
|
|
16
|
+
* scene.add(map.gameObject);
|
|
17
|
+
* map.buildColliders(physics);
|
|
18
|
+
* const spawns = TiledMap.objects(mapJson, { layer: "spawns", flipY: true });
|
|
19
|
+
*/
|
|
20
|
+
class TiledMap {
|
|
21
|
+
/**
|
|
22
|
+
* @method tilesetFor
|
|
23
|
+
* @description Finds the tileset a (masked) global id belongs to.
|
|
24
|
+
* @param {Object} map - Parsed Tiled map JSON
|
|
25
|
+
* @param {number} gid - Masked global tile id (flip flags removed)
|
|
26
|
+
* @returns {Object|null}
|
|
27
|
+
*/
|
|
28
|
+
static tilesetFor(map, gid) {
|
|
29
|
+
const sets = map.tilesets || [];
|
|
30
|
+
let best = null;
|
|
31
|
+
for (const ts of sets) {
|
|
32
|
+
if (ts.firstgid <= gid && (!best || ts.firstgid > best.firstgid)) {
|
|
33
|
+
best = ts;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return best;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** @private */
|
|
40
|
+
static _tileLayer(map, layer) {
|
|
41
|
+
const layers = (map.layers || []).filter((l) => l.type === "tilelayer");
|
|
42
|
+
if (layer == null) return layers[0] || null;
|
|
43
|
+
if (typeof layer === "number") return layers[layer] || null;
|
|
44
|
+
return layers.find((l) => l.name === layer) || null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @method toFrameGrid
|
|
49
|
+
* @description Converts a tile layer into a 2D array of local frame indices
|
|
50
|
+
* (-1 for empty), ready for Tilemap.setMap. Global ids are converted to
|
|
51
|
+
* tileset-local indices and flip flags are stripped.
|
|
52
|
+
* @param {Object} map - Parsed Tiled map JSON
|
|
53
|
+
* @param {Object} [options] - { layer } (name, index, or first tile layer)
|
|
54
|
+
* @returns {number[][]}
|
|
55
|
+
*/
|
|
56
|
+
static toFrameGrid(map, options = {}) {
|
|
57
|
+
const layer = TiledMap._tileLayer(map, options.layer);
|
|
58
|
+
if (!layer) return [];
|
|
59
|
+
const cols = layer.width;
|
|
60
|
+
const rows = layer.height;
|
|
61
|
+
const data = layer.data || [];
|
|
62
|
+
const grid = [];
|
|
63
|
+
for (let r = 0; r < rows; r++) {
|
|
64
|
+
const row = [];
|
|
65
|
+
for (let c = 0; c < cols; c++) {
|
|
66
|
+
const raw = data[r * cols + c] || 0;
|
|
67
|
+
if (raw === 0) {
|
|
68
|
+
row.push(-1);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const gid = (raw & GID_MASK) >>> 0;
|
|
72
|
+
const ts = TiledMap.tilesetFor(map, gid);
|
|
73
|
+
row.push(ts ? gid - ts.firstgid : gid - 1);
|
|
74
|
+
}
|
|
75
|
+
grid.push(row);
|
|
76
|
+
}
|
|
77
|
+
return grid;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @method toTilemap
|
|
82
|
+
* @description Builds a ready-to-render Tilemap from a Tiled map. The tile
|
|
83
|
+
* sheet config (frame size, columns, count) is read from the map's first
|
|
84
|
+
* tileset; the render tile size comes from the map's tilewidth.
|
|
85
|
+
* @param {Object} map - Parsed Tiled map JSON
|
|
86
|
+
* @param {string} texturePath - Path to the tile sheet image
|
|
87
|
+
* @param {Object} [options] - { layer, pixelart = true, name, originX, originY }
|
|
88
|
+
* @returns {Tilemap}
|
|
89
|
+
*/
|
|
90
|
+
static toTilemap(map, texturePath, options = {}) {
|
|
91
|
+
const ts = (map.tilesets || [])[0] || {};
|
|
92
|
+
const tilemap = new Tilemap(options.name || "tiledmap", texturePath, {
|
|
93
|
+
tileSize: options.tileSize ?? map.tilewidth ?? ts.tilewidth ?? 16,
|
|
94
|
+
frameWidth: ts.tilewidth ?? map.tilewidth ?? 16,
|
|
95
|
+
frameHeight: ts.tileheight ?? map.tileheight ?? 16,
|
|
96
|
+
framesPerRow: ts.columns ?? 1,
|
|
97
|
+
totalFrames: ts.tilecount ?? 1,
|
|
98
|
+
pixelart: options.pixelart ?? true,
|
|
99
|
+
});
|
|
100
|
+
const grid = TiledMap.toFrameGrid(map, options);
|
|
101
|
+
tilemap.setMap(grid, {
|
|
102
|
+
originX: options.originX ?? 0,
|
|
103
|
+
originY: options.originY ?? 0,
|
|
104
|
+
flipY: options.flipY ?? true,
|
|
105
|
+
});
|
|
106
|
+
return tilemap;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @method objects
|
|
111
|
+
* @description Extracts the objects from an object layer as plain data
|
|
112
|
+
* (spawn points, triggers, regions). Tiled `properties` arrays are flattened
|
|
113
|
+
* into a `props` object. With `flipY` set, y is converted from Tiled's
|
|
114
|
+
* top-left pixel origin to a y-up world using the map's pixel height.
|
|
115
|
+
* @param {Object} map - Parsed Tiled map JSON
|
|
116
|
+
* @param {Object} [options] - { layer, flipY = false, originX = 0, originY = 0 }
|
|
117
|
+
* @returns {Array<Object>}
|
|
118
|
+
*/
|
|
119
|
+
static objects(map, options = {}) {
|
|
120
|
+
const groups = (map.layers || []).filter((l) => l.type === "objectgroup");
|
|
121
|
+
let group;
|
|
122
|
+
if (options.layer == null) group = groups[0];
|
|
123
|
+
else if (typeof options.layer === "number") group = groups[options.layer];
|
|
124
|
+
else group = groups.find((l) => l.name === options.layer);
|
|
125
|
+
if (!group) return [];
|
|
126
|
+
|
|
127
|
+
const flipY = options.flipY ?? false;
|
|
128
|
+
const originX = options.originX ?? 0;
|
|
129
|
+
const originY = options.originY ?? 0;
|
|
130
|
+
const mapPixelH = (map.height || 0) * (map.tileheight || 0);
|
|
131
|
+
|
|
132
|
+
return (group.objects || []).map((o) => {
|
|
133
|
+
const props = {};
|
|
134
|
+
if (Array.isArray(o.properties)) {
|
|
135
|
+
for (const p of o.properties) props[p.name] = p.value;
|
|
136
|
+
}
|
|
137
|
+
const worldY = flipY ? mapPixelH - o.y : o.y;
|
|
138
|
+
return {
|
|
139
|
+
id: o.id,
|
|
140
|
+
name: o.name || "",
|
|
141
|
+
type: o.type || o.class || "",
|
|
142
|
+
x: originX + o.x,
|
|
143
|
+
y: originY + worldY,
|
|
144
|
+
width: o.width || 0,
|
|
145
|
+
height: o.height || 0,
|
|
146
|
+
rotation: o.rotation || 0,
|
|
147
|
+
gid: o.gid != null ? (o.gid & GID_MASK) >>> 0 : null,
|
|
148
|
+
point: !!o.point,
|
|
149
|
+
ellipse: !!o.ellipse,
|
|
150
|
+
polygon: o.polygon || null,
|
|
151
|
+
props,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { FLIP_FLAGS, GID_MASK };
|
|
158
|
+
export default TiledMap;
|
|
@@ -10,15 +10,14 @@ import { Vector2 } from "../Physics.js";
|
|
|
10
10
|
*/
|
|
11
11
|
class DirectionalLight {
|
|
12
12
|
constructor(position, direction, color, intensity, width) {
|
|
13
|
-
this.position = position;
|
|
14
|
-
// Normalize the direction vector
|
|
13
|
+
this.position = position;
|
|
15
14
|
const length = Math.sqrt(
|
|
16
15
|
direction.x * direction.x + direction.y * direction.y
|
|
17
16
|
);
|
|
18
|
-
this.direction = new Vector2(direction.x / length, direction.y / length);
|
|
19
|
-
this.color = color;
|
|
20
|
-
this.intensity = intensity;
|
|
21
|
-
this.width = width;
|
|
17
|
+
this.direction = new Vector2(direction.x / length, direction.y / length);
|
|
18
|
+
this.color = color;
|
|
19
|
+
this.intensity = intensity;
|
|
20
|
+
this.width = width;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
/**
|
|
@@ -27,38 +26,30 @@ class DirectionalLight {
|
|
|
27
26
|
* @returns {number} - Light intensity (0-1)
|
|
28
27
|
*/
|
|
29
28
|
getIntensityAtPoint(point) {
|
|
30
|
-
|
|
31
|
-
// Calculate distance from light position to point
|
|
32
29
|
const dx = point.x - this.position.x;
|
|
33
30
|
const dy = point.y - this.position.y;
|
|
34
31
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
35
32
|
|
|
36
|
-
// Calculate the projection of the point onto the light direction
|
|
37
33
|
const lightDirNormalized = new Vector2(this.direction.x, this.direction.y);
|
|
38
34
|
const toPoint = new Vector2(dx, dy);
|
|
39
35
|
const projection =
|
|
40
36
|
toPoint.x * lightDirNormalized.x + toPoint.y * lightDirNormalized.y;
|
|
41
37
|
|
|
42
|
-
// If point is behind the light, no illumination
|
|
43
38
|
if (projection < 0) {
|
|
44
39
|
return 0;
|
|
45
40
|
}
|
|
46
41
|
|
|
47
|
-
// Calculate perpendicular distance from the light beam center
|
|
48
42
|
const perpDistance = Math.abs(
|
|
49
43
|
toPoint.x * lightDirNormalized.y - toPoint.y * lightDirNormalized.x
|
|
50
44
|
);
|
|
51
45
|
|
|
52
|
-
// If point is outside the light beam width, no illumination
|
|
53
46
|
if (perpDistance > this.width / 2) {
|
|
54
47
|
return 0;
|
|
55
48
|
}
|
|
56
49
|
|
|
57
|
-
// Calculate intensity based on distance within the beam
|
|
58
50
|
const widthFactor = 1 - perpDistance / (this.width / 2);
|
|
59
51
|
|
|
60
|
-
|
|
61
|
-
const maxDistance = 1000; // Maximum effective distance
|
|
52
|
+
const maxDistance = 1000;
|
|
62
53
|
const distanceFactor = Math.max(0, 1 - distance / maxDistance);
|
|
63
54
|
|
|
64
55
|
return this.intensity * widthFactor * distanceFactor;
|
package/src/lights/PointLight.js
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
class PointLight {
|
|
9
9
|
constructor(position, color, intensity, radius) {
|
|
10
|
-
this.position = position;
|
|
11
|
-
this.color = color;
|
|
12
|
-
this.intensity = intensity;
|
|
13
|
-
this.radius = radius;
|
|
10
|
+
this.position = position;
|
|
11
|
+
this.color = color;
|
|
12
|
+
this.intensity = intensity;
|
|
13
|
+
this.radius = radius;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import TextureManager from "./TextureManager.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @class AssetManager
|
|
5
|
+
* @description A general async loader for everything a game needs at startup:
|
|
6
|
+
* images/textures, audio, JSON, text, and web fonts. It deduplicates by key,
|
|
7
|
+
* reports aggregate progress (so you can drive a loading bar), and stores the
|
|
8
|
+
* resolved assets for synchronous lookup afterwards via `get(key)`.
|
|
9
|
+
*
|
|
10
|
+
* Textures are routed through TextureManager so the GL upload cache is shared
|
|
11
|
+
* with the rest of the engine.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const assets = new AssetManager();
|
|
15
|
+
* assets.image("player", "player.png", { pixelart: true });
|
|
16
|
+
* assets.audio("jump", "jump.wav");
|
|
17
|
+
* assets.json("level1", "levels/1.json");
|
|
18
|
+
* assets.font("Press Start 2P", "fonts/press-start.woff2");
|
|
19
|
+
* assets.onProgress((loaded, total) => bar.set(loaded / total));
|
|
20
|
+
* await assets.load();
|
|
21
|
+
* const img = assets.get("player"); // HTMLImageElement
|
|
22
|
+
* const data = assets.get("level1"); // parsed JSON
|
|
23
|
+
*/
|
|
24
|
+
class AssetManager {
|
|
25
|
+
constructor() {
|
|
26
|
+
/** @private */
|
|
27
|
+
this._queue = new Map();
|
|
28
|
+
this.assets = new Map();
|
|
29
|
+
/** @private */
|
|
30
|
+
this._paths = new Map();
|
|
31
|
+
/** @private */
|
|
32
|
+
this._progressHandlers = [];
|
|
33
|
+
/** @private */
|
|
34
|
+
this._loaded = 0;
|
|
35
|
+
/** @private */
|
|
36
|
+
this._total = 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @method image
|
|
41
|
+
* @description Queues an image/texture. The GL texture is created lazily by
|
|
42
|
+
* the renderer; `get(key)` returns the HTMLImageElement.
|
|
43
|
+
*/
|
|
44
|
+
image(key, path, options = {}) {
|
|
45
|
+
this._queue.set(key, { type: "image", path, pixelart: !!options.pixelart });
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @method audio
|
|
51
|
+
* @description Queues an audio file (returns an HTMLAudioElement on get()).
|
|
52
|
+
*/
|
|
53
|
+
audio(key, path) {
|
|
54
|
+
this._queue.set(key, { type: "audio", path });
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @method json
|
|
60
|
+
* @description Queues a JSON file (parsed on get()).
|
|
61
|
+
*/
|
|
62
|
+
json(key, path) {
|
|
63
|
+
this._queue.set(key, { type: "json", path });
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @method text
|
|
69
|
+
* @description Queues a plain-text file.
|
|
70
|
+
*/
|
|
71
|
+
text(key, path) {
|
|
72
|
+
this._queue.set(key, { type: "text", path });
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @method font
|
|
78
|
+
* @description Queues a web font. `key` is the font-family name you will use
|
|
79
|
+
* in CSS/canvas; `path` is the font file URL.
|
|
80
|
+
*/
|
|
81
|
+
font(key, path, descriptors = {}) {
|
|
82
|
+
this._queue.set(key, { type: "font", family: key, path, descriptors });
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @method onProgress
|
|
88
|
+
* @description Registers a callback invoked as (loaded, total) after each
|
|
89
|
+
* asset resolves.
|
|
90
|
+
*/
|
|
91
|
+
onProgress(handler) {
|
|
92
|
+
if (typeof handler === "function") this._progressHandlers.push(handler);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @method progress
|
|
98
|
+
* @returns {number} - Fraction loaded in [0, 1] (1 when nothing is queued).
|
|
99
|
+
*/
|
|
100
|
+
progress() {
|
|
101
|
+
return this._total === 0 ? 1 : this._loaded / this._total;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @method load
|
|
106
|
+
* @description Loads every queued asset, resolving when all are done. Failed
|
|
107
|
+
* assets reject the returned promise unless `continueOnError` is true, in
|
|
108
|
+
* which case they resolve to null and loading continues.
|
|
109
|
+
* @param {Object} [options] - { continueOnError = false }
|
|
110
|
+
* @returns {Promise<Map>} - The resolved assets map
|
|
111
|
+
*/
|
|
112
|
+
async load(options = {}) {
|
|
113
|
+
const { continueOnError = false } = options;
|
|
114
|
+
const entries = Array.from(this._queue.entries());
|
|
115
|
+
this._total = entries.length;
|
|
116
|
+
this._loaded = 0;
|
|
117
|
+
|
|
118
|
+
await Promise.all(
|
|
119
|
+
entries.map(async ([key, spec]) => {
|
|
120
|
+
try {
|
|
121
|
+
const asset = await this._loadOne(spec);
|
|
122
|
+
this.assets.set(key, asset);
|
|
123
|
+
if (spec.type === "image") {
|
|
124
|
+
this._paths.set(key, { path: spec.path, pixelart: spec.pixelart });
|
|
125
|
+
}
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (!continueOnError) throw err;
|
|
128
|
+
console.warn(`[AssetManager] Failed to load "${key}":`, err);
|
|
129
|
+
this.assets.set(key, null);
|
|
130
|
+
} finally {
|
|
131
|
+
this._loaded++;
|
|
132
|
+
for (const handler of this._progressHandlers) {
|
|
133
|
+
handler(this._loaded, this._total);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
this._queue.clear();
|
|
140
|
+
return this.assets;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** @private */
|
|
144
|
+
_loadOne(spec) {
|
|
145
|
+
switch (spec.type) {
|
|
146
|
+
case "image":
|
|
147
|
+
return TextureManager.loadImage(spec.path);
|
|
148
|
+
case "audio":
|
|
149
|
+
return AssetManager._loadAudio(spec.path);
|
|
150
|
+
case "json":
|
|
151
|
+
return fetch(spec.path).then((r) => {
|
|
152
|
+
if (!r.ok) throw new Error(`HTTP ${r.status} for ${spec.path}`);
|
|
153
|
+
return r.json();
|
|
154
|
+
});
|
|
155
|
+
case "text":
|
|
156
|
+
return fetch(spec.path).then((r) => {
|
|
157
|
+
if (!r.ok) throw new Error(`HTTP ${r.status} for ${spec.path}`);
|
|
158
|
+
return r.text();
|
|
159
|
+
});
|
|
160
|
+
case "font":
|
|
161
|
+
return AssetManager._loadFont(spec);
|
|
162
|
+
default:
|
|
163
|
+
return Promise.reject(new Error(`Unknown asset type: ${spec.type}`));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** @private */
|
|
168
|
+
static _loadAudio(path) {
|
|
169
|
+
return new Promise((resolve, reject) => {
|
|
170
|
+
const audio = new Audio();
|
|
171
|
+
audio.addEventListener("canplaythrough", () => resolve(audio), {
|
|
172
|
+
once: true,
|
|
173
|
+
});
|
|
174
|
+
audio.addEventListener("error", () =>
|
|
175
|
+
reject(new Error(`Failed to load audio: ${path}`))
|
|
176
|
+
);
|
|
177
|
+
audio.src = path;
|
|
178
|
+
audio.load();
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** @private */
|
|
183
|
+
static async _loadFont(spec) {
|
|
184
|
+
if (typeof FontFace === "undefined" || !document.fonts) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const face = new FontFace(
|
|
188
|
+
spec.family,
|
|
189
|
+
`url(${spec.path})`,
|
|
190
|
+
spec.descriptors
|
|
191
|
+
);
|
|
192
|
+
const loaded = await face.load();
|
|
193
|
+
document.fonts.add(loaded);
|
|
194
|
+
return loaded;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @method get
|
|
199
|
+
* @description Returns a previously loaded asset by key.
|
|
200
|
+
*/
|
|
201
|
+
get(key) {
|
|
202
|
+
return this.assets.get(key);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @method has
|
|
207
|
+
* @returns {boolean} - Whether an asset has been loaded for the key.
|
|
208
|
+
*/
|
|
209
|
+
has(key) {
|
|
210
|
+
return this.assets.has(key);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @method getTexture
|
|
215
|
+
* @description Convenience: returns the cached GL texture for an image asset
|
|
216
|
+
* that was queued with `image()`. Resolves once the upload completes.
|
|
217
|
+
* @param {string} key - The image asset key
|
|
218
|
+
* @returns {Promise<{texture: WebGLTexture, width: number, height: number}>}
|
|
219
|
+
*/
|
|
220
|
+
getTexture(key) {
|
|
221
|
+
const info = this._paths.get(key);
|
|
222
|
+
if (!info) return Promise.reject(new Error(`No image asset "${key}"`));
|
|
223
|
+
return TextureManager.getTexture(info.path, info.pixelart);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @method clear
|
|
228
|
+
* @description Forgets all loaded assets (does not free GL textures; use
|
|
229
|
+
* TextureManager.clear for that).
|
|
230
|
+
*/
|
|
231
|
+
clear() {
|
|
232
|
+
this.assets.clear();
|
|
233
|
+
this._queue.clear();
|
|
234
|
+
this._loaded = 0;
|
|
235
|
+
this._total = 0;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export default AssetManager;
|