Help us understand the problem. What is going on with this article?

【Vue.js】半日でできる、ビデオチャット上で動くオリジナルゲームの開発から公開まで。

Vue.jsを使って簡単なゲーム開発し、完成したものをwh.imというゲームのプラットフォームに公開するまでをまとめてみました。
この記事を読めば、Vue.js未経験の方であっても数時間あればオリジナルゲームを開発・公開し、友達と遊べるようになります!
できる限りわかりやすく書いたので長くなりましたが、ぜひ最後まで読んでみてください(記事の完成に3日かかりました🙄 服装の変化を楽しんでください)。ご質問はコメントまで!

対象読者

  • html / css はある程度わかる方
  • Vue.jsに入門したい人
  • ビデオチャット上でゲームを作ってみたい方

はじめに

wh.im というサービスの開発している@aitaroです。Vue.jsを使い始めてかれこれ1年半になります。今でこそ wh.im を開発できるぐらいの知識がつきましたが(wh.imはVue.jsとそのフレームワークであるNuxt.jsを使っています)、Vue.jsを始めたときは右も左もわからない状態でした。そこで、同じようにVue.js初心者の方に対して、ゲームの作成といった一つのプロダクトの完成とデプロイを目指した入門記事を今回書こうと思いました。wh.im要素が多めはなりますが、Vue.jsの基礎も理解できるようになっているので、是非最後まで走りきってみてください。

また、Vue.js経験者の方にも、wh.imの始め方から公開の仕方までが一通りまとまっているので、ビデオチャットで遊べるでゲームを作ってみたいときは是非参考にしてください。

構成

このエントリは以下の6つのパートと21の章から成ります。

Ⅰ. wh.imとは?

Ⅱ. Vue.jsの環境構築

  • 1. node.jsをインストール
  • 2. npmでvue-cliをインストール
  • 3. Vue.jsのプロジェクトを作る
  • 4. ソースコードを変更してみる。

Ⅲ. wh.imでの開発のための環境設定

  • 5. wh.im上でvueを動かしてみる
  • 6. wh.im上で開発の準備

Ⅳ. ゲームの実装

  • 7. main.jsのでライブラリ読み込み
  • 8. vue.jsのコンポーネントの追加
  • 9. じゃんけん画像の設置
  • 10. グーを選択するメソッドを追加しよう!
  • 11. 選んだ手のstateの登録
  • 12. チョキやパーを選択できるようにしよう!
  • 13. 関数を抽象化しよう!
  • 14. 選択後の画面を作ろう
  • 15. 結果の画面を作ろう
  • 16. 結果の画面に全員の選んだ手を表示してみよう!
  • 17. 最後に見た目を整えよう!

Ⅴ デプロイして公開する

  • 18. githubにコードを上げる
  • 19. netlifyの設定をする
  • 20. wh.imのdevelop画面でゲームの登録をする
  • 21. 遊んでみる!

Ⅵ. まとめ

wh.imとは?

wh.imとは、ビデオチャットしながら遊べるゲームのプラットフォームです。wh.imにアクセスすると、じゃんけんやワードウルフなどのゲームが友達と遊べます。さらに、wh.imの特徴はなんと言ってもオリジナルゲームを投稿できること!自分で作ったゲームを登録すれば、今日中にでもそのゲームで友達と遊ぶことができます。今回はこの wh.im を題材に使って、Vue.jsを用いたゲームを作っていこうと思います!
image.png

wh.imについて詳しくしりたい方は、新型コロナの自宅待機中に、ビデオチャットしながらゲームで遊べるサービスを作った話を是非読んでみてください。

Vue.jsの環境構築

1. node.jsをインストール

まずはMacにnode.jsをインストールしてください。インストールの方法はこちらの記事に簡潔にまとまっています。
Macにnode.jsをインストール

2. npmでvue-cliをインストール

node.jsをインストールしたあなたは、npm使えるようになっているはずです。npmとは、node package managerの略で、JavaScript系のパッケージを管理するためのツールです。
以下の方法で、npmを使ってvue-cliをインストールしましょう。
-gをつけることで、グローバルにインストールされるので、どこからでもvueコマンドを呼び出せるようになります。

