Redesign responsive combat HUD
This commit is contained in:
parent
98d2c799b4
commit
8fdb313cb2
185
src/game-v4.js
185
src/game-v4.js
@ -90,39 +90,7 @@ class GameScene extends Phaser.Scene {
|
||||
this.cameras.main.startFollow(this.player, true, 0.08, 0.08);
|
||||
this.cameras.main.setZoom(1.12);
|
||||
|
||||
this.title = this.add.text(18, 16, 'ARROWSTACK AIRSHIP', {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '12px',
|
||||
color: '#8cd3ff',
|
||||
}).setScrollFactor(0).setDepth(30);
|
||||
|
||||
this.hud = this.add.text(18, 42, '', {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '14px',
|
||||
color: '#e8f2f7',
|
||||
backgroundColor: '#071018cc',
|
||||
padding: { x: 12, y: 10 },
|
||||
lineSpacing: 4,
|
||||
}).setScrollFactor(0).setDepth(30);
|
||||
|
||||
this.status = this.add.text(this.scale.width / 2, 22, '', {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '18px',
|
||||
color: '#ffffff',
|
||||
backgroundColor: '#071018dd',
|
||||
padding: { x: 16, y: 10 },
|
||||
align: 'center',
|
||||
}).setOrigin(0.5, 0).setScrollFactor(0).setDepth(31);
|
||||
|
||||
this.healthGraphics = this.add.graphics().setScrollFactor(0).setDepth(30);
|
||||
this.healthLabels = {
|
||||
player: this.add.text(18, this.scale.height - 64, 'SPARROW', {
|
||||
fontFamily: 'monospace', fontSize: '12px', color: '#d8ecff',
|
||||
}).setScrollFactor(0).setDepth(31),
|
||||
pirate: this.add.text(this.scale.width - 18, this.scale.height - 64, 'PIRATES', {
|
||||
fontFamily: 'monospace', fontSize: '12px', color: '#ffd0d0',
|
||||
}).setOrigin(1, 0).setScrollFactor(0).setDepth(31),
|
||||
};
|
||||
this.createHud();
|
||||
|
||||
this.scale.on('resize', this.layoutHud, this);
|
||||
this.events.once(Phaser.Scenes.Events.SHUTDOWN, () => {
|
||||
@ -132,6 +100,71 @@ class GameScene extends Phaser.Scene {
|
||||
this.spawnWave();
|
||||
}
|
||||
|
||||
createHud() {
|
||||
const fixedText = (x, y, text, style = {}) => this.add.text(x, y, text, {
|
||||
fontFamily: 'monospace',
|
||||
color: '#e8f2f7',
|
||||
...style,
|
||||
}).setScrollFactor(0).setDepth(31);
|
||||
|
||||
this.hudGraphics = this.add.graphics().setScrollFactor(0).setDepth(30);
|
||||
|
||||
this.brandText = fixedText(14, 10, 'ARROWSTACK // SPARROW', {
|
||||
fontSize: '10px',
|
||||
color: '#8cd3ff',
|
||||
});
|
||||
|
||||
this.speedText = fixedText(14, 29, '', {
|
||||
fontSize: '16px',
|
||||
color: '#ffffff',
|
||||
});
|
||||
|
||||
this.headingText = fixedText(this.scale.width / 2, 10, '', {
|
||||
fontSize: '17px',
|
||||
color: '#d8ecff',
|
||||
align: 'center',
|
||||
}).setOrigin(0.5, 0);
|
||||
|
||||
this.waveText = fixedText(this.scale.width / 2, 32, '', {
|
||||
fontSize: '11px',
|
||||
color: '#8cd3ff',
|
||||
align: 'center',
|
||||
}).setOrigin(0.5, 0);
|
||||
|
||||
this.combatText = fixedText(this.scale.width - 76, 11, '', {
|
||||
fontSize: '10px',
|
||||
color: '#c8d6df',
|
||||
align: 'right',
|
||||
lineSpacing: 2,
|
||||
}).setOrigin(1, 0);
|
||||
|
||||
this.playerHullLabel = fixedText(14, 55, '', {
|
||||
fontSize: '10px',
|
||||
color: '#a7f0c1',
|
||||
});
|
||||
|
||||
this.enemyHullLabel = fixedText(this.scale.width - 14, 55, '', {
|
||||
fontSize: '10px',
|
||||
color: '#ffb8b8',
|
||||
align: 'right',
|
||||
}).setOrigin(1, 0);
|
||||
|
||||
this.status = fixedText(this.scale.width / 2, 102, '', {
|
||||
fontSize: '15px',
|
||||
color: '#ffffff',
|
||||
backgroundColor: '#071018dd',
|
||||
padding: { x: 14, y: 8 },
|
||||
align: 'center',
|
||||
}).setOrigin(0.5, 0);
|
||||
|
||||
this.controlsHint = fixedText(this.scale.width / 2, this.scale.height - 22, 'ARROWS move • Q/E broadside • SPACE brake • R restart', {
|
||||
fontSize: '10px',
|
||||
color: '#91a8b7',
|
||||
backgroundColor: '#07101899',
|
||||
padding: { x: 8, y: 4 },
|
||||
}).setOrigin(0.5, 1);
|
||||
}
|
||||
|
||||
createAnimations() {
|
||||
if (!this.anims.exists('player-idle')) {
|
||||
this.anims.create({
|
||||
@ -450,43 +483,50 @@ class GameScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
drawHud() {
|
||||
const width = this.scale.width;
|
||||
const compact = width < 700;
|
||||
const heading = normalizeDegrees(this.player.angle);
|
||||
const activePirates = this.pirates?.countActive(true) || 0;
|
||||
const { pirate: nearest, range } = this.nearestPirate();
|
||||
const rangeText = Number.isFinite(range) ? `${range.toFixed(0).padStart(4, ' ')}m` : ' --';
|
||||
|
||||
this.hud.setText([
|
||||
`speed ${Math.abs(this.playerSpeed).toFixed(0).padStart(3, ' ')} ${this.playerSpeed < 0 ? 'REV' : 'FWD'}`,
|
||||
`heading ${heading.toFixed(0).padStart(3, ' ')}°`,
|
||||
`wave ${String(this.wave).padStart(3, ' ')}`,
|
||||
`pirates ${String(activePirates).padStart(3, ' ')} nearest ${rangeText}`,
|
||||
`defeated ${String(this.defeated).padStart(3, ' ')}`,
|
||||
'',
|
||||
'drag throttle + steer',
|
||||
'Q / E port / starboard',
|
||||
'SPACE brake',
|
||||
]);
|
||||
|
||||
const w = this.scale.width;
|
||||
const h = this.scale.height;
|
||||
const barWidth = Math.min(280, w * 0.32);
|
||||
const barHeight = 16;
|
||||
const y = h - 38;
|
||||
|
||||
this.healthGraphics.clear();
|
||||
this.healthGraphics.fillStyle(0x071018, 0.85);
|
||||
this.healthGraphics.fillRect(18, y, barWidth, barHeight);
|
||||
this.healthGraphics.fillRect(w - 18 - barWidth, y, barWidth, barHeight);
|
||||
|
||||
this.healthGraphics.fillStyle(0x61c98b, 1);
|
||||
this.healthGraphics.fillRect(18, y, barWidth * (this.playerHull / PLAYER_MAX_HULL), barHeight);
|
||||
|
||||
const nearestHull = nearest ? (nearest.getData('hull') || 0) : 0;
|
||||
this.healthGraphics.fillStyle(0xd65757, 1);
|
||||
this.healthGraphics.fillRect(w - 18 - barWidth, y, barWidth * (nearestHull / PIRATE_MAX_HULL), barHeight);
|
||||
const rangeText = Number.isFinite(range) ? `${range.toFixed(0)}m` : '--';
|
||||
|
||||
this.healthLabels.player.setText(`SPARROW ${this.playerHull}/${PLAYER_MAX_HULL}`);
|
||||
this.healthLabels.pirate.setText(nearest ? `NEAREST PIRATE ${nearestHull}/${PIRATE_MAX_HULL}` : `WAVE ${this.wave} CLEAR`);
|
||||
this.speedText.setText(`${Math.abs(this.playerSpeed).toFixed(0)} ${this.playerSpeed < 0 ? 'REV' : 'FWD'}`);
|
||||
this.headingText.setText(`${heading.toFixed(0).padStart(3, '0')}°`);
|
||||
this.waveText.setText(`WAVE ${this.wave} • ${activePirates} HOSTILE${activePirates === 1 ? '' : 'S'}`);
|
||||
this.combatText.setText(compact
|
||||
? `K ${this.defeated}\n${rangeText}`
|
||||
: `DEFEATED ${this.defeated}\nRANGE ${rangeText}`);
|
||||
|
||||
this.playerHullLabel.setText(`SPARROW ${this.playerHull}/${PLAYER_MAX_HULL}`);
|
||||
this.enemyHullLabel.setText(nearest
|
||||
? `PIRATE ${nearestHull}/${PIRATE_MAX_HULL}`
|
||||
: `WAVE ${this.wave} CLEAR`);
|
||||
|
||||
const side = 14;
|
||||
const gap = 14;
|
||||
const barY = compact ? 75 : 72;
|
||||
const barHeight = compact ? 8 : 10;
|
||||
const barWidth = (width - side * 2 - gap) / 2;
|
||||
const enemyX = side + barWidth + gap;
|
||||
|
||||
this.hudGraphics.clear();
|
||||
this.hudGraphics.fillStyle(0x071018, 0.78);
|
||||
this.hudGraphics.fillRect(0, 0, width, compact ? 96 : 98);
|
||||
this.hudGraphics.lineStyle(1, 0x8cd3ff, 0.15);
|
||||
this.hudGraphics.lineBetween(0, compact ? 96 : 98, width, compact ? 96 : 98);
|
||||
|
||||
this.hudGraphics.fillStyle(0x17232d, 0.95);
|
||||
this.hudGraphics.fillRoundedRect(side, barY, barWidth, barHeight, 4);
|
||||
this.hudGraphics.fillRoundedRect(enemyX, barY, barWidth, barHeight, 4);
|
||||
|
||||
this.hudGraphics.fillStyle(0x61c98b, 1);
|
||||
this.hudGraphics.fillRoundedRect(side, barY, barWidth * (this.playerHull / PLAYER_MAX_HULL), barHeight, 4);
|
||||
|
||||
if (nearest) {
|
||||
this.hudGraphics.fillStyle(0xd65757, 1);
|
||||
this.hudGraphics.fillRoundedRect(enemyX, barY, barWidth * (nearestHull / PIRATE_MAX_HULL), barHeight, 4);
|
||||
}
|
||||
}
|
||||
|
||||
checkGameState() {
|
||||
@ -516,10 +556,17 @@ class GameScene extends Phaser.Scene {
|
||||
layoutHud(gameSize) {
|
||||
const width = gameSize.width;
|
||||
const height = gameSize.height;
|
||||
if (this.status) this.status.setPosition(width / 2, 22);
|
||||
if (this.healthLabels) {
|
||||
this.healthLabels.player.setPosition(18, height - 64);
|
||||
this.healthLabels.pirate.setPosition(width - 18, height - 64);
|
||||
const compact = width < 700;
|
||||
|
||||
if (this.headingText) this.headingText.setPosition(width / 2, 9);
|
||||
if (this.waveText) this.waveText.setPosition(width / 2, 32);
|
||||
if (this.combatText) this.combatText.setPosition(width - (compact ? 76 : 14), 11);
|
||||
if (this.enemyHullLabel) this.enemyHullLabel.setPosition(width - 14, compact ? 57 : 54);
|
||||
if (this.playerHullLabel) this.playerHullLabel.setPosition(14, compact ? 57 : 54);
|
||||
if (this.status) this.status.setPosition(width / 2, compact ? 104 : 108);
|
||||
if (this.controlsHint) {
|
||||
this.controlsHint.setPosition(width / 2, height - 12);
|
||||
this.controlsHint.setVisible(!compact);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user