【上級】【サンプル】見るプレイヤーによって同じエンティティでも見え方を変えるには

【サンプル】

📚 上級カテゴリとは?

アドオン制作における基本技能を網羅した方を対象に、複数の技術を掛け合わせることで生まれる「高度なシステム」について解説していくカテゴリです。

📚 サンプルカテゴリとは?

主に上級とセットで使用していくカテゴリです。要素が多くて解説しきれないような複雑な内容のときに、サンプルデータをベースにして要所だけを抑えて解説します。要は、「実際に動くデータあげるから、勝手にみて学べ」ってことです(投げやり)。


📦 今回のサンプルデータ概要

まずは、今回ベースにするサンプルデータで何ができるのかをご紹介します。以下の3つのシステムを実装したワールドになります。

  • 例1) 観戦者タグ(spectator)を持っているプレイヤーは、エンティティ(kei:test_1)を常に透過して見ることができる
  • 例2) 同じチームスコア(team)を持っているプレイヤー同士は、常に透過して見ることができる(スコア0なら見えない)。
  • 例3) エンティティ(kei:test_2)にカーソルを合わせると、強調表示(今回は少し大きく)のアニメーションと効果音が再生される

⬇️ サンプルデータのダウンロード

今回解説するシステムの入ったデータは、以下のリンクからダウンロードできます。まずは実際にワールドに入って、どんな動きをするのか確かめてみてください!

研究_見る人別描画_2026_04_02.mcworld

⚠️ サンプルデータを使う際のお願い(クレジットについて)

正直なところ、今回用意した関数(function)よりも良いものをゼロから自作するのは、かなり大変かと思います。なので、ご自身のワールドや作品でそのまま使っていただいて全然OKです!

ただ、思考停止でコピペするのではなく、以降の解説を読んで「なるほど、こういう仕組みで動いているのか!」としっかり理解してから使ってほしいというのが僕の願いです。

また、もしこのシステムをそのまま使ったアドオンをネットで配布・公開する際は、どこかにクレジット表記をちょこっと添えてもらえると、めちゃくちゃ嬉しいです!

それでは、このデータをベースに要所の解説を行っていきます。ぜひ最後まで付いてきてください!


💡 本題:システムの大まかな流れと核心

今回のシステムは、「見るプレイヤーの条件を加味し、見られる側のエンティティの描画を変える」というものです。

仮に「見られる側のエンティティ(kei:test_1)」がいた時、当たり前ですが、そいつのレンダーコントローラーやアニメーションが「見るプレイヤーごとに異なれば」いいわけです。

ということは、見るプレイヤーごとに、見られる側のエンティティの中の variable(変数)の値が異なればいいということ。口では簡単に言えますが、そんなこと一筋縄ではいきません。ではどうやっていくのか?

それは、ScriptAPIの playAnimation にある、「見られる側のエンティティのアニメーションを、視認できるプレイヤーを指定して再生する機能」を使っていきます。

JavaScript
entity.playAnimation("animation.test", { players: playerList })

このように、見られる側のエンティティに対して再生するアニメーションを視認できるプレイヤーのリスト(playerList)を入れることができます。これを使用して、以下の2つのアニメーションを用意します。

  1. v.apply_observer_filter0 にするアニメーション
  2. v.apply_observer_filter1 にするアニメーション

見られる側のエンティティにかかるアニメーションを「どちらかだけ視認できる」ように制御すると、「プレイヤーAが見た kei:test_1 の変数は0」「プレイヤーBが見た kei:test_1 の変数は1」という状況を作り出せるというわけです。

実際に使用しているアニメーションのJSONデータは以下の通りです。

JSONC
{
	"format_version": "1.8.0",
	"animations": {
		"animation.apply_observer_filter.bit.false": {
      "loop": true,
      "anim_time_update": "v.apply_observer_filter = 0;"
    },
    "animation.apply_observer_filter.bit.true": {
      "loop": true,
      "anim_time_update": "v.apply_observer_filter = 1;"
    }
	}
}

🛠️ 実践:ScriptAPIでの動かし方

これらを踏まえたうえで、実際にScriptAPIでどのように動かしていくのか見ていこうと思います。

実際の使い方は汎用的な function を用意したので、基本的には毎チック(見た目を変えたいタイミング)で実行するだけになります。まずは使い方の例を見てみましょう。

例1)観戦者タグ(spectator)による透過表示

JavaScript
// 毎チック実行
system.runInterval(() => {
  // 描画チェックを適用したいエンティティのリスト作成
  const test1List = world.getDimension("overworld").getEntities({ type: "kei:test_1" });
  // 用意したfunctionに
  // ・エンティティのリスト
  // ・みることのできるプレイヤーの条件
  // を入れる
  applyObserverFilter(test1List, (player, entity) => {
    return player.hasTag("spectator");
  });
});

例2)同じチームスコア(team)同士の透過表示