$ npm install -g @vue/cli

3. Vue.jsのプロジェクトを作る

ここから、簡単なじゃんけんアプリの開発を通じてVue.jsを使ったゲーム開発を学んでいきましょう。

まずはVue.jsのプロジェクトを作成してみます。今回はじゃんけんゲームを作っていくので、jankenとしますが、好きな名前で大丈夫です。

$ vue create janken

すると、次のような選択画面になります。

? Please pick a preset: (Use arrow keys)
❯ default (babel, eslint)
  Manually select features

これはそのままEnterを押してしまいましょう。
最後にこのような画面ができたら成功です!

🎉  Successfully created project janken.
👉  Get started with the following commands:

 $ cd jaknen
 $ npm run serve

作成されたら、画面の指示に従ってそのjankenというディレクトリに移動してみましょう。
この段階で試しに起動してみましょう。

$ cd janken
$ npm run serve

数秒でターミナルがこのような状態になったら成功です。

起動は成功しているので、Chrome等ブラウザのアドレスバーに
http://localhost:8080
と入力してみてください。

このような画面が出てくると思います。
これで環境構築はひとまず終了です!お疲れ様でした🍺

4. ソースコードを変更してみる。

これで、Vue.jsの起動は完了しました。
Vue.jsを初めて触る人向けに、コードの雰囲気を掴んでもらいます。
まず、vue create で作成されたサンプル画面を見ていきましょう。
このサンプル画面のコードは src > components > HelloWorld.vue にあります。

ではそのうちこの文章を変えてみましょう。

src/components/HelloWorld.vue
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>

これを適当に変えてみます。

src/components/HelloWorld.vue
    <p>
      100日目にVue.jsをマスターする俺<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>

変えたら、command + S(winはctrl + S)で変更を保存してみましょう。
すると、ブラウザでみると自動で反映されていると思います!

これはVue.jsのうちのWebpackというライブラリで、HotReloadという機能です!変更が保存されると、自動で反映されます!ただ時々バグるので、もしバグったなというときはおとなしくブラウザの画面をリロードしましょう。

wh.imでの開発のための環境設定

5. wh.im上でvueを動かしてみる

wh.im上でVueを動かしてみましょう。
wh.imから遊び場を作成します。

次に、wh.imを開発モードにします。
URLの最後に&develop=trueを追加して、リロードすると、wh.imが開発モードに変わります。

4開発モードになると、右上のボタンからアプリを選択するときに、開発用(port:8080)が出てきます。

この開発用(port:8080)を選択し、プレイすると先程の画面になります。

これで、開発の準備は整いました。

6. wh.im上で開発の準備

では、実際にwh.imを使ったゲームを作っていきましょう。wh.imはVueで開発しやすいように、ライブラリを用意しています。(ライブラリとは拡張機能みたいなもので、追加することでVueでやれることが広がります)
一旦、ターミナルにいき、 command + C (windows の場合は ctrl + C)でサーバーを止めましょう。
その後、次のコマンドを打ち込んでライブラリを追加します。

$ npm install whim-client-vue

これで、ライブラリが入りました!
package.jsonを開いてみると、追加されているのがわかります。(バージョンは多少ことなる場合があります。)

package.json
{
  "name": "janken",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11",
    "whim-client-vue": "^1.2.1" // ←ここ
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.4.0",
    "@vue/cli-plugin-eslint": "~4.4.0",
    "@vue/cli-plugin-vuex": "~4.4.0",
    "@vue/cli-service": "~4.4.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "vue-template-compiler": "^2.6.11"
  }
}

そして次に、Vueの設定を変更します。janken(ルートディレクトリ)の直下にvue.config.jsを作り、以下のコードを加えましょう。
これはクロスドメインでアプリを呼び出すときに、host名を明示的にlocalhostにするためです。

vue.config.js
module.exports = {
  devServer: {
    host: "localhost"
  },
};

これでもう一度、Vueを起動します。

