[forum知見] Thyme / Scene API 調査

medaka 返信: 0件 2ヶ月前

Algodoo Forum http://www.algodoo.com/forum/ の会話ログから、codex (GPT-5.6 Sol) に抜粋させた
確度は次の意味で付けた。

  • 確認済み: 複数投稿で一致するか、公式の変更履歴・保守担当者の説明と一致する。
  • 有力: 実例はあるが、単一投稿・古い版・再現条件が限定される。
  • 要検証: 投稿間で矛盾する、または保存/再読込・版依存が強い。

重要な結論は、(a) := は生成レコード内の初期値を対象オブジェクトへ入れる、(b) Scene.add* の戻り値は生成直後の配線には便利だが保存可能な恒久参照にしない、(c) entityID/geomID は生成時の一時的な結線値と、再読込後に使うランタイム値を分ける、の三点である。

1. Thyme の基本言語

1.1 宣言、代入、型

Tutorial は := を識別子の宣言、= を既存値の代入・更新として説明する。同じ名前を再度 := するとエラーになるため、初回宣言だけ :=、リセットは = とする(確認済み)。

Scene.my.NumberOfApplesNeeded := 3;
Scene.my.NumberOfApples = 0;

出典: topic-411, post 3257。同投稿は構造(Scene, Sim, App, Keys)で名前空間を分け、文はセミコロンで区切ると説明する。

型は実行時に推論され、明示的な byte/short 等はない。代表例は integer/float/boolean/string/procedure/function/list/object で、異なる型を同じ変数へ代入でき、リストも異種要素を含められる(確認済みだが、型の厳密な内部仕様は要検証)。

integer {a := 1}
float {a := 1.0}
boolean {a := true}
string {a := "1"}
procedure {a := {1+1}}
function {a := (x) => {1+x}}
list {a := [1,"1",true,{1}]}
object {a := scene.addBox({pos := [0,0]})}

出典: topic-11323, post 82061。同投稿で文字列と数の連結 ("a"+1) が "a1" になる例もある。

1.2 リスト、関数、アクション、条件

リストは整数添字でアクセスし、ネストできる。++ はリスト連結として使われる。関数は (引数)=>{コード}、アクション/手続きは {コード} と書く(確認済み)。

Scene.my.ShoppingList := ["Bread","Milk","Apples"];
Scene.my.AddValueOfApple := (dm)=>{
  Scene.my.TotalValueOfApples = Scene.my.TotalValueOfApples + dm * 1.50
};
Scene.my.AddValueOfApple(3.2);

出典: topic-411, post 3257。条件式は三項の形で真偽両方のブロックを要求する。

condition ? {trueCode} : {falseCode}

出典: topic-3976, post 45134(確認済み)。for/関数・infix、配列アクションの例は topic-411, post 3257 にまとまっている。

1.3 イベントとスコープ

衝突などのイベントは (e)=>{...}。e.this はイベント元、e.other は相手、e.pos は接触点、e.normal は法線という説明が複数投稿で一致する(確認済み)。レーザー系は e.laser/e.geom、クリックは e.clickCount/e.handled、キーは e.pressed/e.keyCode などが報告される。

onCollide := (e)=>{ e.other.vel = [1,0] }

出典: topic-411, posts 62394/71401、topic-12168, post 86342。e.this が必要なコードブロックには (e) 引数を明示する(topic-11340, post 82313、確認済み)。

Scene.my.* はシーンに保存されるグローバル/通信領域、通常の識別子やオブジェクト内の _name はローカル状態に使う。scene.my の下へオブジェクトを保存するとルートへ展開され、保存ファイルを壊す可能性があるため、IDだけを保存する(後述)。出典: topic-411, post 71401、topic-11340, posts 82321/84137、topic-12154, post 87159(確認済み)。

2. Scene.add*、生成レコード、戻り値

2.1 生成レコードの書き方

Scene.addBox, addCircle, addPolygon, addHinge, addFixjoint, addThruster, addSpring, addLaserPen, addPen, addGroup などが使える(確認済み)。API 一覧と cloneEntityTo の例は topic-11845, post 84931。

生成引数はオブジェクトレコードで渡す。レコード内の初期値には := を使うのが定石で、= だとスポナー側の変数を代入する文脈になり得る。

Scene.addBox({
  pos := [0,1]; size := [2,.5];
  onCollide := (e)=>{ e.other.vel = [1,0] }
})

出典: topic-411, posts 71401/73275。古い投稿には = の例も残るため、版による許容差は要検証だが、生成スクリプトでは := に統一するのが安全である(確認済みの実務上の定石)。

2.2 生成直後の戻り値

