10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

爆速でwh.imのゲームを実装する(じゃんけん編)

Last updated at Posted at 2020-05-24

はじめに

こんにちは。普段は情報系の学科で大学生をしている者です。

この記事に紹介されている、wh.im(ウィム)というサービスの立ち上げに関わっているのですが、その一環でwh.im上で楽しめるゲームを開発しました。

このサービスの特徴として、誰でもゲームを投稿 できます!そのやり方を知っていただきたく、前回に引き続き記事を書きますので、興味を持った方はぜひゲーム開発を試してみてください!

アプリを公開するまでの一連の流れは、前回の記事にありますので、併せてご覧ください。

Vue.js以外でも実装は可能ですが、今回はVue.jsに限ってご紹介できればと思います!

今回は実際に遊ぶことのできるじゃんけんを例に挙げて、実装方法を説明いたします。

5/28更新

6/13更新

まずは開発環境で実際に動かしてみる(5/28更新)

実際にじゃんけんを開発環境で動かしてみたいと思います。

まず


$ cd ダウンロードしたいディレクトリ
$ git clone https://github.com/whimRTC/whim-janken.git

とします。そして

$ cd whim-janken
$ yarn # or npm install
$ yarn serve # or npm run serve

とします。yarnまたはnpmがインストールされていない場合はインストールしてください。
するとlocalhost:8080にゲームが起動します。

そして、wh.imから「遊び場」へ入室し、そのアドレスの末尾に&develop=trueをつけます。するとwh.imが開発者用のモードとなります。右上のメニューの「アプリを選ぶ」から「開発用(port:8080)」を選ぶことにより、自分の手元でゲームを試すことができます。

janken.png

このように表示されれば成功です!

実際のコードを見てみる

続いて、実際のコードを使いながら通信方法を説明していきます。ここは全くじゃんけんと同じなので、確認不要な方は飛ばしてしまってかまいま

まず表示される画面が書かれているsrc/App.vueをご覧ください。

src/App.vue
<template>
  <div id="app">
    <Main class="main" />

    <Player
      v-for="user in $whim.users"
      :key="user.id"
      :class="whimUserWindowClass(user)"
      :displayUser="user"
    />
  </div>
</template>
<script>
export default {
  name: "App",
  components: {
    Main: () => import("@/components/main/Index"),
    Player: () => import("@/components/player/Index")
  },
  computed: {
    users() {
      return this.$whim.users;
    }
  }
};
</script>

このゲームではMain画面とPlayer画面に分かれて実装されています。そのため、App.vue内でMainPlayerの2つのコンポーネントを呼び出しています。
Mainは画面中央部の画面を、Playerはそれぞれのユーザーのいる場所に表示される画面を表します。
MainPlayerの実装はそれぞれ、 src/components/main/Index.vuesrc/components/player/Index.vueに実装があります。

wh.imを経由した通信の方法

App.vue$whim.usersという呼び出しがあります(this.$whim.usersの省略形です)が、これはwhim-client-vueというパッケージに入っています。このようにすることで、this.$whimから始まる関数だけで、利用者間の非同期通信まわりは全てできるようになっています。

ここに扱える関数一覧を示します。scriptタグ内では適宜thisを先頭に付けてください。

状態取得(呼び出すたびに通信する)

コード 説明
$whim.users [User] ルームに入っているユーザー一覧
$whim.room Room Room Object
$whim.accessUser User 現在アクセスしているUser
$whim.state State ゲームの状態(自由に設計可能)

状態変更

コード 引数 説明
$whim.assignState(Object) Object ゲーム情報を追記更新、

存在しないキーの場合:追記
存在するキーの場合:更新
$whim.resetState(Object) Object ゲーム情報を渡されたObjectにすべて変える

これでは分かりにくいと思うので、後ほどのコードで使っている部分を見ながら、理解していただけると助かります。
より詳細な説明は、開発者ドキュメントをご覧ください。

このゲームのデータ構造