$ npm run serve

vueを再起動したときは、wh.imの方でも一回、ゲームを終了して再度選択する必要があります。

ゲームの実装

7. main.jsのでライブラリ読み込み

ここからは実際にコードを書いていきます。
まず、src/main.jsを開き、whim-client-vueを使うように設定します。
3,4行目を増やしました。

src/main.js
import Vue from 'vue'
import App from './App.vue'
import whimClientVue from "whim-client-vue";
Vue.use(whimClientVue);

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

これは、whim-client-vueのライブラリを呼び出すコードです。

src/main.js
import whimClientVue from "whim-client-vue";

Vue.useを使うことで、さきほど呼び出した、whim-client-vueのライブラリを、Vueに登録します。

src/main.js
Vue.use(whimClientVue);

8. vue.jsのコンポーネントの追加

次に、App.vueを編集します。これは、ページにアクセスすると最初に表示される画面です。
これをデフォルト画面から変更してみます。変更点は、divタグの中身を消したこと、HelloWorldコンポーネントは今回使わないので、componentsを消したことです。

src/App.vue
<template>
  <div id="app">
    Player: {{ $whim.accessUser.name }}
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

すると、自分の名前が表示されていると思います。
template内の {{ }} は、そこの部分を式評価するという意味です。
今回は、そこに $whim.accessUser.name と記述することで、アクセスしてるユーザーの名前がとれました。
ここで言う、アクセスしているユーザーとはそのゲームをプレイしている人のことです。wh.imはビデオチャットサービスなので、複数人でゲームをプレイします。そのとき、このブラウザからアクセスしている人はだれかということをゲーム側で把握しなければなりません。
イメージ図

実際に確認してみましょう。プライベートブラウザや、違うブラウザで、wh.imの同じルームにアクセスします。すると今度は、 Player:モナリザ と表示されました。

このようにアクセスする人によって、表示を変えたい場合は、accessUserを使うことで可能になります。

9. じゃんけん画像の設置

次に、ゲーム上にじゃんけんの選択肢を配置しましょう。
まず、じゃんけんの画像を用意します。
私は素材ライブラリーさんの画像を使わせていただきました。

ダウンロードした画像を、 src/assetsの中に配置します。
ファイル構成は以下のようになると思います。

janken
└── assets
    ├── paper.png
    ├── rock.png
    └── scissors.png

そしてこれを先程のApp.vue画面に配置します。
先程の、Player: {{ $whim.accessPlayer.name }}を下のように書き換えてください。

src/App.vue
    <div>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
        />
      </div>
    </div>

App.vue全体
src/App.vue
<template>
  <div id="app">
    <div>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

そして、wh.imの画面を確認します。すると、以下のような選択画面が現れていると思います!

10. グーを選択するメソッドを追加しよう!

次に、グーチョキパーを選択できるようにします。
目標は、画像をクリックすると文字が選択済み!に変わって、データベースに選択した手が書き込まれることです。
ではまず、データベースに書き込む処理をしましょう。

グーから行きます。
グーの画像に@click="selectRock"を付け足します。以下のような感じです。これはclickしたときにselectRockという関数を実行するという意味です。

src/App.vue
<img
  src="@/assets/rock.png"
  width="150"
  height="150"
  @click="selectRock"
/>

そしてselectRock関数をvueに登録します。
を以下のように編集します。

src/App.vue
<script>
export default {
  name: 'App',
  methods: {
    selectRock() {
      // ここに処理を書いていく
      console.log('selectされた!')
    }
  }
}
</script>

App.vue全体
src/App.vue
<template>
  <div id="app">
    <div>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"  
          @click="selectRock"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    select(hand) {
      // ここに処理を書いていく
      console.log('selectされた!')
    },
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

ここで出てきたconsole.logはブラウザのconsole画面にログを表示する命令です。
これで、グーを押したときの挙動を確認しましょう。
wh.imの画面に戻って、Chromeのデベロッパーコンソールを開きます。macの方は command + option + i で開きます。 そして、その状態でグーをクリックします。するとコンソール画面にselectされた!と表示されるはずです。(以下の画像を参照)

