[forum知見] Thyme 開発概要
調査結果
ダウンロード済み HTML 5,944 ページを、ナビゲーション・署名・画像を除いた本文へ変換した。
ページ送りを統合し、5,125 トピック、39,203 投稿、2,444 コードブロックを抽出した。
HTML parser の失敗は 0 件。以後の精読は抽出済みテキストだけで行った。
重要な結論は次のとおり。
- 生成レコード内の属性は
:=で初期化する。保存データを丸ごとScene.add*へ貼らない。 - 車輪・リンクは重ねただけでは接続されない。
Scene.addHingeに両 geometry と接続位置が必要。 glued := true単独は生成時の背景固定にならない例がある。body := 0を使う。geomID/entityIDは save/load で変わり得る。恒久 ID としてハードコードしない。Scene.add*の戻り値は生成直後の操作には使えるが、Scene.myに entity 本体を保存しない。- 物理更新は
postStepとe.dt、UI 更新はupdate、瞬間イベントはonCollide/ laser event へ分ける。 - 古い forum 例には版依存・既知バグ・exploit が混ざる。実装後の capture と短い再生試験が必須。
1. 安全な生成コード
Scene.addBox, Scene.addCircle 等の引数は必要な属性だけに絞る。
Scene.addBox({
pos := [0, 1];
size := [2, 0.5];
density := 2.0;
friction := 0.8;
restitution := 0.0;
collideSet := 1
})
レコード内で = を使うと、文脈によっては生成元の値を変更してしまう。コピーした .phn / .phz
には body, entityID, geomID や派生・read-only 属性が含まれ、誤結合や
Bad body not in scene の原因になる。
根拠: Scene.add* の属性と :=、
不要な body/ID を除く例。
2. 車・ヒンジ・固定
ヒンジ
ヒンジには両 geometry の ID と、少なくとも接続位置が必要。geom0pos / geom1pos は各
geometry のローカル座標で、[0,0] は重心である。
Scene.addHinge({
geom0 := chassisGeom;
geom1 := wheelGeom;
geom0pos := [-1.5, -0.5];
geom1pos := [0, 0];
size := 0.12;
legacyMode := 0
})
motorized wheel なら次を追加する。
motor := true;
motorSpeed := -12.0; // rad/s
motorTorque := 250.0 // Algodoo 1.7.5+ では Nm
ID だけで接続位置を省略する旧コードはクラッシュ原因になった。motorSpeed は RPM ではなく
rad/s。旧版では motor strength / torque の意味が違うため、古い scene をそのまま定数化しない。
根拠: hinge/fixate/spring の生成、
接続位置省略の危険、
motor 単位。
背景固定
Scene.addBox({
pos := [0, -1];
size := [8, 0.5];
body := 0;
glued := true
})
glued := true だけでは生成物が固定されなかった一方、body := 0 は背景 body へ所属させる。
static := true という geometry 属性の実働例はコーパスから確認できなかった。density := +inf
は固定に近い挙動を作るだけで、solver や慣性への副作用がある。
車輪の安定性
- collision 形状は真円を使い、装飾 rim は非衝突・低密度で分離する。
- hinge はまず motor なしで接続だけを試験し、その後低速・低 torque から上げる。
- wobble は
legacyMode := 0、適切な質量、必要ならSim.frequency増加の順で切り分ける。 materialVelocityは物体速度ではなく conveyor の接触面速度。velの代用ではない。
3. ID と entity 参照
geomID / entityID は生成処理内の結線には使われてきたが、save/load 後も同じとは限らない。
明示 ID を恒久ハンドルとして扱わない。
永続部品は onSpawn で現在の ID を再登録し、使用時に解決する。
// controller object
onSpawn := (e)=>{
Scene.my.controllerID = entityID
};
// client event
controller := Scene.entityByID(Scene.my.controllerID);
controller._tick(e.dt);
controller = ""
Scene.add* の戻り値は、同じ実行内で直後に属性を変える用途に限定する。
wheel := Scene.addCircle({pos := [0, 1]; radius := 0.4});
wheel.color = [0.1, 0.1, 0.1, 1];
wheel._createdAt := Sim.time;
wheel = ""
entity/ClassObject 自体を Scene.my に保存すると、save/load 後の参照切れ、serialize 膨張、
root/config 汚染、場合によっては crash が報告されている。Scene.my へ置くのはプリミティブ、
配列、毎回再取得する数値 ID にする。
根拠: ID が reload で変わる、
Scene.add* 戻り値の危険、
ID-only の疑似クラス。
4. 言語と状態管理
:=は新しい識別子の宣言、=は既存値の更新。- 関数は
(a,b)=>{...}、遅延評価される action/procedure は{...}。 - 条件は
condition ? {trueCode} : {falseCode}。両 branch を書く。 - 配列は
a(i)で参照し、++で連結する。要素更新は slice を組み合わせる例が多い。 {A; B; C}の戻り値は最後のC。末尾へ文字列を「コメント」として置くと戻り値を壊す。- scene 全体の共有値・関数だけ
Scene.my、部品固有状態は_counterのような custom object
variable に置く。
Scene.my は scene と一緒に保存されるが、phunlet/import の古い挙動には差がある。再利用部品は
onSpawn を冪等にし、未定義のときだけ初期化する。
5. イベントの使い分け
| 用途 | イベント | 注意 |
|---|---|---|
| 衝突の瞬間 | onCollide |
e.this, e.other, e.pos, e.normal |
| レーザー側 | onLaserHit |
対象は e.geom、レーザーは e.laser |
| 受光側 | onHitByLaser |
laser event は多重発火し得る |
| 初期化/再登録 | onSpawn |
undo/clone/import を考え冪等にする |
| 消滅時 | onDie |
TTL を簡易遅延に使えるが大量生成しない |
| 物理 step | postStep |
e.dt を掛け、frequency 依存を避ける |
| 描画/UI frame | update |
停止中も走る。物理更新には使わない |
| 入力 | onKey, Keys.isDown |
Keys.bind は config を汚すため配布 scene で使わない |
レーザーや高頻度衝突の重い処理は _nextRun と Sim.time で 0.05~0.2 秒ほど間引く。
イベント順序に依存するロジックは避け、共有状態は next-state へ書いて次 step で反映する。
公式の version 境界:
- Algodoo 2.0 RC5:
readable,owner - Algodoo 2.0.1: custom object variables、
Scene.entityByID/entityByGeomID - Algodoo 2.0.2 beta:
pos,angle,vel,angvelの scriptable 化、entitykeyword - Algodoo 2.1.0:
onClick,onKey,onSpawn,onDie,postStep,update
6. デバッグと性能
- geometry が生成されたか capture する。
- ID と hinge/fixjoint の存在、両接続先、接続位置を確認する。
- motor なしで短く再生し、接続だけを見る。
- motor を低い値で有効化し、再度短く再生する。
- Console と Logfile の error を確認する。
イベントの中身は "" + e で文字列化できる。Console.print("stage=" + value)、Console の
TAB 補完、最小コードへの切り分けを使う。自己参照する reactive property、onSpawn 内で同じ
onSpawn を持つ entity の即時生成、無制限 spawn、深い再帰は Algodoo 全体を落とし得る。
反復は 1 step に詰め込まず分割する。readable(...) / entity lookup は event 内で一度だけ行い、
ローカル変数へキャッシュする。生成 projectile には timeToLive / killer / remove の終了経路を持たせる。