# -*- coding: utf-8 -*-
# 動画「ロボットが店に広がるかを決めるのは、賢さか」— 考察回「壁打ちの型」の2本目(もとは LOG389)の場面
# 書き写した元: 頭脳は脳のように分かれる/scenes_kabe.py(型の決まりは video_rules.md 10節)
# 紙の文字は真ん中にそろえる/線を使わず面で分ける(事実=墨・エイイチの見立て=濃い灰・ハコブくん=薄い灰)
# 朱は判子とリアクション(ボツ・入れとく・この見立ては、エイイチから)だけ/字幕は動かさない
# 脱線(画面いっぱいの絵)は4回: 1時間に6回(マンガ)・朝の街(写真風)・最後の動作(マンガ)・扉(マンガ)。どれも Gemini で作った絵
# 天丼: ハコブくんが固まると、頭の上に「再開」のボタンが出る。エイイチが押す(2回)。最後だけハコブくんが自分で押す
import os
from kabe_kit import *
import scene_kit as _sk
_tl = Timeline()
tl = _tl
N = len(_tl.idx)
A = lambda i, off=0.0: tl.start(i) + off # 動画の頭からの秒
S = tl.speech # その文を読み終わるまでの長さ
LOGO = _sk.LOGO
BRAND = f'
'
SANS = "font-family:'Noto Sans CJK JP',sans-serif;"
BG = "src/bg/"
GEM = "絵は生成AI(Gemini)で作成"
BLOCKS = []
def block(name, sents, fn, video=None):
b0 = _tl.start(sents[0])
T = lambda i, off=0.0: tl.start(i) - b0 + off
E = lambda i: tl.end(i) - b0
b = dict(name=name, sents=list(sents))
if video:
b["video"] = video
else:
b["html"] = page(fn(T, E) + BRAND)
BLOCKS.append(b)
def inkl(text, t, step=0.05):
"""「/」で行を分けて書く(1〜2文字だけ次の行に落ちないように)"""
out, tt = [], t
for k, part in enumerate(text.split("/")):
out.append(ink(part, tt, step)); tt += len(part) * step
return "
".join(out)
def line(text, t, size=30, weight=700, step=0.05, style=""):
return f'
{ink(text, t, step)}
'
def qline(text, t, size=46, step=0.06, style=""):
return f'{ink(text, t, step)}
'
def src(text, t=0.0):
return f'{text}
'
BTN_CSS = """"""
def btn(t_show, t_press, t_hide, who="エイイチが押す"):
"""ハコブくんの頭の上の「再開」ボタン(天丼)"""
return (BTN_CSS + f'{who}再開
'
+ f'ピッ
')
# ================= 0 つかみ =================
def hook(T, E):
fly = (T(2, 0.1), T(3, 0.15))
return (dassen(BG + "1時間に6回.jpg", fly[0] + 0.05, fly[1] - 0.05, words="!1時間に|6回。", word_style="top:120px;font-size:130px", src_note=GEM)
+ paper(tag("考察ノート") + ''
+ line("仮に、成功率", T(0, 0.2), size=34, weight=500)
+ f'
0%
'
+ f'
{ink("1時間に600回働くと、", T(1, 0.3), 0.06)}
'
+ f'
{land("6回", T(1, 3.0), "font-size:120px;font-weight:900")}{ink("人が駆けつける", T(1, 3.5), 0.07)}
'
+ '
' + src("例の計算(X の議論の指摘をもとに)", T(0, 0.5)), 0.0, flights=[fly])
+ stamp("優秀?", T(3, 0.3), "right:380px;top:560px;font-size:64px")
+ shake_js([(T(3, 0.65), 0.3)])
+ counter_js("c99", T(0, 1.0), T(0, 2.6), 99, dec=0))
block("hook", [0, 1, 2, 3, 4], hook)
def hook_q(T, E):
return paper(tag("考察回 壁打ち") +
''
f'ロボットが
'
f'店に広がるかを
'
f'決めるのは、
'
f'{land("賢さ", T(5, 2.4), "font-size:104px;font-weight:900")}か。
', 0.0)
block("hook_q", [5], hook_q)
# ================= 1 自己紹介(DAY124 の映像とテロップ) =================
O_TITLE = '
フィジカルAI解説
案内役:ハコブくん(AI音声)
'
O_EIICHI = '
エイイチ
デザイナー/ロボティクスの研究・開発
'
O_OUTRO = '
相談は概要欄の連絡先から
高評価・チャンネル登録/コメントはエイイチが全部読んでいます
'
OVERLAYS = {"title": _sk.overlay(O_TITLE), "title_eiichi": _sk.overlay(O_EIICHI), "outro": _sk.overlay(O_OUTRO)}
_t_title = tl.end(7) - tl.start(6)
block("title", [6, 7], None, video=("src/day124_crop.mp4", 4.5, "png/ovl/title.png"))
block("title_eiichi", [8], None, video=("src/day124_crop.mp4", 4.5 + _t_title, "png/ovl/title_eiichi.png"))
def title_kabe(T, E):
return paper(tag("考察回") + ''
+ f'
{land("壁打ち", T(9, 0.5))}
'
+ line("エイイチの考えに、ハコブくんが付き合う", T(9, 1.3), size=34, weight=500)
+ '
', 0.0)
block("title_kabe", [9], title_kabe)
# ================= 2 持ち込み(脱線: 朝の街・ボタン1回目) =================
def bring(T, E):
fly = (T(13, 0.3), E(13) - 0.1)
col = lambda kind, head, sub, t: (f''
f'{head}
{sub}
')
return (dassen(BG + "朝の街.jpg", fly[0] + 0.05, fly[1], words="一夜で、|
|!来ない。", word_style="top:150px;font-size:120px", src_note=GEM)
+ paper(tag("持ち込み") + ''
+ line("エイイチが持ち込んだ、X の議論", T(10, 0.4), size=28, weight=500, style="color:#5a564f")
+ f'
{ink("ロボットにも、", T(11, 0.4), 0.06)}
'
f'{land("ChatGPT の瞬間", T(11, 1.4), "font-size:84px;font-weight:900")}{ink("は来る?", T(11, 2.4), 0.08)}
'
+ '
'
+ f'
'
+ card("hb", "明日の朝、僕にも来る", T(13, 0.4), "ハコブくん", w=460, botsu=E(13) + 0.1, fade=T(14, 0.3), style="font-size:30px") + '
'
+ f'
'
+ col("hb", "ChatGPT", "開けば、すぐ試せる", T(15, 0.4))
+ col("fact", "ロボット", "置く場所・止まったとき・店のやり方", T(16, 0.5))
+ '
', 0.0, flights=[fly])
+ btn(T(16, S(16) + 0.2), T(17, -0.25), T(17, 1.2)))
block("bring", [10, 11, 12, 13, 14, 15, 16, 17], bring)
# ================= 3 やり取り1:なぜ配膳ロボットが先に広がったか =================
def why(T, E):
big = lambda txt, t: (f''
f'{txt}
')
return paper(tag("やり取り 1 / どんな仕事から?") + ''
+ qline("どんな仕事から、広がるのか", T(18, 0.3), size=48)
+ f'
{big("失敗が
許せる", T(18, 2.8))}{big("繰り返しが
多い", T(18, 4.2))}
'
+ card("mi", inkl("データも取りやすい。入れるハードルも低い。/広がり始めたのも、このあたり", T(19, 0.4), 0.045), T(19, 0.15), "見立て ・ エイイチ", w=860, anim="wipe")
+ '
', 0.0)
block("why", [18, 19, 20], why)
def first(T, E):
c = lambda txt, t: (f'{txt}
')
return (paper(tag("ファミレスの配膳ロボット") + ''
+ qline("条件が、全部そろっていた", T(21, 0.8), size=50)
+ card("mi", inkl("とうとうロボットが店舗に入った!", T(22, 0.5), 0.06), T(22, 0.2), "エイイチ(初めて店で見たとき)", w=820, anim="wipe", style="font-size:42px;font-weight:700")
+ f'
{c("止まっても、
料理が少し遅れるだけ", T(24, 0.3))}{c("毎日、
何百回も往復する", T(24, 2.6))}{c("ホールの人が
集まらない", T(24, 4.2))}
'
+ '
', 0.0)
+ stamp("感激", T(22, 3.0), "right:380px;top:300px;font-size:64px")
+ shake_js([(T(22, 3.35), 0.3)]))
block("first", [21, 22, 23, 24], first)
def last(T, E):
tf = T(27, S(27) - 0.5)
fly = (T(27, S(27) + 0.1), T(28, 0.7))
return (dassen(BG + "最後の動作.jpg", fly[0] + 0.05, fly[1] - 0.05, words="!最後の|動作。", word_style="top:90px;font-size:130px", src_note=GEM)
+ paper(tag("一番難しい所") + ''
+ qline("一番難しい所を、ロボットにやらせていない", T(25, 0.3), size=40)
+ '
'
+ card("fact", '料理をテーブルに置く
お客さん', T(26, 0.3), None, w=420)
+ card("fact", 'トレーに載せる
店員さん', T(26, 2.2), None, w=420)
+ '
'
+ card("hb", "料理も、僕が置く", T(27, 0.6), "ハコブくん", w=420, botsu=T(28, 0.9), fade=T(28, 1.1), style="font-size:30px")
+ card("mi", inkl("だから、人に任せた", T(28, 0.9), 0.06), T(28, 0.75), "エイイチ", w=480, anim="wipe")
+ qline("頑張らない所を決めて、広がった", T(29, 1.6), size=40)
+ '
', 0.0, flights=[fly])
+ f'ドテッ
'
+ shake_js([(tf + 0.3, 0.25)]))
block("last", [25, 26, 27, 28, 29], last)
# ================= 4 山場:扱えるか =================
def card_mi_float(T):
"""店のスタッフさんでも使えてる(エイイチ)を紙の上部に"""
return (f''
+ card("mi", inkl("店のスタッフさんでも使えてる。ポイント高い", T(32, 0.4), 0.05), T(32, 0.15), "エイイチ", w=820, anim="wipe", fade=T(33, 0.0))
+ '
')
def easy(T, E):
press = T(31, 1.6)
return (BTN_CSS + paper(tag("もう1つの強み") + ''
+ line("使い方は、テーブル番号のボタンを押すだけ", T(31, 0.2), size=34, weight=700)
+ f'
' + ''.join(
f'
{k + 1}
' for k in range(4)) + '
'
+ card("mi", inkl("店のスタッフさんでも使えてる。ポイント高い", T(32, 0.4), 0.05), T(32, 0.15), "エイイチ", w=820, anim="wipe")
+ f'
{ink("研修もマニュアルも、", T(33, 0.3), 0.06)}{land("ほぼ不要", T(33, 2.0), "font-size:70px;font-weight:900")}
'
+ card("hb", inkl("賢くても、止まったときにどうすればいいか/分からなければ、広がらない", T(34, 0.8), 0.04), T(34, 0.5), "裏を返すと", w=900)
+ '
', 0.0)
+ f'ピッ
'
)
block("easy", [30, 31, 32, 33, 34], easy)
def peak(T, E):
return (''
''
''
'
'
f'止まったとき、
'
f'店の人が
'
f'{land("戻せる", T(35, 2.6), "font-weight:900;font-size:84px")}か。
'
f'
ハコブくんの整理 / エイイチ「そうなんだよな」
'
'
')
block("peak", [35], peak)
def q3(T, E):
row = lambda b, txt, t, sub="": (f'')
return paper(tag("見極めの、3つの問い") + ''
+ row("1つ目", "止まった理由が、分かるか", T(37, 0.3), "通れません/物を見失いました")
+ row("2つ目", "その場の人が、戻せるか", T(38, 0.3), "エンジニアを呼ばずに")
+ row("3つ目", "次の動きが、周りに読めるか", T(39, 0.3), "読めないと、怖い")
+ '
', 0.0)
block("q3", [36, 37, 38, 39], q3)
def demo(T, E):
return (paper(tag("デモの点数") + ''
+ qline("この3つは、技術のデモでは", T(40, 0.3), size=46)
+ f'
{land("ほとんど評価されない", T(40, 1.8), "font-size:60px;font-weight:900")}
'
+ card("mi", inkl("デモの点数には、入らない", T(41, 0.4), 0.06), T(41, 0.15), "エイイチ", w=600, anim="wipe")
+ card("hb", "さっきコケた所は、点数に入れないで", T(42, 1.0), "ハコブくん", w=640, style="font-size:30px")
+ '
', 0.0)
+ stamp("入れとく", T(43, 0.05), "right:380px;top:640px;font-size:56px")
+ shake_js([(T(43, 0.4), 0.25)]))
block("demo", [40, 41, 42, 43], demo)
# ================= 5 やり取り2:足りなくなるのは誰か(ボタン2回目) =================
def who(T, E):
return (paper(tag("やり取り 2 / 足りなくなるのは誰か") + ''
+ line("動き回る仕事のロボットは、機体が", T(44, 2.0), size=34, weight=700)
+ f'
{land("安く↓", T(44, 4.6))}
'
+ card("mi", inkl("その分、入れる営業や/エンジニアが足りなくなる", T(46, 0.4), 0.05), T(45, 0.15), "見立て ・ エイイチ", w=760, anim="wipe")
+ card("mi", inkl("利益が残るのは、/作る側より、入れて回す側", T(47, 0.4), 0.05), T(47, 0.15), "見立て ・ エイイチ", w=820, anim="wipe", style="font-size:38px;font-weight:700")
+ '
', 0.0)
+ stamp("この見立ては、エイイチから", T(47, S(47) + 0.1), "left:1060px;top:150px")
+ shake_js([(T(47, S(47) + 0.45), 0.3)]))
block("who", [44, 45, 46, 47], who)
def who2(T, E):
row = lambda txt, t: f'{txt}
'
return (paper(tag("足りなくなる人") + ''
+ qline("足りなくなるのは、こんな人", T(48, 0.2), size=42)
+ row("通路の幅や段差を見る", T(48, 1.8))
+ row("どこまでロボットに任せるか、線を引く", T(48, 3.6))
+ row("止まったときの対処を、店のルールにする", T(48, 5.8))
+ card("hb", "今みたいに戻してくれる人が、足りない", T(49, 1.0), "ハコブくん", w=680, style="font-size:30px;margin-top:16px")
+ '
', 0.0)
+ btn(T(48, S(48) + 0.2), T(49, -0.25), T(49, 1.2)))
block("who2", [48, 49], who2)
def local(T, E):
return paper(tag("地方では") + ''
+ card("mi", inkl("入れるのは、地方の方が切実。/どう解決できるかが鍵", T(50, 0.4), 0.045), T(50, 0.15), "見立て ・ エイイチ", w=860, anim="wipe")
+ card("mi", inkl("家電量販店や電気屋さんは、どうだろう?", T(51, 0.4), 0.05), T(51, 0.15), "エイイチ", w=720, anim="wipe")
+ '
'
+ card("fact", '町の電気屋さん
すぐ来てくれる関係がある
→ 止まったときの駆けつけ', T(52, 0.3), None, w=420)
+ card("hb", '任せる線を、
店と決める
まだ、どちらにもない
→ 離れた所から専門の人', T(53, 0.4), None, w=420)
+ '
'
+ card("mi", inkl("役割を分ける", T(54, 0.3), 0.06), T(54, 0.1), "エイイチ", w=380, anim="wipe")
+ '
', 0.0)
block("local", [50, 51, 52, 53, 54], local)
# ================= 6 やり取り3:止まらない作りと、扉 =================
def iris(T, E):
big = lambda el, unit, t: f'0{unit}
'
return (paper(tag("やり取り 3 / アイリスオーヤマは?") + ''
+ card("mi", inkl("アイリスオーヤマは、どうしてる?", T(55, 0.4), 0.05), T(55, 0.15), "エイイチ", w=640, anim="wipe")
+ f'
{big("c7k", "社以上", T(56, 1.0))}{big("c25k", "台以上", T(56, 2.6))}
'
+ card("fact", inkl("充電切れ・紙パックがいっぱい、で、/すぐ人の作業に戻る", T(57, 0.5), 0.04), T(57, 0.2), "事業の責任者", w=880)
+ card("fact", inkl("運用を直し続ける人が、現場にいない", T(58, 0.5), 0.05), T(58, 0.2), "広める上での壁", w=720)
+ '
' + src("出どころ:マイナビニュース(2026-06-29)", T(56, 0.3)), 0.0)
+ counter_js("c7k", T(56, 1.6), T(56, 3.0), 7000, dec=0) + counter_js("c25k", T(56, 3.0), T(56, 4.4), 25000, dec=0))
block("iris", [55, 56, 57, 58], iris)
def iris2(T, E):
return paper(tag("止まらない作り") + ''
+ '
'
+ card("fact", "掃除が終わると、自分で充電に戻る", T(59, 1.2), "新しい清掃ロボ", w=430)
+ card("fact", "紙パックを、替えやすく", T(59, 3.6), "新しい清掃ロボ", w=380)
+ '
'
+ f'
{land("紙パック", T(60, 0.2))}
'
+ line("直し続ける人がいないなら、人を呼ばない作りにする", T(60, 1.6), size=30, weight=500)
+ card("mi", inkl("家電の会社らしい", T(61, 0.3), 0.06), T(61, 0.1), "エイイチ", w=440, anim="wipe")
+ '
' + src("出どころ:マイナビニュース(2026-06-29)"), 0.0)
block("iris2", [59, 60, 61], iris2)
def door(T, E):
fly = (T(63, S(63) - 0.8), T(64, 0.25))
return (dassen(BG + "扉.jpg", fly[0] + 0.05, fly[1] - 0.05, words="開けて……。", word_style="top:60px;font-size:120px", src_note=GEM)
+ paper(tag("残る壁") + ''
+ qline("残る壁は、下げ膳", T(62, 0.3), size=52)
+ line("食べ終わったお皿を、洗い場まで運ぶ仕事", T(62, 2.0), size=30, weight=500)
+ card("mi", inkl("バックヤードに行くには、/扉を開けられないといけない", T(63, 0.4), 0.045), T(63, 0.15), "見立て ・ エイイチ", w=880, anim="wipe")
+ card("hb", "扉は、体当たりで開ける", T(64, 0.5), "ハコブくん", w=500, botsu=E(64) - 0.6, style="font-size:30px")
+ '
', 0.0, flights=[fly]))
block("door", [62, 63, 64], door)
def door2(T, E):
lab = lambda txt, t: f'{ink(txt, t, 0.05)}
'
seg = lambda txt, w, bg, fg, t: (f'{txt}
')
return paper(tag("扉を通らない手") + ''
+ line("求められるのは、調理場と客席の仕切り", T(65, 0.4), size=34, weight=700)
+ line("形は、地域の決まりや保健所の判断で変わる", T(65, 3.4), size=26, weight=500, style="color:#5a564f")
+ '
'
+ '
'
+ f'
{lab("洗い場側", T(66, 1.0))}
{lab("両側から取り出せる棚", T(66, 1.8))}
{lab("客席側", T(66, 1.0))}
'
+ '
'
+ seg("調理場", 300, "#cfc8ba", "#22201c", T(66, 1.2)) + seg("棚", 300, "#22201c", "#f5f3ee", T(66, 2.6)) + seg("ロボット", 300, "#ece9e2", "#22201c", T(66, 4.4))
+ '
'
+ '
'
+ card("mi", inkl("現場設計だね。/直接、洗い場まで行けたら最高", T(67, 0.4), 0.05), T(67, 0.15), "見立て ・ エイイチ", w=760, anim="wipe")
+ line("新しい店や改装のときに、通り道ごと設計する", T(68, 0.8), size=30, weight=700)
+ '
' + src("出どころ:食品衛生法の施設基準(例:那覇市・北九州市の基準)", T(65, 0.3)), 0.0)
block("door2", [65, 66, 67, 68], door2)
# ================= 7 まとめ =================
SUM_CSS = """@keyframes gather { from { opacity:0; transform: translate(var(--dx),var(--dy)) rotate(var(--r)); } to { opacity:1; transform: none; } }"""
def summ(T, E):
row = lambda b, mid, dx, dy, r, t: (f'{b}{mid}
')
b3 = lambda txt, t: card("hb", txt, t, None, w=290, botsu=t + 0.25, style="font-size:22px;padding:10px 12px;white-space:nowrap")
return (''
+ paper(tag("残っている壁") + ''
+ row("店の作り", "扉・通路・下げ台", 420, 420, 8, T(70, 3.0))
+ row("仕事の分け方", "置くのは人、運ぶのはロボット", 300, -60, -6, T(70, 4.2))
+ row("調整", "保健所・地域の決まり", -420, 180, 10, T(70, 5.4))
+ f'
'
f'{ink("決めていたのは、店の人が自分で扱えるように、", T(71, 0.3), 0.05)}
{land("作業と店の作りを、切り直せるか。", T(71, 3.2), "font-size:50px")}
'
+ f'
{b3("明日の朝、僕にも来る", T(72, 1.4))}{b3("料理も僕が置く", T(72, 1.8))}{b3("扉は体当たり", T(72, 2.2))}
'
+ card("mi", inkl("全部やらなくていい", T(73, 0.3), 0.06), T(73, 0.1), "エイイチ", w=440, anim="wipe", style="padding:10px 30px")
+ '
', 0.0))
block("sum", [69, 70, 71, 72, 73], summ)
# ================= 8 残った問い(ボタン3回目・自分で押す) =================
def rest(T, E):
return paper(tag("残った問い") + ''
+ card("mi", inkl("この辺の問題を解決する力を持つ人が、/重宝されそう", T(74, 0.4), 0.045), T(74, 0.15), "見立て ・ エイイチ", w=860, anim="wipe")
+ qline("店とロボットのあいだに立つ役", T(75, 0.4), size=46)
+ f'
{ink("名前も、求人の枠も、", T(75, 3.8), 0.06)}{land("まだない", T(75, 5.2), "font-size:64px;font-weight:900")}
'
+ card("mi", inkl("大型のお店から、スタートしたい", T(77, 0.3), 0.06), T(77, 0.1), "エイイチ", w=620, anim="wipe")
+ card("hb", '考察ノートの全文
pixelstomotors.com / LOG389
', T(79, 0.3), "サイトの記事", w=600)
+ '
', 0.0)
block("rest", [74, 75, 76, 77, 78, 79], rest)
def ask(T, E):
return (paper(tag("あなたの店では") + ''
+ qline("あなたの店で", T(80, 0.2), size=56)
+ qline("ロボットが止まったら、", T(80, 1.0), size=56)
+ qline("誰が戻しますか。", T(80, 2.2), size=56)
+ '
'
+ card("mi", inkl("それが一番えらい", T(82, 0.3), 0.06), T(82, 0.1), "エイイチ", w=440, anim="wipe")
+ '
', 0.0)
+ btn(T(80, S(80) + 0.3), T(81, -0.3), T(81, 1.0), who="自分で押す"))
block("ask", [80, 81, 82], ask)
# ================= 9 アウトロ =================
block("outro", [83, 84, 85], None, video=("src/day124_crop.mp4", 18.0, "png/ovl/outro.png"))
assert [i for b in BLOCKS for i in b["sents"]] == list(range(N)), "場面の文の番号が抜けている・重なっている"
# ================= ハコブくん =================
# 表情は narration.py の表情が基本。ここは文の途中で替える所: (文, 文の頭から何秒, 何秒間, 表情)
HK_FACE = [
(3, 0.0, 2.0, "doubt"),
(16, S(16) + 0.1, 1.4, "dot"), # 固まる(ボタン1回目)
(22, 0.0, 4.5, "joy"),
(27, S(27) - 0.5, 3.0, "x"), # コケる
(28, 0.0, 2.0, "x"),
(35, 0.0, 6.0, "think"),
(43, 0.0, 1.5, "trouble"),
(47, 0.0, 4.0, "surprise"),
(48, S(48) + 0.1, 1.4, "dot"), # 固まる(ボタン2回目)
(60, 0.0, 2.0, "surprise"),
(63, 0.0, 4.0, "trouble"),
(80, S(80) + 0.2, 1.3, "dot"), # 固まる(自分で押す)
]
HK_FACE = [(A(i, a), A(i, a) + d, f) for i, a, d, f in HK_FACE]
# 体ごとの動き: jump=驚いて全身で飛び出す / fall=前のめりにコケる(up で起き上がる) / reflex=のけぞる
HK_MOVES = [
dict(kind="fall", t0=A(27, S(27) - 0.5), t1=A(29, 0.0), dur=0.5),
dict(kind="jump", t0=A(60, 0.0), dur=1.9),
]
HK_ALPHA = [(A(35, 0.0), tl.end(35), 0.3), # 暗い場面では薄く
(A(27, S(27) + 0.15), A(28, 0.65), 0.0), # 最後の動作の絵にはハコブくんが描いてあるので、重ねない
(A(63, S(63) - 0.75), A(64, 0.2), 0.0)] # 扉の絵も同じ
# ================= エイイチ =================
# (文, 文の頭から何秒, 姿) 1腕組み 2うなずく 3指さす 4首をかしげる 5ひらめく 6のけぞって驚く 7半目でツッコむ 8吹き出して笑う
EI_POSE = [
(0, 0.0, 1), (2, 0.5, 4), (3, 0.0, 7), (4, 1.0, 8), (5, 0.0, 1),
(8, 0.0, 2), (9, 0.0, 1),
(10, 0.0, 3), (12, 0.0, 4), (13, 0.3, 6), (14, 0.0, 1), (16, S(16) + 0.1, 3), (17, 0.8, 2),
(18, 0.0, 1), (19, 0.0, 2), (20, 0.0, 3), (21, 0.0, 2), (22, 0.0, 5), (23, 0.5, 8), (24, 0.0, 1),
(25, 0.0, 4), (27, 0.0, 4), (27, S(27) - 0.4, 6), (28, 0.0, 7), (29, 0.6, 2),
(30, 0.0, 1), (32, 0.0, 3), (33, 0.0, 2), (34, 0.0, 1), (35, 0.0, 2),
(36, 0.0, 1), (39, 0.0, 4), (40, 0.0, 2), (41, 0.0, 3), (42, 0.5, 8), (43, 0.0, 3),
(44, 0.0, 1), (45, 0.0, 3), (47, 0.0, 5), (48, 0.0, 2), (48, S(48) + 0.1, 3), (49, 0.8, 2),
(50, 0.0, 3), (51, 0.0, 4), (52, 0.0, 2), (54, 0.0, 3),
(55, 0.0, 4), (56, 1.0, 6), (57, 0.0, 1), (59, 0.0, 2), (60, 0.0, 6), (61, 0.0, 8),
(62, 0.0, 1), (63, 0.0, 3), (64, 0.0, 7), (65, 0.0, 1), (67, 0.0, 3), (68, 0.0, 2),
(69, 0.0, 2), (72, 0.8, 8), (73, 0.0, 3),
(74, 0.0, 3), (75, 0.0, 2), (76, 0.0, 4), (77, 0.0, 5), (78, 0.0, 1), (79, 0.0, 2),
(80, 0.0, 1), (81, 0.0, 6), (82, 0.0, 8),
(83, 0.0, 2),
]
EI_POSE = sorted((A(i, a), p) for i, a, p in EI_POSE)
# 置き方の替え: (開始, 終了, 置き方)。hide=出さない / big=暗い場面で大きく光らせる
EI_PLACE = [
(A(6), tl.end(7), "hide"),
(A(35), tl.end(35), "big"),
]
# ================= 効果音 =================
# (文, 文の頭から何秒, 音, ナレーションより何dB)
SE = [
(3, 0.3, "se_pop", -8), (22, 3.0, "se_pop", -8), (31, 1.6, "se_ding", -10), (44, 4.6, "se_pop", -10), (11, 1.4, "se_pop", -12),
(2, 0.1, "se_whoosh", -10), (3, 0.15, "se_whoosh", -14),
(13, 0.3, "se_whoosh", -10), (13, 99, "se_pop", -12),
(16, S(16) + 0.2, "se_pop", -12), (17, -0.25, "se_ding", -10),
(27, S(27) - 0.2, "se_whoosh", -12), (27, S(27) + 0.05, "se_pop", -6), (28, 0.7, "se_whoosh", -14), (28, 0.9, "se_pop", -12),
(43, 0.05, "se_pop", -8),
(47, S(47) + 0.1, "se_pop", -8),
(48, S(48) + 0.2, "se_pop", -12), (49, -0.25, "se_ding", -10),
(60, 0.0, "se_rise", -10),
(63, S(63) - 0.8, "se_whoosh", -10), (64, 0.25, "se_whoosh", -14), (64, 1.6, "se_pop", -12),
(72, 1.65, "se_pop", -14), (72, 2.05, "se_pop", -14), (72, 2.45, "se_pop", -14),
(80, S(80) + 0.3, "se_pop", -12), (81, -0.3, "se_ding", -10),
]
SE = [s for s in SE if s[1] != 99]
# 一覧の画像に撮る時刻(場面の頭からの秒)。書かない場面は最後の0.4秒前
def _rel(b, i, off=0.0):
return tl.start(i) - _tl.start(b["sents"][0]) + off
SHEET = {}
for _b in BLOCKS:
_end = _tl.end(_b["sents"][-1]) - _tl.start(_b["sents"][0]) - 0.4
SHEET[_b["name"]] = [_end]
_B = {b["name"]: b for b in BLOCKS}
SHEET["hook"] = [_rel(_B["hook"], 1, 5.0), _rel(_B["hook"], 2, 1.5), SHEET["hook"][0]]
SHEET["easy"] = [_rel(_B["easy"], 32, 1.5), _rel(_B["easy"], 31, 1.8), SHEET["easy"][0]]
SHEET["door2"] = [_rel(_B["door2"], 66, 6.0), SHEET["door2"][0]]
SHEET["bring"] = [_rel(_B["bring"], 13, 2.0), _rel(_B["bring"], 16, S(16) + 0.6), SHEET["bring"][0]]
SHEET["last"] = [_rel(_B["last"], 27, S(27) - 0.1), _rel(_B["last"], 27, S(27) + 1.0), SHEET["last"][0]]
SHEET["who2"] = [_rel(_B["who2"], 48, S(48) + 0.6), SHEET["who2"][0]]
SHEET["iris2"] = [_rel(_B["iris2"], 60, 0.8), SHEET["iris2"][0]]
SHEET["door"] = [_rel(_B["door"], 63, S(63) - 0.2), SHEET["door"][0]]
SHEET["ask"] = [_rel(_B["ask"], 80, S(80) + 0.6), SHEET["ask"][0]]
SHEET["title"] = [2.0]