stickman-explainer は何をしますか?
短いスクリプトを、完成された1080×1920のMP4動画に変換します。ストーリーボードJSONを作成し、scripts/render.pyがすべてのフレームを描画(ゆらゆらとしたインク線、8fpsのラインボイル、フェード/ワイプ/ポップ演出)し、ffmpegでエンコードします。
assets/example_storyboard.jsonは構文の例にすぎません(3シーン)。レイアウトやキャラクターのアクセサリーをテンプレートとしてコピーしないでください。
ビジュアルスタイルのデフォルト
- 9:16のキャンバス(1080×1920、30fps)を使用します。各シーンは1つのアイデアに絞り、通常2.5~4秒、短い字幕をプラットフォームのセーフエリア内に配置します。
- 暖かい紙色(
#f3ebdd)、濃いインク色(#2b2723)、意味のある煉瓦色のアクセント(#b8372d)を使用します。サンゴールド(#d89d48)は太陽モチーフにのみ使用可能です。それ以外はフラットに保ち、グラデーション、影、3D効果は使用しません。 - シンプルな黒インクのフィギュアを、はっきりとした顔と十分な余白を残して描きます。ヘルメット、王冠、マント、赤い服などのアクセサリーは、ストーリーやユーザーが要求しない限り追加しないでください。歴史的な名前からコスチュームを推測しないでください。
- シーンは読みやすく保ちます:最大2体のキャラクターと5つの小さなアイコン。メインの主題を dominant にし、字幕とプロップが重ならないようにします。字幕は2行以内にします。
- 複雑なプロップは認識可能にします。結び目は、明確に交差するループ、中心の締まり、見えるロープの端で描きます。剣には明確なグリップ、ガード、尖った刃を与えます。切断後は、2つのロープ端の間に目に見える隙間を残します。
- ナレーションのタイミングが利用可能な場合、ナレーション全体を通して、現在話されている単語に合わせて赤い字幕強調を追従させます。字幕の空白で区切られた各単語に
karaoke間隔を使用します。アクティブでない単語は通常のテキスト色のままです。 - 非対称のレイアウトを好み、意図的に余白を残します。地面線、太陽、タイトルカード、吹き出しなどのモチーフはオプションです。シーンをサポートする場合にのみ選択してください。
ワークフロー
- スクリプト→シーン。 ストーリーを1文のシーン(字幕6ワード以下、各2.5~4秒)に分割します。パターン:タイトルカード→フック/対立→具体的な明らかに。各シーンは、ちょうど1つの新しい情報を追加します。ナレーションが提供された場合、シーンの境界を話された文に合わせます。
- 各文のビジュアルを選択します。 シーンごとに1つのメタファー:キャラクター+プロップ、吹き出し、またはアイコンの列(X =「持っていない」)。キャラクターは2体まで、アイコンは5つまでにします。
- ストーリーボードを作成します(スキーマは以下)。ワードレベルのナレーションタイミングがある場合は、空白で区切られた字幕の各単語に1つの
karaoke間隔を追加して、赤いアクセントが話されている単語に追従するようにします。ワードタイミングが利用できない場合にのみ、静的ハイライトに**word**を使用します。静的マークアップとkaraokeを組み合わせないでください。 - まずスチルを確認します(高速、約2秒/シーン):
python scripts/render.py story.json --stills out/stills→ 各scene_NN.pngを開き、重なり/配置を修正し、繰り返します。 - 下書き動画:
python scripts/render.py story.json out/draft.mp4 --draft(スーパーサンプリングなし、12fps)。 - 最終版:
python scripts/render.py story.json out/final.mp4 [--audio voiceover.mp3]。 シーンの長さはナレーションの文の長さに合わせる必要があります。ffprobeで音声を測定し、durationをそれに応じて設定します。ワード間隔は、シーンの開始からの秒数であり、ナレーションのアライメントまたは注意深いリスニングから取得する必要があります。各赤い単語が、その音声の前後ではなく、話されている音声とともに表示されることを確認します。
エラーメッセージと検証
render.pyはストーリーボードを描画する前に検証します:不明なtype/pose/face/kind/font、不足している必須フィールド、同じシーン内で繰り返されるid、duration <= 0、1点のみのlinesパス、奇数(偶数でない)解像度、存在しないcarryIDはトレースバックではなく明確なメッセージで拒否されます。決して表示されない要素(シーン終了後のat)に対しては警告が発行されます。音声ファイルが動画よりも短い場合は、最後が無音で埋められます。長い場合は切り詰められます。動画の長さは決して変わりません。
ストーリーボードスキーマ
{"meta": {"width":1080,"height":1920,"fps":30,"boil_fps":8,"transition":0.18,
"palette":{"accent":"#b8372d"}, "audio":null, "font_dirs":[]},
"scenes":[{"duration":3.4, "caption":"Büyük **İskender**'di.",
"caption_at":0.5, "caption_y":null, "elements":[ ... ]}]}
発話に追従する単語強調のために、caption内の空白で区切られた各単語に対して1つの{ "start": 秒, "end": 秒 }オブジェクトを持つkaraoke配列を提供します。時間はシーン内でローカルです。赤い強調は各間隔で切り替わります。karaokeが存在する場合、キャプションに**...**マークアップを含めてはなりません。
{"duration":2.8, "caption":"Düğümü tek hamlede kesti.", "caption_at":0.35,
"karaoke":[{"start":0.35,"end":0.76},{"start":0.76,"end":1.02},
{"start":1.02,"end":1.53},{"start":1.53,"end":2.15}],
"elements":[ ... ]}
すべての要素:type、オプションのid(carry用)、at(秒、出現時間)、dur(出現秒数)、anim = fade | wipe | rise | pop、until/out_dur(フェードアウト)。
位置x,x2,y,ground ≤ 2は幅/高さの割合として読み取られ、それ以外はピクセルとして読み取られます。サイズは常にピクセルです。
| type | キーフィールド |
|---|---|
ground | y(必須); x, x2はオプション、指定しない場合はキャンバスの約2%~98% — ゆらゆらベースライン |
sun | x,y,r,rays |
text | text(**accent**マークアップ)、x,y,size,font = sans/sans_bold/sans_light/serif、tracking、caps、color、max_w、max_lines(デフォルト3)、rules(タイトルカード用の二重線;幅と間隔はテキストに合わせて自動) |
figure | x,ground,h,head_r、pose = stand/arms_open/point/wave/think/shrug/sit、dir(±1)、face = neutral/smile/sad/surprised/angry、オプションのaccessory = helmet/crown/hat/glasses、オプションのcape、look |
prop | kind = barrel/bowl/bucket/house/book/box/triangle/coins/tree、x,y(左上)、w,h、cross(アクセントX)、flip |
thought | x,y,w,h、tail_to [px,py](頭の位置)、inner: バブルローカルpxの要素リスト |
lines | paths(点のリストのリスト、ローカルpx)、closed、smooth、color、width — 任意のカスタムシェイプのためのエスケープハッチ |
シーン間の継続性(carry)
あるシーンの要素を次のシーンに持ち越すには、要素にidを付け、次のシーンでcarryを使って呼び出します。持ち越された要素は同じ線の震え(同じシード)で始まるため、シーンが変わっても「同じ描画」として表示されます。新しいシーンへのトランジションはデフォルトでカット(クロスフェードなし)です。
"carry": [
"ground",
{"id": "sun", "set": {"r": 122}},
{"id": "alex", "set": {"x": 120, "face": "smile"}, "tween": {"at": 0.1, "dur": 0.9}},
{"id": "diog", "until": 0.1, "out_dur": 0.5}
]
行は順番に:"ground"はそのまま持ち越される · sunはフィールド変更して持ち越される(変更はシーン開始時に即時) · alexはフィールド変更し、かつ位置が期間中にスライドする(tween) · diogは持ち越されるが、シーン内でフェードアウトする。
"carry": "all"はすべてを引き継ぐ("carry_except": ["id", ...]で除外)。字幕は決して引き継がれない。- 持ち越されない要素はシーン開始時に即座に消える。スムーズに消えるには
until/out_durを使用する。untilでフェードアウトする要素は次のシーンに持ち越せない。 tweenは位置(xとy/ground)のみをアニメーションします。サイズ(r,h,w)と外観(face,pose)の変更はシーン開始時に即座に適用されます。- 持ち越された要素は新しい要素の下に描画されます。持ち越される要素については、シーン間で
idが一意である必要があります。 - 同じ
idの要素はすべてのシーンで同じシードを使用します。シーン固有の新しい要素にはidを指定しないでください。
render.pyのPROPS/handsを拡張して新しいプロップ/ポーズを追加します。同じインクスタイルを維持します(stroke()を使用し、プレーンな線は使用しない)。
レイアウト:各動画ごとに再決定
固定の座標やテンプレートはありません。各動画の主題は異なるため、配置をシーンの内容から導き出します:
- まず、その文の単一のビジュアルアイデアを選択し(比較、リスト、夢想、旅、数字など)、それに合ったレイアウトを作成します。同じ動画内でシーン間でレイアウトが変更される場合があります。
- 重要な要素にスペースを確保します:メインの主題が最も大きく/最も目立つようにします。サブ要素は小さく保ちます。空白(ネガティブスペース)を意識的に残します。
- 大きさ、位置、数は内容によって異なります(1体か2体のキャラクターか、アイコンの列か、1つの大きなオブジェクトか、地図/タイムラインか)。必要に応じて
linesでカスタムシェイプを描きます。 - 歴史上の人物に自動的にコスチュームや頭飾りを追加しないでください。フィギュアはデフォルトでアクセサリーなしで描かれます。兜、王冠、帽子、マントは、ナレーションまたはユーザーが明示的に要求した場合にのみ追加します。アクセントカラーをキャラクターの服の装飾として広げないでください。
- 結び目と切断のシーンでは、オブジェクトを読みやすく描きます:紐は、明確に重なり合ういくつかのループで描き、中心にしっかりとした結び目を示し、紐の端を区別できるようにします。剣では、柄、ガード、鋭い刃先が識別できるようにします。切断後は、2つの切断された紐の端を間隔を空けて、はっきりと見えるままにします。剣と結び目が字幕で覆われないようにします。特別な紐/剣の形状は
linesで描きます。 - 地面線、太陽、タイトルカードなどのモチーフはオプションです。主題に合わない場合は使用しないでください(例:室内、夜、抽象的な概念)。
- 字幕は、読みやすく、プラットフォームインターフェースから離れた場所に配置します。重なる場合は
caption_yで調整します。重なりとオーバーフローはスチルで目視確認し、数値だけに頼らないでください。 - 描画座標は1080×1920キャンバスのピクセル(または0~2の比率)で指定します。各シーンごとにゼロから値を計画します。
注意点
- エンコードする前に必ずスチルを確認してください。重なり合う要素と切り取られた太陽光線が一般的な欠陥です。下書きには
--draft、詳細にはフルレンダリング:変更のないフレームは再描画されず、10秒の動画はフル品質で約20秒かかります。 - 字幕は幅の86%まで行を折り返します(最大3行)。ただし、1つの単語が収まらない場合や3行を超える場合は縮小します。2行を超える文は2つのシーンに分割します。デフォルトの字幕位置は単なるフォールバックです。必要に応じてシーンで
caption_yを指定します。 - フォント:PoppinsとPlayfair Display(またはLora)をインストールするか、
--font-dirを指定します。TrueTypeフォントが見つからない場合、レンダラーは警告を出力し、トルコ語文字(İ ş ğ â)が欠落する可能性があります。スチルで確認してください。Linux、macOS、Windowsのフォントフォルダーがスキャンされます。 - アクセントカラーは意味的に使用します(主語、キーワード、否定)。色を追加しないでください。
karaokeが存在する場合、提供された間隔を使用して、赤い字幕強調がナレーションに単語ごとに追従します。固定の赤いキーワードを画面に残さないでください。アクティブでない単語はキャプションテキスト色でレンダリングします。