# -*- coding: utf-8 -*-
"""研究のスキル一覧(ショート専用・舞台型の試作1本目・2026-11-06)
奥のモニターに1つずつ出す。中身はエイイチが実際に使っている仕組みだけ(誇張しない)。
#6 のセリフはエイイチの言葉「今まで煩わしかったターミナルでの作業から解放されたこと」から。
"""
import json, os
from kabe_kit import shake_js
NAME, VER = "研究のスキル一覧", 5
HEAD = "ひとりで
ロボット研究?"
LEAD = 0.8
STAGE = "stage"
BG_NAME = "stage_bg_noring" # 床の円を消した舞台(円は OVER で回しながら描く)
T_ON = (0, 1.3)
FOOT_L, FOOT_R, SIZE, SUB_MID = (390, 1525), (690, 1525), 0.88, 1590
SUB_ONE_LINE = True # 字幕は1行ずつにして、箱を足もとの下に収める # 円の奥寄りに、2人を寄せて立たせる(版2: エイイチ「距離感もっと近くて良さそう」)
ORANGE = "#E2683A" # 暗い画面の上で読めるよう、#C4471F を明るくした。色の意味は「いま話している1つ」だけ
FACE_C, DIM, INK = "#1B2433", "#8A93A3", "#F5F3EE"
ITEMS = ["運用分析", "GPUで学習", "依頼箱", "課題地図", "事実確認", "声の単語帳", "記録→記事"]
# 画面の決まり(900×600): 左上に「番号 / 07」と名前、真ん中に言いたいことを1つ、図は面(ベタ)で。線・枠・記号のアイコンは使わない
CSS = f""""""
def win(t0, t1, inner):
an = f"show .15s linear {t0:.3f}s forwards" + (f", hide .15s linear {t1:.3f}s forwards" if t1 else "")
return f'
{inner}
'
def no(n, name):
return f'{n:02d}/ 07{name}
'
def rows(t_in, step, t_pick=None, pick="依頼箱"):
out = []
for k, name in enumerate(ITEMS):
an = f"rowIn .3s cubic-bezier(.2,1.3,.4,1) {t_in + k * step:.3f}s forwards"
if t_pick is not None:
an += f", hiR .35s ease-out {t_pick:.3f}s forwards" if name == pick else f", dimR .35s linear {t_pick:.3f}s forwards"
out.append(f'{k + 1:02d}{name}
')
return '' + "".join(out) + "
"
def scene(T, S):
h = CSS
# 0: 問い(モニターがついたら ? が息をする)
h += win(T(0, 1.3), T(1, 0.0), f'?
')
# 1: 仕組みは7つ(左に大きな 7、右に7行)
h += win(T(1, 0.0), T(2, 0.0),
f'7つの仕組み
'
+ rows(T(1, 1.1), 0.14))
# 2: 01 運用分析(動きの帯を再生ヘッドが進み、止まりが橙に変わる)
t2 = T(2, 0.3); d2 = S(2) - 0.5
segs = [(0.16, 0), (0.08, 1), (0.22, 0), (0.12, 1), (0.25, 0), (0.07, 1), (0.10, 0)]
acc, tr, tg = 0.0, "", ""
for w, stop in segs:
ts = t2 + d2 * (acc + w)
tr += f''
if stop:
tg += f'止まり
'
acc += w
h += win(T(2, 0.0), T(3, 0.0), no(1, "運用分析") + 'ロボットの動画から、
止まった所を読む
'
+ f'{tr}
{tg}')
# 3: 02 GPUで学習(借りる → 学習 → 消す が順に橙になる)
t3 = T(3, 0.0); s3 = S(3)
st = "".join(f'{k + 1}{nm}
'
for k, (nm, tt) in enumerate([("借りる", t3 + 0.7), ("学習", t3 + 1.6), ("消す", t3 + s3 - 0.9)]))
h += win(t3, T(4, 0.0), no(2, "GPUで学習") + '借りて学習して、
終わったら消す
' + f'{st}
')
# 4: 05 事実確認(別のAIが1行ずつ確かめる。言い切りだけ ✗)
t4 = T(4, 0.0)
rr = "".join(f'{"✕" if c == "ng" else "✓"}{tx}
'
for k, (c, tx) in enumerate([("ok", "数字の出どころ"), ("ng", "根拠のない言い切り"), ("ok", "引用の原文")]))
h += win(t4, T(5, 0.0), no(5, "事実確認") + '別のAIに、疑わせる
' + rr)
# 5: いちばん助かってるのは…(7行に戻り、依頼箱だけ橙で残る)
h += win(T(5, 0.0), T(6, 0.0), 'いちばん
助かって
いるのは
'
+ rows(T(5, 0.0), 0.0, t_pick=T(5, 1.0)).replace('top:46px', 'top:46px'))
# 6: 03 依頼箱(依頼が 新規→実行中→済み と進み、ターミナルの行に線が引かれる)
t6 = T(6, 0.0); s6 = S(6)
lanes = "".join(f'{nm}
' for k, nm in enumerate(["新規", "実行中", "済み"]))
h += win(t6, T(7, 0.0), no(3, "依頼箱") + 'Claude Code に、自動で渡す
'
+ lanes + f'依頼
'
+ '$ cd ~/Robotics_UX/ServiceSim
$ claude -p "……"
'
f''
f'解放!
')
# 7: 研究の記録は毎日サイトで
t7 = T(7, 0.0)
h += win(t7, None,
f'pixelstomotors.com
'
f'研究の記録を、毎日公開
')
return h
RING = json.load(open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "stage", "ring_lut.json")))
RING_SEC = 48.0 # 床の円が1周する秒数(ゆっくり回す・版3)
def OVER(T):
"""モニターの外に重ねるもの(版3)。床の円を回す・紙吹雪(7つ!と解放!)・解放!で画面が揺れる。
円は Unity の舞台から消した床(stage_bg_noring.png)の上に、同じカメラの値で描き直す(stage_meta.json の camera)。"""
bursts = [dict(t=T(1, 1.0), n=230), dict(t=T(6, 4.4), n=130)]
js = f"""
"""
return js + shake_js([(T(6, 4.4), 0.6)], amp=7)
FACE = [(-1, 0.0, 1.0, "joy"), (1, 0.0, 2.0, "joy"), (4, 0.0, 2.5, "wink"), (6, 3.5, 2.5, "joy"), ("end", 0.0, 3.0, "smile")]
POSE = [(-1, 0.0, 1), (1, 0.6, 3), (2, 0.0, 2), (5, 0.0, 1), (6, 3.2, 4)]
LEAN = []
MOVES = [(1, 0.6, "hop", 1.4), (3, 0.5, "tilt", 1.2), (5, 0.2, "turn", 2.0), (7, 0.2, "jump", 1.0)]
INTRO = "rise"
SE = [(-1, 0.0, "se_rise", -8), (0, 1.3, "se_boot", -12), (1, 1.0, "se_ding", -8), (1, 1.15, "se_jaran", -12),
(2, 0.0, "se_whoosh", -12), (3, 0.0, "se_whoosh", -12), (4, 0.0, "se_whoosh", -12), (4, 1.0, "se_pop", -10),
(5, 1.0, "se_tick", -10), (6, 0.0, "se_whoosh", -12), (6, 4.4, "se_pechin", -6), (7, 0.4, "se_pop", -9)]
ENDING = dict(kind="jump", at=(6, 3.4))