update : cinematic trigger

This commit is contained in:
math-pixel
2026-05-12 17:07:53 +02:00
parent ae34dc38ed
commit ff79448ce8
5 changed files with 45 additions and 8 deletions
+15
View File
@@ -1,6 +1,21 @@
{ {
"version": 1, "version": 1,
"cinematics": [ "cinematics": [
{
"id": "intro_sequence",
"trigger": "intro_sequence",
"cameraKeyframes": [
{ "time": 0, "position": [8, 5, 12], "target": [0, 2, 0] },
{ "time": 8, "position": [12, 4, -6], "target": [10, 1.4, -8] },
{ "time": 16, "position": [5, 6, -15], "target": [0, 3, -20] },
{ "time": 24, "position": [0, 8, -30], "target": [0, 0, -40] }
],
"dialogueCues": [
{ "time": 0, "dialogueId": "intro_welcome" },
{ "time": 8, "dialogueId": "intro_explanation" },
{ "time": 16, "dialogueId": "intro_mission" }
]
},
{ {
"id": "intro_overview", "id": "intro_overview",
"timecode": 0, "timecode": 0,
+6 -8
View File
@@ -6,6 +6,7 @@ import { AUDIO_PATHS } from "@/data/audioConfig";
export function GameFlow(): null { export function GameFlow(): null {
const step = useGameStore((state) => state.intro.currentStep); const step = useGameStore((state) => state.intro.currentStep);
const setStep = useGameStore((state) => state.setIntroStep); const setStep = useGameStore((state) => state.setIntroStep);
const isCinematicPlaying = useGameStore((state) => state.isCinematicPlaying);
const setActivityCity = useGameStore((state) => state.setActivityCity); const setActivityCity = useGameStore((state) => state.setActivityCity);
const setCanMove = useGameStore((state) => state.setCanMove); const setCanMove = useGameStore((state) => state.setCanMove);
const hasInitialized = useRef(false); const hasInitialized = useRef(false);
@@ -13,20 +14,17 @@ export function GameFlow(): null {
useEffect(() => { useEffect(() => {
if (!hasInitialized.current && step === "intro") { if (!hasInitialized.current && step === "intro") {
hasInitialized.current = true; hasInitialized.current = true;
setStep("start-intro"); setStep("intro_sequence");
} }
}, [step, setStep]); }, [step, setStep]);
useEffect(() => { useEffect(() => {
if (step === "start-intro") { if (step === "intro_sequence" && !isCinematicPlaying) {
const audio = AudioManager.getInstance(); setStep("naming");
audio.playSoundWithCallback(AUDIO_PATHS.intro, 0.5, () => {
setStep("naming");
});
return () => {};
} }
}, [step, isCinematicPlaying, setStep]);
useEffect(() => {
if (step === "bienvenue") { if (step === "bienvenue") {
const audio = AudioManager.getInstance(); const audio = AudioManager.getInstance();
audio.playSoundWithCallback(AUDIO_PATHS.bienvenue, 0.5, () => { audio.playSoundWithCallback(AUDIO_PATHS.bienvenue, 0.5, () => {
+1
View File
@@ -14,6 +14,7 @@ export interface CinematicDialogueCue {
export interface CinematicDefinition { export interface CinematicDefinition {
id: string; id: string;
timecode?: number; timecode?: number;
trigger?: string;
cameraKeyframes: CinematicCameraKeyframe[]; cameraKeyframes: CinematicCameraKeyframe[];
dialogueCues?: CinematicDialogueCue[]; dialogueCues?: CinematicDialogueCue[];
} }
+2
View File
@@ -2,6 +2,7 @@ import type { Vector3Tuple } from "@/types/three/three";
export type GameStep = export type GameStep =
| "intro" | "intro"
| "intro_sequence"
| "start-intro" | "start-intro"
| "naming" | "naming"
| "bienvenue" | "bienvenue"
@@ -14,6 +15,7 @@ export type GameStep =
export const GAME_STEPS: readonly GameStep[] = [ export const GAME_STEPS: readonly GameStep[] = [
"intro", "intro",
"intro_sequence",
"start-intro", "start-intro",
"naming", "naming",
"bienvenue", "bienvenue",
+21
View File
@@ -20,6 +20,7 @@ export function GameCinematics(): null {
const [dialogueManifest, setDialogueManifest] = const [dialogueManifest, setDialogueManifest] =
useState<DialogueManifest | null>(null); useState<DialogueManifest | null>(null);
const playedCinematicsRef = useRef(new Set<string>()); const playedCinematicsRef = useRef(new Set<string>());
const triggeredCinematicsRef = useRef(new Set<string>());
const timelineRef = useRef<gsap.core.Timeline | null>(null); const timelineRef = useRef<gsap.core.Timeline | null>(null);
const activeAudiosRef = useRef(new Set<HTMLAudioElement>()); const activeAudiosRef = useRef(new Set<HTMLAudioElement>());
const startedAtRef = useRef<number | null>(null); const startedAtRef = useRef<number | null>(null);
@@ -64,7 +65,25 @@ export function GameCinematics(): null {
const elapsedTime = clock.getElapsedTime() - startedAtRef.current; const elapsedTime = clock.getElapsedTime() - startedAtRef.current;
const currentStep = useGameStore.getState().intro.currentStep;
manifest.cinematics.forEach((cinematic) => { manifest.cinematics.forEach((cinematic) => {
if (cinematic.trigger) {
if (triggeredCinematicsRef.current.has(cinematic.id)) return;
if (currentStep !== cinematic.trigger) return;
if (cinematic.dialogueCues && !dialogueManifest) return;
triggeredCinematicsRef.current.add(cinematic.id);
playCinematic(camera, cinematic, timelineRef, {
dialogueManifest,
activeAudiosRef,
onComplete: () => {
triggeredCinematicsRef.current.delete(cinematic.id);
},
});
return;
}
if (cinematic.timecode === undefined) return; if (cinematic.timecode === undefined) return;
if (cinematic.timecode > elapsedTime) return; if (cinematic.timecode > elapsedTime) return;
if (cinematic.dialogueCues && !dialogueManifest) return; if (cinematic.dialogueCues && !dialogueManifest) return;
@@ -95,6 +114,7 @@ function playCinematic(
dialogueOptions: { dialogueOptions: {
dialogueManifest: DialogueManifest | null; dialogueManifest: DialogueManifest | null;
activeAudiosRef: MutableRefObject<Set<HTMLAudioElement>>; activeAudiosRef: MutableRefObject<Set<HTMLAudioElement>>;
onComplete?: () => void;
}, },
): void { ): void {
const firstKeyframe = cinematic.cameraKeyframes[0]; const firstKeyframe = cinematic.cameraKeyframes[0];
@@ -113,6 +133,7 @@ function playCinematic(
onComplete: () => { onComplete: () => {
timelineRef.current = null; timelineRef.current = null;
useGameStore.getState().setCinematicPlaying(false); useGameStore.getState().setCinematicPlaying(false);
dialogueOptions.onComplete?.();
}, },
}); });