diff --git a/remotion/src/baukasten/helfer.tsx b/remotion/src/baukasten/helfer.tsx
index 3ca5c66..14efb1f 100644
--- a/remotion/src/baukasten/helfer.tsx
+++ b/remotion/src/baukasten/helfer.tsx
@@ -71,3 +71,23 @@ export const hopfStauch = (frame: number, start: number, dauer = 12) => {
if (frame >= start && frame < start + dauer) return 1.04;
return 1;
};
+
+// Lulus Maul im Clip lulu_C9_hals-runter (gemessen an der Nase, Leinwand-Koordinaten),
+// je [Clip-Bild (24 fps), cx, cy]. Für Dinge, die Lulu beim Halssenken im Maul trägt.
+const C9 = {x: 294.7, y: 24.6, s: 709.43 / 662};
+const C9_NASE: [number, number, number][] = [
+ [0, 276, 143], [17, 276, 143], [21, 260, 151], [25, 246, 158], [29, 231, 167], [33, 215, 179],
+ [37, 196, 197], [41, 180, 217], [45, 165, 245], [49, 151, 277], [53, 143, 301], [57, 134, 341],
+ [61, 130, 371], [65, 128, 403], [69, 126, 435], [73, 127, 461], [77, 129, 489], [81, 135, 520],
+ [85, 142, 542], [89, 147, 563], [93, 155, 577], [97, 166, 587], [101, 176, 593], [105, 181, 597], [120, 179, 596],
+];
+export const luluMaulC9 = (clipBild: number) => {
+ const b = Math.min(120, Math.max(0, clipBild));
+ let i = 0;
+ while (i < C9_NASE.length - 2 && C9_NASE[i + 1][0] < b) i++;
+ const [b0, x0, y0] = C9_NASE[i];
+ const [b1, x1, y1] = C9_NASE[i + 1];
+ const t = b1 === b0 ? 0 : (b - b0) / (b1 - b0);
+ // Maul knapp über der Unterkante der Nase
+ return {cx: C9.x + (x0 + (x1 - x0) * t) * C9.s, cy: C9.y + (y0 + (y1 - y0) * t - 6) * C9.s};
+};
diff --git a/remotion/src/folge01/szene3.tsx b/remotion/src/folge01/szene3.tsx
index 301f664..0ddd25d 100644
--- a/remotion/src/folge01/szene3.tsx
+++ b/remotion/src/folge01/szene3.tsx
@@ -12,7 +12,7 @@ import {APFEL, BLUME_X, BLUME_Y, HAUFEN_X} from './szene2';
// Szene 3: Der Apfel ist zu hoch. Pino hüpft, Noa streckt sich – beide zu klein.
// Pino-Bilder schauen nach rechts (zum Apfel); zu Noa (links) wird er gespiegelt.
-export const PINO_BAUM_X = 500; // Pino unter dem Apfel
+export const PINO_BAUM_X = 470; // Pino unter dem Apfel
export const NOA_BAUM_X = 330; // Noa am untersten Ast
export const S3 = plane('s3', [
diff --git a/remotion/src/folge01/szene4.tsx b/remotion/src/folge01/szene4.tsx
index 8822579..2469716 100644
--- a/remotion/src/folge01/szene4.tsx
+++ b/remotion/src/folge01/szene4.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import {useCurrentFrame} from 'remotion';
import {kurve} from '../bewegung';
-import {Figur, Geher, Kamera} from '../baukasten/figur';
-import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt} from '../baukasten/helfer';
+import {ClipFigur, Figur, Geher, Kamera} from '../baukasten/figur';
+import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt, luluMaulC9, punkt} from '../baukasten/helfer';
import {Apfel, Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur';
import {BODEN, Ort} from '../baukasten/orte';
import {Untertitel} from '../baukasten/untertitel';
@@ -16,11 +16,13 @@ import {NOA_BAUM_X, PINO_BAUM_X} from './szene3';
// Lulus Maul mit dem Apfel (lulu_A_apfel) liegt 149 px links und 542 px über dem Fußpunkt:
// so steht sie, dass sie den Apfel am Baum erreicht.
export const LULU_APFEL_X = APFEL.x + 149;
-// Mit gesenktem Hals (lulu_A_hals-unten) liegt das Maul 244 px links vom Fußpunkt, knapp über dem Boden.
-const LULU_UNTEN_X = 840;
+// Den Hals senkt sie mit dem Clip lulu_C9_hals-runter; der Apfel bleibt dabei im Maul
+// (Maul-Bahn gemessen, luluMaulC9). Unten liegt das Maul direkt neben Pino.
const LULU_WARTE_X = 930; // bis zum Pflücken etwas rechts vom Apfel
-const MAUL_UNTEN = {x: LULU_UNTEN_X - 244, y: BODEN - 22};
-const APFEL_BODEN = {x: 578, y: BODEN - 14};
+const C9_TEMPO = 1.3;
+const C9_START = 17; // vorher steht sie im Clip still
+const C9_UNTEN = 105; // Kopf unten
+const c9Frames = (bilder: number) => (bilder * 30) / 24 / C9_TEMPO;
export const S4 = plane('s4', [
{id: 'bild0', dauer: 3.4},
@@ -33,55 +35,64 @@ export const S4 = plane('s4', [
{id: 'p2', wer: 'PINO'},
{id: 'l2', wer: 'LULU'},
{id: 'l3', wer: 'LULU'},
- {id: 'bild2', dauer: 3.4},
+ {id: 'bild2', dauer: 5.0},
{id: 'p3', wer: 'PINO'},
{id: 'l4', wer: 'LULU'},
{id: 'p4', wer: 'PINO'},
+ {id: 'bild3', dauer: 3.6},
]);
export const Szene4: React.FC = () => {
const f = useCurrentFrame();
const {z, dauer} = S4;
- // Lulu stakst langsam von rechts herein (tapp, tapp)
+ // Lulu stakst von rechts herein (tapp, tapp), wartet, tritt zum Apfel und frisst ihn
const luluAn = z.bild0.ende + 10;
const essen = z.bild2.start + 10;
- const unten = z.bild2.start + 52;
- const ablegen = z.bild2.ende - 6;
+ const runter = essen + 30; // Hals senken (Clip)
+ const unten = runter + c9Frames(C9_UNTEN - C9_START);
+ const ablegen = unten + 10;
+ const hoch = z.p4.ende - 4; // Hals wieder heben (Clip rückwärts)
+ const oben = hoch + c9Frames(C9_UNTEN);
const luluX = kurve(f, [
[0, 1650],
[luluAn, LULU_WARTE_X],
[essen - 16, LULU_WARTE_X],
[essen + 2, LULU_APFEL_X],
- [unten, LULU_APFEL_X],
- [unten + 14, LULU_UNTEN_X],
]);
+ const luluY = BODEN - 6;
const luluGeht = f < luluAn;
+ const imClip = f >= runter && f < oben;
const luluPose: [number, string][] = [
[0, 'lulu_A4_staunen'],
[z.l1.start, 'lulu_A3_lachen'],
[z.l2.start, 'lulu_A5_blick-links'],
[z.l3.start + 20, 'lulu_A4_staunen'],
[essen, 'lulu_A_apfel'],
- [unten, 'lulu_A_hals-unten'],
- [z.p4.ende - 4, 'lulu_A3_lachen'],
+ [oben - 1, 'lulu_A1_augen-zu'],
+ [oben + 6, 'lulu_A3_lachen'],
];
- // Der Apfel: am Baum → in Lulus Maul (im Bild lulu_A_apfel) → am Maul nach unten → vor Pino
+ // Der Apfel: am Baum → im Maul (im Bild lulu_A_apfel) → im Maul mit nach unten → neben Pino
const apfelAmBaum = f < essen + 6;
- const apfelAmMaul = f >= unten + 8;
- const apfel = apfelAmMaul
- ? {
- x: kurve(f, [
- [ablegen, MAUL_UNTEN.x],
- [ablegen + 12, APFEL_BODEN.x],
- ]),
- y: kurve(f, [
- [ablegen, MAUL_UNTEN.y],
- [ablegen + 12, APFEL_BODEN.y],
- ]),
- }
- : APFEL;
+ const clipBild = Math.min(C9_UNTEN, C9_START + ((f - runter) * C9_TEMPO * 24) / 30);
+ const maul = luluMaulC9(clipBild);
+ const amMaul = punkt('lulu', luluX, luluY, maul.cx, maul.cy);
+ const APFEL_BODEN = {x: amMaul.x + 8, y: BODEN - 12};
+ const apfelSichtbar = f >= runter && f < 1e9;
+ const apfel =
+ f < ablegen
+ ? amMaul
+ : {
+ x: kurve(f, [
+ [ablegen, amMaul.x],
+ [ablegen + 12, APFEL_BODEN.x],
+ ]),
+ y: kurve(f, [
+ [ablegen, amMaul.y],
+ [ablegen + 12, APFEL_BODEN.y],
+ ]),
+ };
// Pino staunt, freut sich; Stacheln halb hoch bei „noch viel größer“ (kurz aufplustern)
const pinoPose: [number, string][] = [
@@ -116,10 +127,10 @@ export const Szene4: React.FC = () => {
[z.p2.start + 10, 520, 800, 1.8],
[z.l2.start + 10, 640, 430, 1.6],
[z.l3.ende, 620, 420, 1.7],
- [unten - 10, 620, 440, 1.6],
- [unten + 30, 570, 800, 1.8],
- [z.p3.start, 560, 780, 1.7],
- [dauer, 600, 700, 1.4],
+ [essen + 20, 640, 470, 1.6],
+ [unten, 650, 640, 1.2],
+ [z.p4.ende, 650, 640, 1.2],
+ [dauer, 680, 600, 1.15],
]);
return (
@@ -129,14 +140,22 @@ export const Szene4: React.FC = () => {
{apfelAmBaum && }
{luluGeht ? (
-
+
+ ) : imClip ? (
+ f < hoch ? (
+
+ ) : (
+
+ )
) : (
-
+
)}
+ {/* Übergang Apfel-Bild → Clip: das Apfel-Bild blendet kurz aus */}
+ {f >= runter && f < runter + 8 && }
- {apfelAmMaul && }
+ {apfelSichtbar && }
diff --git a/remotion/src/folge01/szene5.tsx b/remotion/src/folge01/szene5.tsx
index 1d988b2..89236da 100644
--- a/remotion/src/folge01/szene5.tsx
+++ b/remotion/src/folge01/szene5.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {useCurrentFrame} from 'remotion';
import {kurve} from '../bewegung';
-import {Figur, Kamera} from '../baukasten/figur';
+import {ClipFigur, Figur, Kamera} from '../baukasten/figur';
import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt} from '../baukasten/helfer';
import {Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur';
import {BODEN, Ort} from '../baukasten/orte';
@@ -44,23 +44,29 @@ export const Szene5: React.FC = () => {
const f = useCurrentFrame();
const {z, dauer} = S5;
- // Lulu neigt sich zur Blume (nach links), wackelt, richtet sich wieder auf
+ // Lulu senkt den Hals (Clip) bis zur Hälfte, bleibt wackelnd stecken, hebt ihn wieder (Clip rückwärts)
+ const C9_TEMPO = 1.1;
+ const C9_START = 17;
+ const C9_HALB = 52; // so weit kommt sie
+ const c9 = (bilder: number) => (bilder * 30) / 24 / C9_TEMPO;
const beugA = z.bild1.start;
- const beugE = z.l4.ende;
- const neigen = kurve(f, [
- [beugA, 0],
- [beugA + 36, 1],
- [beugE - 10, 1],
- [beugE + 18, 0],
+ const halb = beugA + c9(C9_HALB - C9_START);
+ const wieder = z.l4.ende - 6;
+ const aufrecht = wieder + c9(C9_HALB);
+ const imClip = f >= beugA && f < aufrecht;
+ const steckt = kurve(f, [
+ [halb - 6, 0],
+ [halb + 6, 1],
+ [wieder - 6, 1],
+ [wieder + 4, 0],
]);
- const wackeln = neigen * (Math.sin(f * 0.55) * 3 + Math.sin(f * 0.23) * 2);
- const luluDreh = -24 * neigen + wackeln * 1.4;
+ // angestrengtes Wackeln: kleine Kipp- und Federbewegung um die Füße
+ const luluDreh = steckt * (Math.sin(f * 0.6) * 1.6 + Math.sin(f * 0.27) * 1.2);
+ const luluFeder = 1 - steckt * 0.012 * (1 + Math.sin(f * 0.9));
const luluPose: [number, string][] = [
[0, 'lulu_A4_staunen'],
[z.l1.start + 10, 'lulu_A3_lachen'],
- [beugA + 10, 'lulu_A1_augen-zu'],
- [z.l3.start, 'lulu_B2_erschrocken'],
- [z.bild2.start, 'lulu_B1_traurig'],
+ [aufrecht - 1, 'lulu_B1_traurig'],
[z.n1.start + 20, 'lulu_A4_staunen'],
];
@@ -101,7 +107,15 @@ export const Szene5: React.FC = () => {
-
+ {imClip ? (
+ f < wieder ? (
+
+ ) : (
+
+ )
+ ) : (
+
+ )}
diff --git a/remotion/src/folge01/szene6.tsx b/remotion/src/folge01/szene6.tsx
index f89fbcc..ab73203 100644
--- a/remotion/src/folge01/szene6.tsx
+++ b/remotion/src/folge01/szene6.tsx
@@ -46,7 +46,7 @@ export const Szene6: React.FC = () => {
[da, PINO_AN_BLUME.x],
[zurueck, PINO_AN_BLUME.x],
[mitteZurueck, VORN.x],
- [wieder, NOA_BLUME_X + 120],
+ [wieder, NOA_BLUME_X + 80],
]);
const pinoBoden = kurve(f, [
[hin, BODEN],
@@ -67,30 +67,43 @@ export const Szene6: React.FC = () => {
const s = 136 / 571; // Pino: Bildschirm-Pixel je Leinwand-Pixel
const pfoten = {x: pinoX + 4, y: pinoBoden - 210 * s};
- // Die Blume: am Zaun → in Pinos Pfoten → zu Noa, die sie hochhält
- const geben = z.bild1.start + 6;
- const hoch = geben + 26;
- // rechte Hand in noa_A_arme-hoch: Leinwand (800, 190), Fuß (675, 700), Maßstab 340/535
- const NOA_HAENDE = {x: NOA_BLUME_X + 79, y: BODEN - 310};
+ // Die Blume: am Zaun → in Pinos Pfoten → Noa hockt sich hin und nimmt sie → steht auf und hält sie hoch
+ const hocke = wieder - 12;
+ const nehmen = wieder + 14;
+ const aufstehen = nehmen + 14;
+ const BLENDE = 14;
+ // Hände: noa_B_hocken auf den Knien (Leinwand ca. 700, 520), noa_A_arme-hoch rechte Hand (800, 190); Fuß (675, 700), Maßstab 340/535
+ const m = 340 / 535;
+ const HAND_HOCKE = {x: NOA_BLUME_X + (700 - 675) * m, y: BODEN + (520 - 700) * m};
+ const HAND_OBEN = {x: NOA_BLUME_X + (800 - 675) * m, y: BODEN + (190 - 700) * m};
let blume: {x: number; y: number; s: number} = {x: BLUME_X, y: BLUME_Y, s: 0.8};
- if (f >= pflueck && f < geben) blume = {x: pfoten.x, y: pfoten.y + 20, s: 0.55};
- if (f >= geben)
+ if (f >= pflueck) blume = {x: pfoten.x, y: pfoten.y + 20, s: 0.55};
+ if (f >= nehmen)
blume = {
x: kurve(f, [
- [geben, NOA_BLUME_X + 120],
- [hoch, NOA_HAENDE.x],
+ [nehmen, pfoten.x],
+ [nehmen + 10, HAND_HOCKE.x],
+ [aufstehen, HAND_HOCKE.x],
+ [aufstehen + BLENDE, HAND_OBEN.x],
]),
y: kurve(f, [
- [geben, BODEN - 20],
- [hoch, NOA_HAENDE.y],
+ [nehmen, pfoten.y + 20],
+ [nehmen + 10, HAND_HOCKE.y + 12],
+ [aufstehen, HAND_HOCKE.y + 12],
+ [aufstehen + BLENDE, HAND_OBEN.y + 12],
+ ]),
+ s: kurve(f, [
+ [nehmen, 0.55],
+ [aufstehen + BLENDE, 0.62],
]),
- s: 0.62,
};
+ const wechsel = (t: number) => (f >= t && f < t + BLENDE ? Math.sin(((f - t) / BLENDE) * Math.PI) : 0);
+ const noaStauch = 1 - 0.05 * wechsel(hocke) + 0.03 * wechsel(aufstehen);
const noaPose: [number, string][] = [
[0, 'noa_A3_lachen'],
- [geben, 'noa_A_arme-hoch'],
- [z.e1.start, 'noa_A_arme-hoch'],
+ [hocke, 'noa_B_hocken'],
+ [aufstehen, 'noa_A_arme-hoch'],
[z.e3.start, 'noa_A3_lachen'],
];
const blumeImBild = f < z.e3.start;
@@ -114,7 +127,7 @@ export const Szene6: React.FC = () => {
[da, 1450, 780, 1.7],
[zurueck, 1350, 780, 1.6],
[wieder, 960, 740, 1.6],
- [hoch, 960, 660, 1.5],
+ [aufstehen + BLENDE, 960, 680, 1.5],
[nahA - 1, 960, 660, 1.5],
[nahA, LULU_NASE.x - 20, LULU_NASE.y + 60, 3.2],
[nahE - 1, LULU_NASE.x - 20, LULU_NASE.y + 60, 3.0],
@@ -133,7 +146,7 @@ export const Szene6: React.FC = () => {
{f < pflueck && }
-
+
{pinoGehtHin ? (
) : pinoGehtZurueck ? (
diff --git a/remotion/src/folge01/szene7.tsx b/remotion/src/folge01/szene7.tsx
index 46f4c6a..accd96f 100644
--- a/remotion/src/folge01/szene7.tsx
+++ b/remotion/src/folge01/szene7.tsx
@@ -63,7 +63,7 @@ export const Szene7: React.FC = () => {
<>
-
+