🐶

cluster アイテム持ったらプレイヤーについてくるサンプル [ベータ機能]

2024/07/07に公開

アイテム持ったら プレイヤーについてくる サンプル

サンプルワールド

アイテム持ったらプレイヤーについてくる サンプル
https://cluster.mu/w/58932bdb-02cb-487f-aa78-3f6e25a79014

使うコンポーネント や メソッド

Creator Kit

ClusterScript

ItemHandle

ベータを有効にする

addForceAt (Beta) を利用するので、ベータ機能を有効にする必要があります。

Unity - > cluster -> 設定 を開いて ベータ機能を有効にするのチェックを入れる

プレイヤーに付いてくるアイテム

  1. GameObject に Movable Item と Scriptable Item をつける

  1. Unity の Project ウインドウ で 適当なフォルダを作成する
  2. Project ウインドウ -> Code -> cluster -> ClusterScript で Partner.js ファイルを作成する
  3. 下記の内容をファイル内に書く
  4. 1で作成した GameObject の Scriptable Item の Source Code Asset に設定する
Partner.js
/** プレイヤーを追いかける スクリプト 
 * 
 * 概要
 * プレイヤーが特定のアイテムを持っている時だけ、プレイヤーを追いかける
 * 
 * 1. プレイヤーが特定のアイテムを持った場合に itemHandle.send でメッセージが送られてくる
 * 2. $.onReceive で受信する
 * 4. プレイヤーを追いかける
 * 5. プレイヤーがアイテムを落とした場合に、モードを変更して追いかけるのをやめる
 * 6. ターゲット設定している プレイヤーを null にして解除する
 */

const force = 30;
const maxDistance = 10;
const minDistance = 1;
const rad2deg = (rad) => rad * 180 / Math.PI;

const MODE = {
  INITIAL:0,
  INITIALIZED:1,
  FOLLOW:2,
  UNFOLLOW:3,
};

const RECEIVE_TYPE = {
  FOLLOW: 'follow',
  UNFOLLOW: 'unfollow',
}

const setMode = (mode) => {
  $.state.mode = mode;
  switch(mode) {
    case MODE.INITIAL:
      initial();
      break;
    case MODE.INITIALIZED:
      // 初期化完了
      break;
    case MODE.FOLLOW:
      // プレイヤーを追いかける
      follow();
      break;
    case MODE.UNFOLLOW:
      // 追いかけるのをやめる
      unfollow();
      break;
  }
}

const initial = () => {
  $.log('onStart');
  $.state.targetPlayerIdfc = null;

  setMode(MODE.INITIALIZED);
}

// プレイヤーを追いかける
const follow = () => {
  $.log('follow');
  // 近くのプレイヤーを探す
  $.state.targetPlayerIdfc = $.state.arg.idfc;
}

// プレイヤーを追いかけるのをやめる
const unfollow = () => {
  $.log('unfollow');
  $.state.targetPlayerIdfc = null;
}

// 近くの対象プレイヤーを探す
const findPlayer = () => {
  if ($.state.targetPlayerIdfc == null) return null;

  const position = $.getPosition();
  const players = $.getPlayersNear(position, maxDistance);

  let targetPlayer = null;
  players.forEach(player => {
    if(player.idfc === $.state.targetPlayerIdfc) {
      targetPlayer = player;
    }
  });
  // ログアウトしている場合は null を返す
  if (targetPlayer.exists() == false) return null;

  return targetPlayer;
}

// 近くのプレイヤーを追いかける
const followTargetPlayer = (deltaTime) => {
  const position = $.getPosition();

  // 近くのプレイヤーを探す
  const player = findPlayer();
  if (player == null) {
    // プレイヤーが見つからない場合は追いかけない
    return;
  } 

  // プレイヤーがいる方向の角度を計算してY軸回転
  const direction = player.getPosition().clone().sub(position);
  const angle = rad2deg(Math.atan2(direction.x, direction.z));

  const rotation = new Quaternion().setFromEulerAngles(new Vector3(0, angle, 0));
  $.setRotation(rotation);

  // プレイヤーと一定より離れているときに、正面方向に押し出す力を加えて前進
  if (direction.length() > minDistance) {
    const v = new Vector3(0, 0, force).applyQuaternion(rotation);
    $.addForceAt(v, position);
  }
}