stateはゲームに合わせて自由に設計することができます。
今回のゲームでは、ユーザーのIDをキーとしてじゃんけんの手`('rock', 'scissors', 'paper')を値に持つObjectとします。

これらの変数は開発環境ではどんな時でも見ることができます。右上のメニューから「SHOW APP STATE」を選択すると

image.png

image.png

白い部分には私のuserIdが表示されています。

このように、全員のじゃんけんの手を確認することができます。うまくやれば、カンニングすることもできます(友達とやるときはやめましょう)。

Main

次にMain画面のコードについて説明していきます。Main画面は書くプレイヤー上にに表示されます。じゃんけんゲームでは、各プレーヤーが選択中か選択済みかがボタンを出しています。

src/components/player/Index.vue
<template>
  <div class="container">
    <a v-if="isAllSelected" class="btn" @click="reset">もう一度</a>
    <Me v-if="!isAccessUserSelected" class="me" />
  </div>
</template>

<script>
export default {
  name: "Main",
  components: {
    Me: () => import("@/components/main/Me")
  },
  computed: {
    users() {
      return this.$whim.users;
    },
    isAllSelected() {
      return (
        this.users.length > 0 &&
        this.users.every(user => this.$whim.state[user.id])
      );
    },
    isAccessUserSelected() {
      return !!this.$whim.state[this.$whim.accessUser.id];
    }
  },
  methods: {
    reset() {
      this.$whim.resetState();
    }
  }
};
</script>
<!-- 以下略 -->

ここでcomputedプロパティでusersisAllSelectedの値を定義しています。
じゃんけんでは、state[user.id]にじゃんけんの手の状態が入るようになっていて、isAllSelectedでは全員が選択しているかどうかを判定しています。つまり、state[user.id]が全員undefinedでないかどうかを判定しています。users.length > 0としているのは、データベースからuserが取得できていない状態での判定を防ぐためです。
このようにVue内で先ほど定義した関数を用いることでゲームを実装できます。

Meコンポーネントについては次で説明します。

Me

src/components/player/Me.vue
<template>
  <div>
    <h2 class="subtitle">
      選択してください!
    </h2>
    <div>
      <img src="@/assets/rock.png" class="img" @click="select('rock')" />
      <img
        src="@/assets/scissors.png"
        class="img"
        @click="select('scissors')"
      />
      <img src="@/assets/paper.png" class="img" @click="select('paper')" />
    </div>
  </div>
</template>
<script>
export default {
  name: "Me",
  methods: {
    select(show) {
      this.$whim.setState(this.$whim.accessUser.id, show);
    }
  }
};
</script>

じゃんけんの手を選択したときに、select(show)メソッドが呼ばれ、

this.$whim.setState(this.$whim.accessUser.id, show);

によってデータベースのstate[accessUser.id]に自分の手が登録されます。

Player

次にやや複雑なのですが、Player画面を実装していきます。

src/components/player/Index.vue
<template>
  <div class="container">
    <Result v-if="isAllSelected" :displayUser="displayUser" />
    <h2 v-else-if="isAccessUserSelected && isSelected" class="subtitle">
      選択済み
    </h2>
    <h2 v-else-if="isAccessUserSelected" class="subtitle">
      選択中
    </h2>
  </div>
</template>
<script>
export default {
  name: "Player",
  components: {
    Result: () => import("@/components/player/Result")
  },
  props: {
    displayUser: {
      type: Object,
      required: true
    }
  }, // 表示されているUserの情報
  computed: {
    users() {
      return this.$whim.users;
    },
    isMe() {
      return this.$whim.accessUser.id === this.displayUser.id;
    },
    isAccessUserSelected() {
      return !!this.$whim.state[this.$whim.accessUser.id];
    },
    isSelected() {
      return !!this.$whim.state[this.displayUser.id];
    },
    isAllSelected() {
      return (
        this.users.length > 0 &&
        this.users.every(user => this.$whim.state[user.id])
      );
    }
  }
};
</script>

computedの内容を中心にご説明します。

まず、先ほどと同じisAllSelectedがすなわち全員が手を選択した状態を表していて、これがtrueのときResultコンポーネントを表示します。

また、isSelectedthis.$whim.state[this.displayUser.id]、すなわちデータベース上のstate[画面の人のID]を取得していて、その画面の人がどのじゃんけんの手を選んだ状態かどうかが得られています。this.displayUserがこの画面のプレーヤーを表すことに注意してください(displayUserの情報はApp.vueで渡されています)。isSelectedtrueである、すなわち選んだ状態のとき、画面に「選択済み」が表示されます。

次に、isMeでこの画面が自分が映っている画面であるかを取得しています。これがtrueのときに表示される、自分のじゃんけんの手を決める画面はMeというコンポーネントで実装されています。

そして、上のいずれの状態でもないときに、「選択中」と表示します。

Result

src/components/player/Result.vue
<template>
  <div>
    <h2 class="subtitle">
      結果
    </h2>
    <div>
      <img
        v-if="show === 'rock'"
        src="@/assets/rock.png"
        width="150"
        height="150"
      />
      <img
        v-if="show === 'scissors'"
        src="@/assets/scissors.png"
        width="150"
        height="150"
      />
      <img
        v-if="show === 'paper'"
        src="@/assets/paper.png"
        width="150"
        height="150"
      />
    </div>
  </div>
</template>
<script>
export default {
  name: "Result",
  props: {
    displayUser: {
      type: Object,
      required: true
    }
  },
  computed: {
    show: function() {
      return this.$whim.state[this.displayUser.id];
    }
  }
};
</script>
<style lang="scss" scoped></style>

state[ユーザーのid]には手が保存されているのでshowでプレーヤーの手を返します。displayUserは呼び出し元のPlayerから渡されています。

以上のコードがあれば、じゃんけんゲームが正常に動きます。

最後に

今回は、ゲームの中では最も単純なじゃんけんゲームについて説明しました。次回はNGワードゲームの実装について説明したいと思っています。乞うご期待!

5/28更新

爆速でwh.imのゲームを実装する(NGワードゲーム編)」が公開されました。

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?