3Dプログラミング講座・その27
不変データ管理と非同期ループの分離設計マルチスレッドテスト




[Core Logic] GLoop による状態遷移

function GLoop(id) {
  // 1. 世界の時間軸を更新(ID:0の責任)
  if (id === 0) {
    data = data.merge({ prop: { time: data.property.prop.time + 1 } });
    // 描画準備
    context.fillStyle = 'rgb(0,0,0)';
    context.fillRect(0,0,600,600);
  }

  // 2. 共通データから現在の状態を読み取る
  const p = data.property.prop;
  const as = p.ArcStates;
  const currentTime = p.time;

  // 3. 自分の座標を計算
  const x = Math.sin(currentTime * p.ArcSpeeds[id] * 2 * Math.PI / 180) * 150 + 300;
  as[id].pos = [x, as[id].pos[1]];

  // 4. 新しい状態を不変データとしてマージ
  data = data.merge({ prop: { ArcStates: as }});

  // 5. 描画と次フレームの予約
  DrawArc(id);
  TMan.timer[id].setalerm(function() { GLoop(id); }, 50);
}

◆ 講座のポイント:命(タイマー)と器(データ)

「WinMainとスレッドの関係を、JSの不変データ管理(ManagedClass)で再構築する。 排他制御(Mutex)に悩まされた日々への、2026年なりの回答です。」

今回のマルチスレッド風テストでは、「タイマー(実行体)」「ManagedClass(データ)」を完全に分離しました。

● なぜタイマーを外に出したのか
ManagedClassは merge のたびに新しいインスタンスを生成します。 もしタイマーをその中に入れると、マージの瞬間に「実行中の関数接続」が切れ、命が止まってしまうからです。
● 高い拡張性の確認
テストの結果、N6LVector のような複雑なクラスでもメソッドを保持したままマージ可能であることが分かりました。 これは将来、ポリゴン人形のような膨大なパラメータ管理にも応用可能です。

◆ ManagedClass 実践リファレンス

1. 宣言と初期化

ManagedClassを使用する際は、まず「デフォルトのプロパティ」を定義し、その後にインスタンスを生成します。

// 1. プロパティの器(テンプレート)を用意
MyManagedClassDefaultProperty = {
    variablename: "CharacterData",
    prop: { hp: 100, position: new N6LVector([0,0,0]) }
};

// 2. インスタンス化
var data = new MyManagedClass();

2. マージ(状態更新)の作法

データを更新する際は merge メソッドを使用します。第一引数に「変更したい差分」を渡し、第二引数(任意)でディープマージの可否を選択します。

// 基本:HPだけを更新(他の値は維持される)
data = data.merge({ prop: { hp: 90 } });

// 応用:ネストされたオブジェクトの特定階層だけを上書き
data = data.merge({ prop: { position: newPos } }, true);

◆ 使用上の重要な注意点

  • メソッドの消失に注意: プロパティに自作クラスを入れる場合、そのクラスに clone() メソッドが実装されていないと、マージの際にプロトタイプが剥がれ、ただの Object になり計算メソッドが消滅します。
  • 参照の共有: merge は「変更のない枝」の参照をそのままコピーします。配列などを直接破壊的に操作(as[id].pos = ...)すると、不変性が崩れる可能性があるため、厳密に扱う場合は一度スライス等でコピーしてからマージすることを推奨します。
  • 代入の必須: data.merge(...) は元の data を書き換えません。必ず data = data.merge(...) と戻り値を再代入してください。

3. データの出力と配列への変換(toArrayIfIndexed)

ManagedClassは不変性を守るため、内部的に配列を「インデックス付きオブジェクト」として保持します。これを標準的な配列として取り出し、forEachなどの配列メソッドを使用したい場合は、toArrayIfIndexedを使用します。

// 内部では {0: "ball", 1: "glove"} という構造
const itemsObj = data.property.profile.items;

// 配列に変換(force=true を推奨)
const itemsArray = toArrayIfIndexed(itemsObj, true);

// 配列操作が可能になる
itemsArray.forEach(item => console.log("所持品:", item));
【開発者のヒント】 不変データの管理において、配列の直接操作(pushやsplice)はバグの温床です。 「使う時だけ配列として取り出す」というこの設計は、データの整合性を100%守り抜くための強力な防壁となります。

【教訓】 命(タイマー)をコピーしてはいけない。器(データ)だけを新しく作り替え続けるのが、モダンな不変データ管理の鉄則です。

◆ 動作ラボ:不変データとマージの検証

以下のフレーム内で、実際に ManagedClass がどのようにデータを複製し、 不変性を保ちながらマージ(成長)していくかをリアルタイムで確認できます。




 ■■■ 3Dプログラミング入門講座 ■■■ 
NAS6LIB
ポリゴンテスト解説・x3dom使い方
その1:ベクトル
その2:行列
その3:クォータニオン
その4:基本総復習・実践
その5:応用その1・メッシュアニメーション、動的テクスチャ
その6:応用その2・GLSL、カスタムシェーダー、キーボード、ファイル
その7:応用その3・ゲームプログラミング、タグの動的追加
その8:応用その4・GLSL、シェーダー、その2
その9:物理演算その1・電卓で相対性理論を解く方法
その10:物理演算その2・相対性理論的ニュートン力学
その11:物理演算その3・ケプラー方程式で惑星軌道シミュレーターを作る

その12:物理演算その4・ルンゲクッタ法で作った 相対性理論的ニュートン力学物理エンジンで惑星軌道シミュレーターを作る

その13:経路探索(A*:A-STAR)&巡回セールスマン問題 : 巨大サイズ : くろにゃんこ

その14:プログラミングにおける配列テーブルテクニック
その15:javascriptのクラス活用法
その16:透視射影公式テスト

その17:ケプラー方程式カプセルライブラリ使用法
その18:CSVファイル処理
その19:物理演算その5・重力多体問題
その20:同次座標について(3D座標系の基本の基本)
その21:おさらいコモンクラスの宣言
その22:物理エンジンライブラリ解説(ケプラー方程式・ルンゲクッタ・相対論的万有引力)
その23:サイト内のリンク先の更新確認がjavascriptで出来るようにする方法
その24:グレゴリオ暦日付計算
その25:シャローコピーディープコピーからマネージドクラスの模倣
その26:マネージドクラステンプレート解説
その27:不変データ(ManagedClass)と非同期ループの分離設計マネージドマルチスレッドテスト


 ■■■ THREE.JSプログラミング講座 ■■■ 
THREE.JSテスト解説・THREE.JS使い方
THREE.JS examplesをいじってみた(フレネル反射透過シェーダー)

THREE.JS (半透明シェーダー)

THREE.JS 3D演算で必要な計算(具体例)★とても重要★
THREE.JS THREE-VRM をいじってみた



<< その26:マネージドクラステンプレート解説 : 工事中 >>




戻る