🚀

【clusterベータ】プレイヤーを追いかけるスクリプトを三次元化

2023/12/08に公開

公式の、プレイヤーを追いかけるスクリプトを三次元化します。つまり上下にも追いかけてきます。
なお回転は難しいので、とりあえずは二次元のときのままです。いちおう記事の最後にやり方は書いてあります。

・三次元で追いかけてくるので、重力は使わない設定にします。そのためonPhysicsUpdateではなく、そのままonUpdate内で処理します。
・AddForceAtではなく、プレイヤーとの距離の差を正規化(normalize)してからスピード・経過時間を掛け算したものを現在位置に足して自ら計算し、位置を移動させます。

坂の上まで追いかけてくる

要するに変わる部分はスクリプトの最後のほうの一部、こんな感じです。

  if (direction.length() > minDistance) {
    direction.normalize().multiplyScalar(force * deltaTime);
    $.setPosition(position.add(direction));
  }

スクリプト全文

アイテムのRigidbodyのUse Gravity(重力を使用)はオフにしておいてください。
あと、元々はonUpdateとonPhysicsUpdateの両方でpositionを求めていましたが、関数が1つになったので1回にしています。

あと、forceは元のスクリプトでは30でしたが、この場合は3くらいでいいでしょう。

const force = 3;
const maxDistance = 10;
const minDistance = 1;

const rad2deg = (rad) => (rad * 180) / Math.PI;

$.onUpdate((deltaTime) => {
  let position = $.getPosition();

  let players = $.getPlayersNear(position, maxDistance);

  let targetPlayer = null;
  let targetDistance = Infinity;

  // それぞれのプレイヤーとの距離を計算し、最も近いものを探す
  players.forEach((player) => {
    let playerPosition = player.getPosition();
    let distance = playerPosition.clone().sub(position).length();

    if (distance < targetDistance) {
      targetDistance = distance;
      targetPlayer = player;
    }
  });

  // 最も近いプレイヤーのPlayerHandleを保存
  $.state.targetPlayer = targetPlayer;

  let player = $.state.targetPlayer;

  if (player == null) return;

  // 追跡中のプレイヤーがログアウトなどでいなくなっていた場合
  if (player.exists() == false) return;

  // プレイヤーがいる方向の角度を計算してY軸回転
  let direction = player.getPosition().clone().sub(position);
  let angle = rad2deg(Math.atan2(direction.x, direction.z)); // atan2は弧度法での角度を返すため、度数法に変換する
  let rotation = new Quaternion().setFromEulerAngles(new Vector3(0, angle, 0));

  $.setRotation(rotation);

  // ここからが違う
  // プレイヤーと一定より離れているときに、正面方向に前進
  if (direction.length() > minDistance) {
    direction.normalize().multiplyScalar(force * deltaTime);
    $.setPosition(position.add(direction));
  }
});

上下を向かせるアイデア

rotationを出すところ、こんな風にすることで上下の回転を実現することもできます。

  let xzLength = new Vector2(direction.x, direction.z).length();
  let xRot = Math.atan(-direction.y/xzLength)/Math.PI*180;
  let rotation = new Quaternion().setFromEulerAngles(new Vector3(xRot, angle, 0));

まずないとは思いますが、xlLengthが0になってしまったときのため、if(xzLength > 0) { }でくくったほうがより無難かもしれません。0で割り算するとエラーになるので。
(初期位置が同じとかだとありうる)

Discussion