From 34611bc63370b04fab4bbf908c27ec134ce1bc70 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sun, 21 Jun 2026 22:59:45 +0900 Subject: [PATCH] Finalize title screen name --- docs/roadmap.md | 2 +- index.html | 2 +- src/game/audio/SoundDirector.ts | 18 +++++- src/game/scenes/TitleScene.ts | 97 +++++++++++++++++++++++++-------- 4 files changed, 94 insertions(+), 25 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index dc0e04d..f41a863 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,4 +1,4 @@ -# Heros Web Roadmap +# 삼국지: 세 형제의 맹세 Roadmap ## Initial Goal diff --git a/index.html b/index.html index 3f3b670..677af75 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - Heros Web + 삼국지: 세 형제의 맹세
diff --git a/src/game/audio/SoundDirector.ts b/src/game/audio/SoundDirector.ts index 5666047..f8165b5 100644 --- a/src/game/audio/SoundDirector.ts +++ b/src/game/audio/SoundDirector.ts @@ -3,6 +3,7 @@ class SoundDirector { private master?: GainNode; private musicGain?: GainNode; private started = false; + private muted = false; private sequenceTimer?: number; start() { @@ -17,7 +18,7 @@ class SoundDirector { this.context = new AudioContextCtor(); this.master = this.context.createGain(); - this.master.gain.value = 0.34; + this.master.gain.value = this.muted ? 0 : 0.34; this.master.connect(this.context.destination); this.musicGain = this.context.createGain(); @@ -34,6 +35,21 @@ class SoundDirector { void this.context?.resume(); } + setMuted(muted: boolean) { + this.muted = muted; + + if (!this.context || !this.master) { + return; + } + + this.master.gain.cancelScheduledValues(this.context.currentTime); + this.master.gain.linearRampToValueAtTime(muted ? 0 : 0.34, this.context.currentTime + 0.18); + } + + isMuted() { + return this.muted; + } + playHover() { this.playTone(660, 0.035, 0.04, 'sine'); } diff --git a/src/game/scenes/TitleScene.ts b/src/game/scenes/TitleScene.ts index 6dcd6a9..48cb983 100644 --- a/src/game/scenes/TitleScene.ts +++ b/src/game/scenes/TitleScene.ts @@ -4,6 +4,7 @@ import { palette } from '../ui/palette'; export class TitleScene extends Phaser.Scene { private focusedButton?: Phaser.GameObjects.Text; + private settingsPanel?: Phaser.GameObjects.Container; constructor() { super('TitleScene'); @@ -117,21 +118,29 @@ export class TitleScene extends Phaser.Scene { private drawTitleMark(width: number, height: number) { const compact = width < 720; const titleX = compact ? width / 2 : 92; - const title = this.add.text(titleX, height - (compact ? 168 : 172), '桃園結義', { - fontSize: compact ? '48px' : '56px', + const title = this.add.text(titleX, height - (compact ? 190 : 204), '삼국지', { + fontFamily: '"Malgun Gothic", "Noto Sans KR", serif', + fontSize: compact ? '50px' : '64px', color: '#f1e3c2', fontStyle: '700', + padding: { top: 8, bottom: 8 }, shadow: { offsetX: 0, offsetY: 4, color: '#080a0e', blur: 10, fill: true } }); title.setOrigin(compact ? 0.5 : 0, 0); - const subtitle = this.add.text(compact ? width / 2 : 96, height - 98, 'Heros Web', { - fontSize: '24px', + const subtitle = this.add.text(compact ? width / 2 : 96, height - (compact ? 112 : 118), '세 형제의 맹세', { + fontFamily: '"Malgun Gothic", "Noto Sans KR", serif', + fontSize: compact ? '26px' : '34px', color: '#d8b15f', - fontStyle: '600', + fontStyle: '700', + padding: { top: 10, bottom: 8 }, shadow: { offsetX: 0, offsetY: 2, color: '#080a0e', blur: 6, fill: true } }); subtitle.setOrigin(compact ? 0.5 : 0, 0); + + const lineX = compact ? width / 2 - 110 : 98; + const lineY = height - (compact ? 76 : 70); + this.add.rectangle(lineX, lineY, compact ? 220 : 310, 2, palette.gold, 0.68).setOrigin(0, 0.5); } private drawMenu(width: number, height: number) { @@ -146,10 +155,7 @@ export class TitleScene extends Phaser.Scene { this.createMenuButton(menuX, menuY - 70, '새 게임', true, () => this.startGame()); this.createMenuButton(menuX, menuY, '이어하기', false, () => undefined); - this.createMenuButton(menuX, menuY + 70, '설정', true, () => { - soundDirector.playSelect(); - this.flashButtonText('설정은 다음 단계에서 열립니다.'); - }); + this.createMenuButton(menuX, menuY + 70, '설정', true, () => this.openSettingsPanel(menuX, menuY)); } private createMenuButton( @@ -203,22 +209,69 @@ export class TitleScene extends Phaser.Scene { text.setBackgroundColor('rgba(0,0,0,0)'); } - private flashButtonText(message: string) { - const toast = this.add.text(this.scale.width - 318, this.scale.height * 0.43 + 138, message, { - fontSize: '16px', - color: '#d8b15f', - fixedWidth: 250, + private openSettingsPanel(x: number, y: number) { + if (this.settingsPanel) { + this.closeSettingsPanel(); + return; + } + + soundDirector.playSelect(); + + const panel = this.add.container(x, y + 238); + const backdrop = this.add.rectangle(0, 0, 286, 188, 0x0e151d, 0.9); + backdrop.setStrokeStyle(1, palette.gold, 0.52); + + const title = this.add.text(0, -66, '설정', { + fontSize: '24px', + color: '#f1e3c2', + fontStyle: '700', + fixedWidth: 210, align: 'center' }); - toast.setOrigin(0.5); - this.tweens.add({ - targets: toast, - alpha: 0, - y: toast.y + 12, - duration: 1200, - ease: 'Sine.easeOut', - onComplete: () => toast.destroy() + title.setOrigin(0.5); + + const soundText = this.createSettingsButton(0, -10, this.soundLabel()); + soundText.on('pointerdown', () => { + soundDirector.setMuted(!soundDirector.isMuted()); + soundText.setText(this.soundLabel()); + soundDirector.playSelect(); }); + + const close = this.createSettingsButton(0, 48, '닫기'); + close.on('pointerdown', () => { + soundDirector.playSelect(); + this.closeSettingsPanel(); + }); + + panel.add([backdrop, title, soundText, close]); + panel.setAlpha(0); + this.settingsPanel = panel; + this.tweens.add({ targets: panel, alpha: 1, y: y + 228, duration: 160, ease: 'Sine.easeOut' }); + } + + private createSettingsButton(x: number, y: number, label: string) { + const text = this.add.text(x, y, label, { + fontSize: '20px', + color: '#d8b15f', + fixedWidth: 210, + align: 'center', + padding: { top: 8, bottom: 8 } + }); + text.setOrigin(0.5); + text.setInteractive({ useHandCursor: true }); + text.on('pointerover', () => text.setColor('#f1e3c2')); + text.on('pointerout', () => text.setColor('#d8b15f')); + + return text; + } + + private soundLabel() { + return `음향: ${soundDirector.isMuted() ? '꺼짐' : '켜짐'}`; + } + + private closeSettingsPanel() { + this.settingsPanel?.destroy(); + this.settingsPanel = undefined; } private startGame() {