【Meta Horizon】デフォルトスクリプトとローカルスクリプトの違いと所有権の管理
Meta Horizon WorldsのDesktop Editorを用いた開発において、特に重要な概念である「デフォルトスクリプト(Default)」と「ローカルスクリプト(Local)」の違い、およびそれに関連する所有権の挙動について紹介します。
スクリプトの種類と特徴
Worlds Desktop EditorではTypeScriptを用いて開発を行いますが、スクリプトには実行環境の異なる2つのタイプが存在します。
1. デフォルトスクリプト (Default)
新規にスクリプトを作成すると、標準でこの設定になります。
- 実行環境: 常にサーバーで実行されます。
- 特徴: ワールドに接続している全ユーザーに対して送受信が可能です。
- 注意点: プレイヤーの入力がサーバーを経由して返ってくるため、操作に対して遅延(レイテンシ)が発生します。
2. ローカルスクリプト (Local)
スクリプトの設定画面から「Local」を指定することで利用可能です。
- 実行環境: エンティティの所有者(オーナー)の端末で実行されます。
-
特徴:
- 所有者への反映には遅延がなく、リアルタイムな応答が可能です。
- エンティティの状態を所有者ごとに独立させることができます。
- 所有権の移動(破棄→再生成というプロセスを経る)が可能です。
- 注意点: 各プレイヤーに対して共通して動作させたいロジック(グローバルなゲーム進行管理など)には向きません。
イベント通信の使い分け
スクリプト間のイベント連携(イベントドリブン)には、スクリプトの種類と所有者によって使い分けが必要です。
| 送信元 | 送信先 | 推奨されるイベント | 条件 |
|---|---|---|---|
| Default / Local | Default / Local | LocalEvent | 所有者が同じ場合のみ |
| Default | Local | NetworkEvent | サーバー ⇔ クライアント間通信となるため |
| Local | Default | NetworkEvent | クライアント ⇔ サーバー間通信となるため |
カスタムUIとスクリプトの関係
カスタムUIはどちらのスクリプトタイプでも実装可能ですが、基本的にはローカルスクリプトでの作成を推奨します。
- 理由: カスタムUIで動的な表示・スタイル制御を行う「Binding」機能は、頻繁に使用すると処理遅延の原因になります。
-
影響:
- Localの場合: 自身のスクリプト(自分のUI)にのみ影響します。
- Defaultの場合: サーバー負荷となり、他のデフォルトスクリプトの処理まで遅延させる恐れがあります。
※ 全ユーザーに共通のUIを表示したい場合を除き、Local設定にしましょう。
ハマりポイント:親子関係にあるエンティティの所有権
Meta Horizonの仕様において、親エンティティの所有権を変更しても、子エンティティの所有権は自動的に変更されません。
これが原因で、親エンティティの所有権だけを変更しても、その子として配置されているローカルスクリプト(カスタムUIなど)が表示されない・動作しないという問題が発生します。
❌ うまくいかない例
親エンティティの所有権のみを変更しているため、子エンティティがついてきません。
preStart() {
this.connectCodeBlockEvent(
this.entity,
hz.CodeBlockEvents.OnPlayerEnterWorld,
(player: hz.Player) => {
if (this.props.localEntity) {
// 親だけ変更しても子は変わらない
this.props.localEntity.owner.set(player);
}
}
);
}
⭕ うまくいく例(再帰的な所有権の変更)
子エンティティも含めて、再帰的に所有権を設定する処理を実装します。
static propsDefinition = {
localEntity: { type: hz.PropTypes.Entity },
};
preStart() {
this.connectCodeBlockEvent(
this.entity,
hz.CodeBlockEvents.OnPlayerEnterWorld,
(player: hz.Player) => {
if (this.props.localEntity) {
// 再帰処理を呼び出す
this.setOwnershipRecursive(this.props.localEntity, player);
}
}
);
}
// 再帰的に所有権を変更するヘルパー関数
setOwnershipRecursive(targetEntity: hz.Entity, newOwner: hz.Player) {
// 1. 自分自身の所有権を変更
targetEntity.owner.set(newOwner);
// 2. 子エンティティを取得し、それぞれに対して同じ処理(再帰)を行う
const children = targetEntity.children.get();
children.forEach(child => {
this.setOwnershipRecursive(child, newOwner);
});
}
補足:Asset利用時のヒント
ローカルスクリプトは、ログインしたユーザーとセットでエンティティを利用するケースが多いため、PropTypes.Assetで指定したアセットを spawnAsset で動的に生成し、その生成したエンティティに対して所有権を渡す実装パターンも有効です。



