update : cinematic trigger
This commit is contained in:
@@ -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,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, () => {
|
||||||
|
|||||||
@@ -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,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",
|
||||||
|
|||||||
@@ -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?.();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user