Skip to content

Commit

Permalink
refactor: Improve CPU Particles API + Perf (#3118)
Browse files Browse the repository at this point in the history
This PR relates to the v1 milestone for refactoring particles to be a more modern excalibur look and feel.

Additionally this PR also improves CPU particle performance significantly by leveraging pooling
  • Loading branch information
eonarheim authored Jul 15, 2024
1 parent 223844b commit c2cb169
Show file tree
Hide file tree
Showing 15 changed files with 706 additions and 793 deletions.
40 changes: 39 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,42 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Breaking Changes

-
- `ex.Particle` and `ex.ParticleEmitter` now have an API that looks like modern Excalibur APIs
* `particleSprite` is renamed to `graphic`
* `particleRotationalVelocity` is renamed to `angularVelocity`
* `fadeFlag` is renamed to `fade`
* `acceleration` is renamed to `acc`
* `particleLife` is renamed to `life`
* `ParticleEmitter` now takes a separate `particle: ParticleConfig` parameter to disambiguate between particles parameters and emitter ones
```typescript
const emitter = new ex.ParticleEmitter({
width: 10,
height: 10,
radius: 5,
emitterType: ex.EmitterType.Rectangle,
emitRate: 300,
isEmitting: true,
particle: {
transform: ex.ParticleTransform.Global,
opacity: 0.5,
life: 1000,
acc: ex.vec(10, 80),
beginColor: ex.Color.Chartreuse,
endColor: ex.Color.Magenta,
startSize: 5,
endSize: 100,
minVel: 100,
maxVel: 200,
minAngle: 5.1,
maxAngle: 6.2,
fade: true,
maxSize: 10,
graphic: swordImg.toSprite(),
randomRotation: true,
minSize: 1
}
});
```

### Deprecated

Expand Down Expand Up @@ -44,6 +79,9 @@ are doing mtv adjustments during precollision.

### Updates

- Perf improvements to `ex.ParticleEmitter`
* Use the same integrator as the MotionSystem in the tight loop
* Leverage object pools to increase performance and reduce allocations
- Perf improvements to collision narrowphase and solver steps
* Working in the local polygon space as much as possible speeds things up
* Add another pair filtering condition on the `SparseHashGridCollisionProcessor` which reduces pairs passed to narrowphase
Expand Down
44 changes: 20 additions & 24 deletions sandbox/src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,33 +915,29 @@ game.add(player);
// Add particle emitter
// sprite.anchor = new ex.Vector(0.5, 0.5);
var emitter = new ex.ParticleEmitter({
isEmitting: false,
emitRate: 494,
pos: new ex.Vector(100, 300),
width: 2,
height: 2,
minVel: 417,
maxVel: 589,
minAngle: Math.PI,
maxAngle: Math.PI * 2,
isEmitting: false,
emitRate: 494,
opacity: 0.84,
fadeFlag: true,
particleLife: 2465,
maxSize: 20.5,
minSize: 10,
acceleration: new ex.Vector(0, 460),
beginColor: ex.Color.Red,
endColor: ex.Color.Yellow,
particleSprite: blockSprite,
particleRotationalVelocity: Math.PI / 10,
randomRotation: true
});
const original = (ex.ParticleEmitter.prototype as any)._createParticle;
(ex.ParticleEmitter.prototype as any)._createParticle = function () {
const particle = original.call(this);
particle.graphics.onPostDraw = () => {};
return particle;
};
particle: {
minVel: 417,
maxVel: 589,
minAngle: Math.PI,
maxAngle: Math.PI * 2,
opacity: 0.84,
fade: true,
life: 2465,
maxSize: 20.5,
minSize: 10,
beginColor: ex.Color.Red,
endColor: ex.Color.Yellow,
graphic: blockSprite,
angularVelocity: Math.PI / 10,
acc: new ex.Vector(0, 460),
randomRotation: true
}
});
game.add(emitter);

var exploding = false;
Expand Down
45 changes: 26 additions & 19 deletions sandbox/tests/emitter/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
var game = new ex.Engine({
width: 400,
height: 400
height: 400,
displayMode: ex.DisplayMode.FitScreenAndFill
});

var swordImg = new ex.ImageSource('https://cdn.rawgit.com/excaliburjs/Excalibur/7dd48128/assets/sword.png');

var actor = new ex.Actor({
anchor: ex.vec(0.5, 0.5),
pos: game.screen.center,
Expand All @@ -18,28 +21,32 @@ actor.actions.repeatForever((ctx) => {
var emitter = new ex.ParticleEmitter({
width: 10,
height: 10,
emitterType: ex.EmitterType.Rectangle,
particleTransform: ex.ParticleTransform.Global,
radius: 5,
minVel: 100,
maxVel: 200,
minAngle: 5.1,
maxAngle: 6.2,
emitterType: ex.EmitterType.Rectangle,
emitRate: 300,
opacity: 0.5,
fadeFlag: true,
particleLife: 1000,
maxSize: 10,
minSize: 1,
startSize: 5,
endSize: 100,
acceleration: ex.vec(10, 80),
beginColor: ex.Color.Chartreuse,
endColor: ex.Color.Magenta
isEmitting: true,
particle: {
transform: ex.ParticleTransform.Global,
opacity: 0.5,
life: 1000,
acc: ex.vec(10, 80),
beginColor: ex.Color.Chartreuse,
endColor: ex.Color.Magenta,
startSize: 5,
endSize: 100,
minVel: 100,
maxVel: 200,
minAngle: 5.1,
maxAngle: 6.2,
fade: true,
maxSize: 10,
graphic: swordImg.toSprite(),
randomRotation: true,
minSize: 1
}
});
emitter.isEmitting = true;

game.start();
game.start(new ex.Loader([swordImg]));
game.add(actor);

actor.angularVelocity = 2;
Expand Down
2 changes: 1 addition & 1 deletion src/engine/Debug/DebugSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { BodyComponent } from '../Collision/BodyComponent';
import { CollisionSystem } from '../Collision/CollisionSystem';
import { CompositeCollider } from '../Collision/Colliders/CompositeCollider';
import { GraphicsComponent } from '../Graphics/GraphicsComponent';
import { Particle } from '../Particles';
import { Particle } from '../Particles/Particles';
import { DebugGraphicsComponent } from '../Graphics/DebugGraphicsComponent';
import { CoordPlane } from '../Math/coord-plane';
import { Debug } from '../Graphics/Debug';
Expand Down
6 changes: 1 addition & 5 deletions src/engine/Graphics/GraphicsSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Camera } from '../Camera';
import { Query, System, SystemPriority, SystemType, World } from '../EntityComponentSystem';
import { Engine } from '../Engine';
import { GraphicsGroup } from './GraphicsGroup';
import { Particle } from '../Particles'; // this import seems to bomb wallaby
import { ParallaxComponent } from './ParallaxComponent';
import { CoordPlane } from '../Math/coord-plane';
import { BodyComponent } from '../Collision/BodyComponent';
Expand Down Expand Up @@ -142,10 +141,7 @@ export class GraphicsSystem extends System {
}
entity.events.emit('predraw', new PreDrawEvent(this._graphicsContext, delta, entity));

// TODO remove this hack on the particle redo
// Remove this line after removing the wallaby import
const particleOpacity = entity instanceof Particle ? entity.opacity : 1;
this._graphicsContext.opacity *= graphics.opacity * particleOpacity;
this._graphicsContext.opacity *= graphics.opacity;

// Draw the graphics component
this._drawGraphicsComponent(graphics, transform);
Expand Down
Loading

0 comments on commit c2cb169

Please sign in to comment.