[Gather.town]自身と一番近い参加者とじゃんけんをする方法

- リモートコミュニケーションツールとして[Gather](https://gather.town/)を利用しています。
- Gatherは基本機能以外の仕掛けが沢山あり、その中の1つが[ブラウザコンソール等で扱えるSocket API](http://gather-game-client-docs.s3-website-us-west-2.amazonaws.com/classes/Game.html)の存在です。
- 「現在位置の取得」や「参加者情報の取得」など豊富に存在しており、それらを適切に組み合わせることで色々な操作を行うことが可能です。
- 今回はそれらを利用して**自身と一番近い参加者とじゃんけんが出来る仕組み**を実装します。
**[過去の実装記事一覧はこちら](https://dev.classmethod.jp/search?q=gather&author=t_o_d&page=1)**

## 結果
- 以下の内容をChromeコンソール画面に入力して実行すると、**自身と一番近い参加者とじゃんけんをする**ことができます。
    - **※実行には[一定の権限(Admin or Builder)](https://support.gather.town/help/user-roles)が必要です。**

```javascript
// 自身のID及びMap
const myId = gameSpace.id;
const myMap = gameSpace.mapId;
const nearPlayerId = getNearPlayerId();

// じゃんけん開始
await play();

// じゃんけん処理
async function play(){
  // じゃんけん開始
  const hands = ["✊","✌","🖐"];
  const judge = ["あいこ","負け","勝ち"];
  const pr = Math.floor(Math.random()*3);
  const cr = Math.floor(Math.random()*3);
  game.setEmote(hands[pr],myId);
  game.setEmote(hands[cr],nearPlayerId);

  // 指定秒間スリープ
  const seconds = 2000;
  await new Promise(r => setTimeout(r,seconds));

  // 判定
  const result = (3 + pr - cr) % 3;
  if(result === 0){ // あいこ
    // 再実行
    play();
    return;
  }else if(result === 1){ // 負け
    game.setEmote("",myId);
    game.setEmote("🎉",nearPlayerId);
  }else { // 勝ち
    game.setEmote("🎉",myId);
    game.setEmote("",nearPlayerId);
  }

  // 指定秒後に初期化して終了
  await new Promise(r => setTimeout(r,seconds));
  game.setEmote("",myId);
  game.setEmote("",nearPlayerId);
}

// 自身と一番近い人のプレイヤーID
function getNearPlayerId() {
  // 自身の現在位置の座標
  const myCoordinate = (({ x, y }) => ({ x, y }))(gameSpace.gameState[myId]);

  // 自身以外の参加者の情報(ID, 名前, x座標, y座標)
  const otherInfo = Object.values(gameSpace.gameState).filter(obj => obj.map === myMap && obj.id !== myId).map(({ id, name, x, y }) => ({ id, name, x, y }));

  // 近い座標の取得
  function getClosestCoordinate(targetCoordinate, coordinates) {
    return coordinates.reduce((closest, current) => {
      const closestDistance = getDistance(targetCoordinate, closest);
      const currentDistance = getDistance(targetCoordinate, current);
      return currentDistance < closestDistance ? current : closest;
    });
  }

  // 2点間の座標距離の計算
  function getDistance(firstCoordinate, secondCoordinate) {
    return Math.hypot(firstCoordinate.x - secondCoordinate.x, firstCoordinate.y - secondCoordinate.y);
  }

  // 近い人の情報
  const closestOther = otherInfo.find(({ x, y }) => {
    const otherCoordinates = otherInfo.map(({ x, y }) => ({ x, y }));
    const closestCoordinate = getClosestCoordinate(myCoordinate, otherCoordinates)
    return x === closestCoordinate.x && y === closestCoordinate.y
  });

  return closestOther.id
}
```

![gather-janken-init.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/512e5d77-6335-0c60-d0ad-3aa79e1b9a9f.png)

- マップ内を色々移動して実行後、自身(**緑服**)と**その位置に応じた一番近い参加者**と以下のようにじゃんけんが出来ていれば成功です。
    - **開始後、じゃんけん参加者の上に手の絵文字(✊,✌️,🖐)が表示**
    - **勝敗がつけば、勝った方の絵文字の表示が🎉になり、負けた方は消える。**
    - **あいこであれば、再度じゃんけんを開始。**
    - **🎉リアクションが消えたら、じゃんけんゲーム終了。**

![gather-janken-first.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/af3a8841-9f37-aea5-b7e2-2271244b83da.png)

![gather-janken-first-result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/1e6414c0-c350-a815-25ea-91165bbdb399.png)

![gather-janken-aiko.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/666dffb7-4ee5-873b-bceb-88ec8d9bfaf2.png)

## 内容
- 上記のスクリプトは以下のGather.townで用意されているSocket APIを利用しています。
    - **gameSpace.id** : 自身の参加者ID
    - **gameSpace.mapId** : 自身がいるマップ名
    - **gameSpace.gameState.参加者(任意)** : 参加者の情報。参加者指定も可
    - **game.setEmote(対象絵文字,対象者ID)** : 対象者のリアクション設定。対象の絵文字を設定することで可能。
- まず**自身と最も距離が近い参加者のID**を取得する関数を実装します。
    - **[実装方法はこちらで解説しています。](https://dev.classmethod.jp/articles/gather-town-closest-player/)**
- 次にじゃんけん処理を実装します。
    - **[勝敗判定はこちらを参考にしています](https://staku.designbits.jp/check-janken/)**
- 最後にその処理内で指定秒ずつ(**開始時、判定時、終了時**)setEmote関数を使って**リアクション設定**をすればGather内でじゃんけんを行なっているように見えます。

## 補足
- 他の操作の情報は[こちらのサイト](http://gather-game-client-docs.s3-website-us-west-2.amazonaws.com/classes/Game.html)から確認できます。
- より詳細な情報を知るためには、以下のようにコンソール画面で「**game**」「**gameSpace**」を入力して実行すれば確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/2c395806-8892-8871-37b6-8c7aef6df4a9.png)

## 参考
- [Gather Town]([Gather](https://gather.town/))
- [Gather Socket API](http://gather-game-client-docs.s3-website-us-west-2.amazonaws.com/classes/Game.html)
- [じゃんけん判定](https://staku.designbits.jp/check-janken/)