Scene.add* は生成オブジェクトへの参照を返す例がある。

object := Scene.addCircle({pos := [0,0]; radius := .5});
object.spawnTime := Sim.time;
object.color = [1,0,0,1];

出典: topic-1322, post 26885。1 行でプロパティを変更する例もある。

(scene.addLaserPen({pos := app.mousepos})).color = {
  [.5 + sin(Sim.time*10)/2, 0, 0, 1]
}

出典: topic-1322, post 14077。scene.my.box = scene.addBox({...})、衝突相手を scene.my.box = e.other として後から更新する実例もある(topic-9033, posts 66290/66505、有力)。

ただし、この参照を Scene.my に保存して恒久ハンドルにするのは危険である。保存/再読込後に参照が消える、メソッドが失敗する、ルート展開でファイルを破損するという報告が繰り返され、保守担当者も “return value of Scene.add* is a dangerous business” と注意している。

出典: topic-3285, posts 37424/50844、topic-1322, post 26885、topic-11340, posts 82321/84137。

使い分け: 戻り値は「同一スクリプトの次の行で色・速度・カスタム _ 変数を設定する」ための生成時参照に限定し、Scene.my には entityID/geomID など数値を保存する。

2.3 readable、owner、entity

readable(x) はイベントの一時属性を読める形にし、readable(owner) は属性ブロックの所有元を参照するために使う。Algodoo 2.0 RC5 の変更履歴で readable/owner が導入された(topic-4993, post 53404、確認済み)。2.0.2 系では entity キーワードが owner を置き換えるとの記録もある(topic-5834, post 57796、版依存)。

id := (readable(probe)).geom;

実オブジェクトの geom を得る小さなプローブ例:

scene.my.geomIdByPos = (gpos) => {
  probe := scene.addPen({pos := gpos});
  id := (readable(probe)).geom;
  scene.removeEntity(probe);
  id
}

出典: topic-10999, post 78667(有力。removeEntity の寿命/リークは別節の矛盾に注意)。

同一オブジェクトのスクリプトから対象ジオメトリへ戻る例:

es := (Scene.entityByGeomID((readable(owner)).geom))._es
postStep := (e)=>{
  _es = math.vec.len((Scene.entityByGeomID((readable(e.this)).geom)).vel)
}

出典: topic-11340, posts 82305/82307。onSpawn で子オブジェクトの参照を付ける例もあるが、参照をシリアライズしないためプロパティ名に _ を付ける/Scene.my に上げないという注意がある(topic-11340, post 82313、確認済み)。

3. ID と検索・再読込

3.1 entityID、geomID、body

  • entityID: ヒンジ等の非衝突エンティティも含め、Scene.entityByID のキーになる。onSpawn=(e)=>{scene.my.thrusterID = e.this.entityID} のように実行時に捕捉できる(topic-10590, posts 72760/72764、確認済み)。
  • geomID: 衝突可能な形状のキー。Scene.entityByGeomID で形状を得る。ヒンジは geom0, geom1 を保持し、レーザー/スラスタ等の単一取り付けは geom を使う(topic-9564, posts 67451/68917、topic-10089, posts 68888/68906、確認済み)。
  • body: 剛体グループ。body := 0 は背景に固定する静的形状、同じ body 値は同一剛体になる。古い別名・設定に density := +inf がある(topic-1010, posts 9951/9970/9995、topic-1141, posts 11673/11705、有力/版依存)。

Scene.entityByID/Scene.entityByGeomID は 2.0.1 beta で強化された API として記録される(topic-5108, post 54420、確認済み)。EntityByID は全エンティティ、EntityByGeomID は衝突ジオメトリのみという整理は topic-12674, post 87582(確認済み)。

3.2 生成時 ID と恒久 ID は別物

同一スクリプト内で「固定値を生成レコードへ入れ、後続のヒンジが参照する」例はある。

Scene.addBox({geomID := 10; pos := [0,1]; size := [2,.5]});
Scene.addCircle({geomID := 11; pos := [0,2]; radius := .5});
Scene.addHinge({geom0 := 10; geom1 := 11;
                geom0pos := [0,.5]; geom1pos := [0,-.5]})

同じ定石を多段ループで使った実例(ID 生成、2 形状生成、続けて addHinge)は topic-10066, post 68817(確認済み: そのスクリプト内の配線)。矩形2個+ヒンジのコードは topic-11251, posts 81061/81063、Fixjoint の相対アンカー説明は topic-11267, posts 81378/81386 にある。

ただし、固定 entityID/geomID を指定しても entityByID が見つけられない、保存後に ID が変わる、という反証がある。

