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

エンティティの検索

コードは、自分で作成していないエンティティを必要とすることがよくあります。プレイヤー、鍵で開くドア、レベル内のすべての敵などです。どのエンティティも、自分自身とその子孫からほかのエンティティを検索できるので、app.rootからシーン全体を検索することも、その下の任意のエンティティからシーンの一部だけを検索することもできます。

検索メソッド​

検索するエンティティ最初に一致したもの一致したものすべて
名前でfindByName('door')find('name', 'door')
パスでfindByPath('house/door')-
タグで-findByTag('enemy')
コンポーネントを持つものfindComponent('light')findComponents('light')
スクリプトを持つものfindScript('health')findScripts('health')
独自の条件を満たすものfindOne(entity => ...)find(entity => ...)

検索は深さ優先です。エンティティの最初の子とそのすべての子孫を調べてから、2番目の子に進みます。検索の対象は、メソッドを呼び出したエンティティ自身とその子孫です。ただしfindByTag()は子孫だけを検索します。最初に一致したものを探すメソッドは、見つからないとnullを返します。例外はfindScript()で、undefinedを返します。一致したものすべてを探すメソッドは、空の配列を返します。

findComponent()とfindComponents()はエンティティではなくコンポーネントを、findScript()とfindScripts()はスクリプトのインスタンスを返します。いずれも、所属するエンティティを指すentityプロパティを持っています。

名前で検索​

const door = app.root.findByName('door');

名前は一意である必要がないので、findByName()は一致するエンティティのうち最初に到達したものを返します。その名前を持つすべてのエンティティを取得するには、find('name', 'door')を使います。エンティティに一意の名前を付けているなら、findByName()が最も簡単な見つけ方です。

パスで検索​

パスは、目的のエンティティに至るまでのエンティティの名前をスラッシュで区切ったもので、検索を始めるエンティティの下から始まります。シーンのほかの場所で名前が重複していても、エンティティを1つに特定できます。

const door = house.findByPath('ground-floor/door');

エンティティのpathプロパティは、階層のルートからのパスです。エディターで作ったシーンでは、すべてのエンティティがRootという名前のシーンのルートエンティティの下にあるので、app.rootからのパスはRootから始まります。

const door = app.root.findByPath('Root/house/ground-floor/door');
console.log(door.path); // 'Root/house/ground-floor/door'

タグで検索​

タグはエンティティのグループに付けるラベルで、findByTag()はタグを持つすべての子孫を返します。複数のタグを渡すといずれかのタグを持つエンティティを、タグの配列を渡すとすべてのタグを持つエンティティを検索します。

const enemies = app.root.findByTag('enemy');

// 'enemy'または'boss'のタグを持つエンティティ
const hostile = app.root.findByTag('enemy', 'boss');

// 'enemy'と'flying'の両方のタグを持つエンティティ
const flyers = app.root.findByTag(['enemy', 'flying']);

1つのエンティティを調べるには、同じ方法で引数を受け取るentity.tags.has()を使います。goblin.tags.has('enemy', 'boss')は、goblinがどちらかのタグを持っていればtrueです。

コンポーネントやスクリプトで検索​

// シーン内のすべてのライト
const lights = app.root.findComponents('light');
lights.forEach(light => light.enabled = false);

// playerの下にある最初のHealthスクリプトのインスタンス。クラスでも名前でも検索できます
const health = player.findScript(Health);

独自の条件で検索​

find()とfindOne()は関数を受け取ります。関数は各エンティティについて呼び出され、目的のエンティティに対してtrueを返します。

const coins = level.find(entity => entity.name.startsWith('coin') && entity.enabled);

集めずにサブツリーのすべてのエンティティを調べるには、forEach()を使います。

level.forEach(entity => console.log(entity.path));

各環境での検索​

シーン全体を検索するにはapp.rootのメソッドを、あるエンティティの下を検索するにはそのエンティティのメソッドを呼び出します。スクリプトの中では、this.app.rootがルートで、this.entityがスクリプト自身のエンティティです。

const wheels = this.entity.findByTag('wheel');

効率的な検索​

検索は対象となるすべてのエンティティを調べるので、そのコストは検索するシーンの大きさに比例して増えます。

  • 一度だけ検索する。 スクリプトが必要とするエンティティは、毎フレーム検索するのではなく、initializeメソッドで見つけて保持します。
  • 近くから検索する。 分かっている最も近いエンティティから検索すれば、app.rootから検索するよりも調べるエンティティが少なくて済みます。
  • 参照を優先する。 エンティティ属性、ref、保持している要素は、どんな検索よりも速く、名前が変わっても壊れません。

実行中のアプリケーションの階層を調べ、エンティティやコンポーネントの現在の設定を確認するには、Inspectorデバッグパネルを使います。

関連情報​