JavaScript
// 毎チック実行
system.runInterval(() => {
  // 描画チェックを適用したいエンティティのリスト作成
  const playerList = world.getAllPlayers()
  // 用意したfunctionに
  // ・エンティティのリスト
  // ・みることのできるプレイヤーの条件
  // を入れる
  applyObserverFilter(playerList, (player, entity) => {
    const playerTeam = getScore(player, "team");
    const entityTeam = getScore(entity, "team");
    // チームスコアが存在し、かつ一致する場合のみtrue
    return (playerTeam !== undefined) && (playerTeam === entityTeam) && playerTeam != 0;
  });
});

例3)カーソルを合わせたエンティティの強調表示

JavaScript
// 毎チック実行
system.runInterval(() => {
  // 描画チェックを適用したいエンティティのリスト作成
  const test2List = world.getDimension("overworld").getEntities({ type: "kei:test_2" });
  // 用意したfunctionに
  // ・エンティティのリスト
  // ・みることのできるプレイヤーの条件
  // を入れる
  applyObserverFilter(test2List, (player, entity) => {
    // 以下視点先のエンティティがtest_2であるかどうかの条件式
    const viewDirection = player.getViewDirection();
    const headLocation = player.getHeadLocation();
    const entitiesFromRay = player.dimension.getEntitiesFromRay(headLocation, viewDirection, { maxDistance: 3.5 });
    const targetHit = entitiesFromRay.find(hit => hit.entity.id !== player.id);
    return targetHit !== undefined && targetHit.entity.id === entity.id;
  });
});

以上のようになります。 「エンティティのリスト」「見ることのできるプレイヤーの条件」を入れるだけで機能するように関数を作成しました。

条件を入れる場所は (player, entity) => {} というアロー関数になっており、中で player は見る側のプレイヤー、entity は見られる側のエンティティとして条件に使用できます。return で見ることができるようにしたい場合は true を、見れなくしたい場合は false を返すようにしてください。


⚙️ 関数 applyObserverFilter の中身を解説

次に、裏側で動いている関数の仕組みを見ていきましょう。

JavaScript
export function applyObserverFilter(entities, conditionFunc) {
  const playerList = world.getAllPlayers();

  for (const entity of entities) {
    const trueAnimationList = [];
    const falseAnimationList = [];

    for (const player of playerList) {
      if (conditionFunc(player, entity)) {
        trueAnimationList.push(player);
      } else {
        falseAnimationList.push(player);
      }
    }

    if (trueAnimationList.length > 0) {
      const playAnimationOptions = { players: trueAnimationList };
      entity.playAnimation(`animation.apply_observer_filter.bit.true`, playAnimationOptions);
    }

    if (falseAnimationList.length > 0) {
      const playAnimationOptions = { players: falseAnimationList };
      entity.playAnimation(`animation.apply_observer_filter.bit.false`, playAnimationOptions);
    }
  }
}

中身はこのようになっています。

見られる側のエンティティのリストをもとに、1体ずつ「そのエンティティ」と「見る側のプレイヤーの条件」を参照します。そして、プレイヤーを以下のどちらかのリストに振り分けます。

  • trueAnimationList:変数(v.apply_observer_filter)を 1 にするアニメーションを見せるプレイヤーのリスト
  • falseAnimationList:変数(v.apply_observer_filter)を 0 にするアニメーションを見せるプレイヤーのリスト

振り分けた後、それぞれ animation.apply_observer_filter.bit.trueanimation.apply_observer_filter.bit.false のどちらかが見える状態でエンティティに適用する流れとなっています。


💡 余談:さらなる高みへのヒント(数値の扱いと複数の状態)

今回のサンプルでは、分かりやすくするために v.apply_observer_filter という変数に「0か1(trueかfalse)」の1種類の状態を代入するだけのシンプルな仕組みを紹介しました。

では、ここから「プレイヤーのスコア(数値)」をそのまま渡したり、「観戦者かどうか」と「特定のチームかどうか」といった複数の状態を同時に持たせたりすることはできるのでしょうか?

結論から言うと、可能です。

ただし、これは決して一筋縄ではいきません。 例えば数値を渡そうと思うと、1種類のtrue/falseを複数組み合わせて「二進数」として擬似的に送る複雑な仕組みを組まなければいけません。また、複数の状態を独立して管理するなら、アニメーションコントローラーなどを指定し競合しないように同時に起動させる仕組み作りなど、かなりの工夫が求められます。

ただ文字を少し書き換えれば動くような簡単なものではないため、今回はあえて具体的な答えは伏せておきます。

ですが、「見る人によって変数を書き換える」という今回の土台さえあれば、道は確実に存在します。

「もし、ここで数値を渡せる仕組みが作れたら…?」「変数を独立して複数持たせるにはどう組めばいいかな?」と、ぜひ自分なりの拡張にチャレンジしてみてください。それを悩み抜き、実装できた時が、真の上級者への第一歩です!


📝 まとめ

ここまでくれば、あとは各エンティティの JSON(リソース側)の中で v.apply_observer_filter を使用して、レンダーコントローラーで描画を出したり消したり、アニメーションコントローラーで動きを付けたりするだけです。

あとは上級者の皆さんならいけるはず!…だよね?

わからないことや、「自分の作っているシステムに組み込むにはどうしたらいい?」といった質問などがあれば、ぜひ気軽にコメントしていただければと思います!

コメント

タイトルとURLをコピーしました