Add dialogue character portraits

This commit is contained in:
2026-06-21 23:59:16 +09:00
parent 591141c492
commit 674fbe6659
6 changed files with 48 additions and 27 deletions

View File

@@ -1,11 +1,20 @@
import Phaser from 'phaser';
import { prologuePages, type StoryPage } from '../data/scenario';
import { prologuePages, type PortraitKey, type StoryPage } from '../data/scenario';
import { palette } from '../ui/palette';
const portraitKeys: Record<PortraitKey, string> = {
liuBei: 'portrait-liu-bei',
guanYu: 'portrait-guan-yu',
zhangFei: 'portrait-zhang-fei'
};
export class StoryScene extends Phaser.Scene {
private pageIndex = 0;
private background?: Phaser.GameObjects.Image;
private chapterText?: Phaser.GameObjects.Text;
private portrait?: Phaser.GameObjects.Image;
private portraitFrame?: Phaser.GameObjects.Rectangle;
private portraitDivider?: Phaser.GameObjects.Rectangle;
private nameText?: Phaser.GameObjects.Text;
private bodyText?: Phaser.GameObjects.Text;
private progressText?: Phaser.GameObjects.Text;
@@ -43,7 +52,7 @@ export class StoryScene extends Phaser.Scene {
const panelW = width - 144;
const panelH = 172;
this.add.rectangle(panelX, panelY, panelW, panelH, palette.panelDark, 0.86).setOrigin(0);
this.add.rectangle(panelX, panelY, panelW, panelH, palette.panelDark, 0.88).setOrigin(0);
this.add.rectangle(panelX, panelY, panelW, 3, palette.gold, 0.9).setOrigin(0);
this.add.rectangle(panelX, panelY + panelH, panelW, 1, palette.gold, 0.35).setOrigin(0);
@@ -56,7 +65,15 @@ export class StoryScene extends Phaser.Scene {
shadow: { offsetX: 0, offsetY: 2, color: '#05070a', blur: 6, fill: true }
});
this.nameText = this.add.text(panelX + 24, panelY + 28, '', {
this.portraitFrame = this.add.rectangle(panelX + 86, panelY + 86, 136, 136, 0x090d12, 0.88);
this.portraitFrame.setStrokeStyle(2, palette.gold, 0.58);
this.portrait = this.add.image(panelX + 86, panelY + 86, 'portrait-liu-bei');
this.portrait.setDisplaySize(128, 128);
this.portraitDivider = this.add.rectangle(panelX + 166, panelY + 28, 1, panelH - 56, palette.gold, 0.22).setOrigin(0);
this.nameText = this.add.text(panelX + 198, panelY + 28, '', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", serif',
fontSize: '24px',
color: '#f1e3c2',
@@ -64,11 +81,11 @@ export class StoryScene extends Phaser.Scene {
padding: { top: 4, bottom: 4 }
});
this.bodyText = this.add.text(panelX + 24, panelY + 78, '', {
this.bodyText = this.add.text(panelX + 198, panelY + 78, '', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", serif',
fontSize: '25px',
color: '#e8dfca',
wordWrap: { width: panelW - 56, useAdvancedWrap: true },
wordWrap: { width: panelW - 236, useAdvancedWrap: true },
lineSpacing: 10,
padding: { top: 4, bottom: 6 },
shadow: { offsetX: 0, offsetY: 2, color: '#05070a', blur: 5, fill: true }
@@ -99,9 +116,25 @@ export class StoryScene extends Phaser.Scene {
private applyPage(page: StoryPage) {
this.applyBackground(page.background);
this.chapterText?.setText(page.chapter);
this.nameText?.setPosition(96, this.scale.height - 210);
this.bodyText?.setPosition(96, this.scale.height - 160);
this.bodyText?.setWordWrapWidth(this.scale.width - 200, true);
if (page.portrait) {
this.portraitFrame?.setVisible(true);
this.portrait?.setVisible(true);
this.portraitDivider?.setVisible(true);
this.portrait?.setTexture(portraitKeys[page.portrait]);
this.portrait?.setDisplaySize(128, 128);
this.nameText?.setPosition(270, this.scale.height - 210);
this.bodyText?.setPosition(270, this.scale.height - 160);
this.bodyText?.setWordWrapWidth(this.scale.width - 374, true);
} else {
this.portraitFrame?.setVisible(false);
this.portrait?.setVisible(false);
this.portraitDivider?.setVisible(false);
this.nameText?.setPosition(96, this.scale.height - 210);
this.bodyText?.setPosition(96, this.scale.height - 160);
this.bodyText?.setWordWrapWidth(this.scale.width - 200, true);
}
this.nameText?.setText(page.speaker ?? '나레이션');
this.bodyText?.setText(page.text);
this.progressText?.setText(`${this.pageIndex + 1} / ${prologuePages.length} SPACE / ENTER`);