Fix player smoke world-space emission

This commit is contained in:
Admin 2026-09-19 04:31:04 +00:00
parent 979485e762
commit fa34292a2c
2 changed files with 22 additions and 16 deletions

View File

@ -186,11 +186,13 @@ class GameScene extends Phaser.Scene {
smokeTexture.destroy();
}
this.playerSmoke = this.add.particles(this.player.x, this.player.y, 'player-smoke', {
lifespan: { min: 520, max: 920 },
scale: { start: 0.34, end: 1.15 },
alpha: { start: 0.24, end: 0 },
this.playerSmoke = this.add.particles(0, 0, 'player-smoke', {
lifespan: { min: 620, max: 980 },
scale: { start: 0.5, end: 1.45 },
alpha: { start: 0.46, end: 0 },
rotate: { min: 0, max: 360 },
speedX: 0,
speedY: 0,
frequency: -1,
quantity: 1,
});
@ -214,25 +216,26 @@ class GameScene extends Phaser.Scene {
const sternX = this.player.x - forwardX * PLAYER_SMOKE_REAR_OFFSET;
const sternY = this.player.y - forwardY * PLAYER_SMOKE_REAR_OFFSET;
this.playerSmoke.setPosition(sternX, sternY);
const emissionIntervalMs = Phaser.Math.Linear(175, 58, forwardThrust);
const emissionIntervalMs = Phaser.Math.Linear(150, 48, forwardThrust);
this.playerSmokeAccumulatorMs += deltaMs;
while (this.playerSmokeAccumulatorMs >= emissionIntervalMs) {
const spread = Phaser.Math.FloatBetween(-0.16, 0.16);
const trailHeading = heading + Math.PI + spread;
const trailSpeed = Phaser.Math.Linear(16, 46, forwardThrust) * Phaser.Math.FloatBetween(0.82, 1.16);
this.playerSmoke.setParticleSpeed(
Math.cos(trailHeading) * trailSpeed,
Math.sin(trailHeading) * trailSpeed,
);
const velocityX = Math.cos(trailHeading) * trailSpeed;
const velocityY = Math.sin(trailHeading) * trailSpeed;
for (const engineSide of [-1, 1]) {
const jitter = Phaser.Math.FloatBetween(-1.5, 1.5);
const engineX = sternX + sideX * (PLAYER_SMOKE_ENGINE_OFFSET * engineSide + jitter);
const engineY = sternY + sideY * (PLAYER_SMOKE_ENGINE_OFFSET * engineSide + jitter);
this.playerSmoke.emitParticleAt(engineX, engineY, 1);
const particle = this.playerSmoke.emitParticleAt(engineX, engineY, 1);
if (particle) {
particle.velocityX = velocityX;
particle.velocityY = velocityY;
}
}
this.playerSmokeAccumulatorMs -= emissionIntervalMs;

View File

@ -210,11 +210,11 @@ test('player uses the four-frame north-facing sheet as a forward propulsion anim
assert.doesNotMatch(source, /key: 'player-idle'/);
});
test('player propulsion smoke is a separate world-space Phaser particle effect', async () => {
test('player propulsion smoke emits at world engine positions with per-particle trailing velocity', async () => {
const source = await read('src/game-v4.js');
assert.match(source, /this\.createPlayerSmoke\(\)/);
assert.match(source, /this\.add\.particles\(this\.player\.x, this\.player\.y, 'player-smoke'/);
assert.match(source, /this\.add\.particles\(0, 0, 'player-smoke'/);
assert.match(source, /frequency: -1/);
assert.match(source, /this\.updatePlayerSmoke\(deltaMs\)/);
assert.match(source, /const forwardThrust = clamp\(this\.playerSpeed \/ PLAYER_MAX_SPEED, 0, 1\)/);
@ -222,9 +222,12 @@ test('player propulsion smoke is a separate world-space Phaser particle effect',
assert.match(source, /const sternX = this\.player\.x - forwardX \* PLAYER_SMOKE_REAR_OFFSET/);
assert.match(source, /const sternY = this\.player\.y - forwardY \* PLAYER_SMOKE_REAR_OFFSET/);
assert.match(source, /PLAYER_SMOKE_ENGINE_OFFSET \* engineSide/);
assert.match(source, /this\.playerSmoke\.setParticleSpeed\(/);
assert.match(source, /this\.playerSmoke\.emitParticleAt\(engineX, engineY, 1\)/);
assert.match(source, /Phaser\.Math\.Linear\(175, 58, forwardThrust\)/);
assert.match(source, /const particle = this\.playerSmoke\.emitParticleAt\(engineX, engineY, 1\)/);
assert.match(source, /particle\.velocityX = velocityX/);
assert.match(source, /particle\.velocityY = velocityY/);
assert.match(source, /Phaser\.Math\.Linear\(150, 48, forwardThrust\)/);
assert.doesNotMatch(source, /this\.playerSmoke\.setPosition\(sternX, sternY\)/);
assert.doesNotMatch(source, /this\.playerSmoke\.setParticleSpeed\(/);
});
test('mobile HUD skips hidden Phaser rendering and throttles DOM state publishing', async () => {