emeraldengine 2.2.1 → 3.0.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 +2359 -968
- package/dist/types/index.d.ts +72 -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} +5 -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} +60 -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 +80 -0
- package/dist/types/src/ParticleEmitter.d.ts +131 -0
- package/dist/types/{Physics.d.ts → src/Physics.d.ts} +88 -20
- 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 +96 -0
- package/dist/types/src/components/GameObject.d.ts +135 -0
- package/dist/types/src/components/RigidBody.d.ts +183 -0
- package/dist/types/src/importers/Aseprite.d.ts +79 -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/IDManager.d.ts +31 -0
- package/dist/types/src/managers/InputManager.d.ts +290 -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/index.js +72 -0
- package/package.json +74 -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 +18 -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 +251 -118
- package/src/Interpolator.js +124 -0
- package/src/Material.js +202 -0
- package/src/MathUtils.js +133 -0
- package/src/ParticleEmitter.js +284 -0
- package/src/Physics.js +186 -5
- 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 +22 -3
- package/src/components/CircleCollider.js +19 -3
- package/src/components/CircleColliderDebug.js +24 -24
- package/src/components/Collider.js +140 -34
- package/src/components/GameObject.js +130 -21
- package/src/components/RigidBody.js +123 -2
- package/src/importers/Aseprite.js +142 -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/IDManager.js +24 -2
- package/src/managers/InputManager.js +779 -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/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/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
package/src/EmeraldDB.js
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class EmeraldDB
|
|
3
|
+
* @description Async game-save storage on IndexedDB — the big-world companion
|
|
4
|
+
* to `Storage` (localStorage). Same versioned-envelope semantics (`{v,t,data}`
|
|
5
|
+
* with a `.bak` backup and forward migration), but with no ~5MB quota and no
|
|
6
|
+
* JSON round-trip: values are structured-cloned, so large nested world state
|
|
7
|
+
* (tile grids, inventories, NPC state) saves fast and loads intact, including
|
|
8
|
+
* Maps, Sets, Dates, and typed arrays.
|
|
9
|
+
*
|
|
10
|
+
* All methods are async. The database opens lazily on first use; call
|
|
11
|
+
* `configure()` first if you want a custom database/store name.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Save / load a world with schema migration:
|
|
15
|
+
* await EmeraldDB.save("world", world, { version: 3 });
|
|
16
|
+
* const world = await EmeraldDB.load("world", {
|
|
17
|
+
* version: 3,
|
|
18
|
+
* fallback: makeNewWorld(),
|
|
19
|
+
* migrate: (old, from) => upgradeWorld(old, from),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Plain key/value (no envelope):
|
|
23
|
+
* await EmeraldDB.set("settings", { volume: 0.8 });
|
|
24
|
+
* const settings = await EmeraldDB.get("settings", {});
|
|
25
|
+
*/
|
|
26
|
+
class EmeraldDB {
|
|
27
|
+
/**
|
|
28
|
+
* @method isSupported
|
|
29
|
+
* @description Whether IndexedDB exists in this environment.
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
static isSupported() {
|
|
33
|
+
return typeof indexedDB !== "undefined" && indexedDB !== null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @method configure
|
|
38
|
+
* @description Sets the database and object-store names. Call before the
|
|
39
|
+
* first read/write (once the database is open the names are fixed until
|
|
40
|
+
* `close()`).
|
|
41
|
+
* @param {Object} [options] - { name = "emerald-db", store = "kv" }
|
|
42
|
+
* @returns {EmeraldDB} - the class, for chaining
|
|
43
|
+
*/
|
|
44
|
+
static configure(options = {}) {
|
|
45
|
+
if (options.name) EmeraldDB._name = options.name;
|
|
46
|
+
if (options.store) EmeraldDB._store = options.store;
|
|
47
|
+
return EmeraldDB;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @method _open
|
|
52
|
+
* @description Lazily opens (and creates) the database. Shared promise so
|
|
53
|
+
* concurrent calls open once.
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
static _open() {
|
|
57
|
+
if (EmeraldDB._db) return EmeraldDB._db;
|
|
58
|
+
if (!EmeraldDB.isSupported()) {
|
|
59
|
+
return Promise.reject(
|
|
60
|
+
new Error(
|
|
61
|
+
"[EmeraldDB] > IndexedDB is not available in this environment."
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
EmeraldDB._db = new Promise((resolve, reject) => {
|
|
66
|
+
const request = indexedDB.open(EmeraldDB._name, 1);
|
|
67
|
+
request.onupgradeneeded = () => {
|
|
68
|
+
const db = request.result;
|
|
69
|
+
if (!db.objectStoreNames.contains(EmeraldDB._store)) {
|
|
70
|
+
db.createObjectStore(EmeraldDB._store);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
request.onsuccess = () => {
|
|
74
|
+
const db = request.result;
|
|
75
|
+
db.onversionchange = () => {
|
|
76
|
+
db.close();
|
|
77
|
+
EmeraldDB._db = null;
|
|
78
|
+
};
|
|
79
|
+
resolve(db);
|
|
80
|
+
};
|
|
81
|
+
request.onerror = () => {
|
|
82
|
+
EmeraldDB._db = null;
|
|
83
|
+
reject(
|
|
84
|
+
request.error || new Error("[EmeraldDB] > Failed to open database.")
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
return EmeraldDB._db;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @method _tx
|
|
93
|
+
* @description Runs `fn(store)` inside a transaction and resolves with its
|
|
94
|
+
* request's result when the transaction completes.
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
static async _tx(mode, fn) {
|
|
98
|
+
const db = await EmeraldDB._open();
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
const tx = db.transaction(EmeraldDB._store, mode);
|
|
101
|
+
const store = tx.objectStore(EmeraldDB._store);
|
|
102
|
+
let result;
|
|
103
|
+
const request = fn(store);
|
|
104
|
+
if (request) {
|
|
105
|
+
request.onsuccess = () => {
|
|
106
|
+
result = request.result;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
tx.oncomplete = () => resolve(result);
|
|
110
|
+
tx.onerror = () => reject(tx.error);
|
|
111
|
+
tx.onabort = () =>
|
|
112
|
+
reject(tx.error || new Error("[EmeraldDB] > Transaction aborted."));
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @method set
|
|
118
|
+
* @description Stores a value under a key (structured clone — objects, Maps,
|
|
119
|
+
* Sets, typed arrays all survive).
|
|
120
|
+
* @param {string} key
|
|
121
|
+
* @param {*} value
|
|
122
|
+
* @returns {Promise<void>}
|
|
123
|
+
*/
|
|
124
|
+
static async set(key, value) {
|
|
125
|
+
await EmeraldDB._tx("readwrite", (store) => store.put(value, key));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @method get
|
|
130
|
+
* @description Reads a value; resolves `fallback` when the key is missing.
|
|
131
|
+
* @param {string} key
|
|
132
|
+
* @param {*} [fallback=undefined]
|
|
133
|
+
* @returns {Promise<*>}
|
|
134
|
+
*/
|
|
135
|
+
static async get(key, fallback = undefined) {
|
|
136
|
+
const value = await EmeraldDB._tx("readonly", (store) => store.get(key));
|
|
137
|
+
return value === undefined ? fallback : value;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @method remove
|
|
142
|
+
* @description Deletes a key.
|
|
143
|
+
* @returns {Promise<void>}
|
|
144
|
+
*/
|
|
145
|
+
static async remove(key) {
|
|
146
|
+
await EmeraldDB._tx("readwrite", (store) => store.delete(key));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @method keys
|
|
151
|
+
* @description Lists every key in the store.
|
|
152
|
+
* @returns {Promise<string[]>}
|
|
153
|
+
*/
|
|
154
|
+
static async keys() {
|
|
155
|
+
const keys = await EmeraldDB._tx("readonly", (store) => store.getAllKeys());
|
|
156
|
+
return keys || [];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @method clear
|
|
161
|
+
* @description Deletes everything in the store.
|
|
162
|
+
* @returns {Promise<void>}
|
|
163
|
+
*/
|
|
164
|
+
static async clear() {
|
|
165
|
+
await EmeraldDB._tx("readwrite", (store) => store.clear());
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @method save
|
|
170
|
+
* @description Writes a versioned save: the payload is wrapped in a
|
|
171
|
+
* `{ v, t, data }` envelope and the previous value (if any) is mirrored to
|
|
172
|
+
* `key + ".bak"` in the same transaction, so a torn write can always recover.
|
|
173
|
+
* @param {string} key
|
|
174
|
+
* @param {*} data - Any structured-cloneable value
|
|
175
|
+
* @param {Object} [options] - { version = 1, backup = true }
|
|
176
|
+
* @returns {Promise<void>}
|
|
177
|
+
*/
|
|
178
|
+
static async save(key, data, options = {}) {
|
|
179
|
+
const version = options.version ?? 1;
|
|
180
|
+
const envelope = { v: version, t: Date.now(), data };
|
|
181
|
+
const backup = options.backup !== false;
|
|
182
|
+
const db = await EmeraldDB._open();
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
const tx = db.transaction(EmeraldDB._store, "readwrite");
|
|
185
|
+
const store = tx.objectStore(EmeraldDB._store);
|
|
186
|
+
if (backup) {
|
|
187
|
+
const read = store.get(key);
|
|
188
|
+
read.onsuccess = () => {
|
|
189
|
+
if (read.result !== undefined) store.put(read.result, key + ".bak");
|
|
190
|
+
store.put(envelope, key);
|
|
191
|
+
};
|
|
192
|
+
} else {
|
|
193
|
+
store.put(envelope, key);
|
|
194
|
+
}
|
|
195
|
+
tx.oncomplete = () => resolve();
|
|
196
|
+
tx.onerror = () => reject(tx.error);
|
|
197
|
+
tx.onabort = () =>
|
|
198
|
+
reject(tx.error || new Error("[EmeraldDB] > Save aborted."));
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @method load
|
|
204
|
+
* @description Reads a versioned save. Falls back to the `.bak` mirror when
|
|
205
|
+
* the primary is missing/invalid, migrates old versions forward via
|
|
206
|
+
* `migrate(data, fromVersion)`, and (by default) rewrites migrated data in
|
|
207
|
+
* the new format.
|
|
208
|
+
* @param {string} key
|
|
209
|
+
* @param {Object} [options] - { version = 1, fallback = null, migrate, rewrite = true }
|
|
210
|
+
* @returns {Promise<*>}
|
|
211
|
+
*/
|
|
212
|
+
static async load(key, options = {}) {
|
|
213
|
+
let envelope = EmeraldDB._asEnvelope(await EmeraldDB.get(key));
|
|
214
|
+
if (!envelope) {
|
|
215
|
+
envelope = EmeraldDB._asEnvelope(await EmeraldDB.get(key + ".bak"));
|
|
216
|
+
}
|
|
217
|
+
const { data, migrated } = EmeraldDB._resolveEnvelope(envelope, options);
|
|
218
|
+
if (migrated && options.rewrite !== false) {
|
|
219
|
+
await EmeraldDB.save(key, data, { version: options.version ?? 1 });
|
|
220
|
+
}
|
|
221
|
+
return data;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* @method hasSave
|
|
226
|
+
* @description Whether a versioned save (or its backup) exists.
|
|
227
|
+
* @returns {Promise<boolean>}
|
|
228
|
+
*/
|
|
229
|
+
static async hasSave(key) {
|
|
230
|
+
if ((await EmeraldDB.get(key)) !== undefined) return true;
|
|
231
|
+
return (await EmeraldDB.get(key + ".bak")) !== undefined;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @method removeSave
|
|
236
|
+
* @description Deletes a versioned save AND its backup.
|
|
237
|
+
* @returns {Promise<void>}
|
|
238
|
+
*/
|
|
239
|
+
static async removeSave(key) {
|
|
240
|
+
await EmeraldDB.remove(key);
|
|
241
|
+
await EmeraldDB.remove(key + ".bak");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @method importFromStorage
|
|
246
|
+
* @description Copies a save written by the localStorage `Storage` class
|
|
247
|
+
* into EmeraldDB (envelope preserved). Use once when upgrading an existing
|
|
248
|
+
* game to IndexedDB saves.
|
|
249
|
+
* @param {string} key
|
|
250
|
+
* @returns {Promise<boolean>} - true if something was imported
|
|
251
|
+
*/
|
|
252
|
+
static async importFromStorage(key) {
|
|
253
|
+
if (typeof localStorage === "undefined") return false;
|
|
254
|
+
const raw = localStorage.getItem(key);
|
|
255
|
+
if (raw == null) return false;
|
|
256
|
+
let value;
|
|
257
|
+
try {
|
|
258
|
+
value = JSON.parse(raw);
|
|
259
|
+
} catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
await EmeraldDB.set(key, value);
|
|
263
|
+
const bak = localStorage.getItem(key + ".bak");
|
|
264
|
+
if (bak != null) {
|
|
265
|
+
try {
|
|
266
|
+
await EmeraldDB.set(key + ".bak", JSON.parse(bak));
|
|
267
|
+
} catch {}
|
|
268
|
+
}
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @method close
|
|
274
|
+
* @description Closes the database handle (reopens lazily on next use).
|
|
275
|
+
*/
|
|
276
|
+
static async close() {
|
|
277
|
+
if (!EmeraldDB._db) return;
|
|
278
|
+
try {
|
|
279
|
+
const db = await EmeraldDB._db;
|
|
280
|
+
db.close();
|
|
281
|
+
} catch {}
|
|
282
|
+
EmeraldDB._db = null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @method _asEnvelope
|
|
287
|
+
* @description Returns the value if it looks like a save envelope, else null.
|
|
288
|
+
* A plain (pre-versioning) object is wrapped as version 0 so it can migrate.
|
|
289
|
+
* @private
|
|
290
|
+
*/
|
|
291
|
+
static _asEnvelope(value) {
|
|
292
|
+
if (value === undefined || value === null) return null;
|
|
293
|
+
if (
|
|
294
|
+
typeof value === "object" &&
|
|
295
|
+
typeof value.v === "number" &&
|
|
296
|
+
"data" in value
|
|
297
|
+
) {
|
|
298
|
+
return value;
|
|
299
|
+
}
|
|
300
|
+
return { v: 0, t: 0, data: value };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @method _resolveEnvelope
|
|
305
|
+
* @description Pure envelope resolution shared by load(): applies fallback
|
|
306
|
+
* and forward migration. Exposed for tests.
|
|
307
|
+
* @private
|
|
308
|
+
*/
|
|
309
|
+
static _resolveEnvelope(envelope, options = {}) {
|
|
310
|
+
const version = options.version ?? 1;
|
|
311
|
+
const fallback = options.fallback ?? null;
|
|
312
|
+
if (!envelope) return { data: fallback, migrated: false };
|
|
313
|
+
if (envelope.v === version) return { data: envelope.data, migrated: false };
|
|
314
|
+
if (typeof options.migrate === "function") {
|
|
315
|
+
return {
|
|
316
|
+
data: options.migrate(envelope.data, envelope.v),
|
|
317
|
+
migrated: true,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return { data: fallback, migrated: false };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
EmeraldDB._name = "emerald-db";
|
|
325
|
+
EmeraldDB._store = "kv";
|
|
326
|
+
EmeraldDB._db = null;
|
|
327
|
+
|
|
328
|
+
export default EmeraldDB;
|
package/src/FPSCounter.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @class FPSCounter
|
|
3
|
-
* @description Represents a FPS counter
|
|
4
|
-
*/
|
|
5
|
-
class FPSCounter {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.lastFrameTime = performance.now();
|
|
8
|
-
this.frameCount = 0;
|
|
9
|
-
this.fps = 0;
|
|
10
|
-
this.fpsElement = document.createElement("div");
|
|
11
|
-
this.fpsElement.style.position = "absolute";
|
|
12
|
-
this.fpsElement.style.top = "10px";
|
|
13
|
-
this.fpsElement.style.left = "10px";
|
|
14
|
-
this.fpsElement.style.color = "white";
|
|
15
|
-
this.fpsElement.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
|
|
16
|
-
this.fpsElement.style.padding = "5px";
|
|
17
|
-
this.fpsElement.style.fontFamily = "Arial";
|
|
18
|
-
this.fpsElement.style.fontSize = "14px";
|
|
19
|
-
document.body.appendChild(this.fpsElement);
|
|
20
|
-
this.accumulatedTime = 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @method update
|
|
25
|
-
* @description Updates the FPS counter
|
|
26
|
-
*/
|
|
27
|
-
update() {
|
|
28
|
-
const now = performance.now();
|
|
29
|
-
const delta = now - this.lastFrameTime;
|
|
30
|
-
this.lastFrameTime = now;
|
|
31
|
-
this.accumulatedTime += delta;
|
|
32
|
-
this.frameCount++;
|
|
33
|
-
|
|
34
|
-
if (this.accumulatedTime >= 1000) {
|
|
35
|
-
this.fps = this.frameCount;
|
|
36
|
-
this.frameCount = 0;
|
|
37
|
-
this.accumulatedTime = 0;
|
|
38
|
-
this.fpsElement.textContent = `FPS: ${this.fps}`;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default FPSCounter;
|
|
1
|
+
/**
|
|
2
|
+
* @class FPSCounter
|
|
3
|
+
* @description Represents a FPS counter
|
|
4
|
+
*/
|
|
5
|
+
class FPSCounter {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.lastFrameTime = performance.now();
|
|
8
|
+
this.frameCount = 0;
|
|
9
|
+
this.fps = 0;
|
|
10
|
+
this.fpsElement = document.createElement("div");
|
|
11
|
+
this.fpsElement.style.position = "absolute";
|
|
12
|
+
this.fpsElement.style.top = "10px";
|
|
13
|
+
this.fpsElement.style.left = "10px";
|
|
14
|
+
this.fpsElement.style.color = "white";
|
|
15
|
+
this.fpsElement.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
|
|
16
|
+
this.fpsElement.style.padding = "5px";
|
|
17
|
+
this.fpsElement.style.fontFamily = "Arial";
|
|
18
|
+
this.fpsElement.style.fontSize = "14px";
|
|
19
|
+
document.body.appendChild(this.fpsElement);
|
|
20
|
+
this.accumulatedTime = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @method update
|
|
25
|
+
* @description Updates the FPS counter
|
|
26
|
+
*/
|
|
27
|
+
update() {
|
|
28
|
+
const now = performance.now();
|
|
29
|
+
const delta = now - this.lastFrameTime;
|
|
30
|
+
this.lastFrameTime = now;
|
|
31
|
+
this.accumulatedTime += delta;
|
|
32
|
+
this.frameCount++;
|
|
33
|
+
|
|
34
|
+
if (this.accumulatedTime >= 1000) {
|
|
35
|
+
this.fps = this.frameCount;
|
|
36
|
+
this.frameCount = 0;
|
|
37
|
+
this.accumulatedTime = 0;
|
|
38
|
+
this.fpsElement.textContent = `FPS: ${this.fps}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default FPSCounter;
|
package/src/GLUtils.js
CHANGED
|
@@ -1,67 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
gl.
|
|
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
|
-
gl.
|
|
53
|
-
gl.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);
|
|
62
|
-
buffer.itemSize = itemSize;
|
|
63
|
-
buffer.numItems = data.length / itemSize;
|
|
64
|
-
return buffer;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { initShaderProgram, loadShader, initVertexBuffer, initInstancedBuffer };
|
|
1
|
+
function initShaderProgram(gl, vShader, fShader) {
|
|
2
|
+
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vShader);
|
|
3
|
+
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fShader);
|
|
4
|
+
|
|
5
|
+
const shaderProgram = gl.createProgram();
|
|
6
|
+
gl.attachShader(shaderProgram, vertexShader);
|
|
7
|
+
gl.attachShader(shaderProgram, fragmentShader);
|
|
8
|
+
gl.linkProgram(shaderProgram);
|
|
9
|
+
|
|
10
|
+
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
|
|
11
|
+
alert(
|
|
12
|
+
`[GLUtils.js] > Unable to initialize the shader program: ${gl.getProgramInfoLog(
|
|
13
|
+
shaderProgram
|
|
14
|
+
)}`
|
|
15
|
+
);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return shaderProgram;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadShader(gl, type, source) {
|
|
22
|
+
const shader = gl.createShader(type);
|
|
23
|
+
|
|
24
|
+
gl.shaderSource(shader, source);
|
|
25
|
+
|
|
26
|
+
gl.compileShader(shader);
|
|
27
|
+
|
|
28
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
29
|
+
alert(
|
|
30
|
+
`[GLUtils.js] An error occurred compiling the shaders: ${gl.getShaderInfoLog(
|
|
31
|
+
shader
|
|
32
|
+
)}`
|
|
33
|
+
);
|
|
34
|
+
gl.deleteShader(shader);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return shader;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const initVertexBuffer = (gl, vertices) => {
|
|
42
|
+
const floatArray = new Float32Array(vertices);
|
|
43
|
+
const buffer = gl.createBuffer();
|
|
44
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
45
|
+
gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW);
|
|
46
|
+
buffer._bufferSize = floatArray.byteLength;
|
|
47
|
+
buffer._src = floatArray;
|
|
48
|
+
return buffer;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function initInstancedBuffer(gl, data, itemSize) {
|
|
52
|
+
const buffer = gl.createBuffer();
|
|
53
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
54
|
+
gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);
|
|
55
|
+
buffer.itemSize = itemSize;
|
|
56
|
+
buffer.numItems = data.length / itemSize;
|
|
57
|
+
return buffer;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { initShaderProgram, loadShader, initVertexBuffer, initInstancedBuffer };
|
package/src/Instance.js
CHANGED
|
@@ -26,7 +26,6 @@ class Instance {
|
|
|
26
26
|
this.parent = null;
|
|
27
27
|
this.frame = frame;
|
|
28
28
|
|
|
29
|
-
// Animation properties
|
|
30
29
|
this.isAnimating = false;
|
|
31
30
|
this.animationFrames = [];
|
|
32
31
|
this.currentAnimationIndex = 0;
|
|
@@ -35,8 +34,45 @@ class Instance {
|
|
|
35
34
|
this.playOnce = false;
|
|
36
35
|
this.originalFrame = frame;
|
|
37
36
|
|
|
38
|
-
// Components
|
|
39
37
|
this.components = [];
|
|
38
|
+
|
|
39
|
+
/** @private */
|
|
40
|
+
this._tint = null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @method setColor
|
|
45
|
+
* @description Sets this instance's tint. Accepts a Color (0..255 channels) or
|
|
46
|
+
* raw 0..1 components. Multiplies the texture/base color in the shader.
|
|
47
|
+
* @param {Color|number} colorOrR - A Color instance, or the red channel
|
|
48
|
+
* @param {number} [g]
|
|
49
|
+
* @param {number} [b]
|
|
50
|
+
* @param {number} [a]
|
|
51
|
+
*/
|
|
52
|
+
setColor(colorOrR, g, b, a = 1) {
|
|
53
|
+
if (colorOrR && typeof colorOrR === "object") {
|
|
54
|
+
this._tint = [
|
|
55
|
+
(colorOrR.r ?? 255) / 255,
|
|
56
|
+
(colorOrR.g ?? 255) / 255,
|
|
57
|
+
(colorOrR.b ?? 255) / 255,
|
|
58
|
+
(colorOrR.a ?? 255) / 255,
|
|
59
|
+
];
|
|
60
|
+
} else {
|
|
61
|
+
this._tint = [colorOrR, g, b, a];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @method setTexCoords
|
|
67
|
+
* @description Gives this instance an explicit UV quad (8 floats, one vec2
|
|
68
|
+
* per corner in the same order as getFrameTexCoords) instead of the shared
|
|
69
|
+
* frame grid. Lets one InstancedTexture batch tiles from an atlas with
|
|
70
|
+
* margins/spacing or per-instance flips. Pass null to go back to `frame`.
|
|
71
|
+
* @param {number[]|Float32Array|null} uv8 - 8 UV floats, or null to clear
|
|
72
|
+
*/
|
|
73
|
+
setTexCoords(uv8) {
|
|
74
|
+
/** @private */
|
|
75
|
+
this._regionUV = uv8 ? Array.from(uv8) : null;
|
|
40
76
|
}
|
|
41
77
|
|
|
42
78
|
/**
|
|
@@ -131,15 +167,15 @@ class Instance {
|
|
|
131
167
|
if (this.playOnce) {
|
|
132
168
|
this.stopAnimation();
|
|
133
169
|
} else {
|
|
134
|
-
this.currentAnimationIndex = 0;
|
|
170
|
+
this.currentAnimationIndex = 0;
|
|
135
171
|
}
|
|
136
172
|
}
|
|
137
173
|
|
|
138
174
|
this.lastFrameTime = currentTime;
|
|
139
|
-
return true;
|
|
175
|
+
return true;
|
|
140
176
|
}
|
|
141
177
|
|
|
142
|
-
return false;
|
|
178
|
+
return false;
|
|
143
179
|
}
|
|
144
180
|
|
|
145
181
|
/**
|