消除我特牛
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
xiaochuwoteniu/插件/Particle/MyParticleSimulator.ts

665 lines
24 KiB

//const { ccclass, property } = cc._decorator;
/**
* 单个粒子信息
*/
export class MyParticle {
pos = cc.v2(0, 0);
startPos = cc.v2(0, 0);
color = cc.color(0, 0, 0, 255);
deltaColor = { r: 0, g: 0, b: 0, a: 255 };
size = 0;
deltaSize = 0;
rotation = 0;
deltaRotation = 0;
timeToLive = 0;
drawPos = cc.v2(0, 0);
aspectRatio = 1;
// Mode A
dir = cc.v2(0, 0);
radialAccel = 0;
tangentialAccel = 0;
// Mode B
angle = 0;
degreesPerSecond = 0;
radius = 0;
deltaRadius = 0;
frameindex = 0//当前帧
lastFrameTime = 0;
topLife = 0; //当刷新帧率为-1时,自动按总生活周期
init() {
this.pos.set(cc.Vec2.ZERO)
this.startPos.set(cc.Vec2.ZERO)
this.color.fromHEX('0xFF000000');
this.deltaColor.r = this.deltaColor.g = this.deltaColor.b = 0;
this.deltaColor.a = 255;
this.size = 0;
this.deltaSize = 0;
this.rotation = 0;
this.deltaRotation = 0;
this.timeToLive = 0;
this.drawPos.set(cc.Vec2.ZERO)
this.aspectRatio = 1;
// Mode A
this.dir.set(cc.Vec2.ZERO)
this.radialAccel = 0;
this.tangentialAccel = 0;
// Mode B
this.angle = 0;
this.degreesPerSecond = 0;
this.radius = 0;
this.deltaRadius = 0;
this.frameindex = 0//当前帧
this.lastFrameTime = 0;
this.topLife = 0; //当刷新帧率为-1时,自动按总生活周期
}
}
export default class MyParticleSimulator {
pool: cc.js.Pool;
// sys: MyParticleSysInfo;
sys: cc.ParticleSystem;
particles: MyParticle[] = null;
active: boolean = false;
readyToPlay: boolean = true;
finished: boolean = false;
elapsed: number = 0;
emitCounter: number = 0;
_uvFilled: number = 0;
_worldRotation: number = 0;
animationRate: number = 0; //帧率
animFrameNums: number = 0; //图片有几帧
_pos = new cc.Vec2(0, 0);
_tpa = new cc.Vec2(0, 0);
_tpb = new cc.Vec2(0, 0);
_tpc = new cc.Vec2(0, 0);
constructor(system, frameNums, animRate) {
this.pool = new cc.js.Pool((par: MyParticle) => {
par.pos.set(cc.Vec2.ZERO)
par.startPos.set(cc.Vec2.ZERO)
par.color.fromHEX('0xFF000000');
par.deltaColor.r = par.deltaColor.g = par.deltaColor.b = 0;
par.deltaColor.a = 255;
par.size = 0;
par.deltaSize = 0;
par.rotation = 0;
par.deltaRotation = 0;
par.timeToLive = 0;
par.drawPos.set(cc.Vec2.ZERO)
par.aspectRatio = 1;
// Mode A
par.dir.set(cc.Vec2.ZERO)
par.radialAccel = 0;
par.tangentialAccel = 0;
// Mode B
par.angle = 0;
par.degreesPerSecond = 0;
par.radius = 0;
par.deltaRadius = 0;
par.frameindex = 0//当前帧
par.lastFrameTime = 0;
par.topLife = 0; //当刷新帧率为-1时,自动按总生活周期
}, 1024);
this.sys = system;
this.particles = [];
this.active = false;
this.readyToPlay = true;
this.finished = false;
this.elapsed = 0;
this.emitCounter = 0;
this._uvFilled = 0;
this._worldRotation = 0;
this.animationRate = animRate;
this.animFrameNums = frameNums;
this.pool.get = function () {
return this._get() || new MyParticle();
};
}
stop() {
this.active = false;
this.readyToPlay = false;
this.elapsed = this.sys.duration;
this.emitCounter = 0;
}
reset() {
cc.js.Pool
this.active = true;
this.readyToPlay = true;
this.elapsed = 0;
this.emitCounter = 0;
this.finished = false;
let particles = this.particles;
for (let id = 0; id < particles.length; ++id) {
this.pool.put(particles[id]);
}
particles.length = 0;
}
/**
* 开始喷射
*/
emitParticle(pos) {
let psys = this.sys;
let clampf = cc.misc.clampf;
let particle = this.pool.get();
this.particles.push(particle);
particle.timeToLive = psys.life + psys.lifeVar * (Math.random() - 0.5) * 2;
// Init particle
// timeToLive
// no negative life. prevent division by 0
particle.timeToLive = psys.life + psys.lifeVar * (Math.random() - 0.5) * 2;
let timeToLive = particle.timeToLive = Math.max(0, particle.timeToLive);
// position
particle.pos.x = psys.sourcePos.x + psys.posVar.x * (Math.random() - 0.5) * 2;
particle.pos.y = psys.sourcePos.y + psys.posVar.y * (Math.random() - 0.5) * 2;
// Color
let sr, sg, sb, sa;
let startColor = psys.startColor, startColorVar = psys.startColorVar;
let endColor = psys.endColor, endColorVar = psys.endColorVar;
particle.color.r = sr = clampf(startColor.r + startColorVar.r * (Math.random() - 0.5) * 2, 0, 255);
particle.color.g = sg = clampf(startColor.g + startColorVar.g * (Math.random() - 0.5) * 2, 0, 255);
particle.color.b = sb = clampf(startColor.b + startColorVar.b * (Math.random() - 0.5) * 2, 0, 255);
particle.color.a = sa = clampf(startColor.a + startColorVar.a * (Math.random() - 0.5) * 2, 0, 255);
particle.deltaColor.r = (clampf(endColor.r + endColorVar.r * (Math.random() - 0.5) * 2, 0, 255) - sr) / timeToLive;
particle.deltaColor.g = (clampf(endColor.g + endColorVar.g * (Math.random() - 0.5) * 2, 0, 255) - sg) / timeToLive;
particle.deltaColor.b = (clampf(endColor.b + endColorVar.b * (Math.random() - 0.5) * 2, 0, 255) - sb) / timeToLive;
particle.deltaColor.a = (clampf(endColor.a + endColorVar.a * (Math.random() - 0.5) * 2, 0, 255) - sa) / timeToLive;
// size
let startS = psys.startSize + psys.startSizeVar * (Math.random() - 0.5) * 2;
startS = Math.max(0, startS); // No negative value
particle.size = startS;
if (psys.endSize === cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE) {
particle.deltaSize = 0;
} else {
var endS = psys.endSize + psys.endSizeVar * (Math.random() - 0.5) * 2;
endS = Math.max(0, endS); // No negative values
particle.deltaSize = (endS - startS) / timeToLive;
}
// rotation
var startA = psys.startSpin + psys.startSpinVar * (Math.random() - 0.5) * 2;
var endA = psys.endSpin + psys.endSpinVar * (Math.random() - 0.5) * 2;
particle.rotation = startA;
particle.deltaRotation = (endA - startA) / timeToLive;
// position
particle.startPos.x = pos.x;
particle.startPos.y = pos.y;
// aspect ratio
particle.aspectRatio = this.sys['_aspectRatio'] || 1;
// direction
let a = cc.misc.degreesToRadians(psys.angle + this._worldRotation + psys.angleVar * (Math.random() - 0.5) * 2);
// Mode Gravity: A
if (psys.emitterMode === cc.ParticleSystem.EmitterMode.GRAVITY) {
let s = psys.speed + psys.speedVar * (Math.random() - 0.5) * 2;
// direction
particle.dir.x = Math.cos(a);
particle.dir.y = Math.sin(a);
particle.dir.mulSelf(s);
// radial accel
particle.radialAccel = psys.radialAccel + psys.radialAccelVar * (Math.random() - 0.5) * 2;
// tangential accel
particle.tangentialAccel = psys.tangentialAccel + psys.tangentialAccelVar * (Math.random() - 0.5) * 2;
// rotation is dir
if (psys.rotationIsDir) {
particle.rotation = -cc.misc.radiansToDegrees(Math.atan2(particle.dir.y, particle.dir.x));
}
}
// Mode Radius: B
else {
// Set the default diameter of the particle from the source position
var startRadius = psys.startRadius + psys.startRadiusVar * (Math.random() - 0.5) * 2;
var endRadius = psys.endRadius + psys.endRadiusVar * (Math.random() - 0.5) * 2;
particle.radius = startRadius;
particle.deltaRadius = (psys.endRadius === cc.ParticleSystem.START_RADIUS_EQUAL_TO_END_RADIUS) ? 0 : (endRadius - startRadius) / timeToLive;
particle.angle = a;
particle.degreesPerSecond = cc.misc.degreesToRadians(psys.rotatePerS + psys.rotatePerSVar * (Math.random() - 0.5) * 2);
}
particle.frameindex = 0;
particle.lastFrameTime = particle.timeToLive;
particle.topLife = particle.timeToLive;
}
getWorldRotation(node) {
let rotation = 0;
let tempNode = node;
while (tempNode) {
rotation += tempNode.angle;
tempNode = tempNode.parent;
}
return rotation;
}
updateUVs(force) {
let assembler = this.sys['_assembler'];
if (!assembler) {
return;
}
let buffer = this.sys['_assembler'].getBuffer();
if (buffer) {
const FLOAT_PER_PARTICLE = 4 * this.sys['_assembler']._vfmt._bytes / 4;
let vbuf = buffer._vData;
let uv = this.sys['_renderSpriteFrame'].uv;
let start = force ? 0 : this._uvFilled;
let particleCount = this.particles.length;
for (let i = start; i < particleCount; i++) {
let offset = i * FLOAT_PER_PARTICLE;
vbuf[offset + 2] = uv[0];
vbuf[offset + 3] = uv[1];
vbuf[offset + 7] = uv[2];
vbuf[offset + 8] = uv[3];
vbuf[offset + 12] = uv[4];
vbuf[offset + 13] = uv[5];
vbuf[offset + 17] = uv[6];
vbuf[offset + 18] = uv[7];
}
this._uvFilled = particleCount;
}
}
updateParticleBuffer(particle, pos, buffer, offset) {
let vbuf = buffer._vData;
let uintbuf = buffer._uintVData;
let x = pos.x, y = pos.y;
let width = particle.size;
let height = width;
let aspectRatio = particle.aspectRatio;
aspectRatio > 1 ? (height = width / aspectRatio) : (width = height * aspectRatio);
width = width / this.animFrameNums;
let halfWidth = width / 2;
let halfHeight = height / 2;
// pos
if (particle.rotation) {
let x1 = -halfWidth, y1 = -halfHeight;
let x2 = halfWidth, y2 = halfHeight;
let rad = -cc.misc.degreesToRadians(particle.rotation);
let cr = Math.cos(rad), sr = Math.sin(rad);
// bl
vbuf[offset] = x1 * cr - y1 * sr + x;
vbuf[offset + 1] = x1 * sr + y1 * cr + y;
// br
vbuf[offset + 5] = x2 * cr - y1 * sr + x;
vbuf[offset + 6] = x2 * sr + y1 * cr + y;
// tl
vbuf[offset + 10] = x1 * cr - y2 * sr + x;
vbuf[offset + 11] = x1 * sr + y2 * cr + y;
// tr
vbuf[offset + 15] = x2 * cr - y2 * sr + x;
vbuf[offset + 16] = x2 * sr + y2 * cr + y;
}
else {
// bl
vbuf[offset] = x - halfWidth;
vbuf[offset + 1] = y - halfHeight;
// br
vbuf[offset + 5] = x + halfWidth;
vbuf[offset + 6] = y - halfHeight;
// tl
vbuf[offset + 10] = x - halfWidth;
vbuf[offset + 11] = y + halfHeight;
// tr
vbuf[offset + 15] = x + halfWidth;
vbuf[offset + 16] = y + halfHeight;
}
if (this.animFrameNums > 1) {
if (this.animationRate == -1) {
//自动的
var fix = 1 / this.animFrameNums;
let fixTimes = particle.topLife / this.animFrameNums;
let idx = Math.floor(particle.timeToLive / fixTimes);
idx = this.animFrameNums - idx - 1;
vbuf[offset + 2] = fix * idx;
vbuf[offset + 12] = fix * idx;
vbuf[offset + 7] = vbuf[offset + 2] + fix;
vbuf[offset + 17] = vbuf[offset + 7];
} else {
let duration = 1 / this.animationRate;
var fix = 1 / this.animFrameNums;
vbuf[offset + 2] = fix * particle.frameindex;
vbuf[offset + 12] = fix * particle.frameindex;
vbuf[offset + 7] = vbuf[offset + 2] + fix;
vbuf[offset + 17] = vbuf[offset + 7];
if (particle.lastFrameTime - particle.timeToLive > duration) {
particle.frameindex += 1;
particle.lastFrameTime = particle.timeToLive;
if (particle.frameindex >= this.animFrameNums) {
particle.frameindex = 0;
}
}
}
}
// color
uintbuf[offset + 4] = particle.color._val;
uintbuf[offset + 9] = particle.color._val;
uintbuf[offset + 14] = particle.color._val;
uintbuf[offset + 19] = particle.color._val;
}
step(dt) {
dt = dt > cc.director['_maxParticleDeltaTime'] ? cc.director['_maxParticleDeltaTime'] : dt;
let psys = this.sys;
let node = psys.node;
let particles = this.particles;
const FLOAT_PER_PARTICLE = 4 * this.sys['_assembler']._vfmt._bytes / 4;
const PositionType = cc.ParticleSystem.PositionType;
// Calculate pos
node['_updateWorldMatrix']();
if (psys.positionType === PositionType.FREE) {
this._worldRotation = this.getWorldRotation(node);
let m = node['_worldMatrix'].m;
this._pos.x = m[12];
this._pos.y = m[13];
} else if (psys.positionType === PositionType.RELATIVE) {
this._worldRotation = node.angle;
this._pos.x = node.x;
this._pos.y = node.y;
} else {
this._worldRotation = 0;
}
// Emission
if (this.active && psys.emissionRate) {
var rate = 1.0 / psys.emissionRate;
//issue #1201, prevent bursts of particles, due to too high emitCounter
if (particles.length < psys.totalParticles)
this.emitCounter += dt;
while ((particles.length < psys.totalParticles) && (this.emitCounter > rate)) {
this.emitParticle(this._pos);
this.emitCounter -= rate;
}
this.elapsed += dt;
if (psys.duration !== -1 && psys.duration < this.elapsed) {
psys.stopSystem();
}
}
// Request buffer for particles
let buffer = psys['_assembler'].getBuffer();
let particleCount = particles.length;
buffer.reset();
buffer.request(particleCount * 4, particleCount * 6);
// Fill up uvs
if (particleCount > this._uvFilled) {
this.updateUVs(false);
}
// Used to reduce memory allocation / creation within the loop
let particleIdx = 0;
while (particleIdx < particles.length) {
// Reset temporary vectors
this._tpa.x = this._tpa.y = this._tpb.x = this._tpb.y = this._tpc.x = this._tpc.y = 0;
let particle = particles[particleIdx];
// life
particle.timeToLive -= dt;
if (particle.timeToLive > 0) {
// Mode A: gravity, direction, tangential accel & radial accel
if (psys.emitterMode === cc.ParticleSystem.EmitterMode.GRAVITY) {
let tmp = this._tpc, radial = this._tpa, tangential = this._tpb;
// radial acceleration
if (particle.pos.x || particle.pos.y) {
radial.set(particle.pos);
radial.normalizeSelf();
}
tangential.set(radial);
radial.mulSelf(particle.radialAccel);
// tangential acceleration
let newy = tangential.x;
tangential.x = -tangential.y;
tangential.y = newy;
tangential.mulSelf(particle.tangentialAccel);
tmp.set(radial);
tmp.addSelf(tangential);
tmp.addSelf(psys.gravity);
tmp.mulSelf(dt);
particle.dir.addSelf(tmp);
tmp.set(particle.dir);
tmp.mulSelf(dt);
particle.pos.addSelf(tmp);
}
// Mode B: radius movement
else {
// Update the angle and radius of the particle.
particle.angle += particle.degreesPerSecond * dt;
particle.radius += particle.deltaRadius * dt;
particle.pos.x = -Math.cos(particle.angle) * particle.radius;
particle.pos.y = -Math.sin(particle.angle) * particle.radius;
}
// color
particle.color.r += particle.deltaColor.r * dt;
particle.color.g += particle.deltaColor.g * dt;
particle.color.b += particle.deltaColor.b * dt;
particle.color.a += particle.deltaColor.a * dt;
// size
particle.size += particle.deltaSize * dt;
if (particle.size < 0) {
particle.size = 0;
}
// angle
particle.rotation += particle.deltaRotation * dt;
// update values in quad buffer
let newPos = this._tpa;
newPos.set(particle.pos);
if (psys.positionType !== PositionType.GROUPED) {
newPos.addSelf(particle.startPos);
}
let offset = FLOAT_PER_PARTICLE * particleIdx;
this.updateParticleBuffer(particle, newPos, buffer, offset);
// update particle counter
++particleIdx;
} else {
// life < 0
let deadParticle = particles[particleIdx];
if (particleIdx !== particles.length - 1) {
particles[particleIdx] = particles[particles.length - 1];
}
this.pool.put(deadParticle);
particles.length--;
}
}
if (particles.length > 0) {
buffer.uploadData();
psys['_assembler']._ia._count = particles.length * 6;
}
else if (!this.active && !this.readyToPlay) {
this.finished = true;
psys['_finishedSimulation']();
}
}
// Simulator.prototype.step = function (dt) {
// dt = dt > cc.director._maxParticleDeltaTime ? cc.director._maxParticleDeltaTime : dt;
// let psys = this.sys;
// let node = psys.node;
// let particles = this.particles;
// const FLOAT_PER_PARTICLE = 4 * this.sys._assembler._vfmt._bytes / 4;
// const PositionType = cc.ParticleSystem.PositionType;
// // Calculate pos
// node._updateWorldMatrix();
// if (psys.positionType === PositionType.FREE) {
// this._worldRotation = getWorldRotation(node);
// let m = node._worldMatrix.m;
// _pos.x = m[12];
// _pos.y = m[13];
// } else if (psys.positionType === PositionType.RELATIVE) {
// this._worldRotation = node.angle;
// _pos.x = node.x;
// _pos.y = node.y;
// } else {
// this._worldRotation = 0;
// }
// // Emission
// if (this.active && psys.emissionRate) {
// var rate = 1.0 / psys.emissionRate;
// //issue #1201, prevent bursts of particles, due to too high emitCounter
// if (particles.length < psys.totalParticles)
// this.emitCounter += dt;
// while ((particles.length < psys.totalParticles) && (this.emitCounter > rate)) {
// this.emitParticle(_pos);
// this.emitCounter -= rate;
// }
// this.elapsed += dt;
// if (psys.duration !== -1 && psys.duration < this.elapsed) {
// psys.stopSystem();
// }
// }
// // Request buffer for particles
// let buffer = psys._assembler.getBuffer();
// let particleCount = particles.length;
// buffer.reset();
// buffer.request(particleCount * 4, particleCount * 6);
// // Fill up uvs
// if (particleCount > this._uvFilled) {
// this.updateUVs();
// }
// // Used to reduce memory allocation / creation within the loop
// let particleIdx = 0;
// while (particleIdx < particles.length) {
// // Reset temporary vectors
// _tpa.x = _tpa.y = _tpb.x = _tpb.y = _tpc.x = _tpc.y = 0;
// let particle = particles[particleIdx];
// // life
// particle.timeToLive -= dt;
// if (particle.timeToLive > 0) {
// // Mode A: gravity, direction, tangential accel & radial accel
// if (psys.emitterMode === cc.ParticleSystem.EmitterMode.GRAVITY) {
// let tmp = _tpc, radial = _tpa, tangential = _tpb;
// // radial acceleration
// if (particle.pos.x || particle.pos.y) {
// radial.set(particle.pos);
// radial.normalizeSelf();
// }
// tangential.set(radial);
// radial.mulSelf(particle.radialAccel);
// // tangential acceleration
// let newy = tangential.x;
// tangential.x = -tangential.y;
// tangential.y = newy;
// tangential.mulSelf(particle.tangentialAccel);
// tmp.set(radial);
// tmp.addSelf(tangential);
// tmp.addSelf(psys.gravity);
// tmp.mulSelf(dt);
// particle.dir.addSelf(tmp);
// tmp.set(particle.dir);
// tmp.mulSelf(dt);
// particle.pos.addSelf(tmp);
// }
// // Mode B: radius movement
// else {
// // Update the angle and radius of the particle.
// particle.angle += particle.degreesPerSecond * dt;
// particle.radius += particle.deltaRadius * dt;
// particle.pos.x = -Math.cos(particle.angle) * particle.radius;
// particle.pos.y = -Math.sin(particle.angle) * particle.radius;
// }
// // color
// particle.color.r += particle.deltaColor.r * dt;
// particle.color.g += particle.deltaColor.g * dt;
// particle.color.b += particle.deltaColor.b * dt;
// particle.color.a += particle.deltaColor.a * dt;
// // size
// particle.size += particle.deltaSize * dt;
// if (particle.size < 0) {
// particle.size = 0;
// }
// // angle
// particle.rotation += particle.deltaRotation * dt;
// // update values in quad buffer
// let newPos = _tpa;
// newPos.set(particle.pos);
// if (psys.positionType !== PositionType.GROUPED) {
// newPos.addSelf(particle.startPos);
// }
// let offset = FLOAT_PER_PARTICLE * particleIdx;
// this.updateParticleBuffer(particle, newPos, buffer, offset);
// // update particle counter
// ++particleIdx;
// } else {
// // life < 0
// let deadParticle = particles[particleIdx];
// if (particleIdx !== particles.length - 1) {
// particles[particleIdx] = particles[particles.length - 1];
// }
// pool.put(deadParticle);
// particles.length--;
// }
// }
// if (particles.length > 0) {
// buffer.uploadData();
// psys._assembler._ia._count = particles.length * 6;
// }
// else if (!this.active && !this.readyToPlay) {
// this.finished = true;
// psys._finishedSimulation();
// }
// }
}