@eva/plugin-renderer-particle 2.1.0-beta.1
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 +7 -0
- package/dist/EVA.plugin.renderer.particle.js +664 -0
- package/dist/EVA.plugin.renderer.particle.min.js +1 -0
- package/dist/plugin-renderer-particle.cjs.js +573 -0
- package/dist/plugin-renderer-particle.cjs.prod.js +1 -0
- package/dist/plugin-renderer-particle.d.ts +190 -0
- package/dist/plugin-renderer-particle.esm.js +567 -0
- package/index.js +7 -0
- package/package.json +27 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import { Component, decorators, OBSERVER_TYPE, resource } from '@eva/eva.js';
|
|
2
|
+
import { type } from '@eva/inspector-decorator';
|
|
3
|
+
import { Renderer, RendererSystem } from '@eva/plugin-renderer';
|
|
4
|
+
import { Particle, ParticleContainer } from 'pixi.js';
|
|
5
|
+
|
|
6
|
+
/******************************************************************************
|
|
7
|
+
Copyright (c) Microsoft Corporation.
|
|
8
|
+
|
|
9
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
10
|
+
purpose with or without fee is hereby granted.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
13
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
14
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
15
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
16
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
17
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
18
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
19
|
+
***************************************************************************** */
|
|
20
|
+
|
|
21
|
+
function __decorate(decorators, target, key, desc) {
|
|
22
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
23
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
24
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
25
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function __metadata(metadataKey, metadataValue) {
|
|
29
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
33
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
34
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
35
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
36
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
37
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
38
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
43
|
+
var e = new Error(message);
|
|
44
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
class ParticleEmitter extends Component {
|
|
48
|
+
constructor() {
|
|
49
|
+
super(...arguments);
|
|
50
|
+
this.resource = '';
|
|
51
|
+
this.auto = true;
|
|
52
|
+
this.duration = -1;
|
|
53
|
+
this.frequency = 250;
|
|
54
|
+
this.quantity = 1;
|
|
55
|
+
this.maxParticles = 500;
|
|
56
|
+
this.lifespan = 1000;
|
|
57
|
+
this.gravityX = 0;
|
|
58
|
+
this.gravityY = 0;
|
|
59
|
+
/** Runtime 标记:由 System 设置,触发时立即停止发射。 */
|
|
60
|
+
this.paused = false;
|
|
61
|
+
}
|
|
62
|
+
init(obj) {
|
|
63
|
+
if (obj)
|
|
64
|
+
Object.assign(this, obj);
|
|
65
|
+
}
|
|
66
|
+
/** 立刻停止发射(已存在的粒子继续运行直至 lifespan 到期)。 */
|
|
67
|
+
stop() {
|
|
68
|
+
this.paused = true;
|
|
69
|
+
}
|
|
70
|
+
/** 恢复发射。 */
|
|
71
|
+
start() {
|
|
72
|
+
this.paused = false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
ParticleEmitter.componentName = 'ParticleEmitter';
|
|
76
|
+
__decorate([
|
|
77
|
+
type('string'),
|
|
78
|
+
__metadata("design:type", String)
|
|
79
|
+
], ParticleEmitter.prototype, "resource", void 0);
|
|
80
|
+
__decorate([
|
|
81
|
+
type('boolean'),
|
|
82
|
+
__metadata("design:type", Boolean)
|
|
83
|
+
], ParticleEmitter.prototype, "auto", void 0);
|
|
84
|
+
__decorate([
|
|
85
|
+
type('number'),
|
|
86
|
+
__metadata("design:type", Number)
|
|
87
|
+
], ParticleEmitter.prototype, "duration", void 0);
|
|
88
|
+
__decorate([
|
|
89
|
+
type('number'),
|
|
90
|
+
__metadata("design:type", Number)
|
|
91
|
+
], ParticleEmitter.prototype, "frequency", void 0);
|
|
92
|
+
__decorate([
|
|
93
|
+
type('number'),
|
|
94
|
+
__metadata("design:type", Number)
|
|
95
|
+
], ParticleEmitter.prototype, "quantity", void 0);
|
|
96
|
+
__decorate([
|
|
97
|
+
type('number'),
|
|
98
|
+
__metadata("design:type", Number)
|
|
99
|
+
], ParticleEmitter.prototype, "maxParticles", void 0);
|
|
100
|
+
__decorate([
|
|
101
|
+
type('number'),
|
|
102
|
+
__metadata("design:type", Number)
|
|
103
|
+
], ParticleEmitter.prototype, "gravityX", void 0);
|
|
104
|
+
__decorate([
|
|
105
|
+
type('number'),
|
|
106
|
+
__metadata("design:type", Number)
|
|
107
|
+
], ParticleEmitter.prototype, "gravityY", void 0);
|
|
108
|
+
|
|
109
|
+
const TAU = Math.PI * 2;
|
|
110
|
+
const easings = {
|
|
111
|
+
linear: t => t,
|
|
112
|
+
'sine.in': t => 1 - Math.cos((t * Math.PI) / 2),
|
|
113
|
+
'sine.out': t => Math.sin((t * Math.PI) / 2),
|
|
114
|
+
'sine.inout': t => -(Math.cos(Math.PI * t) - 1) / 2,
|
|
115
|
+
'quad.in': t => t * t,
|
|
116
|
+
'quad.out': t => 1 - (1 - t) * (1 - t),
|
|
117
|
+
'quad.inout': t => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2),
|
|
118
|
+
'cubic.in': t => t * t * t,
|
|
119
|
+
'cubic.out': t => 1 - Math.pow(1 - t, 3),
|
|
120
|
+
'expo.out': t => (t === 1 ? 1 : 1 - Math.pow(2, -10 * t)),
|
|
121
|
+
};
|
|
122
|
+
function resolveEase(name) {
|
|
123
|
+
if (!name)
|
|
124
|
+
return easings.linear;
|
|
125
|
+
const k = name.toLowerCase();
|
|
126
|
+
return easings[k] || easings.linear;
|
|
127
|
+
}
|
|
128
|
+
function rand(min, max) {
|
|
129
|
+
return min + Math.random() * (max - min);
|
|
130
|
+
}
|
|
131
|
+
/** Range 取值:每次 emit 时算一次具体值。`{start, end}` 留给粒子自己每帧插值。 */
|
|
132
|
+
function sampleRange(value, fallback = 0) {
|
|
133
|
+
if (value == null)
|
|
134
|
+
return fallback;
|
|
135
|
+
if (typeof value === 'number')
|
|
136
|
+
return value;
|
|
137
|
+
if (Array.isArray(value))
|
|
138
|
+
return value[Math.floor(Math.random() * value.length)];
|
|
139
|
+
if ('min' in value && 'max' in value)
|
|
140
|
+
return rand(value.min, value.max);
|
|
141
|
+
if ('start' in value && 'end' in value)
|
|
142
|
+
return value.start;
|
|
143
|
+
return fallback;
|
|
144
|
+
}
|
|
145
|
+
function getRangeBounds(value) {
|
|
146
|
+
if (value == null || typeof value === 'number' || Array.isArray(value))
|
|
147
|
+
return null;
|
|
148
|
+
if ('start' in value && 'end' in value)
|
|
149
|
+
return value;
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
function samplePosition(zone) {
|
|
153
|
+
if (!zone)
|
|
154
|
+
return { x: 0, y: 0 };
|
|
155
|
+
return sampleShape(zone.shape, zone.type === 'edge');
|
|
156
|
+
}
|
|
157
|
+
function sampleShape(shape, edgeOnly) {
|
|
158
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
159
|
+
switch (shape.type) {
|
|
160
|
+
case 'point':
|
|
161
|
+
return { x: (_a = shape.x) !== null && _a !== void 0 ? _a : 0, y: (_b = shape.y) !== null && _b !== void 0 ? _b : 0 };
|
|
162
|
+
case 'rect': {
|
|
163
|
+
const x0 = (_c = shape.x) !== null && _c !== void 0 ? _c : 0;
|
|
164
|
+
const y0 = (_d = shape.y) !== null && _d !== void 0 ? _d : 0;
|
|
165
|
+
if (edgeOnly) {
|
|
166
|
+
const perim = 2 * (shape.width + shape.height);
|
|
167
|
+
const t = Math.random() * perim;
|
|
168
|
+
if (t < shape.width)
|
|
169
|
+
return { x: x0 + t, y: y0 };
|
|
170
|
+
if (t < shape.width + shape.height)
|
|
171
|
+
return { x: x0 + shape.width, y: y0 + (t - shape.width) };
|
|
172
|
+
if (t < 2 * shape.width + shape.height)
|
|
173
|
+
return { x: x0 + shape.width - (t - shape.width - shape.height), y: y0 + shape.height };
|
|
174
|
+
return { x: x0, y: y0 + shape.height - (t - 2 * shape.width - shape.height) };
|
|
175
|
+
}
|
|
176
|
+
return { x: rand(x0, x0 + shape.width), y: rand(y0, y0 + shape.height) };
|
|
177
|
+
}
|
|
178
|
+
case 'circle': {
|
|
179
|
+
const cx = (_e = shape.x) !== null && _e !== void 0 ? _e : 0;
|
|
180
|
+
const cy = (_f = shape.y) !== null && _f !== void 0 ? _f : 0;
|
|
181
|
+
const angle = Math.random() * TAU;
|
|
182
|
+
const r = edgeOnly ? shape.radius : shape.radius * Math.sqrt(Math.random());
|
|
183
|
+
return { x: cx + Math.cos(angle) * r, y: cy + Math.sin(angle) * r };
|
|
184
|
+
}
|
|
185
|
+
case 'ellipse': {
|
|
186
|
+
const cx = (_g = shape.x) !== null && _g !== void 0 ? _g : 0;
|
|
187
|
+
const cy = (_h = shape.y) !== null && _h !== void 0 ? _h : 0;
|
|
188
|
+
const angle = Math.random() * TAU;
|
|
189
|
+
const k = edgeOnly ? 1 : Math.sqrt(Math.random());
|
|
190
|
+
return { x: cx + Math.cos(angle) * shape.rx * k, y: cy + Math.sin(angle) * shape.ry * k };
|
|
191
|
+
}
|
|
192
|
+
case 'line': {
|
|
193
|
+
const t = Math.random();
|
|
194
|
+
return { x: shape.x1 + (shape.x2 - shape.x1) * t, y: shape.y1 + (shape.y2 - shape.y1) * t };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function pointInShape(shape, px, py) {
|
|
199
|
+
var _a, _b, _c, _d, _e, _f;
|
|
200
|
+
switch (shape.type) {
|
|
201
|
+
case 'rect': {
|
|
202
|
+
const x0 = (_a = shape.x) !== null && _a !== void 0 ? _a : 0;
|
|
203
|
+
const y0 = (_b = shape.y) !== null && _b !== void 0 ? _b : 0;
|
|
204
|
+
return px >= x0 && px <= x0 + shape.width && py >= y0 && py <= y0 + shape.height;
|
|
205
|
+
}
|
|
206
|
+
case 'circle': {
|
|
207
|
+
const dx = px - ((_c = shape.x) !== null && _c !== void 0 ? _c : 0);
|
|
208
|
+
const dy = py - ((_d = shape.y) !== null && _d !== void 0 ? _d : 0);
|
|
209
|
+
return dx * dx + dy * dy <= shape.radius * shape.radius;
|
|
210
|
+
}
|
|
211
|
+
case 'ellipse': {
|
|
212
|
+
const dx = (px - ((_e = shape.x) !== null && _e !== void 0 ? _e : 0)) / shape.rx;
|
|
213
|
+
const dy = (py - ((_f = shape.y) !== null && _f !== void 0 ? _f : 0)) / shape.ry;
|
|
214
|
+
return dx * dx + dy * dy <= 1;
|
|
215
|
+
}
|
|
216
|
+
case 'point':
|
|
217
|
+
case 'line':
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
class Emitter {
|
|
222
|
+
constructor(params, container, texture) {
|
|
223
|
+
this.frameTextures = null;
|
|
224
|
+
this.particles = [];
|
|
225
|
+
this.elapsed = 0;
|
|
226
|
+
this.emittedTotal = 0;
|
|
227
|
+
this.emitTimer = 0;
|
|
228
|
+
this.explodeDone = false;
|
|
229
|
+
this.params = params;
|
|
230
|
+
this.container = container;
|
|
231
|
+
this.texture = texture;
|
|
232
|
+
}
|
|
233
|
+
setTexture(texture) {
|
|
234
|
+
this.texture = texture;
|
|
235
|
+
}
|
|
236
|
+
/** Provide a texture pool when component.frame is set; emitOne samples from it. */
|
|
237
|
+
setFrameTextures(textures) {
|
|
238
|
+
this.frameTextures = textures && textures.length > 0 ? textures : null;
|
|
239
|
+
}
|
|
240
|
+
setParams(params) {
|
|
241
|
+
this.params = params;
|
|
242
|
+
}
|
|
243
|
+
destroy() {
|
|
244
|
+
this.particles.length = 0;
|
|
245
|
+
this.container.particleChildren.length = 0;
|
|
246
|
+
this.container.update();
|
|
247
|
+
}
|
|
248
|
+
update(dt) {
|
|
249
|
+
this.elapsed += dt;
|
|
250
|
+
this.advanceParticles(dt);
|
|
251
|
+
this.spawn(dt);
|
|
252
|
+
this.container.update();
|
|
253
|
+
}
|
|
254
|
+
advanceParticles(dt) {
|
|
255
|
+
var _a, _b;
|
|
256
|
+
const dtSec = dt / 1000;
|
|
257
|
+
const out = [];
|
|
258
|
+
for (const lp of this.particles) {
|
|
259
|
+
lp.age += dt;
|
|
260
|
+
if (lp.age > lp.lifespan) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
lp.vx += lp.ax * dtSec + ((_a = this.params.gravityX) !== null && _a !== void 0 ? _a : 0) * dtSec;
|
|
264
|
+
lp.vy += lp.ay * dtSec + ((_b = this.params.gravityY) !== null && _b !== void 0 ? _b : 0) * dtSec;
|
|
265
|
+
lp.particle.x += lp.vx * dtSec;
|
|
266
|
+
lp.particle.y += lp.vy * dtSec;
|
|
267
|
+
lp.particle.rotation += lp.rotateSpeed * dtSec;
|
|
268
|
+
const t = lp.age / lp.lifespan;
|
|
269
|
+
if (lp.scaleStart && lp.scaleEnd) {
|
|
270
|
+
const k = (lp.scaleEase || easings.linear)(t);
|
|
271
|
+
lp.particle.scaleX = lp.scaleStart.x + (lp.scaleEnd.x - lp.scaleStart.x) * k;
|
|
272
|
+
lp.particle.scaleY = lp.scaleStart.y + (lp.scaleEnd.y - lp.scaleStart.y) * k;
|
|
273
|
+
}
|
|
274
|
+
if (lp.alphaStart != null && lp.alphaEnd != null) {
|
|
275
|
+
const k = (lp.alphaEase || easings.linear)(t);
|
|
276
|
+
lp.particle.alpha = lp.alphaStart + (lp.alphaEnd - lp.alphaStart) * k;
|
|
277
|
+
}
|
|
278
|
+
if (this.params.deathZone && this.shouldDie(lp.particle.x, lp.particle.y)) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
out.push(lp);
|
|
282
|
+
}
|
|
283
|
+
this.particles = out;
|
|
284
|
+
this.container.particleChildren.length = 0;
|
|
285
|
+
for (const lp of this.particles)
|
|
286
|
+
this.container.particleChildren.push(lp.particle);
|
|
287
|
+
}
|
|
288
|
+
shouldDie(x, y) {
|
|
289
|
+
const dz = this.params.deathZone;
|
|
290
|
+
if (!dz)
|
|
291
|
+
return false;
|
|
292
|
+
const inside = pointInShape(dz.shape, x, y);
|
|
293
|
+
return dz.mode === 'onLeave' ? !inside : inside;
|
|
294
|
+
}
|
|
295
|
+
spawn(dt) {
|
|
296
|
+
var _a, _b, _c;
|
|
297
|
+
const p = this.params;
|
|
298
|
+
if (p.duration != null && p.duration > 0 && this.elapsed > p.duration)
|
|
299
|
+
return;
|
|
300
|
+
if (p.stopAfter != null && this.emittedTotal >= p.stopAfter)
|
|
301
|
+
return;
|
|
302
|
+
if (p.paused)
|
|
303
|
+
return;
|
|
304
|
+
const max = (_a = p.maxParticles) !== null && _a !== void 0 ? _a : 500;
|
|
305
|
+
if (this.particles.length >= max)
|
|
306
|
+
return;
|
|
307
|
+
if (p.explode != null && !this.explodeDone) {
|
|
308
|
+
const n = Math.min(p.explode, max - this.particles.length);
|
|
309
|
+
for (let i = 0; i < n; i++)
|
|
310
|
+
this.emitOne();
|
|
311
|
+
this.explodeDone = true;
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (p.explode != null)
|
|
315
|
+
return;
|
|
316
|
+
if (p.auto === false)
|
|
317
|
+
return;
|
|
318
|
+
this.emitTimer += dt;
|
|
319
|
+
const freq = (_b = p.frequency) !== null && _b !== void 0 ? _b : 250;
|
|
320
|
+
const quantity = (_c = p.quantity) !== null && _c !== void 0 ? _c : 1;
|
|
321
|
+
while (this.emitTimer >= freq && this.particles.length < max) {
|
|
322
|
+
for (let i = 0; i < quantity && this.particles.length < max; i++) {
|
|
323
|
+
if (p.stopAfter != null && this.emittedTotal >= p.stopAfter)
|
|
324
|
+
break;
|
|
325
|
+
this.emitOne();
|
|
326
|
+
}
|
|
327
|
+
this.emitTimer -= freq;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
emitOne() {
|
|
331
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
332
|
+
const p = this.params;
|
|
333
|
+
const pos = samplePosition(p.emitZone);
|
|
334
|
+
const speedScalar = sampleRange(p.speed, 0);
|
|
335
|
+
let vx;
|
|
336
|
+
let vy;
|
|
337
|
+
if (p.moveTo) {
|
|
338
|
+
const dx = p.moveTo.x - pos.x;
|
|
339
|
+
const dy = p.moveTo.y - pos.y;
|
|
340
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
341
|
+
vx = (dx / len) * (speedScalar || 100);
|
|
342
|
+
vy = (dy / len) * (speedScalar || 100);
|
|
343
|
+
}
|
|
344
|
+
else if (p.speedX != null || p.speedY != null) {
|
|
345
|
+
vx = sampleRange(p.speedX, 0);
|
|
346
|
+
vy = sampleRange(p.speedY, 0);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
const angleDeg = sampleRange(p.angle, 0);
|
|
350
|
+
const angleRad = (angleDeg * Math.PI) / 180;
|
|
351
|
+
vx = Math.cos(angleRad) * speedScalar;
|
|
352
|
+
vy = Math.sin(angleRad) * speedScalar;
|
|
353
|
+
}
|
|
354
|
+
const ax = sampleRange(p.accelerationX, 0);
|
|
355
|
+
const ay = sampleRange(p.accelerationY, 0);
|
|
356
|
+
const scaleX = p.scaleX != null ? sampleRange(p.scaleX, 1) : sampleRange(p.scale, 1);
|
|
357
|
+
const scaleY = p.scaleY != null ? sampleRange(p.scaleY, 1) : sampleRange(p.scale, 1);
|
|
358
|
+
const alpha = sampleRange(p.alpha, 1);
|
|
359
|
+
const rotation = sampleRange(p.rotate, 0);
|
|
360
|
+
const tintNum = Array.isArray(p.tint) ? p.tint[Math.floor(Math.random() * p.tint.length)] : (_a = p.tint) !== null && _a !== void 0 ? _a : 0xffffff;
|
|
361
|
+
const tex = this.frameTextures
|
|
362
|
+
? this.frameTextures[Math.floor(Math.random() * this.frameTextures.length)]
|
|
363
|
+
: this.texture;
|
|
364
|
+
const particle = new Particle({
|
|
365
|
+
texture: tex,
|
|
366
|
+
x: pos.x,
|
|
367
|
+
y: pos.y,
|
|
368
|
+
scaleX,
|
|
369
|
+
scaleY,
|
|
370
|
+
alpha,
|
|
371
|
+
rotation,
|
|
372
|
+
tint: tintNum,
|
|
373
|
+
anchorX: 0.5,
|
|
374
|
+
anchorY: 0.5,
|
|
375
|
+
});
|
|
376
|
+
const lp = {
|
|
377
|
+
particle,
|
|
378
|
+
age: 0,
|
|
379
|
+
lifespan: sampleRange(p.lifespan, 1000),
|
|
380
|
+
vx,
|
|
381
|
+
vy,
|
|
382
|
+
ax,
|
|
383
|
+
ay,
|
|
384
|
+
rotateSpeed: 0,
|
|
385
|
+
};
|
|
386
|
+
const scaleBounds = getRangeBounds(p.scale);
|
|
387
|
+
const scaleXBounds = getRangeBounds(p.scaleX);
|
|
388
|
+
const scaleYBounds = getRangeBounds(p.scaleY);
|
|
389
|
+
if (scaleBounds || scaleXBounds || scaleYBounds) {
|
|
390
|
+
const sxs = (_c = (_b = scaleXBounds === null || scaleXBounds === void 0 ? void 0 : scaleXBounds.start) !== null && _b !== void 0 ? _b : scaleBounds === null || scaleBounds === void 0 ? void 0 : scaleBounds.start) !== null && _c !== void 0 ? _c : scaleX;
|
|
391
|
+
const sxe = (_e = (_d = scaleXBounds === null || scaleXBounds === void 0 ? void 0 : scaleXBounds.end) !== null && _d !== void 0 ? _d : scaleBounds === null || scaleBounds === void 0 ? void 0 : scaleBounds.end) !== null && _e !== void 0 ? _e : scaleX;
|
|
392
|
+
const sys = (_g = (_f = scaleYBounds === null || scaleYBounds === void 0 ? void 0 : scaleYBounds.start) !== null && _f !== void 0 ? _f : scaleBounds === null || scaleBounds === void 0 ? void 0 : scaleBounds.start) !== null && _g !== void 0 ? _g : scaleY;
|
|
393
|
+
const sye = (_j = (_h = scaleYBounds === null || scaleYBounds === void 0 ? void 0 : scaleYBounds.end) !== null && _h !== void 0 ? _h : scaleBounds === null || scaleBounds === void 0 ? void 0 : scaleBounds.end) !== null && _j !== void 0 ? _j : scaleY;
|
|
394
|
+
lp.scaleStart = { x: sxs, y: sys };
|
|
395
|
+
lp.scaleEnd = { x: sxe, y: sye };
|
|
396
|
+
lp.scaleEase = resolveEase((_k = scaleXBounds === null || scaleXBounds === void 0 ? void 0 : scaleXBounds.ease) !== null && _k !== void 0 ? _k : scaleBounds === null || scaleBounds === void 0 ? void 0 : scaleBounds.ease);
|
|
397
|
+
particle.scaleX = sxs;
|
|
398
|
+
particle.scaleY = sys;
|
|
399
|
+
}
|
|
400
|
+
const alphaBounds = getRangeBounds(p.alpha);
|
|
401
|
+
if (alphaBounds) {
|
|
402
|
+
lp.alphaStart = alphaBounds.start;
|
|
403
|
+
lp.alphaEnd = alphaBounds.end;
|
|
404
|
+
lp.alphaEase = resolveEase(alphaBounds.ease);
|
|
405
|
+
particle.alpha = alphaBounds.start;
|
|
406
|
+
}
|
|
407
|
+
this.particles.push(lp);
|
|
408
|
+
this.container.particleChildren.push(particle);
|
|
409
|
+
this.emittedTotal++;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const SPRITE_KEY_SEP = '_s|r|c_'; // Sync with plugin-renderer-sprite resourceKeySplit.
|
|
414
|
+
/**
|
|
415
|
+
* Resolve resource instance to a primary texture + optional frame texture pool.
|
|
416
|
+
* - IMAGE resource → instance is a Texture, return single.
|
|
417
|
+
* - SPRITE resource → instance is `{[fullKey]: Texture}`. Pick frames listed in `frame`
|
|
418
|
+
* (string or array); fall back to first frame as the primary texture.
|
|
419
|
+
*/
|
|
420
|
+
/**
|
|
421
|
+
* Check if a value is a PixiJS Texture. Uses duck typing because separate copies of pixi.js
|
|
422
|
+
* may be loaded across plugins (ESM module duplication breaks `instanceof`).
|
|
423
|
+
*/
|
|
424
|
+
function isTexture(v) {
|
|
425
|
+
return !!v && v.isTexture === true && !!v.orig && !!v.uvs;
|
|
426
|
+
}
|
|
427
|
+
function pickFrameTextures(instance, resourceName, frame) {
|
|
428
|
+
if (isTexture(instance)) {
|
|
429
|
+
return { texture: instance, frameTextures: null };
|
|
430
|
+
}
|
|
431
|
+
if (instance && typeof instance === 'object') {
|
|
432
|
+
const lookup = (name) => {
|
|
433
|
+
var _a;
|
|
434
|
+
const fullKey = resourceName + SPRITE_KEY_SEP + name;
|
|
435
|
+
const v = (_a = instance[fullKey]) !== null && _a !== void 0 ? _a : instance[name];
|
|
436
|
+
return isTexture(v) ? v : null;
|
|
437
|
+
};
|
|
438
|
+
const frameNames = Array.isArray(frame) ? frame : frame ? [frame] : [];
|
|
439
|
+
const frameTextures = [];
|
|
440
|
+
for (const f of frameNames) {
|
|
441
|
+
const t = lookup(f);
|
|
442
|
+
if (t)
|
|
443
|
+
frameTextures.push(t);
|
|
444
|
+
}
|
|
445
|
+
if (frameTextures.length > 0) {
|
|
446
|
+
return { texture: frameTextures[0], frameTextures };
|
|
447
|
+
}
|
|
448
|
+
// No frame requested → fall back to first texture in the map.
|
|
449
|
+
for (const k in instance) {
|
|
450
|
+
const v = instance[k];
|
|
451
|
+
if (isTexture(v))
|
|
452
|
+
return { texture: v, frameTextures: null };
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return { texture: null, frameTextures: null };
|
|
456
|
+
}
|
|
457
|
+
let ParticleEmitterSystem = class ParticleEmitterSystem extends Renderer {
|
|
458
|
+
constructor() {
|
|
459
|
+
super(...arguments);
|
|
460
|
+
this.name = 'ParticleEmitter';
|
|
461
|
+
this.records = {};
|
|
462
|
+
this.lastDeltaTime = 1000 / 60;
|
|
463
|
+
}
|
|
464
|
+
init() {
|
|
465
|
+
this.renderSystem = this.game.getSystem(RendererSystem);
|
|
466
|
+
this.renderSystem.rendererManager.register(this);
|
|
467
|
+
}
|
|
468
|
+
update(e) {
|
|
469
|
+
if (e === null || e === void 0 ? void 0 : e.deltaTime)
|
|
470
|
+
this.lastDeltaTime = e.deltaTime;
|
|
471
|
+
super.update(e);
|
|
472
|
+
}
|
|
473
|
+
rendererUpdate(gameObject) {
|
|
474
|
+
const record = this.records[gameObject.id];
|
|
475
|
+
if (!record)
|
|
476
|
+
return;
|
|
477
|
+
record.emitter.setParams(record.component);
|
|
478
|
+
record.emitter.update(this.lastDeltaTime);
|
|
479
|
+
}
|
|
480
|
+
componentChanged(changed) {
|
|
481
|
+
var _a, _b, _c, _d;
|
|
482
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
483
|
+
if (changed.componentName !== 'ParticleEmitter')
|
|
484
|
+
return;
|
|
485
|
+
const component = changed.component;
|
|
486
|
+
const gameObjectId = changed.gameObject.id;
|
|
487
|
+
if (changed.type === OBSERVER_TYPE.ADD) {
|
|
488
|
+
const asyncId = this.increaseAsyncId(gameObjectId);
|
|
489
|
+
let texture = null;
|
|
490
|
+
let frameTextures = null;
|
|
491
|
+
if (component.resource) {
|
|
492
|
+
const { instance } = yield resource.getResource(component.resource);
|
|
493
|
+
if (!this.validateAsyncId(gameObjectId, asyncId))
|
|
494
|
+
return;
|
|
495
|
+
if (!instance) {
|
|
496
|
+
console.error(`GameObject:${changed.gameObject.name}'s ParticleEmitter resource load error`);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const picked = pickFrameTextures(instance, component.resource, component.frame);
|
|
500
|
+
texture = picked.texture;
|
|
501
|
+
frameTextures = picked.frameTextures;
|
|
502
|
+
}
|
|
503
|
+
if (!texture)
|
|
504
|
+
return;
|
|
505
|
+
const pc = new ParticleContainer({
|
|
506
|
+
dynamicProperties: { position: true, rotation: true, scale: true, color: true, uvs: true },
|
|
507
|
+
});
|
|
508
|
+
pc.texture = texture;
|
|
509
|
+
this.containerManager.getContainer(gameObjectId).addChildAt(pc, 0);
|
|
510
|
+
const emitter = new Emitter(component, pc, texture);
|
|
511
|
+
emitter.setFrameTextures(frameTextures);
|
|
512
|
+
this.records[gameObjectId] = { container: pc, emitter, component };
|
|
513
|
+
}
|
|
514
|
+
else if (changed.type === OBSERVER_TYPE.CHANGE) {
|
|
515
|
+
const record = this.records[gameObjectId];
|
|
516
|
+
if (!record)
|
|
517
|
+
return;
|
|
518
|
+
if (((_b = (_a = changed.prop) === null || _a === void 0 ? void 0 : _a.prop) === null || _b === void 0 ? void 0 : _b[0]) === 'resource') {
|
|
519
|
+
const asyncId = this.increaseAsyncId(gameObjectId);
|
|
520
|
+
const { instance } = yield resource.getResource(component.resource);
|
|
521
|
+
if (!this.validateAsyncId(gameObjectId, asyncId))
|
|
522
|
+
return;
|
|
523
|
+
if (instance) {
|
|
524
|
+
const picked = pickFrameTextures(instance, component.resource, component.frame);
|
|
525
|
+
if (picked.texture) {
|
|
526
|
+
record.container.texture = picked.texture;
|
|
527
|
+
record.emitter.setTexture(picked.texture);
|
|
528
|
+
}
|
|
529
|
+
record.emitter.setFrameTextures(picked.frameTextures);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
record.component = component;
|
|
533
|
+
record.emitter.setParams(component);
|
|
534
|
+
}
|
|
535
|
+
else if (changed.type === OBSERVER_TYPE.REMOVE) {
|
|
536
|
+
this.increaseAsyncId(gameObjectId);
|
|
537
|
+
const record = this.records[gameObjectId];
|
|
538
|
+
if (!record)
|
|
539
|
+
return;
|
|
540
|
+
record.emitter.destroy();
|
|
541
|
+
(_d = (_c = this.containerManager) === null || _c === void 0 ? void 0 : _c.getContainer(gameObjectId)) === null || _d === void 0 ? void 0 : _d.removeChild(record.container);
|
|
542
|
+
record.container.destroy({ children: true });
|
|
543
|
+
delete this.records[gameObjectId];
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
destroy() {
|
|
548
|
+
var _a, _b;
|
|
549
|
+
for (const key in this.records) {
|
|
550
|
+
const id = parseInt(key);
|
|
551
|
+
const record = this.records[id];
|
|
552
|
+
record.emitter.destroy();
|
|
553
|
+
(_b = (_a = this.containerManager) === null || _a === void 0 ? void 0 : _a.getContainer(id)) === null || _b === void 0 ? void 0 : _b.removeChild(record.container);
|
|
554
|
+
record.container.destroy({ children: true });
|
|
555
|
+
delete this.records[id];
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
ParticleEmitterSystem.systemName = 'ParticleEmitter';
|
|
560
|
+
ParticleEmitterSystem = __decorate([
|
|
561
|
+
decorators.componentObserver({
|
|
562
|
+
ParticleEmitter: [{ prop: ['resource'], deep: false }],
|
|
563
|
+
})
|
|
564
|
+
], ParticleEmitterSystem);
|
|
565
|
+
var ParticleEmitterSystem$1 = ParticleEmitterSystem;
|
|
566
|
+
|
|
567
|
+
export { Emitter, ParticleEmitter, ParticleEmitterSystem$1 as ParticleEmitterSystem };
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eva/plugin-renderer-particle",
|
|
3
|
+
"version": "2.1.0-beta.1",
|
|
4
|
+
"description": "@eva/plugin-renderer-particle",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"module": "dist/plugin-renderer-particle.esm.js",
|
|
7
|
+
"bundle": "EVA.plugin.renderer.particle",
|
|
8
|
+
"unpkg": "dist/EVA.plugin.renderer.particle.min.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"types": "dist/plugin-renderer-particle.d.ts",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"eva.js",
|
|
16
|
+
"eva-plugin-renderer-particle"
|
|
17
|
+
],
|
|
18
|
+
"author": "dingyike.dyk <dingyike.dyk@alibaba-inc.com>",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"homepage": "https://eva.js.org",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@eva/inspector-decorator": "^2.0.0-beta.0",
|
|
23
|
+
"@eva/eva.js": "2.1.0-beta.1",
|
|
24
|
+
"@eva/plugin-renderer": "2.1.0-beta.1",
|
|
25
|
+
"pixi.js": "^8.6.3"
|
|
26
|
+
}
|
|
27
|
+
}
|