Aokashi Room

なんでも書き続けるAokashiの部屋

WWA Message Loader を作ってました

WWAのマップデータから各パーツのメッセージを読むこむだけのWWA Loaderを作りました。

なんで作ったのか

このWWA MessageLoaderの開発がはじめったのは去年の10月中旬で、 WWA COLLECTION の配信に向けて不適切なメッセージが無いか確認する必要がありました。

WWAマップ作成ツールには、メッセージの検索機能を持っていません。

ですので、WWAマップデータのメッセージを出力されたプログラムが開発されたのです。

作り方

まず、WWAのマップデータはWWA Loaderが読み込むと、連想配列 WWAデータ が展開されます。その中の「message」のキーには、配列を持っているのです。

そのメッセージを取って出すだけです。👌

wwaData.wwaData['message'].forEach((text, number) => {
  if (text === '') {
    return;
  }
  // number で番号が表示されるはず。
  let textLines = text.split('\n');
  textLines.forEach((textLine) => {
    // textLine で各行出力されるはず。
  });
}

パーツ番号の表示

しかし、そのメッセージがどのパーツのメッセージなのか分かりません。

WWAデータには、先程説明した「message」以外にも、「objectAttribute」と「mapAttribute」をキーとした配列があるのです。前者は物体パーツの属性、後者は背景パーツの属性です。「属性」と書いてますが、 メッセージ以外すべての値が各パーツごとに格納されています

興味のある方は、下の記事を見ると分かると思います。

github.com

つまり、WWAデータは、メッセージとメッセージ以外でパーツのデータを分けているんですね。

メッセージ以外にあたるパーツの属性からメッセージへ辿るには、各パーツ属性にある数値から message 配列の中を探します。👉

f:id:aokashi:20190224182907p:plain

そのまま上図の通りなんですが、パーツ属性の各パーツの6番目、プログラム内だと0から始まるので5番目に message 配列内の場所が記載されています。上図だと「10」なので、 message 配列のインデックス10にパーツのメッセージが含まれているのです。

ちなみに、WWAマップ作成ツールの仕様上、 どのパーツにも参照されないメッセージ もありますのでご注意ください。パーツを削除したり、移動したりすると発生します。

Vue.js によるコンポーネント

機能は整ったこととして、メッセージ一覧を表示する方法なら、Vue.js とかのビューフレームワークで構築したら、いい感じ*1のプログラムになるんじゃないか?と思い、学習を兼ねて Vue.js のコンポーネント機能を利用して作り直してみました*2

のはずが、上手く進まず、去年10月の開発から今までこれが原因で放置していました。

(ここから先は、コンポーネントの仕組みを理解していることを前提に話します。)

コンポーネントの仕組みについて

f:id:aokashi:20190228232452p:plain

今回の WWA MessageLoader は上記のパーツ番号の表示でどう設計しようか長時間悩んでいました。

とりあえず、上図のように親コンポーネントWWAパーツ一覧、子コンポーネントWWAパーツとしましょうか。

当時の設計では、 WWAパーツ一覧はWWAのデータを渡すだけで、WWAパーツはパーツ情報の表示と必要に応じてデータの取得を行う と考えていました。WWAパーツでは「パーツ情報の表示」なので、下図のようにWWAパーツが親からパーツ番号やパーツ種類を参照できないか実装していましたが、全然上手く行かず・・・。😇

f:id:aokashi:20190224203804p:plain
当時考えていたコンポーネントのデータの渡し方。今これ書いてて恥ずかしいな。

後に、コンポーネントは親子関係を持つことが出来るけど、データは親コンポーネントから子コンポーネントに一方通行しか渡せない。逆方向の場合は、イベントを利用する必要がある。 ということが分かりました。イベントを利用してみるも、こちらも上手く渡せず・・・。🤔

設計がいけなかった

去年11月上旬で放置した後、今年2月に大学から Vue.js 入門を貸出して再び学習しました。📖

gihyo.jp

最近のWebアプリケーションは、サーバーはJSONとかで返すAPIスタイルで、表示をすべてクライアントサイドで済むのか、だったりと今の事情を理解しつつ、コンポーネント周りについて読みました。

「あ、そうだったんだ。子コンポーネントに渡した時点で親コンポーネントからデータは渡せないのか・・・」と。

と気付いたこともそうですが、何よりももう長時間放置していたので、当時の設計の考え方を忘れた🤷‍♂️ というのもあります。

とは言え、下図のように設計を改め、WWAパーツ一覧では各WWAパーツ毎に表示するべきデータを渡して、WWAパーツはそれを表示するだけに済ませる方法で実装できました。👏

f:id:aokashi:20190224203836p:plain

WWAパーツ一覧のソースコードでは、以下のメソッドで各WWAパーツにデータを渡します。

  computed: {
    partsObjects: function() {
      let result = {};
    
      for (let key in this.wwaData['message']) {
        if (this.wwaData['message'][key] === '') {
          continue;
        }

        // makePartsNumber はメッセージ番号を渡すとパーツ種類とパーツ番号を返してくれるメソッド。 methods の中に定義されている。
        let partsInfo = this.makePartsNumber(parseInt(key));
        // 検索条件に満たないパーツをここで continue で弾く

        // こんな感じでまるごとまとめてしまう
        result[key] = {
          number: key,
          message: partsMessage,
          partsType: partsInfo.partsType, // パーツ種類は partsType.js の中に定数定義が入っている。
          partsNumber: partsInfo.number
        };
      }
      return result;
    }
  },

一応教訓としては、 コンポーネント間で受け渡しを行う回数を少なくする ことが大事だと分かりました。

最近オブジェクト指向の扱い方について考え直しているところなんですが、疎結合に作ることがオブジェクト指向とそっくりなんですね―。

検索機能の実装

上記のことがやっと分かり、検索機能も実装しました。ここは イベントの呼び出し を利用して、子である検索ボックスコンポーネントから親であるWWAパーツ一覧コンポーネントへ渡す形としました。

この時点でもエラーは起きましたが、タイプミスだったり、カンマの付け忘れだったり、v-on ディレクティブに付ける要素を間違えたりしただけです。

f:id:aokashi:20190228233102p:plain

検索ボックスのソースコードでは、以下のメソッドでイベントが呼び出されます。

  methods: {
    searchEvent: function() {
      this.$emit('search', { // $emit でイベントを親コンポーネントに呼び出す。ここなら 'search' イベント。
        query: this.query,
        type: this.type,
        onlyPartsMessage: this.onlyPartsMessage
      });
    }
  }

検索ボックスコンポーネントの親にあたるWWAパーツコンポーネントには、 v-on ディレクティブでメソッドが呼び出せます。

  template: `
    <div class="parts-list">
      <search-box v-on:search="setSearch"></search-box>
      <!-- WWAパーツ一覧のコンポーネントの部分 (長いので書かない) -->
    </div>
  `

今の所、検索で渡すデータが「キーワード」「タイプ」「パーツから参照されてないメッセージを除く」の3つの値の配列を毎回キーを書きながら渡していて、冗長になっていますので、そこあたりをきれいにまとめたいなあ。

将来はどうしよう?

当サイトの素材一覧 を、Vue.js で作り直して、素材を探しやすくしたいなあと思います。

恐らく、素材一覧ページと素材情報ページが必要になるので、ルーター機能を学ばないとなあ。

リポジトリ

リポジトリは以下になります。あまり期待しないでください。

github.com

*1:語彙力不足です。すみません。

*2:既存の処理は持ってきているので、作り直しとは言い難いかも?