// 停止させる
const stop = () => {
  const position = $.getPosition();
  $.setPosition(position);
}

$.onStart(() => {
  $.log('onStart');
  setMode(MODE.INITIAL);
});

$.onPhysicsUpdate(deltaTime => {
  switch($.state.mode) {
    case MODE.FOLLOW:
      // プレイヤーを追いかける
      followTargetPlayer(deltaTime);
      break;
    case MODE.UNFOLLOW:
      // 追いかけるのをやめる
      stop();
      break;
  }
});

// プレイヤーが特定のアイテムを 持つ,落とした 場合にメッセージが送られてくる
$.onReceive((messageType, arg, sender) => {
  // $.log('onReceive');
  // $.log(`messageType: ${messageType}`);
  // $.log(`arg: ${arg.idfc}`);
  // $.log(`sender: ${sender}`);

  $.state.arg = arg;
  switch(messageType) {
    case RECEIVE_TYPE.FOLLOW:
      // 持った場合
      setMode(MODE.FOLLOW);
      break;
    case RECEIVE_TYPE.UNFOLLOW:
      // 落とした場合
      setMode(MODE.UNFOLLOW);
      break;
  }
});

プレイヤーが持つ アイテム

  1. 持つと 追従, 落とす(離す) と 追従をやめる アイテム

  1. Unity の Project ウインドウ で 適当なフォルダを作成する
  2. Project ウインドウ -> Code -> cluster -> ClusterScript で GrabItem.js ファイルを作成する
  3. 下記の内容をファイル内に書く
  4. 1で作成した GameObject の Scriptable Item の Source Code Asset に設定する
GrabItem.js
/** プレイヤーが持つと パートナーがついてくるアイテム 
 * 
 */
const maxDistance = 10;

const MODE = {
  INITIAL:0,
  INITIALIZED:1,
  GRAB:2,
  DROP:3,
};

const RECEIVE_TYPE = {
  FOLLOW: 'follow',
  UNFOLLOW: 'unfollow',
}

const initial = () => {
  $.log('onStart');
  $.state.targetPlayer = null;

  setMode(MODE.INITIALIZED);
};

// 持った
const grab = () => {
  $.log('grab');
  const targetPlayer = $.state.targetPlayer;

  // 近くのアイムに送信する
  sendItemsNear(RECEIVE_TYPE.FOLLOW, targetPlayer.idfc);
};

// 落とした
const drop = () => {
  $.log('drop');
  const targetPlayer = $.state.targetPlayer;

  // 近くのアイムに送信する
  sendItemsNear(RECEIVE_TYPE.UNFOLLOW, targetPlayer.idfc);
};

// 近くのアイムに idfc を送信する
const sendItemsNear = (type, idfc) => {
  const position = $.getPosition();
  const items = $.getItemsNear(position, maxDistance);
  const arg = {idfc: idfc};
  items.forEach(item => {
    item.send(type, arg);
  });
};

const setMode = (mode) => {
  $.state.mode = mode;
  switch(mode) {
    case MODE.INITIAL:
      initial();
      break;
    case MODE.INITIALIZED:
      // 初期化完了
      break;
    case MODE.GRAB:
      // 持った
      grab();
      break;
    case MODE.DROP:
      // 落とした
      drop();
      break;
  }
};

$.onStart(() => {
  $.log('onStart');
  setMode(MODE.INITIAL);
});

// 持ったり, 落としたり
$.onGrab((isGrab, isLeftHand, player) => {
  $.state.targetPlayer = player;

  if(isGrab) {
    setMode(MODE.GRAB);
  } else {
    setMode(MODE.DROP);
  }
});

参考記事

ベータ機能】近くのプレイヤーについてくるペットをつくる(プレイヤー情報取得)

Discussion