このように@clickでselectRock関数が実行されたのがわかると思います。

11. 選んだ手のstateの登録

次にデータベースに登録しましょう。selectメソッドを変更していきます。
データーベースはwh.im上ではstateと呼ばれます。また、このstateはデーターベースといいつつ、いわゆるRDBではなくJSON型で保存されます。(そしてこのデーターベースはこのアプリを立ち上げているブラウザ間で同期されます。)
stateを変更するときは、$whim.assignState関数を使います。
今回はアクセスしてるユーザーがグーを出したことを保存したいので、stateに[this.$whim.accessUser.id]: "rock"を書き込みます。

src/App.vue
selectRock() {
  // ここに処理を書いていく
  console.log('selectされた!')
  this.$whim.assignState(
    [this.$whim.accessUser.id]: "rock"
  )
}

動作確認をしてみましょう。グーをクリックしてみます。するとstateにグーが登録されます。wh.imの開発モードにはこのstateを確認する機能があります。右上のメニューボタンから、SHOW APP STATE を選んでください。
すると次にような画面が出てきます。

このObjectがstateです。
今回はstateにoNyPTFZuMbOOZruCR4UMSsP9zRu2:"rock"が登録されていることがわかりました。この、oNyPTFZuMbOOZruCR4UMSsP9zRu2はユーザーidです。

App.vue全体
src/App.vue
<template>
  <div id="app">
    <div>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"  
          @click="selectRock"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    selectRock() {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState(
        [this.$whim.accessUser.id]: "rock"
      )
    },
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

12. チョキやパーを選択できるようにしよう!

今のところまだ、グーしか選択できません。チョキやパーも選択できるようにしましょう。これは今までの要領でいくと簡単です。
まずselect関数を2こ増やします。

src/App.vue
<script>
export default {
  name: 'App',
  methods: {
    selectRock() {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        [this.$whim.accessUser.id]: "rock"
      })
    },
    selectScissors() {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        this.$whim.accessUser.id: "scissors"
      })
    },    
    selectPaper() {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        this.$whim.accessUser.id: "paper"
      })
    }
  }
}
</script>

これで、関数が3つに増えました。これをそれぞれの画像のclickイベントに追加していきます。

src/App.vue
<div>
    <img
      src="@/assets/rock.png"
      width="150"
      height="150"
      @click="selectRock"
    />
    <img
      src="@/assets/scissors.png"
      width="150"
      height="150"
      @click="selectScissors"
    />
    <img
      src="@/assets/paper.png"
      width="150"
      height="150"
      @click="selectPaper"
    />
</div>

これで、3つともクリックしたらstateに反映されるようになりました!
(注意事項:動作確認は必ずSTATE確認画面を閉じてから行ってください。)

App.vue全体
src/App.vue
<template>
  <div id="app">
    <div>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"  
          @click="selectRock"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
          @click="selectScissors"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
          @click="selectPaper"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    selectRock() {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        this.$whim.accessUser.id: "rock"
      })
    },
    selectScissors() {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        this.$whim.accessUser.id: "scissors"
      })
    },    
    selectPaper() {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        this.$whim.accessUser.id: "paper"
      })
    }
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

13. 関数を抽象化しよう!

先程のようにグー・チョキ・パーselect関数を3つ作ってもいいですが、これらの関数には似たような処理が多いです。こういうときは一つの関数にまとめます。これはプログラミングにおいて重要な抽象化の概念です。処理の内容を日本語化するとわかりやすいと思います。

まとめる前

  • selectRock関数: グーをstateに登録する。
  • selectScissors関数: チョキをstateに登録する。
  • selectPaper関数: パーをstateに登録する。

まとめた後

  • select関数: 出した手をstateに登録する。

グー、チョキ、パーが出した手に変わったこと以外は同じです。けどこのままだと、出した手ってなんやねん!ってなると思います。ここで関数の引数機能を使います。引数は関数名(引数名)みたいな感じで使います。
では早速コードを書いてみましょう。

