From c6ca31402d325fb5b0373bc553f043bf5fedd421 Mon Sep 17 00:00:00 2001 From: Lara Date: Thu, 5 Nov 2026 19:50:15 +0000 Subject: [PATCH] =?UTF-8?q?Gehen=20ohne=20rutschende=20F=C3=BC=C3=9Fe=20(b?= =?UTF-8?q?aukasten/gang.ts,=20gemessen=20an=20den=20Geh-Clips),=20Folge?= =?UTF-8?q?=208=20=E2=80=9ENoas=20Laterne=E2=80=9C=20in=20Arbeit,=20Lied?= =?UTF-8?q?=201:=20Pino=20h=C3=BCpft=20wieder=20nach=20oben?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gang.ts: Geschwindigkeit aus der Fußbewegung im Clip, Start/Ende mit geschlossenen Füßen, ganze Schritte; GangTest als Prüfstand (Stiefel steht 21 Bilder auf ±1 px). Lulus Geh-Clip ist kein Gehen – nicht verschieben. - Lied 1: huepfer() ist negativ und wurde abgezogen – Pino sank bei jeder Zahl in den Boden. - folge08: Texte DE/ES/FR, Szene 2–7, Abendgarten mit Hauswand, Laterne mit Sternen/Igel, Wand-Schatten Co-Authored-By: Claude Opus 5.5 --- remotion/src/Root.tsx | 4 + remotion/src/baukasten/gang.ts | 65 ++++++++ remotion/src/folge08/Folge08.tsx | 45 ++++++ remotion/src/folge08/plan.ts | 29 ++++ remotion/src/folge08/stimmen/de.json | 1 + remotion/src/folge08/stimmen/es.json | 1 + remotion/src/folge08/stimmen/fr.json | 1 + remotion/src/folge08/szene2.tsx | 133 ++++++++++++++++ remotion/src/folge08/szene3.tsx | 100 ++++++++++++ remotion/src/folge08/szene4.tsx | 56 +++++++ remotion/src/folge08/szene5.tsx | 47 ++++++ remotion/src/folge08/szene6.tsx | 66 ++++++++ remotion/src/folge08/szene7.tsx | 60 +++++++ remotion/src/folge08/teile.tsx | 219 ++++++++++++++++++++++++++ remotion/src/folge08/texte/de.json | 58 +++++++ remotion/src/folge08/texte/es.json | 58 +++++++ remotion/src/folge08/texte/fr.json | 58 +++++++ remotion/src/folge08/wand.tsx | 61 +++++++ remotion/src/lieder/lied01/Lied01.tsx | 2 +- remotion/src/szenen/GangTest.tsx | 25 +++ 20 files changed, 1088 insertions(+), 1 deletion(-) create mode 100644 remotion/src/baukasten/gang.ts create mode 100644 remotion/src/folge08/Folge08.tsx create mode 100644 remotion/src/folge08/plan.ts create mode 100644 remotion/src/folge08/stimmen/de.json create mode 100644 remotion/src/folge08/stimmen/es.json create mode 100644 remotion/src/folge08/stimmen/fr.json create mode 100644 remotion/src/folge08/szene2.tsx create mode 100644 remotion/src/folge08/szene3.tsx create mode 100644 remotion/src/folge08/szene4.tsx create mode 100644 remotion/src/folge08/szene5.tsx create mode 100644 remotion/src/folge08/szene6.tsx create mode 100644 remotion/src/folge08/szene7.tsx create mode 100644 remotion/src/folge08/teile.tsx create mode 100644 remotion/src/folge08/texte/de.json create mode 100644 remotion/src/folge08/texte/es.json create mode 100644 remotion/src/folge08/texte/fr.json create mode 100644 remotion/src/folge08/wand.tsx create mode 100644 remotion/src/szenen/GangTest.tsx diff --git a/remotion/src/Root.tsx b/remotion/src/Root.tsx index 40354d5..ace1f91 100644 --- a/remotion/src/Root.tsx +++ b/remotion/src/Root.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {Composition} from 'remotion'; import {HintergrundAbend, PapierStruktur, VordergrundAbend} from './szenen/Hintergruende'; import {LaterneTest} from './szenen/LaterneTest'; +import {GANGTEST_DAUER, GangTest} from './szenen/GangTest'; import {VerformTest} from './szenen/VerformTest'; import {FolgenKompositionen} from './baukasten/folge'; import {NEST_BILD} from './baukasten/natur'; @@ -9,6 +10,7 @@ import {NestStill, ORTE, OrtStill} from './baukasten/orte'; import {F01} from './folge01/Folge01'; import {F02} from './folge02/Folge02'; import {F03} from './folge03/Folge03'; +import {F08} from './folge08/Folge08'; import {Banner, BannerRahmen, Profilbild} from './kanal/KanalBilder'; import {VorschauF01} from './kanal/Vorschaubild'; import {ShortKompositionen} from './shorts/Short'; @@ -23,6 +25,7 @@ export const Root: React.FC = () => ( <> + @@ -31,6 +34,7 @@ export const Root: React.FC = () => ( + diff --git a/remotion/src/baukasten/gang.ts b/remotion/src/baukasten/gang.ts new file mode 100644 index 0000000..9c97622 --- /dev/null +++ b/remotion/src/baukasten/gang.ts @@ -0,0 +1,65 @@ +import {GROESSE, NOA_HOEHE} from './figur'; +import {CLIPS, FIGUREN} from './posen'; + +// Gehen ohne rutschende Füße. +// In den Geh-Clips (*_C1_gehen-schleife) geht die Figur auf der Stelle; der aufgesetzte Fuß wandert +// dabei gleichmäßig nach hinten. Bewegt sich die Figur im Bild genau so schnell nach vorn, bleibt der +// Fuß am Boden stehen. Gemessen am 05.11.2026 an den Alpha-Kanten der Füße (Bodenkontakt je Clipbild): +// Noa: 10,1 px pro Clipbild (Video 292 px breit), Füße zusammen bei Clipbild ≈ 10,5, alle 10,25 Bilder ein Schritt +// Pino: 3,6 px pro Clipbild (Video 438 px breit), Füße zusammen bei Clipbild ≈ 15, alle 15 Bilder ein Schritt +// Lulus Clip ist kein Gehen (drei Beine bleiben stehen) – Lulu nie mit dem Clip über den Boden schieben. +// Ein Gang beginnt und endet mit geschlossenen Füßen; die Strecke wird auf ganze Schritte gerundet. + +type Messung = {pxProClipbild: number; videoBreite: number; fuesseZusammen: number; schrittBilder: number}; +const MESSUNG: Record<'noa' | 'pino', Messung> = { + noa: {pxProClipbild: 10.1, videoBreite: 292, fuesseZusammen: 10.5, schrittBilder: 10.25}, + pino: {pxProClipbild: 3.6, videoBreite: 438, fuesseZusammen: 15, schrittBilder: 15}, +}; + +const CLIP_FPS = 24; +const FPS = 30; + +// Bildschirm-Pixel (in Szenenkoordinaten) je Clipbild bei Normalgröße +const pxProClipbild = (figur: 'noa' | 'pino', hoehe = NOA_HOEHE * GROESSE[figur]) => { + const m = MESSUNG[figur]; + const c = CLIPS[`${figur}_C1_gehen-schleife`]; + return ((m.pxProClipbild * c.b) / m.videoBreite) * (hoehe / FIGUREN[figur].refHoehe); +}; + +export type Gang = { + figur: 'noa' | 'pino'; + start: number; // Frame, an dem die Figur losgeht + ende: number; // Frame, an dem sie wieder steht + von: number; + nach: number; // tatsächliches Ziel (auf ganze Schritte gerundet) + tempo: number; // für ClipFigur + clipStart: number; // für ClipFigur: so steht der Clip bei `start` auf „Füße zusammen“ + spiegeln: boolean; // Clips schauen nach links; nach rechts gehen = spiegeln + x: (f: number) => number; + geht: (f: number) => boolean; +}; + +// Plant einen Gang von `von` nach (ungefähr) `nach`, Start bei Frame `start`. +// tempo: wie schnell der Clip läuft (1,25 = jedes Videobild ein Clipbild); die Geschwindigkeit folgt daraus. +export const gang = (figur: 'noa' | 'pino', start: number, von: number, nach: number, tempo = 1.25, hoehe?: number): Gang => { + const m = MESSUNG[figur]; + const clipbilderProFrame = (tempo * CLIP_FPS) / FPS; + const proSchritt = pxProClipbild(figur, hoehe) * m.schrittBilder; + const schritte = Math.max(1, Math.round(Math.abs(nach - von) / proSchritt)); + const richtung = Math.sign(nach - von) || 1; + const ziel = von + richtung * schritte * proSchritt; + const dauer = (schritte * m.schrittBilder) / clipbilderProFrame; + const ende = start + dauer; + return { + figur, + start, + ende, + von, + nach: ziel, + tempo, + clipStart: start - m.fuesseZusammen / clipbilderProFrame, + spiegeln: richtung > 0, + x: (f) => (f <= start ? von : f >= ende ? ziel : von + ((ziel - von) * (f - start)) / dauer), + geht: (f) => f >= start && f < ende, + }; +}; diff --git a/remotion/src/folge08/Folge08.tsx b/remotion/src/folge08/Folge08.tsx new file mode 100644 index 0000000..bbfdb2d --- /dev/null +++ b/remotion/src/folge08/Folge08.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {INTRO_OPTIONEN, Intro, OUTRO, Outro, introZeilen} from '../baukasten/buch'; +import {baueFolge, type SzenenEintrag} from '../baukasten/folge'; +import {mitKlang} from '../baukasten/geraeusche'; +import {TITEL, plane} from './plan'; +import {S2, Szene2} from './szene2'; +import {S3, Szene3} from './szene3'; + +// Folge 8 „Noas Laterne“ (läuft am Mi 11.11., St. Martin) – per Code aus der Figuren-Bibliothek, +// Drehbuch drehbuecher/neu-noas-laterne/de.md. Gehen nur mit baukasten/gang.ts (keine rutschenden Füße). + +const INTRO = plane('intro', introZeilen(TITEL), INTRO_OPTIONEN); + +export const SZENEN: SzenenEintrag[] = [ + { + name: 'Intro', + datei: 'Folge08.tsx', + plan: INTRO, + K: mitKlang(() => , INTRO, { + toene: [{name: 'buch_auf', bei: 'buch', versatz: 0.7, lautstaerke: 0.35}], + }), + musik: {datei: 'musik/folge01/intro.mp3', lautstaerke: 0.35, rein: 0.2, raus: 1.2}, + }, + { + name: 'Was wird das', + datei: 'szene2.tsx', + plan: S2, + K: mitKlang(Szene2, S2, {atmo: 'zimmer', atmoLaut: 0.1, toene: [{name: 'rascheln', bei: 'bild0', versatz: 0.3, lautstaerke: 0.2, laenge: 1.5}]}), + }, + { + name: 'Es wird dunkel', + datei: 'szene3.tsx', + plan: S3, + K: mitKlang(Szene3, S3, {atmo: 'garten_abend', atmoLaut: 0.12, toene: [{name: 'klick', bei: 'klick', versatz: 0.35, lautstaerke: 0.5}]}), + }, + { + name: 'Outro', + datei: 'Folge08.tsx', + plan: OUTRO, + K: mitKlang(() => , OUTRO, {toene: [{name: 'buch_zu', bei: 'buch', versatz: 3.2, lautstaerke: 0.45}]}), + musik: {datei: 'musik/folge01/schlaflied.mp3', lautstaerke: 0.3, rein: 0.1, raus: 2}, + }, +]; + +export const F08 = baueFolge('F08', SZENEN); diff --git a/remotion/src/folge08/plan.ts b/remotion/src/folge08/plan.ts new file mode 100644 index 0000000..0b86e88 --- /dev/null +++ b/remotion/src/folge08/plan.ts @@ -0,0 +1,29 @@ +import {SPRACHE, type FolgenTexte} from '../baukasten/sprache'; +import {plane as basis, type PlanOptionen, type Zeile} from '../baukasten/zeit'; +import DE from './texte/de.json'; +import ES from './texte/es.json'; +import FR from './texte/fr.json'; +import STIMMEN_DE from './stimmen/de.json'; +import STIMMEN_ES from './stimmen/es.json'; +import STIMMEN_FR from './stimmen/fr.json'; + +// Sprechtexte stehen nur in texte/.json (Schlüssel „szene/zeile“), +// die Szenen enthalten nur Ablauf und Bewegung. Aufnahmen je Sprache in +// stimmen/.json bzw. public/stimmen/folge08// (erzeugt von +// werkzeuge/stimmen.mts). + +export const TEXTE: FolgenTexte = {de: DE, es: ES, fr: FR}[SPRACHE]; +const AUFNAHMEN = {de: STIMMEN_DE, es: STIMMEN_ES, fr: STIMMEN_FR}[SPRACHE]; + +export const TITEL = TEXTE.titel; + +// Zeitplan einer Szene dieser Folge, mit den Texten der gewählten Sprache +export const plane = (szene: string, zeilen: Zeile[], opt: PlanOptionen = {}) => + basis( + zeilen.map((z) => { + if (z.text !== undefined || !z.wer) return z; + const text = TEXTE.zeilen[`${szene}/${z.id}`]; + return {...z, text: text ?? `[fehlt: ${szene}/${z.id}]`}; + }), + {...opt, szene, aufnahmen: AUFNAHMEN, ordner: `stimmen/folge08/${SPRACHE}`}, + ); diff --git a/remotion/src/folge08/stimmen/de.json b/remotion/src/folge08/stimmen/de.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/remotion/src/folge08/stimmen/de.json @@ -0,0 +1 @@ +{} diff --git a/remotion/src/folge08/stimmen/es.json b/remotion/src/folge08/stimmen/es.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/remotion/src/folge08/stimmen/es.json @@ -0,0 +1 @@ +{} diff --git a/remotion/src/folge08/stimmen/fr.json b/remotion/src/folge08/stimmen/fr.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/remotion/src/folge08/stimmen/fr.json @@ -0,0 +1 @@ +{} diff --git a/remotion/src/folge08/szene2.tsx b/remotion/src/folge08/szene2.tsx new file mode 100644 index 0000000..0a6edbb --- /dev/null +++ b/remotion/src/folge08/szene2.tsx @@ -0,0 +1,133 @@ +import React from 'react'; +import {interpolate, useCurrentFrame} from 'remotion'; +import {Figur, Kamera, POSEN_SKALA} from '../baukasten/figur'; +import {Blende, Vignette, atmen, huepfer, kamFahrt} from '../baukasten/helfer'; +import {Funkeln} from '../baukasten/natur'; +import {Ort} from '../baukasten/orte'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {Laterne} from './teile'; + +// Szene 2: Was wird das? Nachmittags in Noas Zimmer. Noa bastelt am Tisch, Pino sitzt auf dem Tisch, +// Lulu schaut durchs Fenster herein. Bei jeder Zahl klebt Noa einen Stern auf (Nahaufnahme), dann +// den Papier-Igel; Lulu pustet den Kleber trocken. Aufbau wie Folge 2, Szene 6 (Noas Zimmer). + +const N = 420; // Noas Größe im Zimmer (näher an der Kamera) +const NOA_X = 760; +const PINO_X = 1110; +const TISCH_Y = 802; +const LATERNE = {x: 950, y: TISCH_Y - 64}; +const LULU_X = 1440; + +export const S2 = plane('s2', [ + {id: 'bild0', dauer: 2.5}, + {id: 'l1', wer: 'LULU'}, + {id: 'n1', wer: 'NOA'}, + {id: 'e1', wer: 'ERZ', pause: 3}, + {id: 'n2', wer: 'NOA'}, + {id: 'p1', wer: 'PINO'}, + {id: 'n3', wer: 'NOA'}, + {id: 'z1', wer: 'NOA', pause: 0.8}, + {id: 'z2', wer: 'NOA', pause: 0.8}, + {id: 'z3', wer: 'NOA', pause: 0.8}, + {id: 'z4', wer: 'NOA', pause: 0.8}, + {id: 'z5', wer: 'NOA', pause: 0.6}, + {id: 'p2', wer: 'PINO'}, + {id: 'n4', wer: 'NOA'}, + {id: 'igel', dauer: 1.8}, + {id: 'l2', wer: 'LULU'}, + {id: 'pusten', dauer: 2.6}, + {id: 'p3', wer: 'PINO'}, + {id: 'bild1', dauer: 1.4}, +]); + +const ZAHLEN = ['z1', 'z2', 'z3', 'z4', 'z5'] as const; + +export const Szene2: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S2; + + const sterne = ZAHLEN.filter((id) => f >= z[id].start).length; + const igel = interpolate(f, [z.igel.start + 8, z.igel.start + 26], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); + const pusten = f >= z.pusten.start && f < z.p3.ende; + const wind = pusten ? Math.sin(((f - z.pusten.start) / 30) * Math.PI * 1.6) : 0; + const nah = f >= z.n3.ende - 6 && f < z.igel.ende; // Nahaufnahme beim Zählen und Igel-Kleben + + const kam = nah + ? kamFahrt(f, [ + [z.n3.ende - 6, LATERNE.x - 10, LATERNE.y - 10, 2.6], + [z.igel.ende, LATERNE.x, LATERNE.y - 4, 2.8], + ]) + : kamFahrt(f, [ + [0, 1000, 540, 1.0], + [z.bild0.ende, 1080, 560, 1.12], + [z.l1.ende, 1060, 600, 1.3], + [z.e1.start, 960, 640, 1.45], + [z.p1.start, 1030, 650, 1.55], + [z.l2.start, 1140, 560, 1.2], + [z.pusten.start + 10, 1080, 640, 1.5], + [dauer, 1060, 640, 1.55], + ]); + + // Pino zeigt bei jeder Zahl mit der Pfote: kleiner Hopser; beim Pusten wehen die Stacheln, er kichert + const pinoHopser = ZAHLEN.reduce((a, id) => a + huepfer(f, z[id].start + 2, 10, 9), 0) + huepfer(f, z.p2.start, 16, 11) + huepfer(f, z.p3.start + 4, 12, 10); + const pinoWeh = pusten ? 5 * wind : 0; + + return ( + <> + + + + + {/* Wind vom Pusten: drei weiche Linien vom Fenster zur Laterne */} + {pusten && ( + + {[0, 1, 2].map((i) => { + const p = ((f - z.pusten.start) / 26 + i / 3) % 1; + const x = interpolate(p, [0, 1], [1320, 1000]); + return ; + })} + + )} + + + + {ZAHLEN.map((id, i) => ( + + ))} + + + + + + + ); +}; diff --git a/remotion/src/folge08/szene3.tsx b/remotion/src/folge08/szene3.tsx new file mode 100644 index 0000000..e69f8a7 --- /dev/null +++ b/remotion/src/folge08/szene3.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import {interpolate, useCurrentFrame} from 'remotion'; +import {ClipFigur, Figur, Kamera} from '../baukasten/figur'; +import {gang} from '../baukasten/gang'; +import {Blende, Vignette, atmen, kamFahrt} from '../baukasten/helfer'; +import {Funkeln} from '../baukasten/natur'; +import {BODEN} from '../baukasten/orte'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {Abenddunkel, GartenAbend, Laterne, laterneInNoasHand} from './teile'; + +// Szene 3: Es wird dunkel. Abendgarten, rechts die Hauswand, Papa winkt im Fenster. +// Noa knipst das Lämpchen an (kein Feuer) und singt den Laternenvers; die Laterne pendelt sanft. +// Pino tippelt zu ihr (baukasten/gang.ts, Füße rutschen nicht). Lulu steht schon an der Hauswand – +// sie geht nicht durchs Bild, weil ihr Geh-Clip kein echtes Gehen ist. + +const NOA_X = 760; +const LULU_X = 1640; // rechts vom Fenster, damit Papa zu sehen ist + +export const S3 = plane('s3', [ + {id: 'bild0', dauer: 3.0}, + {id: 'e1', wer: 'ERZ'}, + {id: 'klick', dauer: 1.4}, + {id: 'e2', wer: 'ERZ'}, + {id: 'p1', wer: 'PINO'}, + {id: 'v1', wer: 'NOA'}, + {id: 'v2', wer: 'NOA'}, + {id: 'v3', wer: 'NOA'}, + {id: 'v4', wer: 'NOA', pause: 0.6}, + {id: 'e3', wer: 'ERZ', pause: 0.8}, + {id: 'w1', wer: 'NOA'}, + {id: 'w2', wer: 'NOA'}, + {id: 'w3', wer: 'NOA'}, + {id: 'w4', wer: 'NOA'}, + {id: 'bild1', dauer: 1.8}, +]); + +export const Szene3: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S3; + + const an = z.klick.start + 12; + const licht = interpolate(f, [an, an + 8], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); + const singt = f >= z.v1.start && f < z.w4.ende; + const schwung = singt ? Math.sin((f - z.v1.start) * 0.09) * 7 : Math.sin(f * 0.05) * 2; + const lat = laterneInNoasHand(NOA_X); + + // Pino kommt beim Vers dicht an Noas Stiefel (ganze Schritte, Füße bleiben am Boden) + const PINO = gang('pino', z.v1.start + 6, 300, NOA_X - 120, 2.5); + + const kam = kamFahrt(f, [ + [0, 1000, 540, 1.0], + [z.bild0.ende, 980, 560, 1.06], + [z.klick.start, 760, 640, 1.55], + [z.e2.ende, 720, 640, 1.7], + [z.p1.start + 6, lat.x, lat.y, 2.3], // „Mein Igel leuchtet!“ + [z.p1.ende + 10, 820, 640, 1.35], + [z.w1.start, 900, 600, 1.2], + [dauer, 920, 600, 1.15], + ]); + + return ( + <> + + + + + + + {PINO.geht(f) ? ( + + ) : ( + + )} + + + + + + + ); +}; diff --git a/remotion/src/folge08/szene4.tsx b/remotion/src/folge08/szene4.tsx new file mode 100644 index 0000000..bf11db1 --- /dev/null +++ b/remotion/src/folge08/szene4.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import {useCurrentFrame} from 'remotion'; +import {gang} from '../baukasten/gang'; +import {Blende, kamFahrt} from '../baukasten/helfer'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {PLATZ, Wandszene} from './wand'; + +// Szene 4: Der große Schatten. An der Hauswand steht ein riesiger, weicher Giraffen-Schatten. +// Pinos Stacheln gehen hoch, er rückt an Noas Stiefel. Noa hockt sich hin und stellt die Laterne ins Gras. +// Lulu steht etwas im Dunkeln (Kinder dürfen sie ruhig schon sehen – so bleibt es freundlich). + +export const S4 = plane('s4', [ + {id: 'bild0', dauer: 3.0}, + {id: 'e1', wer: 'ERZ', pause: 3}, + {id: 'p1', wer: 'PINO', leise: true}, + {id: 'n1', wer: 'NOA'}, + {id: 'e2', wer: 'ERZ'}, + {id: 'e3', wer: 'ERZ', pause: 3}, +]); + +export const Szene4: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S4; + const erschrickt = z.bild0.start + 40; + const PINO = gang('pino', erschrickt + 30, PLATZ.pino, PLATZ.noa + 90, 2.5); + const hockt = f >= z.n1.start + 8; + const angst = f >= erschrickt && f < z.e3.ende; + const kam = kamFahrt(f, [ + [0, 960, 560, 1.0], + [erschrickt, 900, 600, 1.12], + [z.e1.start, 820, 720, 1.6], // Pinos Stacheln + [z.p1.ende, 760, 700, 1.45], + [z.e3.start, 1040, 520, 1.0], // der ganze Schatten + [dauer, 1060, 520, 0.98], + ]); + return ( + <> + + + + + ); +}; diff --git a/remotion/src/folge08/szene5.tsx b/remotion/src/folge08/szene5.tsx new file mode 100644 index 0000000..b49b356 --- /dev/null +++ b/remotion/src/folge08/szene5.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import {interpolate, useCurrentFrame} from 'remotion'; +import {Blende, kamFahrt} from '../baukasten/helfer'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {PLATZ, Wandszene} from './wand'; + +// Szene 5: Ach so! Noa hebt die Laterne hoch, jetzt fällt das Licht auf Lulu. Lulu nickt – der Schatten +// nickt mit. Pinos Stacheln legen sich, alle lachen. Der Erzähler erklärt, wie ein Schatten entsteht. + +export const S5 = plane('s5', [ + {id: 'bild0', dauer: 1.6}, + {id: 'l1', wer: 'LULU'}, + {id: 'p1', wer: 'PINO'}, + {id: 'lachen', dauer: 1.6}, + {id: 'e1', wer: 'ERZ'}, + {id: 'bild1', dauer: 1.0}, +]); + +export const Szene5: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S5; + const hell = interpolate(f, [8, 30], [0.55, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); + // Lulu nickt zweimal (vor „Huch!“), der Schatten macht es mit + const nick = interpolate(f, [z.bild0.start + 18, z.bild0.ende + 12], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); + const nicken = Math.sin(nick * Math.PI * 4) * 3 * (nick > 0 && nick < 1 ? 1 : 0); + const kam = kamFahrt(f, [ + [0, 1060, 520, 0.98], + [z.l1.start, 1180, 560, 1.15], + [z.p1.start, 1000, 560, 1.05], + [dauer, 1000, 540, 1.0], + ]); + return ( + <> + + + + + ); +}; diff --git a/remotion/src/folge08/szene6.tsx b/remotion/src/folge08/szene6.tsx new file mode 100644 index 0000000..b0ef6b8 --- /dev/null +++ b/remotion/src/folge08/szene6.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import {interpolate, useCurrentFrame} from 'remotion'; +import {gang} from '../baukasten/gang'; +import {Blende, huepfer, kamFahrt} from '../baukasten/helfer'; +import {BODEN} from '../baukasten/orte'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {PLATZ, Wandszene} from './wand'; + +// Szene 6: Jetzt bin ich der Größte! Noa stellt die Laterne ins Gras, Pino tippelt ganz nah heran. +// Sein Schatten wächst, bis er größer ist als Lulus. Lulu verbeugt sich vor dem großen Igel-Schatten. + +export const S6 = plane('s6', [ + {id: 'p1', wer: 'PINO'}, + {id: 'n1', wer: 'NOA'}, + {id: 'hinstellen', dauer: 1.6}, + {id: 'e1', wer: 'ERZ', pause: 3}, + {id: 'wachsen', dauer: 2.4}, + {id: 'e2', wer: 'ERZ'}, + {id: 'p2', wer: 'PINO'}, + {id: 'verbeugen', dauer: 1.6}, + {id: 'l1', wer: 'LULU'}, + {id: 'p3', wer: 'PINO'}, + {id: 'bild1', dauer: 1.6}, +]); + +const LATERNE = {x: 1010, y: PLATZ.laterneBoden.y}; + +export const Szene6: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S6; + const steht = f >= z.hinstellen.start + 8; + // Pino geht während der Frage schon los und steht beim Wachsen dicht an der Laterne + const PINO = gang('pino', z.e1.start + 20, PLATZ.noa + 90, LATERNE.x - 70, 2.5); + const wachsen = interpolate(f, [z.wachsen.start, z.wachsen.ende], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); + const kPino = steht ? 1.25 + wachsen * 6.2 : undefined; + const kam = kamFahrt(f, [ + [0, 1000, 560, 1.1], + [z.hinstellen.start, 900, 640, 1.25], + [z.wachsen.start, 980, 520, 1.0], + [z.p2.start, 1000, 520, 1.0], + [z.verbeugen.start, 1120, 560, 1.12], + [dauer, 1040, 540, 1.02], + ]); + return ( + <> + = z.p3.start ? Math.sin(f * 1.4) * 2 : 0, // Stacheln zittern vor Freude + }} + lulu={{pose: [[0, 'lulu_A3_lachen'], [z.verbeugen.start, 'lulu_A_hals-unten'], [z.p3.start, 'lulu_A3_lachen']]}} + /> + + + + ); +}; diff --git a/remotion/src/folge08/szene7.tsx b/remotion/src/folge08/szene7.tsx new file mode 100644 index 0000000..f2397ea --- /dev/null +++ b/remotion/src/folge08/szene7.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import {interpolate, useCurrentFrame} from 'remotion'; +import {Blende, kamFahrt} from '../baukasten/helfer'; +import {Untertitel} from '../baukasten/untertitel'; +import {plane} from './plan'; +import {HandSchatten} from './teile'; +import {PLATZ, Wandszene} from './wand'; + +// Szene 7: Schattenspiel. Noa macht mit der Hand vor der Laterne einen Hasen, dann einen Igel. +// Die Kinder machen mit (Pausen). Lulu streckt den Hals lang, ihr Schatten reicht bis zum Dach. + +export const S7 = plane('s7', [ + {id: 'bild0', dauer: 1.2}, + {id: 'n1', wer: 'NOA'}, + {id: 'e1', wer: 'ERZ', pause: 3}, + {id: 'e2', wer: 'ERZ'}, + {id: 'hals', dauer: 2.2}, + {id: 'igel', dauer: 1.2}, + {id: 'p1', wer: 'PINO'}, + {id: 'e3', wer: 'ERZ', pause: 3}, + {id: 'e4', wer: 'ERZ'}, + {id: 'bild1', dauer: 1.2}, +]); + +const LATERNE = {x: 1010, y: PLATZ.laterneBoden.y}; + +export const Szene7: React.FC = () => { + const f = useCurrentFrame(); + const {z, dauer} = S7; + const hase = interpolate(f, [z.bild0.start + 6, z.bild0.ende, z.hals.start, z.hals.start + 10], [0, 1, 1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); + const igel = interpolate(f, [z.igel.start, z.igel.start + 12, dauer - 20, dauer - 6], [0, 1, 1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); + const strecken = interpolate(f, [z.hals.start, z.hals.start + 20, z.hals.ende - 10, z.hals.ende + 6], [1, 1.12, 1.12, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}); + const kam = kamFahrt(f, [ + [0, 1000, 520, 1.0], + [z.n1.start, 1000, 470, 1.12], + [z.hals.start, 1100, 500, 0.98], + [z.igel.start, 1000, 480, 1.1], + [dauer, 1000, 500, 1.06], + ]); + return ( + <> + + + + + } + /> + + + + ); +}; diff --git a/remotion/src/folge08/teile.tsx b/remotion/src/folge08/teile.tsx new file mode 100644 index 0000000..fbe1704 --- /dev/null +++ b/remotion/src/folge08/teile.tsx @@ -0,0 +1,219 @@ +import React from 'react'; +import {Figur, NOA_HOEHE, type Posenfolge} from '../baukasten/figur'; +import {FIGUREN} from '../baukasten/posen'; +import {punkt} from '../baukasten/helfer'; +import {BODEN, Ort} from '../baukasten/orte'; + +// Bildteile für Folge 8 „Noas Laterne“: Abendgarten mit Hauswand, die gebastelte Laterne, +// Schatten an der Wand und Hand-Schatten. Alles in Szenenkoordinaten (1920 × 1080). + +// Hauswand rechts im Garten (Szene 3) bzw. als ganzer Hintergrund an der Wand (Szene 4–7) +export const WAND = {x0: 1060, x1: 1980, oben: 170, unten: BODEN - 30}; +export const FENSTER = {x: 1180, y: 280, b: 230, h: 210}; + +// Abendgarten in der „blauen Stunde“: Nachtgarten aufgehellt, damit es freundlich und nicht unheimlich wirkt +export const GartenAbend: React.FC<{wandVoll?: boolean; papaWinkt?: number}> = ({wandVoll = false, papaWinkt = 0}) => { + const x0 = wandVoll ? -60 : WAND.x0; + return ( + <> +
+ +
+
+ + + + + + + + + + + + {/* Wand mit Dachkante und Sockel */} + + + + {!wandVoll && } + {/* erleuchtetes Fenster mit Papa als Umriss */} + + + + + + {/* winkender Arm */} + + + + + + + + + {/* Haken für die Laterne (Szene 8) */} + + + + ); +}; + +export const HAKEN = {x: FENSTER.x + FENSTER.b + 66, y: FENSTER.y + 62}; +export const WAND_VOLL = {...WAND, x0: -60}; + +// Die gebastelte Laterne: gelbes Papier, fünf Glitzersterne, ein Papier-Igel, innen ein Lämpchen. +// (x, y) = Mitte; sterne = wie viele schon aufgeklebt sind; igel 0–1; licht 0–1. +const STERNE: [number, number][] = [ + [-34, -28], + [22, -40], + [40, 6], + [-6, 30], + [-44, 18], +]; +const Stern: React.FC<{x: number; y: number; r: number; glanz: number}> = ({x, y, r, glanz}) => { + const pkt = new Array(10) + .fill(0) + .map((_, i) => { + const w = (Math.PI / 5) * i - Math.PI / 2; + const rr = i % 2 ? r * 0.45 : r; + return `${x + Math.cos(w) * rr},${y + Math.sin(w) * rr}`; + }) + .join(' '); + return 0.5 ? '#fff6c2' : '#f2c84b'} stroke="#c9952a" strokeWidth={1.5} />; +}; + +export const Laterne: React.FC<{x: number; y: number; s?: number; sterne?: number; igel?: number; licht?: number; schwung?: number; stab?: boolean}> = ({ + x, + y, + s = 1, + sterne = 5, + igel = 1, + licht = 0, + schwung = 0, + stab = true, +}) => ( +
+ {licht > 0 && ( +
+ )} + + + + 0 ? '#fffbe2' : '#fbe79a'} /> + 0 ? '#ffd65c' : '#f2cf5c'} /> + 0 ? '#f0a531' : '#d9b03e'} /> + + + {stab && } + + + + {[-0.62, -0.2, 0.2, 0.62].map((k) => ( + + ))} + {STERNE.slice(0, sterne).map(([sx, sy], i) => ( + + ))} + {igel > 0 && ( + + + {[-14, -8, -2, 4, 10].map((dx) => ( + + ))} + + + + )} + + +
+); + +// Weicher Schatten einer Figur an der Wand. Er steht hinter der Figur, ist um `k` vergrößert und +// liegt vom Licht weg verschoben; je näher die Figur am Licht, desto größer (k aus `schattenGroesse`). +// Weich, rund und bläulich-dunkel, ohne harten Rand (Drehbuch: groß, aber nicht unheimlich). +export const schattenGroesse = (abstand: number) => Math.min(3.2, Math.max(1.05, 1 + 260 / Math.max(60, abstand))); + +export const WandSchatten: React.FC<{ + pose: string | Posenfolge; + x: number; // Fußpunkt der Figur + licht: {x: number; y: number}; + hoehe: number; + k: number; + wand?: {x0: number; x1: number; oben: number; unten: number}; + deckkraft?: number; + drehung?: number; + stauchY?: number; +}> = ({pose, x, licht, hoehe, k, wand = WAND, deckkraft = 0.42, drehung = 0, stauchY = 1}) => ( +
+
+ +
+
+); + +// Hand-Schatten an der Wand: Hase (zwei Ohren) und Igel (Faust mit gespreizten Fingern) +export const HandSchatten: React.FC<{art: 'hase' | 'igel'; x: number; y: number; s?: number; deckkraft?: number; wackeln?: number}> = ({art, x, y, s = 1, deckkraft = 0.45, wackeln = 0}) => ( + + {art === 'hase' ? ( + + + + + + + + ) : ( + + + {[-70, -35, 0, 35, 70].map((dx, i) => ( + + ))} + + + + )} + +); + +// Wo die Laterne in der Pose noa_A_laterne hängt, und wie groß unsere Laterne sein muss, um die orange +// Laterne des Bildes ganz abzudecken (gemessen: 131 × 134 Bildpunkte, Mitte bei 73/423 im Bild, Bild bei 339/156) +export const laterneInNoasHand = (noaX: number, noaY = BODEN) => ({ + ...punkt('noa', noaX, noaY, 339 + 73.5, 156 + 423), + s: (1.2 * NOA_HOEHE) / FIGUREN.noa.refHoehe, +}); + +// Abend-Dämmerung über der ganzen Szene, aufgehellt um die Laterne (warmes Licht, Rest bläulich) +export const Abenddunkel: React.FC<{licht: {x: number; y: number}; staerke?: number; radius?: number}> = ({licht, staerke = 0.4, radius = 500}) => ( +
+); diff --git a/remotion/src/folge08/texte/de.json b/remotion/src/folge08/texte/de.json new file mode 100644 index 0000000..f754c65 --- /dev/null +++ b/remotion/src/folge08/texte/de.json @@ -0,0 +1,58 @@ +{ + "titel": "Noas Laterne", + "zeilen": { + "s2/l1": "Was machst du da, Noa?", + "s2/n1": "Rate mal! Es ist rund. Es hat einen Stab. Und innen drin leuchtet es.", + "s2/e1": "Rund, mit einem Stab, und innen leuchtet es. Was bastelt Noa wohl?", + "s2/n2": "Eine Laterne!", + "s2/p1": "Eine Laterne! Mit was drauf?", + "s2/n3": "Mit Sternen. Hilfst du mir zählen?", + "s2/z1": "Eins …", + "s2/z2": "zwei …", + "s2/z3": "drei …", + "s2/z4": "vier …", + "s2/z5": "fünf!", + "s2/p2": "Und ein Igel! Da muss ein Igel drauf!", + "s2/n4": "Natürlich muss da ein Igel drauf.", + "s2/l2": "Der Kleber ist noch nass. Ich puste!", + "s2/p3": "Das kitzelt!", + "s3/e1": "Es wird Abend in Noas Garten. Jetzt ist die richtige Zeit für eine Laterne.", + "s3/e2": "Noas Laterne hat ein kleines Lämpchen. Kein Feuer. So kann nichts passieren.", + "s3/p1": "Ooooh. Mein Igel leuchtet!", + "s3/v1": "Mein kleines Licht, so rund und warm,", + "s3/v2": "ich trag es durch den Garten.", + "s3/v3": "Der Mond schaut schon, die Sterne auch,", + "s3/v4": "die wollen auf uns warten.", + "s3/e3": "Kannst du mitsingen?", + "s3/w1": "Mein kleines Licht, so rund und warm,", + "s3/w2": "ich trag es durch den Garten.", + "s3/w3": "Der Mond schaut schon, die Sterne auch,", + "s3/w4": "die wollen auf uns warten.", + "s4/e1": "Schau mal, Pinos Stacheln gehen hoch. Wie fühlt sich Pino jetzt?", + "s4/p1": "Noa … da ist was Großes an der Wand. Ich hab ein bisschen Angst.", + "s4/n1": "Gut, dass du es mir sagst. Komm, wir schauen zusammen hin.", + "s4/e2": "Wenn man Angst hat, darf man das sagen. Dann ist man nicht allein.", + "s4/e3": "Der Schatten hat vier lange Beine. Und einen ganz langen Hals. Wem gehört wohl dieser Schatten?", + "s5/l1": "Huch! Das bin ja ich!", + "s5/p1": "Das ist ja Lulus Schatten! Der ist ja noch größer als Lulu!", + "s5/e1": "Die Laterne leuchtet auf Lulu. Hinter Lulu kommt das Licht nicht hin. Da wird es dunkel. Das ist ein Schatten.", + "s6/p1": "Mein Schatten ist so klein.", + "s6/n1": "Warte mal. Ich hab eine Idee.", + "s6/e1": "Pino geht ganz nah an die Laterne. Was meinst du: Wird sein Schatten jetzt größer oder kleiner?", + "s6/e2": "Größer! Je näher man am Licht ist, desto größer wird der Schatten.", + "s6/p2": "Jetzt bin ich der Größte von allen!", + "s6/l1": "Guten Abend, großer Pino!", + "s6/p3": "Einmal klein und einmal groß!", + "s7/n1": "Schau mal, ein Hase!", + "s7/e1": "Mach mal mit deiner Hand einen Hasen. Zwei Finger hoch für die Ohren.", + "s7/e2": "Hallo, Hase!", + "s7/p1": "Das bin ja ich!", + "s7/e3": "Und jetzt einen Igel. Mach eine Faust und streck die Finger hoch, wie Stacheln.", + "s7/e4": "Hallo, kleiner Igel!", + "s8/p1": "Jetzt ist es gar nicht mehr dunkel.", + "s8/n1": "Die Laterne passt auf dich auf. Gute Nacht, Pino.", + "s8/e1": "Und bei dir? Ist da auch ein kleines Licht, wenn du schlafen gehst?", + "s8/t1": "Tschüss! Bis bald!", + "s8/p2": "… bis bald …" + } +} \ No newline at end of file diff --git a/remotion/src/folge08/texte/es.json b/remotion/src/folge08/texte/es.json new file mode 100644 index 0000000..d10b776 --- /dev/null +++ b/remotion/src/folge08/texte/es.json @@ -0,0 +1,58 @@ +{ + "titel": "El farolillo de Noa", + "zeilen": { + "s2/l1": "¿Qué haces, Noa?", + "s2/n1": "¡Adivina! Es redondo. Tiene un palito. Y por dentro brilla.", + "s2/e1": "Redondo, con un palito, y por dentro brilla. ¿Qué estará haciendo Noa?", + "s2/n2": "¡Un farolillo!", + "s2/p1": "¡Un farolillo! ¿Y qué lleva encima?", + "s2/n3": "Estrellas. ¿Me ayudas a contar?", + "s2/z1": "Una …", + "s2/z2": "dos …", + "s2/z3": "tres …", + "s2/z4": "cuatro …", + "s2/z5": "¡cinco!", + "s2/p2": "¡Y un erizo! ¡Tiene que llevar un erizo!", + "s2/n4": "Claro que tiene que llevar un erizo.", + "s2/l2": "El pegamento todavía está mojado. ¡Yo soplo!", + "s2/p3": "¡Me hace cosquillas!", + "s3/e1": "Se hace de noche en el jardín de Noa. Ahora es el momento para un farolillo.", + "s3/e2": "El farolillo de Noa tiene una lucecita. Nada de fuego. Así no puede pasar nada.", + "s3/p1": "¡Ooooh! ¡Mi erizo brilla!", + "s3/v1": "Mi farolillo, redondo y calentito,", + "s3/v2": "lo llevo por el jardín.", + "s3/v3": "La luna mira, y las estrellas,", + "s3/v4": "nos esperan hasta el fin.", + "s3/e3": "¿Cantas con nosotros?", + "s3/w1": "Mi farolillo, redondo y calentito,", + "s3/w2": "lo llevo por el jardín.", + "s3/w3": "La luna mira, y las estrellas,", + "s3/w4": "nos esperan hasta el fin.", + "s4/e1": "Mira, a Pino se le levantan las púas. ¿Cómo se siente Pino ahora?", + "s4/p1": "Noa… hay algo muy grande en la pared. Tengo un poquito de miedo.", + "s4/n1": "Qué bien que me lo dices. Ven, vamos a mirar juntos.", + "s4/e2": "Si tienes miedo, lo puedes decir. Así no estás solo.", + "s4/e3": "La sombra tiene cuatro patas largas. Y un cuello larguísimo. ¿De quién será esta sombra?", + "s5/l1": "¡Huy! ¡Pero si soy yo!", + "s5/p1": "¡Es la sombra de Lulu! ¡Y es todavía más grande que Lulu!", + "s5/e1": "El farolillo ilumina a Lulu. Detrás de Lulu, la luz no llega. Allí se pone oscuro. Eso es una sombra.", + "s6/p1": "Mi sombra es muy pequeña.", + "s6/n1": "Espera. Tengo una idea.", + "s6/e1": "Pino se acerca mucho al farolillo. ¿Qué crees: su sombra se hará más grande o más pequeña?", + "s6/e2": "¡Más grande! Cuanto más cerca de la luz, más grande es la sombra.", + "s6/p2": "¡Ahora soy el más grande de todos!", + "s6/l1": "¡Buenas noches, gran Pino!", + "s6/p3": "¡Una vez pequeño y una vez grande!", + "s7/n1": "¡Mira, un conejo!", + "s7/e1": "Haz un conejo con tu mano. Dos dedos arriba para las orejas.", + "s7/e2": "¡Hola, conejo!", + "s7/p1": "¡Pero si soy yo!", + "s7/e3": "Y ahora un erizo. Cierra la mano y estira los dedos hacia arriba, como púas.", + "s7/e4": "¡Hola, erizo pequeño!", + "s8/p1": "Ahora ya no está nada oscuro.", + "s8/n1": "El farolillo te cuida. Buenas noches, Pino.", + "s8/e1": "¿Y tú? ¿Tienes también una lucecita cuando te vas a dormir?", + "s8/t1": "¡Adiós! ¡Hasta pronto!", + "s8/p2": "…hasta pronto…" + } +} \ No newline at end of file diff --git a/remotion/src/folge08/texte/fr.json b/remotion/src/folge08/texte/fr.json new file mode 100644 index 0000000..9b8984a --- /dev/null +++ b/remotion/src/folge08/texte/fr.json @@ -0,0 +1,58 @@ +{ + "titel": "La lanterne de Noa", + "zeilen": { + "s2/l1": "Qu’est-ce que tu fais, Noa ?", + "s2/n1": "Devine ! C’est rond. Il y a un bâton. Et dedans, ça brille.", + "s2/e1": "Rond, avec un bâton, et dedans, ça brille. Qu’est-ce que Noa fabrique ?", + "s2/n2": "Une lanterne !", + "s2/p1": "Une lanterne ! Avec quoi dessus ?", + "s2/n3": "Avec des étoiles. Tu m’aides à compter ?", + "s2/z1": "Une …", + "s2/z2": "deux …", + "s2/z3": "trois …", + "s2/z4": "quatre …", + "s2/z5": "cinq !", + "s2/p2": "Et un hérisson ! Il faut un hérisson dessus !", + "s2/n4": "Bien sûr qu’il faut un hérisson dessus.", + "s2/l2": "La colle est encore mouillée. Je souffle !", + "s2/p3": "Ça chatouille !", + "s3/e1": "Le soir tombe dans le jardin de Noa. C’est le bon moment pour une lanterne.", + "s3/e2": "La lanterne de Noa a une petite lampe. Pas de feu. Comme ça, il ne peut rien arriver.", + "s3/p1": "Ooooh. Mon hérisson brille !", + "s3/v1": "Ma petite lumière, ronde et douce,", + "s3/v2": "je la promène au jardin.", + "s3/v3": "La lune regarde, les étoiles aussi,", + "s3/v4": "elles nous attendent en chemin.", + "s3/e3": "Tu chantes avec nous ?", + "s3/w1": "Ma petite lumière, ronde et douce,", + "s3/w2": "je la promène au jardin.", + "s3/w3": "La lune regarde, les étoiles aussi,", + "s3/w4": "elles nous attendent en chemin.", + "s4/e1": "Regarde, les piquants de Pino se dressent. Comment se sent Pino maintenant ?", + "s4/p1": "Noa… il y a quelque chose de grand sur le mur. J’ai un petit peu peur.", + "s4/n1": "C’est bien que tu me le dises. Viens, on regarde ensemble.", + "s4/e2": "Quand on a peur, on a le droit de le dire. Comme ça, on n’est pas tout seul.", + "s4/e3": "L’ombre a quatre longues pattes. Et un très long cou. À qui est cette ombre ?", + "s5/l1": "Oh ! Mais c’est moi !", + "s5/p1": "C’est l’ombre de Lulu ! Elle est encore plus grande que Lulu !", + "s5/e1": "La lanterne éclaire Lulu. Derrière Lulu, la lumière n’arrive pas. Là, il fait sombre. C’est une ombre.", + "s6/p1": "Mon ombre est toute petite.", + "s6/n1": "Attends. J’ai une idée.", + "s6/e1": "Pino s’approche tout près de la lanterne. À ton avis : son ombre va devenir plus grande ou plus petite ?", + "s6/e2": "Plus grande ! Plus on est près de la lumière, plus l’ombre est grande.", + "s6/p2": "Maintenant, c’est moi le plus grand de tous !", + "s6/l1": "Bonsoir, grand Pino !", + "s6/p3": "Une fois petit, une fois grand !", + "s7/n1": "Regarde, un lapin !", + "s7/e1": "Fais un lapin avec ta main. Deux doigts en l’air pour les oreilles.", + "s7/e2": "Bonjour, petit lapin !", + "s7/p1": "Mais c’est moi !", + "s7/e3": "Et maintenant un hérisson. Ferme la main et lève les doigts, comme des piquants.", + "s7/e4": "Bonjour, petit hérisson !", + "s8/p1": "Maintenant, il ne fait plus du tout noir.", + "s8/n1": "La lanterne veille sur toi. Bonne nuit, Pino.", + "s8/e1": "Et toi ? Est-ce qu’il y a aussi une petite lumière quand tu vas dormir ?", + "s8/t1": "Au revoir ! À bientôt !", + "s8/p2": "…à bientôt…" + } +} \ No newline at end of file diff --git a/remotion/src/folge08/wand.tsx b/remotion/src/folge08/wand.tsx new file mode 100644 index 0000000..07fbfe4 --- /dev/null +++ b/remotion/src/folge08/wand.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import {ClipFigur, Figur, Kamera, type Posenfolge} from '../baukasten/figur'; +import type {Gang} from '../baukasten/gang'; +import {Vignette} from '../baukasten/helfer'; +import {BODEN} from '../baukasten/orte'; +import {Abenddunkel, GartenAbend, Laterne, WAND_VOLL, WandSchatten, laterneInNoasHand} from './teile'; + +// Gemeinsame Bühne für Szene 4–7: die drei an der Hauswand, die Laterne als einzige Lichtquelle. +// Jede Figur wirft ihren Schatten an die Wand; er nimmt dieselbe Pose, Drehung und Stauchung an +// (nickt Lulu, nickt ihr Schatten mit). Feste Plätze, damit die Szenen zusammenpassen. + +export const PLATZ = {noa: 700, pino: 880, lulu: 1330, laterneBoden: {x: 560, y: BODEN - 56}}; + +export type Buehne = { + kam: {x: number; y: number; zoom: number}; + laterne: {inHand: boolean; x?: number; y?: number; licht: number; schwung?: number}; + noa: {pose: string | Posenfolge; x?: number}; + pino: {pose: string | Posenfolge; x: number; gang?: Gang; drehung?: number; y?: number; k?: number}; + lulu: {pose: string | Posenfolge; x?: number; hell?: number; drehung?: number; stauchY?: number; k?: number}; + wandExtra?: React.ReactNode; // z. B. Hand-Schatten + f: number; +}; + +export const Wandszene: React.FC = ({kam, laterne, noa, pino, lulu, wandExtra, f}) => { + const noaX = noa.x ?? PLATZ.noa; + const luluX = lulu.x ?? PLATZ.lulu; + const hand = laterneInNoasHand(noaX); + const lat = laterne.inHand ? {x: hand.x, y: hand.y} : {x: laterne.x ?? PLATZ.laterneBoden.x, y: laterne.y ?? PLATZ.laterneBoden.y}; + const s = hand.s; + const kLulu = lulu.k ?? 1 + 220 / Math.max(120, Math.abs(luluX - lat.x)); + const kPino = pino.k ?? 1 + 220 / Math.max(80, Math.abs(pino.x - lat.x)); + const kNoa = 1 + 220 / Math.max(120, Math.abs(noaX - lat.x)); + const hell = lulu.hell ?? 1; + const pinoGeht = pino.gang?.geht(f); + return ( + <> + + + {/* Schatten an der Wand (hinter den Figuren) */} + + + + {wandExtra} + + + {laterne.inHand ? ( + + ) : ( + + )} + {pinoGeht && pino.gang ? ( + + ) : ( + + )} + + + + + ); +}; diff --git a/remotion/src/lieder/lied01/Lied01.tsx b/remotion/src/lieder/lied01/Lied01.tsx index b67adfc..a90a4d6 100644 --- a/remotion/src/lieder/lied01/Lied01.tsx +++ b/remotion/src/lieder/lied01/Lied01.tsx @@ -329,7 +329,7 @@ const Garten: React.FC = () => { ))} - + diff --git a/remotion/src/szenen/GangTest.tsx b/remotion/src/szenen/GangTest.tsx new file mode 100644 index 0000000..b8d502b --- /dev/null +++ b/remotion/src/szenen/GangTest.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import {AbsoluteFill, useCurrentFrame} from 'remotion'; +import {ClipFigur, Figur} from '../baukasten/figur'; +import {gang} from '../baukasten/gang'; + +// Prüfstand für baukasten/gang.ts: Noa und Pino gehen nach rechts, die Füße dürfen nicht rutschen. +const NOA = gang('noa', 10, 300, 1500); +const PINO = gang('pino', 10, 300, 900, 2.5); + +export const GangTest: React.FC = () => { + const f = useCurrentFrame(); + const figur = (g: typeof NOA, y: number) => + g.geht(f) ? ( + + ) : ( + + ); + return ( + + {figur(NOA, 500)} + {figur(PINO, 950)} + + ); +}; +export const GANGTEST_DAUER = Math.ceil(Math.max(NOA.ende, PINO.ende)) + 20;