したがって、生成時の一時配線では明示 ID が実機で有効な版だけ使い、恒久参照では各オブジェクトの onSpawn から新しい実 ID を Scene.my に数値で再登録し、必要時に Scene.entityByID/Scene.entityByGeomID で解決する。確度は「同一実行中の明示 ID 配線=確認済み(例限定)」「保存をまたぐ固定 ID=要検証/推奨しない」。

3.3 onSpawn による再登録パターン

最小形は次の通り。

Scene.addThruster({
  onSpawn := (e)=>{ scene.my.thrusterID = e.this.entityID }
});

// 別オブジェクトのイベントなど
onCollide := (e)=>{
  t := Scene.entityByID(scene.my.thrusterID);
  t.force = 10
}

出典: topic-10590, posts 72760/72764。保存後は scene.my の数値だけが残り、開き直し時に各 onSpawn が現在の ID を上書きする。単なる scene.my.entityExists の三項チェックについては「未定義なら false」と「オブジェクトは真偽値ではない」が衝突しているため、エラーを出さない存在判定 API としては採用しない(topic-11334, posts 82191-82209、要検証)。

3.4 高度な疑似クラス(保存安全性に注意)

Algodoo には通常のユーザー定義 class はない。alloc と -> で一時オブジェクト/Map のような構造を作れるが、終了/再読込で普通のリストへ潰れる報告がある。

tmp := alloc;
tmp.root := 1;
tmp.branch := 2;

出典: topic-12154, posts 86390-86392(要検証)。実用的な疑似クラスは、非表示・非衝突・背景固定の Box をクラス本体にし、onSpawn で毎回実 ID を登録する方法である。

// クラス本体(Box)の onSpawn
onSpawn = (e)=>{ scene.my.myClass = entityID };

// 利用側。scene.my には数値だけ保存
myClass := Scene.entityByID(scene.my.myClass);
myClass._init();

scene.my へエンティティそのものを保存すると属性がルートへ展開され、シーンを壊す。entityID を保存し onSpawn で再登録する、という定石は topic-12154, posts 87159/87160 に詳しい(高度・要実機検証)。_ 接頭辞の custom var/method は 2.0.1 でシリアライズ対象になったという変更履歴がある topic-5108, post 54420 が、オブジェクト参照そのものを保存しない点は変わらない。

4. ヒンジ/Fixjoint と複数オブジェクトの安全な配線

4.1 アンカー座標

addHinge/addFixjoint の geom0/geom1 は形状の geom ID、geom0pos/geom1pos は各形状中心からのローカル座標。[0,0] は中心で、world0pos を使う旧形式もある(topic-795, posts 8014/45569、確認済み)。単一形状へ付くレーザー・スラスタ等は geom フィールドであり、geom0 と混同しない(topic-10089, posts 68888/68906)。

4.2 同一スクリプトで作ってすぐ結ぶ定石

保存をまたがない生成トランザクションなら、ID カウンタを一度だけ進め、全形状を作り、その直後にヒンジを作る。

build := () => {
  g0 := 2000; g1 := 2001;
  Scene.addBox({geomID := g0; pos := [0,1]; size := [3,.5]});
  Scene.addCircle({geomID := g1; pos := [-1.1,.3]; radius := .55});
  Scene.addHinge({geom0 := g0; geom1 := g1;
                  geom0pos := [-1.1,-.25]; geom1pos := [0,0];
                  size := .2; legacyMode := 0})
};
build()

これは topic-10066, post 68817 の多段生成例と、topic-11251, posts 81061/81063 の Box2個+Hinge例を一般化したもの(同一実行中の配線は確認済み)。レコード内の値を := にし、ヒンジは形状生成後に置く。geom0pos/geom1pos の符号と順序は形状の中心・回転に依存するので、まず [0,0] で作成して目視し、次にローカルアンカーを調整する。

4.3 作成後・保存後の再接続

生成スクリプトで使った g0/g1 を恒久 ID とみなさない。各形状の onSpawn で現在の entityID、必要なら (readable(e.this)).geom を Scene.my の数値に登録し、ヒンジ側/制御側で Scene.entityByID または Scene.entityByGeomID を呼ぶ。起動直後に参照が未登録なら、ヒンジ生成を 1 tick 遅らせる/undefined を扱う初期化を置く(遅延手法は投稿で統一されていないため要検証)。

// Box A
onSpawn := (e)=>{
  scene.my.aEntityID = e.this.entityID;
  scene.my.aGeomID = (readable(e.this)).geom
};

// Box B も同様。制御側
a := Scene.entityByID(scene.my.aEntityID);
b := Scene.entityByGeomID(scene.my.bGeomID);