src/App.vue
<script>
export default {
  name: 'App',
  methods: {
    select(hand) {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        [this.$whim.accessUser.id]: hand
      })
    }
  }
}
</script>

そして、この関数の呼び出し側も変更します。

src/App.vue
  <div>
    <img
      src="@/assets/rock.png"
      width="150"
      height="150"
      @click="select('rock')"
    />
    <img
      src="@/assets/scissors.png"
      width="150"
      height="150"
      @click="select('scissors')"
    />
    <img
      src="@/assets/paper.png"
      width="150"
      height="150"
      @click="select('paper')"
    />
  </div>

これで、先程と同じ用に動くはずです。確認してみましょう。

お気づきの人もいるかも知れませんが、このステップでは機能としてなにも進んでいません。コードの書き方を変えただけです。この機能を変えずによりよいコードにすることをリファクタリングといいます。「機能が増えないなら時間の無駄やんw」という人もいるかも知れませんが、どんどん大きなプロジェクトになっていくと、それをメンテナンスするためにはコードの品質が重要になっていきます。そして、「良いコード」を書くのは一朝一夕でできることでもないので、普段から意識的に「良いコード」を書くことを意識することがおすすめです。

App.vue全体
src/App.vue
<template>
  <div id="app">
    <div>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"
          @click="select('rock')"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
          @click="select('scissors')"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
          @click="select('paper')"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    select(hand) {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        [this.$whim.accessUser.id]: hand
      })
    },
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

14. 選択後の画面を作ろう

今のままですと、何回もじゃんけんが選択できてしまいます。選択が終わったら、選択済みにかえましょう。
選択が終わったかどうかは、stateを見たらわかります。stateが空なら未選択ですし、stateに自分のaccessUserIdがあれば、選択済みです。これは$whim.state[$whim.accessUser.id]で確認できます。また、vue.jsのテンプレートではv-if, v-else を使って条件分岐ができます。確認してみましょう。

src/App.vue
<template>
  <div id="app">
    <div v-if="$whim.state[$whim.accessUser.id]">
      <h2>
        {{ $whim.state[$whim.accessUser.id] }}を選択済みです。
      </h2>
    </div>
    <div v-else>
      <h2>
        選択してください!
      </h2>
(以下略)

$whim.stateというのは以下のようなObject型です。(SHOW APP STATE で確認できると思います。)

{
    jibunNoID: "paper"
}

ここでいうjibunNoID$whim.accessUser.idで取れる値です。(これはscriptの方で書いたthis.$whim.accessUser.idと同じですが、vueのtemplateではthisは省略します。)
なので、すでにpaperを選んでいる場合は$whim.state[$whim.accessUser.id]の値はpaperになりますし、何も選んでいない場合はundefinedになります。これを利用して、v-ifで条件分岐をしています。

App.vue全体
src/App.vue
<template>
  <div id="app">
    <div v-if="$whim.state[$whim.accessUser.id]">
      <h2>
        {{ $whim.state[$whim.accessUser.id] }}を選択済みです。
      </h2>
    </div>
    <div v-else>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"
          @click="select('rock')"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
          @click="select('scissors')"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
          @click="select('paper')"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    select(hand) {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        [this.$whim.accessUser.id]: hand
      })
    },
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

15. 結果の画面を作ろう

最後に結果の画面です。まずは、二人プレイでどのようにstateが登録されているか確認します。

一人目はpaperを選んでいて、二人目はrockを選んでいます。これを画面に表示します。

まずはじめに、全員がじゃんけんの手をすでに選んだかどうかを確認します。javascriptではfor文でループを表すことができます。usersを一人ずつ、stateに手が登録されているかを確認します。for文の詳しい説明はこちらなどを参考にしてください。

src/App.vue
let result = true
for (let i = 0; i < this.$whim.users.length; i++ ) {
    if(!this.$whim.state[this.$whim.users[i].id]){
        result = false
    }  
}
return result

