2020年9月4日
ゲーム作りで学ぶVue.js (5) methodsで駒表示

駒台の表示ロジック

本章からゲームロジックで必要になる関数を作っていきます。

駒台に獲得した駒を表示する関数を作ります。

ComKomadai.vueに駒ラベルを表示するかを決めるshowKomaLabelという関数を作ります。
関数はmethodsで定義します。

src/components/ComKomadai.vue

<script>
import { mapState } from "vuex"
export default {
  computed: {
    ...mapState(["komaList", "comKomadai", "comPoint"])
  },
  methods: {  // 以下追加
    showKomaLabel(komaIndex) {
      if (this.comKomadai.includes(komaIndex)) {
        return this.komaList[komaIndex].label
      } else {
        return null
      }
    }
  }
}
</script>  

引数のkomaIndexはfuやkinなどの値が渡ってくる想定です。
this.comKomadaistore.jsstateで定義したcomKomadaiです。

まだ作成していませんが、Comが駒を獲得したらcomKomadaiにfuやkinなどが追加されます。
例えばcomKomadai["fu", "kin"]だとしたら、showKomaLabel("fu")の戻り値は歩で、showKomaLabel("kaku")の戻り値はnullとなります。

これをテンプレートで所定の駒の位置で引数を渡せば表示ロジックが出来上がります。

src/components/ComKomadai.vue

<tbody>
  <tr>
    <td>{{ showKomaLabel("kaku") }}</td>
    <td>{{ showKomaLabel("kin") }}</td>
    <td>{{ showKomaLabel("gin") }}</td>
  </tr>
  <tr>
    <td>{{ showKomaLabel("keima") }}</td>
    <td>{{ showKomaLabel("kyosha") }}</td>
    <td>{{ showKomaLabel("fu") }}</td>
  </tr>
</tbody>

comKomadaiはまだ空の配列なので駒台には何も表示されません。
試しにstore.jscomKomadai["fu"]にすると、歩が表示されるのが確認できます。

05-010komadai-sample

PlayerKomadai.vueにもshowKomaLabelを作ります。

src/components/PlayerKomadai.vue

<script>
import { mapState } from "vuex"
export default {
  computed: {
    ...mapState(["komaList", "playerKomadai", "playerPoint"])
  },
  methods: {  // 以下追加
    showKomaLabel(komaIndex) {
      if (this.playerKomadai.includes(komaIndex)) {
        return this.komaList[komaIndex].label
      }
    }
  }
}
</script>

Comとの違いはincludesの対象になる配列がplayerKomadaiになっただけです。
ComKomadai.vueと同じshowKomaLabelという関数名でも別のコンポーネントなので問題ありません。

Comと同じようにテンプレートで使用します。

src/components/PlayerKomadai.vue

<tbody>
  <tr>
    <td>{{ showKomaLabel("fu") }}</td>
    <td>{{ showKomaLabel("kyosha") }}</td>
    <td>{{ showKomaLabel("keima") }}</td>
  </tr>
  <tr>
    <td>{{ showKomaLabel("gin") }}</td>
    <td>{{ showKomaLabel("kin") }}</td>
    <td>{{ showKomaLabel("kaku") }}</td>
  </tr>
</tbody>

駒をコンポーネント化

次は選択する駒の表示部分を改良します。

ゲームが進んでいくと一度使ったコマは使えませんので、薄く表示しています。

05-020opacitysample

これを実装していきたいのですが、その前に駒をコンポーネントにします。
ShogiPoker.vueが肥大化しそうなので適度にコンポーネントを作ると都合が良いです。

ComKoma.vueを作成します。

src/components/ComKoma.vue

<template>
  <td :class="getKomaClass()">{{ koma.label }}</td>
</template>

<script>
import { mapState } from "vuex"
export default {
  props: {
    komaIndex: {
      type: String,
      required: true
    },
    koma: {
      type: Object,
      required: true
    }
  },
  computed: {
    ...mapState(["comSelectable"])
  },
  methods: {
    getKomaClass() {
      if (!this.comSelectable.includes(this.komaIndex)) {
        return "unselectable"
      }
    }
  }
}
</script>

