メインコンテンツまでスキップ

共有パッケージ

エディター、ランタイム、そしてあなた自身のコードは、同じパッケージから組み立てられています。 そのうちのいくつかは、あなたも使えます。どれが使えるかは、コードがどこで動くかによります。

パッケージ提供するものscriptプラグインcomponent 拡張
@was/ecsentity、component、world
@was/enginecomponent のクラス群
@was/signalsリアクティビティの仕組み
@was/svdtスキーマとバリデーション
@was/utils小さなヘルパー
@was/uiエディター自身の component ライブラリ
@was/iconsエディターのアイコンセット
reactReact 19
script は ctx.effect を通じてリアクティビティを得ます

script から signals パッケージを直接 import することはできません。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; // エフェクトは 2 回ではなく 1 回だけ走る
});

stop();
関数はたらき
signal(v)監視できる値。.value で読み書きします
computed(fn)派生値。読んでいるものが変わったときだけ再計算されます
effect(fn)いま実行し、読んだものが変わるたびに再実行。停止関数を返します
batch(fn)書き込みをまとめ、監視側を最後に 1 回だけ走らせます
untracked(fn)依存にならずに読みます
ref(obj)オブジェクト全体を、末端までリアクティブにします
snapshot(obj)リアクティブでない素のコピー
raw(obj)追跡なしの、元のオブジェクト
readonly(obj)書き込めないビュー

React のパネル向けには、useSignaluseComputeduseSignalEffectuseLiveSignal を備えた 対になるパッケージがあります。配線なしで、signal から component が再描画されます。

自分では一度も import しなくても、これが効いてくる理由

エディターとランタイムのふるまいを決めているのがこれです。ポーリングは行われず、念のための 再描画も起きません。パネルが更新されるのは、誰かに言われたからではなく、データが変わったからです。 → このエンジンを選ぶ理由

@was/ui — エディターの component

プラグインのパネルと component 拡張は、エディターが使っているのと同じライブラリで作れます。 だから、埋め込みのウェブページではなくネイティブに見えます。30 ほどの component があります。

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 — 世界のモデル

EntityComponentSpace。プラグインは、エンジンが内部で使っているのとまったく同じ API で シーンを読み書きします。別に用意された、機能の劣る「プラグイン API」は存在しません。

Space — 世界

呼び出し返すもの
getEntity(id)1 つの entity、またはなし
hasEntity(id)存在するかどうか
getEntities()すべての entity
queryEntities(A, B, …)それらの component をすべて持つ entity
makeQueryEntities(A, B, …)同じクエリを事前に組み立てたもの。繰り返し使う用
createEntity(id?, components?)世界に追加された新しい entity
addEntity(…e) · removeEntity(…e)入れる、または取り出す
getSystem(S) · hasSystem(S)system に手を伸ばす
execute()1 フレーム実行する
手を伸ばすべきは queryEntities

インデックスに支えられているので、「light と transform を持つものすべて」を尋ねるのは安価です。 getEntities() は同じではありません。世界全体を渡してきて、あなたに絞り込ませます。

Entity — 世界の中のもの

呼び出しはたらき
getComponent(Type)1 つの component、または null
getComponents(A, B)指定した順で、まとめて複数
hasComponent(A, B)それらをすべて持っているか
addComponent(…c)追加する。すでにある型の追加は無視されます
removeComponent(…c)クラスでもインスタンスでも指定して外す
component(fn)すべての component に対し、いまも今後も fn を実行
clone() · clean()複製する、または丸裸にする
.id · .componentsその id と、型をキーにした component

Component — データ

呼び出しはたらき
.x または get('x')フィールドを読む。エフェクトの中ではそれが購読にもなります
$data全体を素のオブジェクトとして
$rawData購読せずに、保存されているオブジェクトを読み取り専用で
update({ … })書き込む唯一の手段
updateAt(path, value)大きな component の奥に、全体を再検証せずに書き込む
version(field?)変更のたびに進むカウンター。フィールドを読まずに依存できます
reset(data?)既定値に戻す
clone()複製
大きなリストの性能を支えているのが version() です

大きな値を読むと、その中の葉すべてを購読することになります。代わりにバージョンのカウンターに 依存すれば、中身を全部見張らずに「変わった」ことだけを知れます。2 万件のリストでもパネルが 生き延びるのは、この仕組みのおかげです。

@was/engine — component のクラス群

すべての component の型がクラスとして用意されています。TransformComponentMaterialComponentRigidBodyComponent など。クラスを import し、getComponenthasComponentquery に渡します。

全項目を含む一覧:コンポーネントのリファレンス

@was/svdt — スキーマ

component を記述しているバリデーション層です。解釈ではなくコンパイルされるので、アニメーションの 経路でパースがコストになりません。

import { s, compile } from '@was/svdt';

const schema = s.object({ speed: s.f64(1), name: s.string('') });
const codec = compile(schema); // 宣言時に 1 回だけコンパイルする。呼び出しごとにしてはいけない
const value = codec.parse(input);

形を記述する

数値: s.f32 s.f64 s.i8 s.u8 s.i16 s.u16 s.i32 s.u32。いずれも既定値を取ります。 スカラー: s.bools.strings.colors.literals.enums.unknowns.ref。 ベクトル: s.vec2s.vec3s.mat4。 複合: s.objects.variants.arrays.records.unions.preprocesss.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)差分を反転する。undo の土台です
pack · unpackコンパクトなバイナリ形式との相互変換

データではなくスキーマ自体を調べるには introspectkeysrequiredKeys があります。

@was/utils

どこでも使う小物です。pickomitassignkeysvaluesentriescapitalizebasenameextnamedispose(後片付け関数をまとめて 1 つにする)、そしてアップロード検証の 裏側にある MIME 関連のヘルパー。


次へ: 用語集