@luna-editor/engine 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Player.js +120 -25
- package/dist/components/BackgroundLayer.js +7 -5
- package/dist/components/GameScreen.d.ts +8 -2
- package/dist/components/GameScreen.js +253 -26
- package/dist/components/OverlayUI.d.ts +2 -3
- package/dist/components/OverlayUI.js +3 -14
- package/dist/components/PluginComponentProvider.js +1 -1
- package/dist/contexts/DataContext.js +25 -8
- package/dist/contexts/PlaybackTextContext.d.ts +32 -0
- package/dist/contexts/PlaybackTextContext.js +29 -0
- package/dist/data-api-types.d.ts +6 -2
- package/dist/hooks/useFontLoader.d.ts +9 -2
- package/dist/hooks/useFontLoader.js +126 -87
- package/dist/hooks/usePlayerLogic.js +3 -0
- package/dist/hooks/usePluginAPI.js +1 -1
- package/dist/hooks/useSoundPlayer.js +33 -5
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/plugin/PluginManager.d.ts +9 -1
- package/dist/plugin/PluginManager.js +59 -19
- package/dist/sdk.d.ts +1 -1
- package/dist/types.d.ts +29 -2
- package/package.json +1 -1
package/dist/Player.js
CHANGED
|
@@ -24,6 +24,7 @@ import { PluginComponentProvider } from "./components/PluginComponentProvider";
|
|
|
24
24
|
import { TimeWaitIndicator } from "./components/TimeWaitIndicator";
|
|
25
25
|
import { AudioProvider } from "./contexts/AudioContext";
|
|
26
26
|
import { DataProvider } from "./contexts/DataContext";
|
|
27
|
+
import { PlaybackTextProvider } from "./contexts/PlaybackTextContext";
|
|
27
28
|
import { useBacklog } from "./hooks/useBacklog";
|
|
28
29
|
import { useConversationBranch } from "./hooks/useConversationBranch";
|
|
29
30
|
import { useFontLoader } from "./hooks/useFontLoader";
|
|
@@ -38,10 +39,17 @@ import { ComponentType } from "./sdk";
|
|
|
38
39
|
import { convertBranchBlockToScenarioBlock } from "./utils/branchBlockConverter";
|
|
39
40
|
import { BranchNavigator } from "./utils/branchNavigator";
|
|
40
41
|
import { VariableManager } from "./utils/variableManager";
|
|
41
|
-
|
|
42
|
+
const EMPTY_PLUGINS = [];
|
|
43
|
+
const EMPTY_SOUNDS = [];
|
|
44
|
+
export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGINS, sounds = EMPTY_SOUNDS, onEnd, onScenarioEnd, onScenarioStart, onScenarioCancelled, onSettingsChange, className, autoplay = false, preventDefaultScroll = true, screenSize: screenSizeProp, disableKeyboardNavigation = false, }) => {
|
|
42
45
|
var _a, _b, _c, _d;
|
|
46
|
+
// scenario.blocks が存在しない場合は空の配列を使用
|
|
47
|
+
const scenario = useMemo(() => {
|
|
48
|
+
var _a;
|
|
49
|
+
return (Object.assign(Object.assign({}, scenarioProp), { blocks: (_a = scenarioProp.blocks) !== null && _a !== void 0 ? _a : [] }));
|
|
50
|
+
}, [scenarioProp]);
|
|
43
51
|
// デフォルト値とマージ
|
|
44
|
-
const mergedSettings = Object.assign({ textSpeed: 80, autoPlaySpeed: 3, bgmVolume: 0.5, seVolume: 1.0, voiceVolume: 1.0, effectVolume: 1.0, textSoundVolume: 1.0, defaultBackgroundFadeDuration: 0 }, settings);
|
|
52
|
+
const mergedSettings = Object.assign({ textSpeed: 80, autoPlaySpeed: 3, bgmVolume: 0.5, seVolume: 1.0, voiceVolume: 1.0, effectVolume: 1.0, textSoundVolume: 1.0, defaultBackgroundFadeDuration: 0, inactiveCharacterBrightness: 0.8, characterSpacing: 0.2 }, settings);
|
|
45
53
|
// プラグインからの設定更新ハンドラ
|
|
46
54
|
const handleSettingsUpdate = useCallback((updatedSettings) => {
|
|
47
55
|
var _a, _b;
|
|
@@ -84,6 +92,7 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
84
92
|
}, [pluginManager, mergedSettings.muteAudio]);
|
|
85
93
|
// 画面サイズの初期化
|
|
86
94
|
const [, setScreenSize] = useScreenSizeAtom();
|
|
95
|
+
const aspectRatioContainerRef = useRef(null);
|
|
87
96
|
useEffect(() => {
|
|
88
97
|
// screenSizeが明示的に指定されている場合はそれを使用(プレビュー用)
|
|
89
98
|
if (screenSizeProp) {
|
|
@@ -93,16 +102,40 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
93
102
|
// クライアントサイドでのみ実行
|
|
94
103
|
if (typeof window === "undefined")
|
|
95
104
|
return;
|
|
105
|
+
// 初期値として一時的にウィンドウサイズを設定
|
|
96
106
|
setScreenSize({ width: window.innerWidth, height: window.innerHeight });
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
const container = aspectRatioContainerRef.current;
|
|
108
|
+
if (!container)
|
|
109
|
+
return;
|
|
110
|
+
// アスペクト比コンテナのサイズを監視
|
|
111
|
+
const updateScreenSize = () => {
|
|
112
|
+
const rect = container.getBoundingClientRect();
|
|
113
|
+
// サイズが0の場合はスキップ(まだレンダリングされていない)
|
|
114
|
+
if (rect.width === 0 || rect.height === 0)
|
|
115
|
+
return;
|
|
116
|
+
console.log("[Player] screenSize更新:", {
|
|
117
|
+
width: rect.width,
|
|
118
|
+
height: rect.height,
|
|
119
|
+
windowWidth: window.innerWidth,
|
|
120
|
+
windowHeight: window.innerHeight,
|
|
121
|
+
});
|
|
99
122
|
setScreenSize({
|
|
100
|
-
width:
|
|
101
|
-
height:
|
|
123
|
+
width: rect.width,
|
|
124
|
+
height: rect.height,
|
|
102
125
|
});
|
|
103
126
|
};
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
// 初期サイズを設定(次のフレームで実行してレンダリング完了を待つ)
|
|
128
|
+
requestAnimationFrame(() => {
|
|
129
|
+
updateScreenSize();
|
|
130
|
+
});
|
|
131
|
+
// ResizeObserverでコンテナのサイズ変更を監視
|
|
132
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
133
|
+
updateScreenSize();
|
|
134
|
+
});
|
|
135
|
+
resizeObserver.observe(container);
|
|
136
|
+
return () => {
|
|
137
|
+
resizeObserver.disconnect();
|
|
138
|
+
};
|
|
106
139
|
}, [setScreenSize, screenSizeProp]);
|
|
107
140
|
// 表示可能なブロックのインデックスを事前計算
|
|
108
141
|
const displayableBlockIndices = useMemo(() => {
|
|
@@ -113,7 +146,6 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
113
146
|
"fullscreen_text",
|
|
114
147
|
"click_wait",
|
|
115
148
|
"time_wait",
|
|
116
|
-
"action_node",
|
|
117
149
|
];
|
|
118
150
|
const indices = scenario.blocks
|
|
119
151
|
.map((block, index) => ({ block, index }))
|
|
@@ -269,8 +301,28 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
269
301
|
}, [scenario.id, autoplay, hasStarted, pluginManager]);
|
|
270
302
|
// 画像を事前読み込み
|
|
271
303
|
const imagesLoaded = usePreloadImages(scenario);
|
|
272
|
-
//
|
|
273
|
-
|
|
304
|
+
// シナリオ内の全テキストを抽出(フォントのプリロード用)
|
|
305
|
+
// サブセットフォントの分割ファイルを事前に読み込むことで、
|
|
306
|
+
// 文字送り中のフォント切り替わりを防止
|
|
307
|
+
const allScenarioText = useMemo(() => {
|
|
308
|
+
var _a;
|
|
309
|
+
const texts = [];
|
|
310
|
+
for (const block of scenario.blocks) {
|
|
311
|
+
// dialogue, narration, fullscreen_textのcontent
|
|
312
|
+
if (block.content) {
|
|
313
|
+
texts.push(block.content);
|
|
314
|
+
}
|
|
315
|
+
// speaker name
|
|
316
|
+
if ((_a = block.speaker) === null || _a === void 0 ? void 0 : _a.name) {
|
|
317
|
+
texts.push(block.speaker.name);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// 重複を除去してユニークな文字のみを残す
|
|
321
|
+
const uniqueChars = [...new Set(texts.join(""))].join("");
|
|
322
|
+
return uniqueChars;
|
|
323
|
+
}, [scenario.blocks]);
|
|
324
|
+
// フォントを読み込み(シナリオ内の全テキストでプリロード)
|
|
325
|
+
const { isLoaded: fontsLoaded } = useFontLoader(scenario.fonts, allScenarioText);
|
|
274
326
|
// プラグインの読み込み状態
|
|
275
327
|
const [pluginsLoaded, setPluginsLoaded] = useState(false);
|
|
276
328
|
// 読み込み済みプラグインのパッケージ名を追跡
|
|
@@ -292,7 +344,7 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
292
344
|
for (const plugin of newPlugins) {
|
|
293
345
|
if (isCancelled)
|
|
294
346
|
return;
|
|
295
|
-
yield pluginManager.loadPlugin(plugin.packageName, plugin.bundleUrl, plugin.config);
|
|
347
|
+
yield pluginManager.loadPlugin(plugin.packageName, plugin.bundleUrl, plugin.config, plugin.assets, plugin.plugin);
|
|
296
348
|
loadedPluginNamesRef.current.add(plugin.packageName);
|
|
297
349
|
}
|
|
298
350
|
if (isCancelled)
|
|
@@ -309,6 +361,9 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
309
361
|
loadPlugins();
|
|
310
362
|
return () => {
|
|
311
363
|
isCancelled = true;
|
|
364
|
+
// Strict Mode再マウント時にプラグインが再ロード・再登録できるようにする
|
|
365
|
+
loadedPluginNamesRef.current.clear();
|
|
366
|
+
pluginManager.resetForRemount();
|
|
312
367
|
};
|
|
313
368
|
}, [plugins, sounds]);
|
|
314
369
|
// 初回レンダリング完了フラグ
|
|
@@ -549,7 +604,16 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
549
604
|
scenarioName: scenario.name || "scenario",
|
|
550
605
|
});
|
|
551
606
|
}
|
|
552
|
-
}, [
|
|
607
|
+
}, [
|
|
608
|
+
isFirstRenderComplete,
|
|
609
|
+
hasStarted,
|
|
610
|
+
currentBlock,
|
|
611
|
+
pluginsLoaded,
|
|
612
|
+
onScenarioStart,
|
|
613
|
+
pluginManager,
|
|
614
|
+
scenario.id,
|
|
615
|
+
scenario.name,
|
|
616
|
+
]);
|
|
553
617
|
// restartをusePlayerLogicの前に定義(分岐状態もリセット + キャンセルコールバック)
|
|
554
618
|
const restart = useCallback(() => {
|
|
555
619
|
// リスタート時はシナリオがキャンセルされたとみなす
|
|
@@ -712,7 +776,10 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
712
776
|
displayableBlockIndices.length,
|
|
713
777
|
handleNextInternal,
|
|
714
778
|
onEnd,
|
|
715
|
-
onScenarioEnd,
|
|
779
|
+
onScenarioEnd, // プラグインのonScenarioEndフックを呼び出し
|
|
780
|
+
pluginManager,
|
|
781
|
+
scenario.id,
|
|
782
|
+
scenario.name,
|
|
716
783
|
]);
|
|
717
784
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
718
785
|
const _handlePrevious = useCallback(() => {
|
|
@@ -722,7 +789,16 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
722
789
|
}, [state.currentBlockIndex, handlePreviousInternal]);
|
|
723
790
|
// 現在の背景を計算
|
|
724
791
|
const currentBackground = useMemo(() => calculateCurrentBackground(actualBlockIndex), [calculateCurrentBackground, actualBlockIndex]);
|
|
725
|
-
//
|
|
792
|
+
// displayText を JSX に変換(PlaybackTextProvider 用)
|
|
793
|
+
const displayTextElement = useMemo(() => {
|
|
794
|
+
if (displayText.includes("\n")) {
|
|
795
|
+
return displayText.split("\n").map((line, index, array) => (_jsxs(React.Fragment, { children: [line, index < array.length - 1 && _jsx("br", {})] }, index)));
|
|
796
|
+
}
|
|
797
|
+
return displayText;
|
|
798
|
+
}, [displayText]);
|
|
799
|
+
// DataContext の構築
|
|
800
|
+
// DataRefContext で安定した参照を提供することで、
|
|
801
|
+
// useDataAPI() を使うコンポーネントの再レンダリングを防ぐ
|
|
726
802
|
const dataContext = useMemo(() => {
|
|
727
803
|
var _a, _b, _c;
|
|
728
804
|
return ({
|
|
@@ -732,9 +808,7 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
732
808
|
scenarioId: scenario.id,
|
|
733
809
|
scenarioName: scenario.name,
|
|
734
810
|
currentBlock: currentBlock || null,
|
|
735
|
-
displayText:
|
|
736
|
-
? displayText.split("\n").map((line, index, array) => (_jsxs(React.Fragment, { children: [line, index < array.length - 1 && _jsx("br", {})] }, index)))
|
|
737
|
-
: displayText,
|
|
811
|
+
displayText: displayTextElement,
|
|
738
812
|
isTyping,
|
|
739
813
|
displayedCharacters,
|
|
740
814
|
},
|
|
@@ -753,6 +827,8 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
753
827
|
seVolume: mergedSettings.seVolume,
|
|
754
828
|
voiceVolume: mergedSettings.voiceVolume,
|
|
755
829
|
skipMode: "unread",
|
|
830
|
+
selectedFontFamily: mergedSettings.selectedFontFamily,
|
|
831
|
+
selectedUIFontFamily: mergedSettings.selectedUIFontFamily,
|
|
756
832
|
},
|
|
757
833
|
pluginAssets: {
|
|
758
834
|
getAssetUrl: (pluginName, filename) => {
|
|
@@ -766,6 +842,7 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
766
842
|
fonts: {
|
|
767
843
|
fonts: (_c = scenario.fonts) !== null && _c !== void 0 ? _c : [],
|
|
768
844
|
selectedFontFamily: mergedSettings.selectedFontFamily,
|
|
845
|
+
selectedUIFontFamily: mergedSettings.selectedUIFontFamily,
|
|
769
846
|
isLoaded: fontsLoaded,
|
|
770
847
|
},
|
|
771
848
|
emotionEffect: emotionEffectState,
|
|
@@ -775,7 +852,7 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
775
852
|
actualBlockIndex,
|
|
776
853
|
scenario,
|
|
777
854
|
currentBlock,
|
|
778
|
-
|
|
855
|
+
displayTextElement,
|
|
779
856
|
isTyping,
|
|
780
857
|
displayedCharacters,
|
|
781
858
|
backlog,
|
|
@@ -789,6 +866,7 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
789
866
|
mergedSettings.textSpeed,
|
|
790
867
|
mergedSettings.voiceVolume,
|
|
791
868
|
mergedSettings.selectedFontFamily,
|
|
869
|
+
mergedSettings.selectedUIFontFamily,
|
|
792
870
|
currentBackground,
|
|
793
871
|
fontsLoaded,
|
|
794
872
|
]);
|
|
@@ -828,6 +906,12 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
828
906
|
const [width, height] = settings.aspectRatio.split(":").map(Number);
|
|
829
907
|
return `${width}/${height}`;
|
|
830
908
|
};
|
|
909
|
+
const getAspectRatioValue = () => {
|
|
910
|
+
if (!(settings === null || settings === void 0 ? void 0 : settings.aspectRatio))
|
|
911
|
+
return 16 / 9;
|
|
912
|
+
const [width, height] = settings.aspectRatio.split(":").map(Number);
|
|
913
|
+
return width / height;
|
|
914
|
+
};
|
|
831
915
|
// 条件付きレンダリングを JSX で処理(フックの後、early return なし)
|
|
832
916
|
return (_jsxs(_Fragment, { children: [!currentBlock && !state.isEnded && (_jsx("div", { className: clsx("flex items-center justify-center p-8", className), children: _jsx("p", { className: "text-gray-500", children: "\u30B7\u30CA\u30EA\u30AA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093" }) })), state.isEnded && (_jsx(EndScreen, { scenarioName: scenario.name, onRestart: restart, className: className })), currentBlock && !state.isEnded && (_jsxs(_Fragment, { children: [(!imagesLoaded ||
|
|
833
917
|
!fontsLoaded ||
|
|
@@ -844,16 +928,27 @@ export const Player = ({ scenario, settings, plugins = [], sounds = [], onEnd, o
|
|
|
844
928
|
touchAction: "none",
|
|
845
929
|
userSelect: "none",
|
|
846
930
|
WebkitUserSelect: "none",
|
|
847
|
-
}, children: _jsx("div", { className: "relative bg-white flex flex-col
|
|
931
|
+
}, children: _jsx("div", { ref: aspectRatioContainerRef, className: "relative bg-white flex flex-col overflow-hidden", style: screenSizeProp
|
|
932
|
+
? {
|
|
933
|
+
// プレビューモード: 親コンテナ(固定論理サイズ)を100%で埋める
|
|
934
|
+
width: "100%",
|
|
935
|
+
height: "100%",
|
|
936
|
+
}
|
|
937
|
+
: {
|
|
938
|
+
// 通常再生: ビューポートに合わせてアスペクト比を維持
|
|
939
|
+
aspectRatio: getAspectRatio(),
|
|
940
|
+
width: `min(100vw, calc(100vh * ${getAspectRatioValue()}))`,
|
|
941
|
+
height: `min(100vh, calc(100vw / ${getAspectRatioValue()}))`,
|
|
942
|
+
}, children: _jsx(DataProvider, { data: dataContext, onSettingsUpdate: handleSettingsUpdate, onEmotionEffectUpdate: handleEmotionEffectUpdate, onCancelScenario: cancelScenario, children: _jsx(AudioProvider, { settings: {
|
|
848
943
|
bgmVolume: mergedSettings.bgmVolume,
|
|
849
944
|
seVolume: mergedSettings.seVolume,
|
|
850
945
|
voiceVolume: mergedSettings.voiceVolume,
|
|
851
946
|
effectVolume: mergedSettings.effectVolume,
|
|
852
947
|
textSoundVolume: mergedSettings.textSoundVolume,
|
|
853
948
|
muteAudio: (_b = mergedSettings.muteAudio) !== null && _b !== void 0 ? _b : false,
|
|
854
|
-
}, children: [_jsxs("div", { className: "h-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.Background, pluginManager: pluginManager, fallback: BackgroundLayer }, "background"), _jsx(GameScreen, { scenario: scenario, currentBlock: currentBlock, previousBlock: previousBlock, displayedCharacters: displayedCharacters }, "game-screen")] }), _jsx(OverlayUI, { children: _jsxs("div", { className: "h-full w-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.DialogueBox, pluginManager: pluginManager, fallback: DialogueBox }, "dialogue-box"), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "conversation_branch" && (_jsx(PluginComponentProvider, { type: ComponentType.ConversationBranch, pluginManager: pluginManager, fallback: ConversationBranchBox }, "conversation-branch")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "fullscreen_text" && (_jsx(FullscreenTextBox, { onComplete: () => handleNext("click") }, "fullscreen-text")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "click_wait" && (_jsx(ClickWaitIndicator, {}, "click-wait")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "time_wait" && (_jsx(TimeWaitIndicator, { duration: (_d = (_c = currentBlock.options) === null || _c === void 0 ? void 0 : _c.duration) !== null && _d !== void 0 ? _d : 1, onComplete: () => handleNext("time_wait") }, "time-wait")), pluginManager
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
949
|
+
}, children: _jsxs(PlaybackTextProvider, { displayText: displayTextElement, isTyping: isTyping, children: [_jsxs("div", { className: "h-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.Background, pluginManager: pluginManager, fallback: BackgroundLayer }, "background"), _jsx(GameScreen, { scenario: scenario, currentBlock: currentBlock, previousBlock: previousBlock, displayedCharacters: displayedCharacters, inactiveCharacterBrightness: mergedSettings.inactiveCharacterBrightness, characterSpacing: mergedSettings.characterSpacing }, "game-screen")] }), _jsx(OverlayUI, { children: _jsxs("div", { className: "h-full w-full relative", children: [_jsx(PluginComponentProvider, { type: ComponentType.DialogueBox, pluginManager: pluginManager, fallback: DialogueBox }, "dialogue-box"), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "conversation_branch" && (_jsx(PluginComponentProvider, { type: ComponentType.ConversationBranch, pluginManager: pluginManager, fallback: ConversationBranchBox }, "conversation-branch")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "fullscreen_text" && (_jsx(FullscreenTextBox, { onComplete: () => handleNext("click") }, "fullscreen-text")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "click_wait" && (_jsx(ClickWaitIndicator, {}, "click-wait")), (currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.blockType) === "time_wait" && (_jsx(TimeWaitIndicator, { duration: (_d = (_c = currentBlock.options) === null || _c === void 0 ? void 0 : _c.duration) !== null && _d !== void 0 ? _d : 1, onComplete: () => handleNext("time_wait") }, "time-wait")), pluginManager
|
|
950
|
+
.getRegisteredComponents()
|
|
951
|
+
.filter((type) => type !== ComponentType.DialogueBox &&
|
|
952
|
+
type !== ComponentType.ConversationBranch)
|
|
953
|
+
.map((componentType) => (_jsx(PluginComponentProvider, { type: componentType, pluginManager: pluginManager }, componentType)))] }) })] }) }) }) }) })] }))] }));
|
|
859
954
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { useDataAPI } from "../contexts/DataContext";
|
|
4
4
|
/**
|
|
5
5
|
* iOS/macOSかどうかを判定
|
|
@@ -56,8 +56,10 @@ function getMediaType(url) {
|
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
58
|
* 背景メディアコンポーネント
|
|
59
|
+
* React.memo でラップして、props が変わらない限り再レンダリングを防ぐ
|
|
60
|
+
* これにより displayText の更新による不要な再描画を防止
|
|
59
61
|
*/
|
|
60
|
-
const BackgroundMedia = ({ background, opacity, zIndex, useAppleFormat })
|
|
62
|
+
const BackgroundMedia = memo(function BackgroundMedia({ background, opacity, zIndex, useAppleFormat }) {
|
|
61
63
|
var _a, _b;
|
|
62
64
|
// OS判定に基づいて使用するURLを決定
|
|
63
65
|
// iOS/macOS: mp4 (imageUrl)
|
|
@@ -84,7 +86,7 @@ const BackgroundMedia = ({ background, opacity, zIndex, useAppleFormat }) => {
|
|
|
84
86
|
transition: "none", // requestAnimationFrameでアニメーション
|
|
85
87
|
pointerEvents: isHighLayer ? "none" : undefined,
|
|
86
88
|
}, "data-background-object-id": background.objectId, "data-background-state-id": background.stateId, "data-background-layer": layer, children: mediaType === "video" ? (_jsx("video", { src: videoUrl, className: `w-full h-full ${objectFitClass}`, autoPlay: true, muted: true, playsInline: true, loop: background.loop }, videoUrl)) : (_jsx("img", { src: background.imageUrl, alt: background.stateName, className: `w-full h-full ${objectFitClass}` }, background.imageUrl)) }));
|
|
87
|
-
};
|
|
89
|
+
});
|
|
88
90
|
/**
|
|
89
91
|
* 背景の配列が同じかどうかを比較
|
|
90
92
|
*/
|
|
@@ -107,7 +109,7 @@ function areBackgroundsEqual(a, b) {
|
|
|
107
109
|
* プラグインによって置き換え可能
|
|
108
110
|
* フェードイン・フェードアウト対応
|
|
109
111
|
*/
|
|
110
|
-
export const BackgroundLayer = ({ className
|
|
112
|
+
export const BackgroundLayer = memo(({ className }) => {
|
|
111
113
|
const dataAPI = useDataAPI();
|
|
112
114
|
// Apple環境かどうかを判定(iOS/macOSではmp4を使用)
|
|
113
115
|
const useAppleFormat = useMemo(() => isApplePlatform(), []);
|
|
@@ -214,5 +216,5 @@ export const BackgroundLayer = ({ className, }) => {
|
|
|
214
216
|
const zIndex = getLayerZIndex(layer, index);
|
|
215
217
|
return (_jsx(BackgroundMedia, { background: bg, opacity: fadeState.isFading ? fadeState.fadeProgress : 1, zIndex: zIndex, useAppleFormat: useAppleFormat }, `curr-${bg.objectId}-${bg.stateId}-${index}`));
|
|
216
218
|
})] }));
|
|
217
|
-
};
|
|
219
|
+
});
|
|
218
220
|
export { getMediaType };
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import type React from "react";
|
|
2
1
|
import type { DisplayedCharacter, PublishedScenario, ScenarioBlock } from "../types";
|
|
3
2
|
interface GameScreenProps {
|
|
4
3
|
scenario: PublishedScenario;
|
|
5
4
|
currentBlock: ScenarioBlock;
|
|
6
5
|
previousBlock?: ScenarioBlock | null;
|
|
7
6
|
displayedCharacters: DisplayedCharacter[];
|
|
7
|
+
inactiveCharacterBrightness?: number;
|
|
8
|
+
characterSpacing?: number;
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
/**
|
|
11
|
+
* ゲームスクリーンコンポーネント
|
|
12
|
+
* React.memo でラップして、props が変わらない限り再レンダリングを防ぐ
|
|
13
|
+
* これにより displayText の更新による不要な再描画を防止
|
|
14
|
+
*/
|
|
15
|
+
export declare const GameScreen: import("react").NamedExoticComponent<GameScreenProps>;
|
|
10
16
|
export {};
|