mirror of
https://github.com/PlayableIntelligence/game-creator.git
synced 2026-09-19 07:34:10 +08:00
Replace text mute buttons with Phaser Graphics API speaker icon
Subagents consistently use this.add.text() for mute buttons because the skill doc said "speaker icon" without providing code. Now SKILL.md has complete drawMuteIcon() + _createMuteButton() code using fillRect, fillTriangle, arc, and lineBetween that subagents can copy verbatim. - SKILL.md: full Mute Button section with Graphics API drawing code - game-creator.md, make-game.md: ban text buttons, reference skill code - flappy-bird example: working reference implementation (UIScene, AudioBridge, GameState, EventBus) - phaser-2d template: add AUDIO_TOGGLE_MUTE event Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { eventBus, Events } from '../core/EventBus.js';
|
||||
import { gameState } from '../core/GameState.js';
|
||||
import { audioManager } from './AudioManager.js';
|
||||
import { menuTheme, gameplayBGM, gameOverTheme } from './music.js';
|
||||
import { flapSfx, scoreSfx, deathSfx, clickSfx } from './sfx.js';
|
||||
@@ -16,4 +17,11 @@ export function initAudioBridge() {
|
||||
eventBus.on(Events.BIRD_FLAP, () => flapSfx());
|
||||
eventBus.on(Events.SCORE_CHANGED, () => scoreSfx());
|
||||
eventBus.on(Events.BIRD_DIED, () => deathSfx());
|
||||
|
||||
// Mute toggle
|
||||
eventBus.on(Events.AUDIO_TOGGLE_MUTE, () => {
|
||||
gameState.isMuted = !gameState.isMuted;
|
||||
try { localStorage.setItem('muted', gameState.isMuted); } catch (_) {}
|
||||
if (gameState.isMuted) audioManager.stopMusic();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ export const Events = {
|
||||
MUSIC_GAMEPLAY: 'music:gameplay',
|
||||
MUSIC_GAMEOVER: 'music:gameover',
|
||||
MUSIC_STOP: 'music:stop',
|
||||
AUDIO_TOGGLE_MUTE: 'audio:toggleMute',
|
||||
};
|
||||
|
||||
class EventBus {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class GameState {
|
||||
constructor() {
|
||||
this.bestScore = 0;
|
||||
this.isMuted = localStorage.getItem('muted') === 'true';
|
||||
this.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
import Phaser from 'phaser';
|
||||
import { GAME, COLORS, TRANSITION, UI } from '../core/Constants.js';
|
||||
import { eventBus, Events } from '../core/EventBus.js';
|
||||
import { gameState } from '../core/GameState.js';
|
||||
|
||||
const ICON_SIZE = 16;
|
||||
const MUTE_MARGIN = 12;
|
||||
|
||||
function drawMuteIcon(gfx, muted, size) {
|
||||
gfx.clear();
|
||||
const s = size;
|
||||
|
||||
gfx.fillStyle(0xffffff);
|
||||
gfx.fillRect(-s * 0.15, -s * 0.15, s * 0.15, s * 0.3);
|
||||
gfx.fillTriangle(-s * 0.15, -s * 0.3, -s * 0.15, s * 0.3, -s * 0.45, 0);
|
||||
|
||||
if (!muted) {
|
||||
gfx.lineStyle(2, 0xffffff);
|
||||
gfx.beginPath();
|
||||
gfx.arc(0, 0, s * 0.2, -Math.PI / 4, Math.PI / 4);
|
||||
gfx.strokePath();
|
||||
gfx.beginPath();
|
||||
gfx.arc(0, 0, s * 0.35, -Math.PI / 4, Math.PI / 4);
|
||||
gfx.strokePath();
|
||||
} else {
|
||||
gfx.lineStyle(3, 0xff4444);
|
||||
gfx.lineBetween(s * 0.05, -s * 0.25, s * 0.35, s * 0.25);
|
||||
gfx.lineBetween(s * 0.05, s * 0.25, s * 0.35, -s * 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
export class UIScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
@@ -10,7 +37,6 @@ export class UIScene extends Phaser.Scene {
|
||||
create() {
|
||||
const cx = GAME.WIDTH / 2;
|
||||
|
||||
// Big centered score (Flappy Bird style)
|
||||
this.scoreText = this.add.text(cx, GAME.HEIGHT * UI.SCORE_Y_RATIO, '0', {
|
||||
fontSize: `${Math.round(GAME.HEIGHT * UI.SCORE_FONT_RATIO)}px`,
|
||||
fontFamily: UI.FONT,
|
||||
@@ -33,9 +59,31 @@ export class UIScene extends Phaser.Scene {
|
||||
};
|
||||
|
||||
eventBus.on(Events.SCORE_CHANGED, this.onScoreChanged);
|
||||
this._createMuteButton();
|
||||
|
||||
this.events.on('shutdown', () => {
|
||||
eventBus.off(Events.SCORE_CHANGED, this.onScoreChanged);
|
||||
});
|
||||
}
|
||||
|
||||
_createMuteButton() {
|
||||
const x = this.cameras.main.width - MUTE_MARGIN - ICON_SIZE;
|
||||
const y = this.cameras.main.height - MUTE_MARGIN - ICON_SIZE;
|
||||
|
||||
this.muteBg = this.add.circle(x, y, ICON_SIZE + 4, 0x000000, 0.3)
|
||||
.setInteractive({ useHandCursor: true })
|
||||
.setDepth(100);
|
||||
|
||||
this.muteIcon = this.add.graphics().setDepth(100);
|
||||
this.muteIcon.setPosition(x, y);
|
||||
drawMuteIcon(this.muteIcon, gameState.isMuted, ICON_SIZE);
|
||||
|
||||
this.muteBg.on('pointerdown', () => this._toggleMute());
|
||||
this.input.keyboard.on('keydown-M', () => this._toggleMute());
|
||||
}
|
||||
|
||||
_toggleMute() {
|
||||
eventBus.emit(Events.AUDIO_TOGGLE_MUTE);
|
||||
drawMuteIcon(this.muteIcon, gameState.isMuted, ICON_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Reference in New Issue
Block a user