ここで、this.$whim.usersは今部屋にいるuser全員です。
まず、resultにtrueを設定しておきます。そして、users一人ずつ、手がセットしているかをthis.$whim.state[this.$whim.users[i].id]で確認します。
ここでセットされていなかったら、resultをfalseに変更します。こうすることで、ひとりでもじゃんけんの手を選んでなかったらresultがfalseになります。これを関数として実装し、またtemplate側でv-ifを使います。(もともと、v-ifだったところはv-else-ifに変えました)

この処理をisEveryoneSelectとして追加したコードは次のようになります。

src/App.vue
<template>
  <div id="app">
    <div v-if="isEveryoneSelect">
      <h2>
        全員が選択を終わりました。
      </h2>
    </div>
    <div v-else-if="$whim.state[$whim.accessUser.id]">
      <h2>
        {{ $whim.state[$whim.accessUser.id] }}を選択済みです。
      </h2>
    </div>
    <div v-else>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"
          @click="select('rock')"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
          @click="select('scissors')"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
          @click="select('paper')"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    select(hand) {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        [this.$whim.accessUser.id]: hand
      })
    },
  },
  computed: {
    isEveryoneSelect() {
      let result = true
      for (let i = 0; i < this.$whim.users.length; i++ ) {
        if(!this.$whim.state[this.$whim.users[i].id]){
            result = false
        }  
      }
      return result
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

ここで使用したcomputedとは、値を動的に算出するときに使うものです。今回ですと、usersが変わりうるので、methodsよりもcomputedのほうが適切です。詳しくはこの記事を読むといいでしょう。
では実際に確認しましょう。一人プレイにときはじゃんけんの手を選ぶと、すぐに全員が選択を終わりましたと出ると思いますが、2人プレイのときは、選択画面→グーを選択済みです。→全員が選択を終わりました。と順番に変更すると思います。

16. 結果の画面に全員の選んだ手を表示してみよう!

全員の選択が終わったら結果を表示します。ここではVue.jsのv-forという機能を使います。v-forはtemplate内でループをするという機能です。今回の場合、全員分の結果を表示させないと行けないので、usersに対してv-forでループさせます。

src/App.vue
<div v-if="isEveryoneSelect">
  <h2 v-for="user in $whim.users" :key="user.id">
    {{user.name}}の出した手は、{{$whim.state[user.id]}}です。
  </h2>
</div>

最終的に以下のような結果画面になれば成功です。

App.vue全体
src/App.vue
<template>
  <div id="app">
    <div v-if="isEveryoneSelect">
      <h2 v-for="user in $whim.users" :key="user.id">
        {{user.name}}の出した手は、{{$whim.state[user.id]}}です。
      </h2>
    </div>
    <div v-else-if="$whim.state[$whim.accessUser.id]">
      <h2>
        {{ $whim.state[$whim.accessUser.id] }}を選択済みです。
      </h2>
    </div>
    <div v-else>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"
          @click="select('rock')"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
          @click="select('scissors')"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
          @click="select('paper')"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    select(hand) {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        [this.$whim.accessUser.id]: hand
      })
    },
  },
  computed: {
    isEveryoneSelect() {
      let result = true
      for (let i = 0; i < this.$whim.users.length; i++ ) {
        if(!this.$whim.state[this.$whim.users[i].id]){
            result = false
        }  
      }
      return result
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

17. 最後に見た目を整えよう!

最後に画像やcssを使って、見た目を整えます。Vue.jsでは srcの前でコロンをつけることで、動的に値を設定します。

全体のコードはこちら

src/App.vue
<template>
  <div id="app">
    <!-- class="result"を追記します。 -->
    <div v-if="isEveryoneSelect"  class="result">
      <div v-for="user in $whim.users" :key="user.id">
        <!-- じゃんけんの画像(rock.pngなど)を出した手に応じて表示します。 -->
        <img
          :src="require('@/assets/' + $whim.state[user.id] + '.png')"
          width="150"
          height="150"
        />
        <h2>{{user.name}}</h2>
      </div>
    </div>
    <div v-else-if="$whim.state[$whim.accessUser.id]">
      <h2>
        {{ $whim.state[$whim.accessUser.id] }}を選択済みです。
      </h2>
    </div>
    <div v-else>
      <h2>
        選択してください!
      </h2>
      <div>
        <img
          src="@/assets/rock.png"
          width="150"
          height="150"
          @click="select('rock')"
        />
        <img
          src="@/assets/scissors.png"
          width="150"
          height="150"
          @click="select('scissors')"
        />
        <img
          src="@/assets/paper.png"
          width="150"
          height="150"
          @click="select('paper')"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    select(hand) {
      // ここに処理を書いていく
      console.log('selectされた!')
      this.$whim.assignState({
        [this.$whim.accessUser.id]: hand
      })
    },
  },
  computed: {
    isEveryoneSelect() {
      let result = true
      for (let i = 0; i < this.$whim.users.length; i++ ) {
        if(!this.$whim.state[this.$whim.users[i].id]){
            result = false
        }  
      }
      return result
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
/* resultクラスに対応するcssを追記します。 */
.result {
  display: flex;
  justify-content: center;
}
</style>

画像は、この行の$whim.state[user.id]('rock', 'cissors', 'paper'のいずれか)が動的に表示されます。

:src="require('@/assets/' + $whim.state[user.id] + '.png')"

ここまでで、実装は終わりです。お疲れ様でした🍺

デプロイして公開する

18. githubにコードを上げる

ではここから、このゲームの公開方法に移ろうと思います。今のところ、このゲームはローカルで動いてるだけなので、なんらかのサーバにホスティングする必要があります。まず1ステップ目として、今作ったゲームをgithubに上げましょう。具体的は方法はここらへんの記事が参考になると思います。

まずは、新しいリポジトリを作ります。

その後、codeをgithubに上げます。
以下のようなコマンドになるともいます。

$ git add .
$ git commit -m "janken完成"
$ git remote add origin git@github.com:username/janken.git
$ git push -u origin master

最終的にこのような感じで上げれたら完成です。

19. netlifyの設定をする

次にnetlifyの設定をします。こちらの記事が参考になりますが、この記事でも解説しようと思います。Netlifyは静的なサイトを無料でホスティングしてくれるサービスです。Netlifyのサイトよりアカウントを作成します。githubのアカウントに関連付けて作成します。

この画面に来たら、 New site from Git を選択します。

GitHubを選択します

自分のレポジトリ一覧が出てくるので、先程作ったレポジトリを選択します。
最後にデプロイ設定を以下のようにします。

Build commandはnpm run build
Publish directoryはdistに設定します。

設定が終われば Deploy site を押しましょう!

この画面になれば成功です!

表示されているURLをコピーしておきましょう。

20. wh.imのdevelop画面でゲームの登録をする

wh.imの開発者用画面でアプリを登録します。
アクセスすると、googleでのログイン画面になると思います。(これは、gmailで開発者の認証のためです。)

ここで自分のgoogleアカウントで登録します。
すると次の画面に遷移します。これが開発者アプリ登録画面です。

右上のボタンから、新規アプリを登録します。
その後、必要事項を記入していきます。
ゲームのhostのURLは先程コピーしたURLを記入します。
最後のチェックボックスは色々な人が遊べるように公開したい場合は、チェックをします。(個人的に遊ぶだけの場合はチェックを入れなくて大丈夫です。)

saveを押すと公開完了です!

21. 遊んでみる!

早速遊んでみましょう。まず、IDをコピーします。そして、wh.imに移動します。(今回は開発モードにする必要はありません!)
アプリ選択画面を開き、コピーしたIDを入力しましょう。

これで選べます!
友達と遊んでみましょう!

まとめ

長い記事になりましたが、これでVue.js未経験者の方でもwh.imを使ったゲーム公開までの流れは一通り理解できたかと思います。Vue.jsをもっと学んで行くと、ワードウルフといった複雑なゲームも作れるようになるので、是非挑戦して見てください!

最後に、これらの情報が体系的にまとめた(つもりの)、開発者用ドキュメントも用意しているので、参考にしてみてください。

aitaro
東京大学大学院 技術経営戦略学専攻
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした