Open HyperCrowd opened 1 month ago
To create a temporary shape, emitter, and shader pipeline in Phaser and have them automatically destroy after a set duration, you can use Phaser’s time.delayedCall function. This method will allow each effect to be created, displayed, and then removed after the duration expires. Here’s an example demonstrating this approach:
function createTemporaryEffects(gameObject, duration) {
// 1. Create a shape (e.g., a circle) around the game object
const graphics = this.add.graphics();
graphics.lineStyle(2, 0xff0000, 1); // Set color and line thickness
graphics.strokeCircle(gameObject.x, gameObject.y, gameObject.width * 0.6); // Draw circle
// Update the shape position if the object moves
this.events.on('update', () => {
graphics.x = gameObject.x;
graphics.y = gameObject.y;
});
// 2. Create a particle emitter that follows the game object
const particles = this.add.particles('particleTexture');
const emitter = particles.createEmitter({
speed: { min: 200, max: 400 },
scale: { start: 0.5, end: 0 },
blendMode: 'ADD'
});
emitter.startFollow(gameObject);
// 3. Apply a shader pipeline to the game object
const shaderPipeline = this.game.renderer.pipelines.get('CustomShader') || this.game.renderer.addPipeline('CustomShader', new Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline({
game: this.game,
renderer: this.game.renderer,
fragShader: `
precision mediump float;
uniform float time;
uniform vec2 resolution;
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
gl_FragColor = vec4(uv.x * sin(time), uv.y * cos(time), 0.5, 1.0);
}
`
}));
shaderPipeline.setFloat2('resolution', this.game.config.width, this.game.config.height);
gameObject.setPipeline('CustomShader');
// Animate the shader over time
this.events.on('update', (time, delta) => {
shaderPipeline.setFloat1('time', time / 1000); // Progresses time for shader animation
});
// 4. Schedule destruction after the duration
this.time.delayedCall(duration, () => {
// Remove the graphics shape
graphics.destroy();
// Stop and remove the particle emitter
emitter.stop();
particles.destroy();
// Remove the shader pipeline
gameObject.resetPipeline();
// Optionally remove the shader from the renderer if you’re done with it
this.game.renderer.removePipeline('CustomShader');
});
}
// Usage: Attach effects to `myGameObject` for 3000 ms (3 seconds)
createTemporaryEffects.call(this, myGameObject, 3000);
In Phaser, you don’t need to create a separate particle system for each entity. You can create a single Particles instance and then use multiple emitters from that instance to follow different entities. This approach is efficient and keeps your codebase cleaner since all emitters are centralized under one particle system.
Here’s how you can achieve this:
Here's how the code would look:
function attachParticleEffectToEntity(particles, gameObject) {
// Create an emitter for the specific gameObject
const emitter = particles.createEmitter({
speed: { min: 200, max: 400 },
scale: { start: 0.5, end: 0 },
blendMode: 'ADD'
});
// Attach the emitter to follow the gameObject
emitter.startFollow(gameObject);
return emitter;
}
// Create a single particles instance
const particles = this.add.particles('particleTexture');
// Use the same particles instance to create emitters for multiple entities
const emitter1 = attachParticleEffectToEntity.call(this, particles, entity1);
const emitter2 = attachParticleEffectToEntity.call(this, particles, entity2);
const emitter3 = attachParticleEffectToEntity.call(this, particles, entity3);
// Schedule cleanup after a specific duration for each emitter (e.g., 3 seconds)
this.time.delayedCall(3000, () => {
emitter1.stop();
emitter2.stop();
emitter3.stop();
});
For shader pipelines (renderer pipelines), you can reuse the same pipeline for multiple game objects in Phaser. You don’t need to create a new pipeline instance for each object; instead, you can set the same pipeline on different objects and update shared uniforms if needed. Phaser will automatically apply the shader to all objects using the pipeline.
Here’s how you can set up a single pipeline and apply it to multiple game objects, with cleanup after a set duration:
// Step 1: Create a single custom shader pipeline if it hasn’t been added yet
const shaderCode = `
precision mediump float;
uniform float time;
uniform vec2 resolution;
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
gl_FragColor = vec4(uv.x * sin(time), uv.y * cos(time), 0.5, 1.0);
}
`;
if (!this.game.renderer.pipelines.get('CustomShader')) {
const customPipeline = this.game.renderer.addPipeline('CustomShader', new Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline({
game: this.game,
renderer: this.game.renderer,
fragShader: shaderCode
}));
customPipeline.setFloat2('resolution', this.game.config.width, this.game.config.height);
}
// Step 2: Apply the pipeline to multiple game objects
gameObject1.setPipeline('CustomShader');
gameObject2.setPipeline('CustomShader');
gameObject3.setPipeline('CustomShader');
// Update time uniform for animation across all objects using the pipeline
this.events.on('update', (time) => {
const customPipeline = this.game.renderer.pipelines.get('CustomShader');
if (customPipeline) {
customPipeline.setFloat1('time', time / 1000);
}
});
// Step 3: Schedule pipeline removal for each object after a duration
const duration = 3000; // e.g., 3 seconds
this.time.delayedCall(duration, () => {
gameObject1.resetPipeline();
gameObject2.resetPipeline();
gameObject3.resetPipeline();
// Optionally remove the pipeline from the renderer if it’s no longer needed
this.game.renderer.removePipeline('CustomShader');
});
Gotta use sprites of shapes instead :/
https://docs.phaser.io/phaser/concepts/gameobjects/graphics#generate-texture
When a Graphics object is rendered it will render differently based on if the game is running under Canvas or WebGL. Under Canvas it will use the HTML Canvas context drawing operations to draw the path. Under WebGL the graphics data is decomposed into polygons. Both of these are expensive processes, especially with complex shapes.
If your Graphics object doesn't change much (or at all) once you've drawn your shape to it, then you will help performance by calling Phaser.GameObjects.Graphics#generateTexture. This will 'bake' the Graphics object into a Texture, and return it. You can then use this Texture for Sprites or other display objects. If your Graphics object updates frequently then you should avoid doing this, as it will constantly generate new textures, which will consume memory.
As you can tell, Graphics objects are a bit of a trade-off. While they are extremely useful, you need to be careful in their complexity and quantity of them in your game.