共有パッケージ
これらのパッケージは、自分のコードからインポートできます。ここでは、どこで何が使えるかと、各パッケージでできることを説明します。
コードでは、エディターに表示されるオブジェクトをエンティティと呼びます。どちらも同じものです。
インポートできるパッケージは、コードが動く場所によって決まります。
| パッケージ | できること | スクリプト | プラグイン | コンポーネント拡張 |
|---|---|---|---|---|
@was/ecs | エンティティ、コンポーネント、ワールド | ✓ | ✓ | ✓ |
@was/engine | コンポーネントのクラス | ✓ | ✓ | ✓ |
@was/signals | リアクティブな値 | — | ✓ | ✓ |
@was/svdt | スキーマと検証 | ✓ | ✓ | — |
@was/utils | 小さなヘルパー | ✓ | ✓ | — |
@was/ui | エディター自身のコンポーネントライブラリ | — | ✓ | ✓ |
@was/icons | エディターのアイコンセット | — | ✓ | ✓ |
react | React 19 | — | ✓ | ✓ |
ctx.effect を使いますスクリプトから @was/signals を直接インポートすることはできません。代わりに ctx.effect を使ってください。使い方は同じで、オブジェクトがなくなると止まるので、動き続けるものが残りません。
@was/signals:リアクティブな値
シグナルは、誰に読まれているかを知っている値です。値が変わると、それに依存しているものだけが更新されます。
import { signal, computed, effect, batch, untracked } from '@was/signals';
const score = signal(0);
const doubled = computed(() => score.value * 2);
const stop = effect(() => {
render(score.value); // score が変わったときだけ再実行される
});
batch(() => {
score.value += 1;
score.value += 1; // effect の実行は 2 回ではなく 1 回
});
stop();
| 関数 | 役割 |
|---|---|
signal(v) | 監視できる値。.value で読み書きします |
computed(fn) | 派生した値。読んでいるものが変わったときだけ再計算されます |
effect(fn) | すぐに実行され、読んだものが変わるたびに再実行されます。停止用の関数を返します |
batch(fn) | 書き込みをまとめ、監視側の実行を最後の 1 回にします |
untracked(fn) | 依存関係をつくらずに値を読みます |
ref(obj) | オブジェクト全体を、入れ子の値の奥までリアクティブにします |
snapshot(obj) | リアクティブではない、ただのコピー |
raw(obj) | 追跡なしの元のオブジェクト |
readonly(obj) | 書き込みできないビュー |
React のパネル向けには、useSignal、useComputed、useSignalEffect、useLiveSignal を提供する関連パッケージがあります。これを使うと、余計な配線なしに、シグナルの変化でコンポーネントが再レンダリングされます。
@was/ui:エディターのコンポーネント
プラグインのパネルやコンポーネント拡張は、エディターと同じコンポーネントでつくりましょう。そうすれば、パネルが埋め込まれたウェブページではなく、エディターの一部のように見えます。ライブラリには次のコンポーネントがあります。
Accordion · Avatar · Button · Card · Checkbox · Chip · CloseButton
DimensionInput · Draggable · Dropdown · Flag · Icon · IconButton · Input
Menu · Modal · Outside · Panel · Popover · Portal · Render · Scroll
Search · Section · Segmented · Select · Skeleton · Switcher · Tabs
Toast · Toolbar · Tooltip
アイコンには @was/icons を使います。
パネルで使えるのは、ライブラリに含まれているユーティリティクラスだけです。text-[13px] のような任意の値は含まれていません。スケールにないサイズには、インラインの style を使ってください。
@was/ecs:エンティティとコンポーネント
このパッケージには Entity、Component、Space があります。プラグインは、機能を絞った別の API ではなく、完全な API でシーンを読み書きできます。
Space:ワールド
| 呼び出し | 返すもの |
|---|---|
getEntity(id) | 1 つのエンティティ。なければ何も返しません |
hasEntity(id) | 存在するかどうか |
getEntities() | すべてのエンティティ |
queryEntities(A, B, …) | 指定したコンポーネントをすべて持つエンティティ全部 |
makeQueryEntities(A, B, …) | 同じクエリを、繰り返し使えるよう一度だけつくったもの |
createEntity(id?, components?) | ワールドに追加された新しいエンティティ |
addEntity(…e) · removeEntity(…e) | エンティティをワールドに追加する、または取り除く |
getSystem(S) · hasSystem(S) | システム、またはそれが存在するかどうか |
execute() | 1 フレームを実行します |
queryEntities を使いましょうこれを使うと、「ライトとトランスフォームを持つものすべて」のような問い合わせも速く返ります。getEntities() はワールド全体を返すので、自分で絞り込む必要があります。
Entity:ワールド内の 1 つのオブジェクト
| 呼び出し | 役割 |
|---|---|
getComponent(Type) | コンポーネントを 1 つ返します。なければ null |
getComponents(A, B) | 複数のコンポーネントを、指定した順にまとめて返します |
hasComponent(A, B) | すべてを持っているかどうかを確認します |
addComponent(…c) | コンポーネントを追加します。すでにある種類は無視されます |
removeComponent(…c) | クラスかインスタンスを指定して、コンポーネントを外します |
component(fn) | 今ある、そしてこれから追加されるすべてのコンポーネントに fn を実行します |
clone() · clean() | エンティティをコピーする、またはコンポーネントをすべて外す |
.id · .components | ID と、種類ごとのコンポーネント |
Component:データ
| 呼び出し | 役割 |
|---|---|
.x または get('x') | フィールドを読みます。effect の中では、そのフィールドを購読もします |
$data | コンポーネント全体を、ただのオブジェクトとして返します |
$rawData | 購読せずに、保存されているオブジェクトを返します。読み取り専用です |
update({ … }) | データを書き込みます。書き込む方法はこれだけです。 |
updateAt(path, value) | 大きなコンポーネントの奥深くに、全体を検証し直さずに書き込みます |
version(field?) | 何かが変わると増えるカウンターです。フィールドを読まずに依存できます |
reset(data?) | 初期値に戻します |
clone() | コピーをつくります |
version() を使いましょう大きな値を読むと、その中のすべての要素を購読することになります。代わりにバージョンのカウンターに依存すれば、すべての要素を監視しなくても、変わったことがわかります。2 万件のリストを持つパネルでも速さを保てるのは、このしくみのおかげです。
@was/engine:コンポーネントのクラス
コンポーネントの種類はそれぞれクラスになっています。TransformComponent、MaterialComponent、RigidBodyComponent などです。クラスをインポートして、getComponent、hasComponent、query に渡します。
すべてのフィールドを含む一覧は、コンポーネントリファレンスを参照してください。
@was/svdt:スキーマ
このパッケージで、データの形を記述し、データを確認できます。
import { s, compile } from '@was/svdt';
const schema = s.object({ speed: s.f64(1), name: s.string('') });
const codec = compile(schema); // コンパイルは宣言する場所で一度だけ。呼び出しのたびには行わない
const value = codec.parse(input);
形を記述する
- 数値:
s.f32 s.f64 s.i8 s.u8 s.i16 s.u16 s.i32 s.u32。それぞれ初期値を指定します。 - スカラー:
s.bool、s.string、s.color、s.literal、s.enum、s.unknown、s.ref。 - ベクトル:
s.vec2、s.vec3、s.mat4。 - 複合型:
s.object、s.variant、s.array、s.record、s.union、s.preprocess、s.lazy。
どれにも、.default(v)、.optional()、.nullable()、.min(n)、.max(n)、.int() をつなげて使えます。
コンパイルしたコーデックを使う
| 呼び出し | 役割 |
|---|---|
parse(input) | 検証して正規化します。入力が不正なら例外を投げます |
safeParse(input) | 同じ処理ですが、例外を投げずに成功かエラーを返します |
parseAt(path, v) | ほかには触れずに、1 つのフィールドだけを検証します |
equals(a, b) | 深い比較 |
diff(a, b) | 変わった部分を返します |
apply(target, p) | 差分を適用します |
invert(p) | 差分を逆にします |
pack · unpack | コンパクトなバイナリ形式との相互変換 |
データではなくスキーマそのものを調べるには、introspect、keys、requiredKeys を使います。
@was/utils
小さなヘルパーです。pick、omit、assign、keys、values、entries、capitalize、basename、extname、dispose(後片付けの関数を 1 つにまとめます)、ファイルの種類を確認する MIME タイプのヘルパーがあります。
次へ: 用語集 — このドキュメント全体で使う用語。