Folge 1, Szene 2, Rückmeldung 1: Laubhaufen aus Ahorn- und Buchenblättern, Noas Füße im Haufen verdeckt, Hocke über Clip, Pino rollt sich auf und schaut zu Noa
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
336c2121d5
commit
132e969d03
2 changed files with 62 additions and 25 deletions
|
|
@ -308,25 +308,45 @@ export const Blume: React.FC<{x: number; y: number; s?: number; r?: number}> = (
|
|||
</svg>
|
||||
);
|
||||
|
||||
// Folge 1: Laubhaufen (Fuß bei x, y; ca. 340 × 140 px bei s = 1).
|
||||
// Ahornblatt (Spitze oben, ca. 90 px hoch bei s = 1) und schmales Buchenblatt, für den Laubhaufen
|
||||
const AHORN = 'M0,40 L4,18 L22,27 L18,12 L40,5 L30,-2 L37,-21 L18,-14 L16,-31 L6,-22 L0,-46 L-6,-22 L-16,-31 L-18,-14 L-37,-21 L-30,-2 L-40,5 L-18,12 L-22,27 L-4,18 Z';
|
||||
const AHORN_ADERN = 'M0,52 L0,-38 M0,10 L30,-14 M0,10 L-30,-14 M0,18 L18,24 M0,18 L-18,24';
|
||||
const BUCHE = 'M0,-44 C 22,-30 24,0 0,30 C -24,0 -22,-30 0,-44 Z';
|
||||
const BUCHE_ADERN = 'M0,40 L0,-36 M0,-14 L12,-24 M0,-2 L14,-12 M0,-8 L-12,-18 M0,4 L-13,-6';
|
||||
const HAUFEN_HELL = ['#e07a2e', '#d4522a', '#e9a83a', '#c2412a', '#f0c048', '#d98a3a', '#b8662a'];
|
||||
const HAUFEN_DUNKEL = ['#9e4a26', '#8a3a22', '#a8682e', '#7a3a24'];
|
||||
|
||||
// Folge 1: Laubhaufen aus großen Herbstblättern (Fuß bei x, y; ca. 400 × 190 px bei s = 1).
|
||||
// Reicht unten 45 px unter den Fußpunkt, damit Füße darin verschwinden.
|
||||
// `wackeln` 0 … 1 lässt ihn zittern, `flach` 0 … 1 drückt ihn zusammen (Noa springt hinein).
|
||||
export const Laubhaufen: React.FC<{x: number; y: number; s?: number; wackeln?: number; flach?: number}> = ({x, y, s = 1, wackeln = 0, flach = 0}) => {
|
||||
const frame = useCurrentFrame();
|
||||
const zitter = wackeln * Math.sin(frame * 1.3) * 3;
|
||||
// hinten/oben zuerst zeichnen, vorne/unten zuletzt
|
||||
const blaetter = new Array(120).fill(0).map((_, i) => {
|
||||
const r = (k: string) => random(`lh${k}${i}`);
|
||||
const u = r('u') * 2.2 - 1.1;
|
||||
const kuppe = Math.pow(Math.max(0, 1 - u * u), 0.6) * 150;
|
||||
const ly = 40 - r('y') * (kuppe + 40);
|
||||
return {i, u, lx: u * 180, ly, ahorn: r('a') < 0.62, s: 0.85 + r('s') * 0.65, w: r('w') * 360, dunkel: ly < -kuppe * 0.55 ? false : r('d') < 0.3, farbe: r('f')};
|
||||
});
|
||||
blaetter.sort((a, b) => a.ly - b.ly);
|
||||
return (
|
||||
<svg
|
||||
width={400}
|
||||
height={180}
|
||||
viewBox="-200 -170 400 180"
|
||||
style={{position: 'absolute', left: x - 200, top: y - 170, overflow: 'visible', transform: `scale(${s * (1 + flach * 0.08)}, ${s * (1 - flach * 0.3)}) rotate(${zitter}deg)`, transformOrigin: '50% 95%'}}
|
||||
width={460}
|
||||
height={260}
|
||||
viewBox="-230 -205 460 260"
|
||||
style={{position: 'absolute', left: x - 230, top: y - 205, overflow: 'visible', transform: `scale(${s * (1 + flach * 0.08)}, ${s * (1 - flach * 0.25)}) rotate(${zitter}deg)`, transformOrigin: '50% 79%'}}
|
||||
>
|
||||
<path d="M -175 0 C -160 -90, -80 -140, 0 -140 C 80 -140, 160 -90, 175 0 Z" fill="#a8602c" />
|
||||
{new Array(70).fill(0).map((_, i) => {
|
||||
const u = random('lh' + i) * 2 - 1;
|
||||
const h = 1 - u * u;
|
||||
const bx = u * 165;
|
||||
const by = -random('lhy' + i) * 132 * h - 4;
|
||||
return <ellipse key={i} cx={bx} cy={by} rx={16 + random('lhr' + i) * 8} ry={8} transform={`rotate(${random('lhd' + i) * 180} ${bx} ${by})`} fill={LAUB[i % LAUB.length]} />;
|
||||
<ellipse cx={0} cy={36} rx={210} ry={22} fill="rgba(70,45,25,0.25)" />
|
||||
{blaetter.map((b) => {
|
||||
const liste = b.dunkel ? HAUFEN_DUNKEL : HAUFEN_HELL;
|
||||
return (
|
||||
<g key={b.i} transform={`translate(${b.lx} ${b.ly}) rotate(${b.w}) scale(${b.s})`}>
|
||||
<path d={b.ahorn ? AHORN : BUCHE} fill={liste[Math.floor(b.farbe * liste.length)]} stroke="rgba(90,40,20,0.35)" strokeWidth={1.5} strokeLinejoin="round" />
|
||||
<path d={b.ahorn ? AHORN_ADERN : BUCHE_ADERN} stroke="rgba(110,50,20,0.4)" strokeWidth={1.6} fill="none" strokeLinecap="round" />
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
</svg>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import {useCurrentFrame} from 'remotion';
|
||||
import {kurve} from '../bewegung';
|
||||
import {Figur, Geher, Kamera} from '../baukasten/figur';
|
||||
import {ClipFigur, Figur, Geher, Kamera, NOA_HOCKE_FRAMES, NoaHocke} from '../baukasten/figur';
|
||||
import {Blende, Vignette, atmen, hopfStauch, huepfer, kamFahrt, ruck} from '../baukasten/helfer';
|
||||
import {Apfel, Blaetterregen, Blaetterwind, Blume, Laubhaufen} from '../baukasten/natur';
|
||||
import {BODEN, Ort} from '../baukasten/orte';
|
||||
|
|
@ -16,7 +16,7 @@ export const APFEL = {x: 560, y: 205}; // der letzte Apfel, ganz oben im Baum
|
|||
export const BLUME_X = 1560; // blaue Blume am Zaun
|
||||
export const BLUME_Y = 805;
|
||||
export const NOA_X = 960; // Noa neben dem Haufen
|
||||
export const PINO_HAUFEN_Y = BODEN - 92; // Pino sitzt oben im Haufen
|
||||
export const PINO_HAUFEN_Y = BODEN - 128; // Pino steckt oben im Haufen (als Kugel)
|
||||
|
||||
export const S2 = plane('s2', [
|
||||
{id: 'bild0', dauer: 2.2},
|
||||
|
|
@ -71,16 +71,18 @@ export const Szene2: React.FC = () => {
|
|||
[schreck, 'noa_B2_erschrocken'],
|
||||
[z.n2.start + 6, 'noa_A4_staunen'],
|
||||
[z.e6.start, 'noa_A6_blick-rechts'],
|
||||
[hocke, 'noa_B_hocken'],
|
||||
[aufstehen, 'noa_A3_lachen'],
|
||||
[z.p2.start + 10, 'noa_A6_blick-rechts'],
|
||||
];
|
||||
// Hocke: Clip „hinsetzen“ bis zur Hocke, zum Aufstehen rückwärts
|
||||
const inHocke = f >= hocke && f < aufstehen + NOA_HOCKE_FRAMES;
|
||||
const hockeStart = f < aufstehen ? hocke : aufstehen;
|
||||
const noaY =
|
||||
BODEN +
|
||||
sprungY +
|
||||
huepfer(f, raus, 60, 16) +
|
||||
ruck(f, schreck, 3) +
|
||||
huepfer(f, z.n4.start + 20, 14, 10);
|
||||
huepfer(f, aufstehen + NOA_HOCKE_FRAMES + 4, 14, 10);
|
||||
|
||||
// Laubhaufen: wird bei der Landung gestaucht, später raschelt er
|
||||
const flach = kurve(f, [
|
||||
|
|
@ -96,24 +98,28 @@ export const Szene2: React.FC = () => {
|
|||
]);
|
||||
|
||||
// Pino: als Stachelkugel im Haufen, kommt hoch, öffnet sich bei bild4
|
||||
const auf = z.bild4.start; // Einroll-Clip rückwärts
|
||||
const AUF_FRAMES = 75; // 121 Clip-Bilder (24 fps) bei Tempo 2
|
||||
const oeffnet = f >= auf && f < auf + AUF_FRAMES;
|
||||
const zuNoa = f >= auf; // die Bilder schauen nach rechts, Noa sitzt links
|
||||
const pinoY = kurve(f, [
|
||||
[schreck - 10, PINO_HAUFEN_Y + 110],
|
||||
[schreck - 10, PINO_HAUFEN_Y + 140],
|
||||
[schreck + 6, PINO_HAUFEN_Y],
|
||||
[z.bild4.start + 14, PINO_HAUFEN_Y],
|
||||
[z.bild4.start + 40, PINO_HAUFEN_Y - 42],
|
||||
[auf + 20, PINO_HAUFEN_Y],
|
||||
[auf + AUF_FRAMES, PINO_HAUFEN_Y - 40],
|
||||
]);
|
||||
const pinoDa = f >= schreck - 10;
|
||||
const pinoPose: [number, string][] = [
|
||||
[0, 'pino_A_kugel'],
|
||||
[z.bild4.start + 20, 'pino_ref_sitzend'],
|
||||
[auf + AUF_FRAMES, 'pino_ref_sitzend'],
|
||||
[z.p1.start, 'pino_B_blick-nach-oben'],
|
||||
[z.p1.start + 40, 'pino_A4_staunen'],
|
||||
[z.p2.start, 'pino_B_blick-nach-oben'],
|
||||
[z.p2.ende - 10, 'pino_A3_lachen'],
|
||||
];
|
||||
// Stacheln zittern vor Angst, solange er eine Kugel ist
|
||||
const angst = f < z.bild4.start + 20 ? Math.sin(f * 1.7) * 1.5 : 0;
|
||||
const pinoStauch = atmen(f, 1, 0.01) * hopfStauch(f, schreck) * (f >= z.bild4.start && f < z.bild4.start + 24 ? kurve(f, [[z.bild4.start, 0.9], [z.bild4.start + 24, 1]]) : 1);
|
||||
const angst = f < auf ? Math.sin(f * 1.7) * 1.5 : 0;
|
||||
const pinoStauch = atmen(f, 1, 0.01) * hopfStauch(f, schreck);
|
||||
|
||||
const kam = kamFahrt(f, [
|
||||
[0, 960, 540, 1.0],
|
||||
|
|
@ -135,11 +141,22 @@ export const Szene2: React.FC = () => {
|
|||
<Ort name="garten_herbst" />
|
||||
<Apfel x={APFEL.x} y={APFEL.y} s={0.9} />
|
||||
<Blume x={BLUME_X} y={BLUME_Y} s={0.8} />
|
||||
{pinoDa && <Figur pose={pinoPose} x={HAUFEN_X + 10} y={pinoY} stauchY={pinoStauch} drehung={angst} schatten={false} blende={8} />}
|
||||
{pinoDa &&
|
||||
(oeffnet ? (
|
||||
<ClipFigur name="pino_C7_einrollen" start={auf} tempo={2} rueckwaerts spiegeln x={HAUFEN_X + 10} y={pinoY} schatten={false} ersatz="pino_ref_sitzend" />
|
||||
) : (
|
||||
<Figur pose={pinoPose} spiegeln={zuNoa} x={HAUFEN_X + 10} y={pinoY} stauchY={pinoStauch} drehung={angst} schatten={false} blende={8} />
|
||||
))}
|
||||
{/* Im Haufen steht Noa hinter den vorderen Blättern */}
|
||||
{imHaufen && <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung)} schatten={false} />}
|
||||
<Laubhaufen x={HAUFEN_X} y={BODEN + 12} wackeln={wackeln} flach={flach} />
|
||||
{imHaufen ? null : noaGeht ? <Geher figur="noa" spiegeln x={noaX} y={BODEN} /> : <Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung)} />}
|
||||
<Laubhaufen x={HAUFEN_X} y={BODEN + 6} wackeln={wackeln} flach={flach} />
|
||||
{imHaufen ? null : noaGeht ? (
|
||||
<Geher figur="noa" spiegeln x={noaX} y={BODEN} />
|
||||
) : inHocke ? (
|
||||
<NoaHocke start={hockeStart} auf={f >= aufstehen} x={noaX} y={BODEN} />
|
||||
) : (
|
||||
<Figur pose={noaPose} x={noaX} y={noaY} stauchY={atmen(f, 2) * hopfStauch(f, landung)} />
|
||||
)}
|
||||
<Blaetterregen start={landung - 2} x0={HAUFEN_X - 60} y0={BODEN - 60} boden={BODEN - 10} anzahl={28} aus={z.e4.start} />
|
||||
</Kamera>
|
||||
<Blaetterwind anzahl={5} />
|
||||
|
|
|
|||
Loading…
Reference in a new issue