readable(e.this) の geom 取得は topic-10999, post 78667、topic-11340, posts 82305/82313 にある。ヒンジ自身の端点は readable(owner).geom0/geom1 を経由して形状へ戻せる(topic-9564, posts 67451/68917)。

4.4 剛体固定と既存物への接続

静的台座は生成レコードに body := 0(または古い版で density := +inf)を入れる。既存物同士を後から body/constraint で結ぶと Algodoo 2.1 系の既知バグ報告があるため、可能なら同一スクリプトで形状とヒンジを作る(topic-10705, posts 73794/73798、要検証)。

5. 検索、選択、グループ、複製、削除

  • Scene.addGroup({name := "selected"; entityIDs := [...]}) で選択グループ、"tracked" でカメラ追跡を設定できる(topic-10705, posts 73684/73710、topic-1295, posts 13656/18657、有力)。
  • 既存選択の entity ID を scene.my.stuff = scene.my.stuff ++ [entityID] で収集する例がある。スクリプトメニューでは順序がランダム、onSpawn では古い順になるという観察差がある(topic-11197, posts 80773/80778、要検証)。
  • Scene.cloneEntityTo(Scene.entityByID(37), [10,10]) は API 一覧にある(topic-11845, post 84931)。clone 後の ID は新しく振られるので、返り値を保存せず onSpawn で再登録する。
  • scene.removeEntity(entity) は削除 API として報告される(topic-10598, post 72922)。density=0/timeToLive=0/衝突時 remove のリーク挙動は投稿間で衝突しているため、MCP スキルでは短いテストシーンで確認してから使う(topic-11345, posts 82366/82369/82399、要検証)。

6. 版の節目

版・出典 変更/影響
2.0 RC5 — topic-4993, post 53404 readable と owner 導入。
2.0.1 beta — topic-5108, post 54420 Scene.entityByID/entityByGeomID 強化、readable、custom _ vars。
2.0.2 beta — topic-5834, post 57796 pos/angle/vel/angvel の scriptable 化、entity キーワード、readable 更新。
2.0.2 の実例 — topic-9564, posts 67451/68917 entityByGeomID と readable(...).geom の実用例。ただし predetermined geomID の挙動に注意。
2.1.x の報告 — topic-10705, posts 73794/73798 既存オブジェクト間の body/constraint 接続に既知バグ報告。

7. MCP/スキル実装に落とす際の保守的な規則

  1. 生成レコードは :=、生成後の更新は =。 特に onCollide, onSpawn などのイベント関数をレコード内で初期化する。
  2. 同一トランザクションで結ぶ。 形状を作成してから、その実行中だけ有効な geom ID でヒンジ/Fixjoint を追加する。形状と constraint の順番・アンカーを目視検証する。
  3. Scene.add* の戻り値は一時ポインタ。 直後の color, vel, _custom 設定には使えるが Scene.my へ保存しない。
  4. 恒久参照は数値 ID + onSpawn。 各オブジェクトが開き直し時に entityID/geomID を Scene.my へ再登録し、利用側で entityByID/entityByGeomID を解決する。
  5. ID 強制を前提にしない。 明示 geomID の同一実行配線は実例があるが、保存/再読込後の値や entityByID 検索は投稿間で不一致。MCP の run テスト後に onSpawn 再捕捉を必ず行う。
  6. オブジェクト参照を scene.my に置かない。 ルート展開・破損・ハングの報告が複数ある。ローカルの _name か数値 ID を使う。
  7. 削除・再接続は狭い範囲で。 Scene.Clear は破壊的 API なのでユーザー許可なしに使わない。removeEntity や既存物への hinge/body 付け替えは短い再生→観察テストを挟む。

8. 未解決点・矛盾

  • 明示 entityID/geomID を Scene.add* へ与えられるというコードと、「特定 ID を強制できない」という後年投稿が併存する。版・保存形式差の可能性があるため、恒久 ID の仕様とは扱わない。
  • Scene.entityByGeomID(id) の戻り値を条件式で存在判定できるか、エラーを出さずに undefined を得られるかで意見が割れる(topic-11334, posts 82191-82209)。MCP スキルの一般手順に入れない。
  • geom0pos/geom1pos のどちらが「第一/第二」形状かの説明に古い投稿の文言揺れがある。実際の geom0/geom1 と同じ座標を [0,0] で結んでから画面で確認する。
  • removeEntity、density=0、timeToLive=0 のリーク/再利用は投稿間で逆の報告がある(topic-11345)。寿命管理は実機テストを必須とする。
  • alloc/-> の疑似クラスは閉じると縮退するとの報告があり、通常のスキルの基本手順へは含めず「高度・要実機検証」に留める。

ログインしてください(返信一覧はログイン後に表示されます。)