Web の画像を表示する
リソース [https://example.com/card.png] を
アセット [Card] として登録する
このスプライトにアセット [Card] を表示する
ダウンロードした画像は、ブラウザーのキャッシュにも Card という名前で保存されます。
TurboWarp カスタム拡張機能 · v0.13.0
Asset Manager は、Web 上の画像・音声、プロジェクト内のコスチューム・背景・音、 そして動的なテキストを登録します。表示、再生、アニメーション、キャッシュを、 共通のブロックとアセット名で操作できます。
紙芝居DSL 4.0集約ランタイム
DSL 4.0ランタイムのパレットでAsset Managerの見出しを探し、その直下の ドキュメントボタンを押すと、このガイドが開きます。集約後もブロックの意味と名前は同じで、 ランタイムが拡張機能別の名前空間とアイコンを加えます。
クイックスタート
Asset Manager は TurboWarp のレンダラー、音声機能、プロジェクト内リソースを利用するため、 サンドボックスを無効にして実行する必要があります。
TurboWarp で 拡張機能を追加 → カスタム拡張機能を選び、次の URL を貼り付けます。
https://cdn.jsdelivr.net/npm/@kubohiroya/turbowarp-asset-manager@0.13.0/dist/asset-manager.js
サンドボックスなしで実行を有効にします。
プロジェクトのほかの場所で使う短い名前を付けます。
リソース [https://example.com/hero.png] を
アセット [Hero] として登録する
種類が合う表示・音声・アニメーションの各ブロックで同じ名前を使えます。
このスプライトにアセット [Hero] を表示する
動作の仕組み
登録時に、選んだ名前と素材の種類・場所を記録します。その後のブロックは名前から種類を調べ、 画像、音声、テキストのうち適切な処理へ自動的に振り分けます。
Web URL、プロジェクト内リソース、動的テキストを登録ブロックへ渡します。アセット登録簿が名前と 種類を記憶し、表示・再生・アニメーションのブロックがスプライト、ステージ、音声出力、 Animated Text のいずれかへ振り分けます。
| 素材 | リソース識別子 | 登録されるもの |
|---|---|---|
| Web 上の画像・音声 | https://example.com/asset.png |
ダウンロードしたデータをメモリーと IndexedDB に保存 |
| スプライトのコスチューム | costume:Sprite1:costume1 |
プロジェクト内の既存コスチュームへの参照 |
| ステージの背景 | backdrop:backdrop1 |
プロジェクト内の既存背景への参照 |
| スプライト/ステージの音 | sound:Sprite1:sound1 または sound:@stage:name |
プロジェクト内の既存の音への参照 |
| 動的テキスト | text:Narration |
実行時変数名。表示時に最新の値を読み取る |
| 以前キャッシュした Web 素材 | RESOURCE_ID を空欄にする |
アセット名に対応する IndexedDB のデータ |
URL を指定すると必ず新しいデータを取得し、同名のキャッシュを更新します。 リソース識別子を空欄にすると通信せず、同じ名前のデータを IndexedDB から読み込みます。 名前ごとの世代を確認してから IndexedDB とメモリーを更新するため、先に開始した古い通信が 遅れて完了しても、最後に開始した有効な登録を上書きしません。
URL を指定した場合は、リソースの取得、メディア種別の正規化、最新世代であることの確認、 同名の IndexedDB エントリーへの保存を行い、メモリーへ登録します。空のリソース識別子を 指定した場合は、通信せず同名の IndexedDB エントリーを読み、メモリーへ登録します。
バージョン0.8.0では、compositionホストがコスチューム、背景、音をstructured
locatorで登録できます。前後の空白、.、/、:、
制御文字を含むScratch名をそのまま照合します。structured locatorでは、登録に使う論理名も
既定でliteralに保持します。
await assets.registerProjectAsset({
name: 'Costume.1 / presentation',
locator: {
kind: 'costume',
target: 'Actor / presenter',
name: 'Costume.1 / source'
}
});
音のlocatorでtargetを省略するとStageを選びます。既存の文字列
resourceId形式には互換性があり、従来のtrimとcolon grammarを維持します。
embedded assetで論理名をliteralにする場合はnameMode: 'literal'を指定します。
バージョン 0.6.0 では、app shell や複合拡張機能からブロックを追加せずに使える cache-first の経路を追加しました。これは上記の従来型「アセット名キー」のキャッシュとは別です。 ホストがネットワーク loader と、期待する SHA-256・バイト数・Content-Type を渡します。 IndexedDB の内容が正しければ通信せず返し、なければダウンロード後に検証を通ったデータだけを 保存します。取得元 URL や認証情報は保存しません。
import {
createAssetManagerComposition,
createVerifiedRemoteCacheDatabaseName
} from '@kubohiroya/turbowarp-asset-manager/composition';
const databaseName = createVerifiedRemoteCacheDatabaseName({
id: story.cacheId,
label: story.fileName
});
const assets = createAssetManagerComposition(undefined, {
verifiedRemoteCache: {
cacheIdentity: {id: story.cacheId, label: story.fileName, databaseName}
}
});
const result = await assets.resolveVerifiedRemoteBinary(model, {
load: async ({url}, {signal}) => {
const response = await fetch(url, {signal});
return {
bytes: await response.arrayBuffer(),
contentType: response.headers.get('content-type'),
transferOwnership: true
};
}
});
台本 manifest から安定した台本 ID と可読なファイル名を渡します。Asset Manager は台本ごとの IndexedDB を識別し、取得データを検証してホストへ返します。ホストは画像、音声、モデルとして 実体化し、scene または story の保持期間に従ってメモリー上のリソースを解放します。その際、 永続化された検証済みデータは削除しません。
| 層 | 保持期間・掃除 | ホスト側の責務 |
|---|---|---|
| 返されたバイナリー | 解決後は呼び出し側が所有 | 登録後に参照を外します。transferOwnership: true なら JavaScript 上の全量コピーを 1 回減らせます。 |
| 実体化したメモリー | retention: scene または retention: story |
保持期間が終わったら releaseAsset または releaseAll を呼びます。 |
| IndexedDB の検証済み bytes | TTL、LRU、origin 全体の上限、または明示消去 | 容量表示と消去手段を提供します。メモリー解放だけではこのキャッシュを削除しません。 |
| 台本 catalog と lease | 台本ごとの可読な DB 情報。実行中の DB は保護 | 実行中は lease を更新し、台本停止・session 終了時に解放します。 |
台本 DB は tw-kamishibai-assets-v1--opening-yaml--story-0001 のような名前です。
小さな共通 catalog には、理解可能な識別情報、サイズ、日時、実行時 lease だけを記録し、
アセット本体は入れません。app shell はこれを使って台本別キャッシュを一覧・掃除・削除できます。
有効な lease がある DB は掃除しません。既定の上限は 256 MiB と、ブラウザーが報告する origin 容量の
20% のうち小さい方で、未使用の台本 DB、次に LRU のレコードから削除します。30 日間使われていない
レコードは削除対象です。IndexedDB の失敗や、他の実行中台本が容量を占有して保存できない場合は、
機械判読可能な warning を返しつつ、検証済み bytes 自体はメモリーで利用できます。
app のストレージ管理には listVerifiedRemoteStoryCaches、
pruneVerifiedRemoteStoryCaches、
deleteVerifiedRemoteStoryCache を使います。実行中の所有権には
renewVerifiedRemoteStoryCacheLease と
releaseVerifiedRemoteStoryCacheLease を使います。DSL の
retention: scene と retention: story はホスト側のメモリー保持方針です。
Asset Manager は YAML を解釈せず、これらを IndexedDB の TTL として扱いません。
バージョン 0.7.0 では、Teachable Machine Pose modelのようなself-contained assetを扱う block非公開のbinary bundle storeを追加しました。composition subpathをimportし、台本専用DBを 指定した上で、台本/source namespace、asset名、manifestのbundle integrityをすべての操作へ 結び付けます。
const assets = createAssetManagerComposition(undefined, {
binaryBundleStore: {
databaseName: `${story.cacheDatabaseName}--bundles-v1`
}
});
await assets.putBinaryBundle({
namespace: `${story.id}/${story.sourceIntegrity}`,
name: 'RescuePose',
integrity: pose.bundleIntegrity,
files: [
{path: 'model.json', size: model.size, integrity: model.integrity, bytes: model.bytes},
{path: 'metadata.json', size: metadata.size, integrity: metadata.integrity, bytes: metadata.bytes},
{path: 'weights.bin', size: weights.size, integrity: weights.integrity, bytes: weights.bytes}
]
});
const stored = await assets.getBinaryBundle({
namespace: `${story.id}/${story.sourceIntegrity}`,
name: 'RescuePose',
integrity: pose.bundleIntegrity
});
ホストが台本単位のkeyとbundle全fileを渡します。Asset Managerは宣言されたsizeとSHA-256を fileごとに検証し、bundleとmetadataを1つのIndexedDB transactionでcommitします。後のreadでも bundle全体を再検証し、missing、partial、corruptなrecordは返しません。
putBinaryBundle は IDBTransaction.oncomplete 後にだけ完了し、入力bytesを
defensive copyします。getBinaryBundle はfile path、size、canonical SHA-256を再検証して
返します。deleteBinaryBundle はdataとmetadataをatomicに削除し、
releaseBinaryStore は永続recordを消さずにstore instanceの処理を中止します。
TTL、LRU、bundle数、byte数の制限は、verified remote cacheやStandalone asset DBとは独立です。
失敗はpayload bytesを含めず、安定した ASSET_BINARY_BUNDLE_* codeで通知します。
バージョン0.10.0では、現在のapplication package内にすでに含まれる大容量binary向けに、 別のcomposition契約を追加しました。上のpersistent bundle storeとは異なり、次回起動時に recordを再利用しません。1つの共有versioned DBを使い、全compound keyへ起動ごとのsession IDを 含め、正常disposeではそのsessionだけを削除します。verified remote cacheは変更しません。
const backing = await assets.createSessionBinaryBacking({
policy: 'prefer',
sessionId: crypto.randomUUID(),
assets: embeddedManifest.assets,
source: embeddedPackageSource,
onFatalError(error) {
stopRuntimeAndShowDiagnostic(error);
}
});
const pose = await backing.get(poseAssetKey);
await backing.dispose();
disabled modeはIndexedDBを開かず元sourceを読みます。preferとrequiredはassetを一件ずつ書き、 commit済みrecordを検証します。preferがdirect modeを選べるのは、確立前のstorage availability failure後にpartial recordを削除できた場合だけです。session mode確立後のread失敗はすべてfatalで、 sourceを途中変更しません。
disabledはIndexedDBを一度も開きません。requiredはbackingを確立できなければ
起動失敗にします。preferがwarning付きdirect readへ移れるのは、確立前にIndexedDBが
unavailable、blocked、quota不足、abortとなり、partial recordを削除できた場合だけです。
descriptor、size、SHA-256の不一致は常にfail closedです。session modeでは全assetのcommitと
read-back成功後にだけsourceを解放します。その後のmissing、corrupt、integrity、abort、connection
closeはfatalとなり、source再読込、暗黙再書込み、mode途中変更は行いません。短いheartbeat leaseと
bounded startup cleanupにより、未期限の別tabを残してcrash sessionを回収します。renderer、audio
decoder、model loaderの失敗はmaterialize診断としてstorage/source errorと分離します。
置換機能は、起動時に固定される 2 つのフラグで段階導入します。どちらも既定値は
false です。利用するプロジェクトでは、拡張機能の読み込み前に次の設定を行います。
globalThis.__TW_ASSET_MANAGER_FEATURE_FLAGS__ = {
ENABLE_LIVE_ASSET_REPLACEMENT: true,
ENABLE_STRICT_ASSET_KIND_REPLACEMENT: true
};
Asset Manager は新しい内容を準備し、登録簿を更新し、同じアセット名と公開種別に結び付いている 対象だけへ表示を適用し直し、成功後に古い所有リソースを解放します。準備や表示の再適用に失敗した 場合は、古い登録、表示、キャッシュを復元します。
画像とテキストはすぐに更新されます。テキストは最新の本文と全スタイルを読み直し、指定された
アニメーションを先頭から開始します。再生中の音声は中断せず、次回再生から新しい登録を使います。
同名の確定処理は直列に実行されるため、開始済みの確定処理は一貫した状態まで完了し、最後に開始された
成功済み登録が残ります。後から開始した無効な試行は、先行する有効な登録を取り消しません。画像では
再適用前に Asset Manager が設定した renderer skin が現在も使われているか確認します。別の衣装変更や
renderer 更新があった対象は古い表示管理から外し、その新しい表示を上書きしません。strict kind を
有効にすると、公開種別 external、costume、
backdrop、sound、text 間の変更と、external の画像・音声間の
変更を拒否します。意図的に種類を変える場合だけ、先に古い登録を削除してください。
0.3.0から更新する場合: 従来の小文字種別トークンと
asset registration error typeを比較しているスクリプトを更新してください。
0.4.0では、以下の大文字診断コードを返します。
利用者向けエラーは [Asset Manager][CODE] で始まり、操作名、asset/resource/actor 名、
修正ヒント、近い候補、元の例外を保持します。登録エラーの Reporter は最新のコードと関連名を返します。
同期失敗は同じ内容を二重ログせず throw し、バックグラウンドの再生・アニメーション失敗だけを
文脈付きで 1 回 console.error します。
| コード | 確認すること |
|---|---|
INVALID_ASSET_NAME | 空でない有効な登録名を使います。 |
ASSET_NOT_REGISTERED | 対象名を登録し、提示された近い候補を確認します。 |
ASSET_TYPE_MISMATCH | 操作が必要とする画像・音声・テキスト種別を使います。 |
ASSET_TYPE_CHANGE | 意図的な公開種別/external媒体種別変更では先に削除します。 |
SPRITE_NOT_FOUND | actor/対象名と描画対象が存在することを確認します。 |
SPRITE_NAME_AMBIGUOUS | actor 対象へ一意な名前を付けます。 |
SOURCE_ASSET_NOT_FOUND | コスチューム、背景、音の名前を確認します。 |
RESOURCE_ID_INVALID | HTTP(S) または対応するプロジェクト内識別子を使います。 |
DEPENDENCY_MISSING | 必要な TurboWarp 拡張またはランタイム機能を読み込みます。 |
STYLE_PROPERTY_INVALID | 対応するテキストスタイルのプロパティを使います。 |
STYLE_VALUE_INVALID | 保存されたテキストスタイル値を修正します。 |
PLAYBACK_FAILED | 音声データとブラウザーの再生許可を確認します。 |
ANIMATION_FAILED | actor、アセット、Animated Text 依存を確認します。 |
REPLACEMENT_FAILED | 読み込み・表示準備を確認します。古い状態は維持されます。 |
ASSETS と DURATIONS が時間軸を表します。画像ならアクターの見た目を変え、
音声なら再生を開始します。長さが 0 のとき、次の素材も同じ時点で開始します。
ASSETS = NoonSkin,Bell,NextSkin、DURATIONS = 0,1,2開始時に NoonSkin を表示します。区切りの長さが 0 なので Bell も同時に再生します。 1 秒後に NextSkin を表示します。ループならさらに 2 秒待って最初へ戻ります。
テキストアセットでは、サンドボックスなしの補助拡張機能を 2 つ使います。 Temporary Variables が本文とスタイルを保持し、Animated Text がスプライトやクローンに描画します。
Narration テキストを登録し、本文と必要なスタイルを設定します。表示ブロックが実行されると、 Asset Manager は Temporary Variables の最新値を読み、すべてのスタイルを適用し直して、 Animated Text に対象スプライトへの表示を依頼します。表示中の本文を更新すると、そのアセットを 表示中のすべての対象が更新されます。
実践的な使用例
リソース [https://example.com/card.png] を
アセット [Card] として登録する
このスプライトにアセット [Card] を表示する
ダウンロードした画像は、ブラウザーのキャッシュにも Card という名前で保存されます。
リソース [costume:Hero:running] を
アセット [HeroRunning] として登録する
このスプライトにアセット [HeroRunning] を表示する
画像は複製されず、プロジェクトが持つ既存コスチュームを参照します。
リソース [sound:@stage:opening] を
アセット [Opening] として登録する
アセット [Opening] を終わるまで再生する
再生直後に次の処理へ進めたい場合は、待たない再生ブロックを使います。
リソース [text:Narration] を
アセット [Narration] として登録する
テキストアセット [Narration] を [むかしむかし…] にする
[Narration] のスタイル [animation] を [typing] にする
このスプライトにアセット [Narration] を表示する
テキストを表示する前に Temporary Variables と Animated Text を読み込んでください。
ブロックガイド
| やりたいこと | 使うブロック | 重要な動作 |
|---|---|---|
| 素材を登録する | register resource … as asset … | URL はキャッシュを更新し、空欄はキャッシュを読みます。 |
| 登録失敗を調べる | asset registration error type/label | 安定した診断コードと関連名を返します。 |
| 登録を確認する | asset … is loaded | どの種類の登録済みアセットにも使えます。 |
| スプライト/クローンに表示する | show asset … on this sprite | 画像とテキストを表示できます。 |
| ステージを変える | set stage backdrop to asset … | 外部画像、コスチューム、背景に使えます。 |
| 音声を再生する | play asset … as sound | スクリプトを待たせるときは「終わるまで」を使います。 |
| アクターを動かす | loop actor … / play actor … once | 画像と音声をひとつのリストに混在できます。 |
| 動的テキストを更新する | set text asset … | そのテキストを表示中の対象も更新されます。 |
| 登録を解放する | delete asset … from memory | プロジェクト所有のコスチュームや音は削除しません。 |
トラブルシューティング
asset registration error type は REPLACEMENT_FAILED を返します。
costume:Hero:running の
ようにすべて指定します。asset registration error type と
asset registration error label で見つからなかった素材を確認できます。コードは
SOURCE_ASSET_NOT_FOUND、素材元のスプライト自体がない場合は
SPRITE_NOT_FOUND です。
ASSET_TYPE_CHANGE は、strict kind により公開種別または external の画像・音声種別の
変更が検出されたことを表します。意図的な変更の場合だけ、既存のメモリー登録を先に削除します。
REPLACEMENT_FAILED の場合は新内容の準備・再適用に失敗しており、古い登録と管理中の
表示が維持されています。