<style scoped>
td {
  transform: rotate(0.5turn);
  -webkit-transform: rotate(0.5turn);
}
.unselectable {
  opacity: 0.4;
}
</style>

getKomaClassというメソッドでcomSelectableに含まれていなければ、unselectableをクラスにつけています。
unselectableにopacity: 0.4;を付ければ選択できない駒を薄く表示することができます。
tdにtransformで逆さに表示するスタイルもここで当てています。

propsは親コンポーネントから渡ってくるプロパティです。
https://jp.vuejs.org/v2/guide/components-props.html

ComKoma.vueShogiPoker.vueで読み込みます。

src/components/ShogiPoker.vue

<script>
import { mapState } from "vuex"
import GameRule from "./GameRule"
import ComKomadai from "./ComKomadai"
import PlayerKomadai from "./PlayerKomadai"
import ResetBtn from "./ResetBtn"
import ComKoma from "./ComKoma"  // 追加

export default {
  components: {
    GameRule,
    ComKomadai,
    PlayerKomadai,
    ResetBtn,
    ComKoma  // 追加
  },

...
</script>

Comの駒表示する部分をコンポーネントを使って書き換えます。

src/components/ShogiPoker.vue

<div class="shogi-ban">
  <table>
    <tbody>
      <tr class="com-row">
        <com-koma
          v-for="(koma, index) in this.komaList"
          :key="index"
          :koma-index="index"
          :koma="koma"
        />
      </tr>

コンポーネントに対してもv-forは使えます。
追加されたkoma-indexkomaComKoma.vueにあるpropsに渡されます。

渡す側はkoma-indexのようにケバブケースで、受け取る側はkomaIndexのようにキャメルケースで書くことが推奨されています。

src/components/ComKoma.vue

props: {
  komaIndex: {
    type: String,
    required: true
  },
  koma: {
    type: Object,
    required: true
  }
},

propsには型をtypeで指定したり、requiredで必須かどうかを定めることができます。

続いてPlayerの駒もコンポーネントにしていきます。

src/components/PlayerKoma.vue

<template>
  <td :class="getKomaClass()">{{ koma.label }}</td>
</template>

<script>
import { mapState } from "vuex"
export default {
  props: {
    komaIndex: {
      type: String,
      required: true
    },
    koma: {
      type: Object,
      required: true
    }
  },
  computed: {
    ...mapState(["playerSelectable"])
  },
  methods: {
    getKomaClass() {
      if (!this.playerSelectable.includes(this.komaIndex)) {
        return "unselectable"
      }
    }
  }
}
</script>

<style scoped>
td:not(.unselectable) {
  cursor: pointer;
}

td:hover,
.unselectable {
  opacity: 0.4;
}
</style>

ほぼCom側と同じです。
Playerは駒を実際に選択できるので、ホバーした時にスタイルを当てています。

ShogiPoker.vuePlayerKomaを使います。

src/components/ShogiPoker.vue

<script>
import { mapState } from "vuex"
import GameRule from "./GameRule"
import ComKomadai from "./ComKomadai"
import PlayerKomadai from "./PlayerKomadai"
import ResetBtn from "./ResetBtn"
import ComKoma from "./ComKoma"
import PlayerKoma from "./PlayerKoma"  // 追加

export default {
  components: {
    GameRule,
    ComKomadai,
    PlayerKomadai,
    ResetBtn,
    ComKoma,
    PlayerKoma  // 追加
  },
...
</script>
src/components/ShogiPoker.vue

      <tr class="player-row">
        <player-koma
          v-for="(koma, index) in this.komaList"
          :key="index"
          :koma-index="index"
          :koma="koma"
        />
      </tr>
    </tbody>
  </table>
</div>

ここまでのコードはこちらにあります。

次章 はemitを使って駒選択の処理を作成します。

連載記事