【Unity】英単語帳Distinctionの日英検索をするAndroidアプリを作る

Creating Game

こんにちは。あっきーです。
早稲田大学を退学して4年間アメリカ留学に行く予定のものです。

いまプログラミングの学習をしていて、unityを使ってアプリやゲームの製作に挑戦しています。

勉強して1,2か月ほどして1つAndroidアプリを作ってみたので今回はそれを紹介します。

ここで学べるテーマとしては

  • 検索機能実装して入力した文字列から必要な情報を取り出す
  • UIの表示を端末ごとで崩れないようにする
  • スプレッドシートを利用してデータを扱う
  • Android用にアプリを作る

主にこの3つです。ぜひこのテーマについて学びたい方は参考にしてください。
また、C#やunityのコードの書き方も勉強になるので、これから始めたいという方もどうぞ。

英単語帳Distinctionで日本語検索から英語表現を調べる

今回実装するのは、簡易的な辞書です。
先に、完成品を動画で見ていただけると分かりやすいかと思うのでまずはご覧ください。

流れとしてはこんな感じです。

  • 日本語検索する
  • それに合った表現をリストで表示
  • 項目をタッチすると詳しい情報を表示

実はDistinctionという英単語帳は内容は文句ないんですが、日本語索引がないため日本語の表現を英語に直したいときに不便だと思ったんですね。
そこで、今回日本語から英語を参照できるようにアプリを作ってみようと思いました。

これはどんな単語帳でも実装できますし、映画やドラマやその他本で見た単語や表現も書くことができるので自作単語カードにもできます。

ということで、簡易的な簡易辞書を作っていきましょう。

目次:別リンクから順々に飛んでください。

  • UnityでAndroidアプリを出力(ビルド)する
  • Unityでスプレッドシートをインポートして利用する
  • InputFieldを使って文字検索する
  • リスト上に並べ、スクロール機能を実装する
  • 詳細画面などウィンドウの開閉を実装する

Unityでプロジェクトを作ろう

まずはプロジェクトを作ります。
今回は2Dプラットフォームを使うのでここだけ注意してください。

Androidのビルド設定をする

プロジェクトを開いたらビルド設定(どの機種に対応させるか)を行います。
今回はAndroid用アプリを作りたいので必要な設定をしていきましょう。

画面上側のツールバーより”File → Bulid Settings…”と行き、Androidプラットフォームに設定を変えておきましょう。

アプリを作ってからビルド設定を変えると変更に時間がかかったりするので最初にやっておくことをおすすめします。
Androidでビルドする際にはいくつか必要なツールがあるので今のうちに準備しておきましょう。
詳しくは以下の記事を参考にしてください。

Android用の画面に設定をしておく

ビルド設定を完了させると、ゲームビューの画面サイズをAndroidの画面サイズに変更することができます。
いろいろ種類があるんですが、一番無難なものとして、“16:9 Portrait”にしておきましょう。
一般的な「縦:横 = 16 : 9」のスマホに適しています。

スプレッドシートの準備

今回の肝であるスプレッドシートの作成と読み込みの準備を最初にしておきましょう。
スプレッドシートを使うメリットはデータ管理がしやすいというところです。

もちろんUnity内で一括でデータを管理するオブジェクト等を作ることはできますが、使い勝手が悪かったりデータ量が多すぎて何を作ったのか分からなくなってしまうこともしばしば。

スプレッドシートなら表形式で見やすくデータ管理できますし、変更も簡単。そして多くの方が使い慣れているかと思うのでとことん利用してやりましょう。

必要なものは以下の通りです。

  • スプレッドシート→GoogleスプレッドシートでもExcelでもOK
  • スプレッドシートを読み込むスクリプト→QuickSheetという便利ツールを利用

スプレッドシートを読み込むためのプログラムを書くのはなかなかしんどいのですでにある便利ツールを利用します。
QuickSheetというやつです。

これのダウンロードと使い方は以下の記事で説明していますので、参考にしてください。

スプレッドシートの作成例

今回はDistinctionという英単語帳を元に作成しているので、こちらに乗っている情報をうまくスプレッドシートにまとめようと思います。

作成例はこんな感じ。

A列から順に

  • id:番号(都合上0から定義)
  • word:見出し語
  • meaningJap:日本語の意味
  • meaningEng:英語の別表現
  • example:英文例
  • origin:起源、由来

という形になっています。とりあえず10個ほど自分で打ち込んでみてください。
これをQuickSheetで読み込めばUnityでデータが使えるようになります。

文字入力を受け付けるようにする

ここから、アプリを設計していきます。
まず、検索するための文字入力を受け付けるオブジェクトを作っていきます。

Canvasを設定

まず、今回はUIに大活躍してもらうのでCanvasを作成しましょう。
ヒエラルキー上で”UI→Canvas”をクリックしてCanvasを作成します。

そしてCanvasで以下のように設定します。

  • UI Scale Mode:Scale With Screen Size
  • Reference Resolution : 720×1280(例)
  • Screen Match Mode:Match Width or Height
  • Match : 1

UI Scale Modeはサイズの指定です。今回は、画面サイズに合わせるようにします。
これで先ほどゲームビューで設定した画面に合わせてCanvasの大きさが設定されることになります。

Reference Resolutionは実際に表示する範囲です。今回は720×1280pxの範囲を表示させるようにしました。

Screen Match Modeは拡大縮小をどのように行うかというものです。ここでは、縦か横のどちらかに比率を合わせるようにします。
その下のMatchでどちらかを選べるか設定できますが、今回は1にして縦に合わせます。
こうすると、縦方向のそれぞれのオブジェクトの距離が一定に保たれるのでスマホなどの縦長の画面には向いています。

Canvasの設定はこれでOKです。

スマホのような画面サイズが様々の場合、Canvasの設定はより注意しないといけないんですが、今回は特に公開するわけでもないので、自分のスマホに合うように設計しておきましょう。

InputFieldを設定する

ここから入力を受けるオブジェクトを作るのですが、Unityはまさにその機能をもったオブジェクトが初めからあります。
InputFieldというオブジェクトです。

ヒエラルキー上で”UI→InputField”をクリックすると作成できます。

InputFieldの設定は特に必要ありませんが、使い方についてはこちらを確認しておきましょう。

スクロール可能なリストを作成する

検索機能の次は、検索結果を出すリストを作っていきます。
単語を全て書くと800ありますので、スクロール機能がついたものを作成しましょう。

Scroll Viewを作成する

スクロール機能をつけるのも、Scroll Viewというオブジェクトを使えば簡単に実装可能です(Unity最高!)。”UI→Scroll View”をクリックしてオブジェクトを追加しましょう。


このような、オブジェクトができたと思います。(この画像は若干設定を変えてます。)
そして以下の設定をしておきましょう。

  • 大きさを変える:なるべく幅を取る
  • 色を変える:好みの色に
  • Scrollbar Horizontalを消す:今回は縦スクロールだけあればOK

Scrollbar Horizontalは横スクロールを実装するためのものなんですが、今回は横にスクロールする予定はないので消してOKです。

Scroll Viewでは基本的に子要素にある“Content”というものに対してスクリプトを書いたりするので理解しておいてください。

Scroll Viewに単語リストを追加する

Scroll Viewを追加できたらここに検索結果として現れる単語リストを作っていきます。

“UI→Panel”を追加し、このPanelをContentというオブジェクトの子要素に設定します。
大きさなどは変えなくていいので、2,3個適当に作ってみましょう。

Grid Layout Groupできれいに並べる

Panelをリスト状に表示させたいので、きれいに並べられるようにしたいです。1つ1つ手動で設定してもいいんですが、何十個もあると大変です。

そこで、Grid Layout Groupというコンポーネントを使ってきれいに並べます。

Scroll ViewのContentにGrid Layout Groupというコンポーネントを設定します。
そして以下のように設定します。

  • Padding:1要素ずつつける周りの余白
  • Cell Size:1要素の大きさ
  • Spacing:要素間の余白の大きさ
  • Start Comer:最初の要素が置かれる角
  • Start Axis:要素を並べる方向
  • Child Alignment:要素の揃え方(中央ぞろえなど)
  • Constraint:1列(1行)における要素数の制限

設定の意味はそれぞれこんな感じです。今回は「縦方向、中央ぞろえ、無制限に並べる」というようにしています。
画面に映らない分はスクロールで表示できるので無制限に並べて問題はありません。

リストの要素を作る

今、作ったリストの要素(Panel)を作っていきます。

先ほどのPanelに3つのTextオブジェクトを追加します。
名前はそれぞれ”Word”, “EnglishMeaning”, “ID”としておきます。
これは、検索にヒットした単語やその意味を表示させるためのものです。

これから作るプログラムの関係上、子要素の順番は図のようにお願いします。

これを好きなように大きさ等を変更してPanel上に配置してください。
Textコンポーネントの設定も参考にしてください。(文字の大きさや、揃え方など)。

詳細画面を作る

次にリストをクリックしたときに出す詳細画面を作っていきます。
ここには、英語の例文だったり、由来などを表示していきます。

“UI→Panel”を用意します。分かりやすいように名前を”WordInfo”とでもしておきましょう。
またその子要素に5つのTextオブジェクトを用意してください。図のように名前を変えておきましょう。

また、ここでもきれいに情報を並べたいので、WordInfoにGrid Layout Groupコンポーネントを追加して設定を変えましょう。(以下は設定の例です。)

設定を終えたら邪魔なので、非アクティブにして見えないようにしておきましょう。

これで基本的な表示は完成しました。

入力文字を受け取り、検索結果を出す

ここから、スクリプト作成に入っていきます。
あらかじめAssetフォルダの中に”Scripts”という名のフォルダを作っておきましょう。ここにスクリプトを保存していきます。
やることは、入力された文字から適切な英単語を取り出すことです。

文字を受け取り、検索結果を出すSetListスクリプト

Scriptsフォルダ内に”SetList”という名前でスクリプトを作成しましょう。
そして、これをScroll View内のContentオブジェクトにアタッチしておいてください。

先にスクリプトの全体をお見せします。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;

public class SetList : MonoBehaviour
{
    public InputField inputField;
    //検索文字列を保存
    public string searchWords;
    //スプレッドシート内の全データ
    public TempMaster tempMaster;
    //パネルのプレハブ
    public GameObject prefab;

    //パネル用の配列
    private List item;
    //tempMaster(スプレッドシート)の一部情報を格納
    private List temp;
    //itemの要素用番号
    private int num;

    //検索を開始しパネルをセット
    void OnEnable()
    {
        searchWords = inputField.text;
        num = 0;
        item = new List();
        temp = new List();

        for(int i = 0 ; i < tempMaster.dataArray.Length ; i++)
        {
            //検索した文字列の1部を含んでいた場合にリストを表示
            if(tempMaster.dataArray[i].Meaningjap.Contains(searchWords))
            {
                //リストに要素を追加する
                item.Add(Instantiate(prefab));
                //単語に関するデータを保存
                temp.Add(tempMaster.dataArray[i]);

                //アイテムの親をこのスクリプトが付いたオブジェクトにする
                item[num].transform.SetParent(this.transform);
                item[num].transform.localScale = new Vector3(1 ,1 ,1);

                //英単語を出力
                item[num].transform.Find("Word").GetComponent().text = tempMaster.dataArray[i].Word;
                //英語の別表現を出力
                item[num].transform.Find("EnglishMeaning").GetComponent().text = tempMaster.dataArray[i].Meaningeng;
                //IDを出力
                item[num].transform.Find("ID").GetComponent().text = tempMaster.dataArray[i].Id.ToString();

                num++;
            }
        }
    }

    void OnDisable()
    {
        for(int i = 0 ; i < num ; i++)
        {
            Destroy(item[i]);
        }
    }
}

大まかな解説はコメントを読んでください。

スクリプト内のTempMasterは僕のスプレッドシートのシート名が"TempMaster"という名前だからです。QuickSheetの解説でもあるように、自分でつけたシート名を使ってください。

インスペクターで設定

プログラムを書いたら、インスペクター上で設定をしていきます。
SetListコンポーネントでオブジェクトの設定をしてください。

  • Input Field→InputFieldオブジェクト
  • TempMaster(自分のシート名)→スプレッドシートから読み込んだScriptableObject
  • Prefab→Contentの子要素のPanel

コード解説


void OnEnable() {}

これは「オブジェクトがアクティブになったときに行う」メソッドです。
今回、入力を確定させたらリストを表示させるために、このメソッドが役に立ちます。


searchWords = inputField.text;

これは入力した文字を保存しているところです。inputField.textが入力した文字でそれをsearchWordsという変数に代入しています。

このsearchWordsと単語の日本語訳を比べてリストを表示させるようにします。


for(int i = 0 ; i < tempMaster.dataArray.Length ; i++)

これで、スプレッドシート内のすべてのデータを取り出しています。さっきのseachWordsと1つ1つ照らし合わせていきます(全データを参照しているので無駄が多いかも)。


if(tempMaster.dataArray[i].Meaningjap.Contains(searchWords))

ここで、リストに表示するかどうかの条件分岐をしています。

前半のtempMaster.dataArray[i].Meaningjapが単語の日本語の意味です。
後半のContains(searchWords)ですが、Containというメソッドが最初から備わっていて、「()内の文字列を含んでいるかどうか」というものを表しています。

つまり今回の場合、「日本語訳に検索文字列が含まれいるかどうか」を判断しているわけです。
例えば、「控えめな」という意味の単語があって、検索に「控えめ」と入力した場合、これは「含まれている」ということになって、if内の処理を行うことができます。

if内ではリストを表示させる処理を行っています。

残りはコメントを見ていただければ分かるかと思います。

画面の表示・非表示を操作するOperationWindowスクリプト

今回リストを表示させたり、詳細画面を表示させたりと、表示と非表示を繰り返し行います。
そのため、画面の表示を操作するスクリプトを作成していきましょう。

"OperationWindow"という名でスクリプトを作成します。
また、ヒエラルキー上で空のオブジェクトを作って、そこにアタッチします。

そして、以下がプログラムです。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class OperationWindow : MonoBehaviour
{
    public GameObject wordList;
    public GameObject wordInfo;

    void Update()
    {
        //戻るボタンを押すと、画面を非表示したり、アプリを終了させる
        if(Input.GetKeyDown(KeyCode.Escape))
        {
            //詳細画面が出ている場合はそれを閉じる
            if(wordInfo.activeSelf)
            {
                wordInfo.SetActive(false);
            }
            //詳細画面が出ていない場合は戻るボタンでアプリ終了
            else
            {
                Application.Quit();
                return;
            }
        }
    }

    //WordListをアクティブにする
    public void OnWordList()
    { 
        wordList.SetActive(!wordList.activeSelf);
    }

    //WordInfoをアクティブにする
    public void OnWordInfo()
    {
        wordInfo.SetActive(!wordInfo.activeSelf);
    }
}

大まかな解説はコメントを見てください。
OnWordListは検索確定時に、OnWordInfoはクリック時に呼び出されるようにしていきます(これから設定)。

インスペクター上で設定

プログラムを書いたら、インスペクター上で設定をしていきます。

  • Word List→Scroll Viewオブジェクト
  • Word Info→WordInfoオブジェクト

クリック時に行われるようにする

画面をクリック時だったり、検索確定時だったりに表示させたいのでもう少し設定をします。

まず、InputFieldの設定です。
InputFieldコンポーネントの下の方に、"On Edit End"というものがありますね。
これは、「入力を完了したときに設定したメソッドを呼び出しますよ」というものです。

ここにOn Word Listを呼び出せるように設定しましょう。

ちなみに"On Value Changed"は「変更があり次第呼び出す」というものです。
こちらを使うと予測変換みたいに、文字入力の都度、リストが表示されます。どちらを使ってもOKです。

次はWordInfoの設定です。
WordInfoというオブジェクトにEvent Triggerというコンポーネントを取り付けます。

そして"Add New Event Type"をクリックして"Pointer Click"を選びます。
後は先ほど同じように、OnWordInfoを設定すればOKです。

コード解説


if(Input.GetKeyDown(KeyCode.Escape))

これは、「Escキー(戻るボタン)を押しされたら」という条件分岐をしています。
Input.GetKeyDownはあらかじめ用意されているメソッドで、「()のキーが押されたらtrueを返す」というものです。
KeyCode.EscapeはEscキー(戻るボタン)のことなので、「Escキーが押されたらif内を実行する」ということになります。

wordList.SetActive(!wordList.activeSelf);

これは、表示非表示を1行で書いたものです。
wordList.activeSelfは「オブジェクトがアクティブか非アクティブか」を表していて、アクティブならtrue、非アクティブならfalseです。

これを!で否定(逆に)しているので、「非表示だったら表示させ、表示されていたら非表示にする」ということになります。

詳細画面で情報を書くItemInfoスクリプト

次はItemInfoスクリプトを作ります。
詳細画面を表示した際に、クリックした要素のデータを受け取りその情報を表示するためのものです。

"ItemInfo"という名前でスクリプトを作り、WordInfoにアタッチしてください。

以下がプログラムです。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ItemInfo : MonoBehaviour
{
    public void SetInfo(TempMasterData tempMasterData)
    {
        transform.GetChild(0).GetComponent().text = tempMasterData.Word;
        transform.GetChild(1).GetComponent().text = tempMasterData.Meaningjap;
        transform.GetChild(2).GetComponent().text = tempMasterData.Meaningeng;
        transform.GetChild(3).GetComponent().text = tempMasterData.Example;
        transform.GetChild(4).GetComponent().text = tempMasterData.Origin;
    }
}

ここにはSetInfoというメソッドだけ作っています。
「クリックしたリスト要素の情報を受け取り、その情報を表示させる」というものです。

リストをクリックしたら詳細画面を表示させるItemButtonスクリプト

Scriptsフォルダの中に"ItemButton"という名のスクリプトを用意しましょう。
そしてこれをScroll Viewの中の"Panel"にアタッチします。

後はプログラムを書いていきます。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ItemBottun : MonoBehaviour
{
    //スプレッドシート内の全データ
    public TempMaster tempMaster;

    private ItemInfo itemInfo;
    private OperationWindow operationWindow;
    private int id;

    // Start is called before the first frame update
    void Start()
    {
        operationWindow = GameObject.Find("WindowOperator").GetComponent();

        //Panel内のIDが書かれたテキストをint型にして保存
        id = int.Parse(transform.Find("ID").GetComponent().text);
    }

    //詳細画面を開くメソッド(クリック時に呼ばれる)
    public void OpenWordInfo()
    {
        //WordInfoのアクティブ化
        operationWindow.OnWordInfo();
        itemInfo = GameObject.Find("WordInfo").GetComponent();
        itemInfo.SetInfo(tempMaster.dataArray[id]);
    }
}

インスペクター上の設定

ItemBottunコンポーネントで以下のように設定します。

  • TempMaster(自分のシート名)→自分のシート名のScriptable Object

クリック時に呼び出す設定

プログラム内のOpenWordInfoは要素がクリックされたときに呼ばれて欲しいので、先ほどやったEvent Triggerコンポーネントを使って設定していきます(やり方は省略)。

コードの解説

itemInfo.SetInfo(tempMaster.dataArray[id]);

先ほど作ったItemInfoスクリプトのSetInfo/code>メソッドを呼び出しています。
tempMaster.dataArray[id]はクリックされた要素の情報をなので、これを引数として渡すことで詳細画面に情報を出力することができます。

Panelをプレハブ化する

最後に、Panelをプレハブ化しましょう。
リストに同じPanelを作成して表示させるため、プレハブにした方が都合がいいです。

Assetファルダ内に"Prefabs"という名前でフォルダを作りましょう。
そして、ヒエラルキーのPanel(Scroll View内の)をこのフォルダへドラッグアンドドロップします。
オブジェクトが青く表示されていればOKです。ヒエラルキー上のPanelはいらないので削除しておきましょう。

ふぅ、疲れました。完成です。

お疲れ様でした。完成です。
後はビルドしてAndroidスマホで起動すれば使えます。
スプレッドシートは毎日10個ほど追加していけば、2ヶ月くらいで完成と思います(ついでに単語学習にもなる)。
(スプレッドシートを変更後は改めてビルドする必要があるので注意を。)

問題点

データを全参照していて効率が悪い

SetListスクリプト内でリストに表示させるか判断するために、forで全データに参照していますが、これはちょっと無駄ですね。テストでは10個程度しかデータがなかったんですが、500,800とかになると検索に時間がかかるかもしれませんね。

全部参照しない方法を探すべきかな。

あいまいでの検索ができない

今回の検索の方法が「検索文字列が日本語訳に完全に含まれている」というものでした。
なので、例えば「イライラさせる」という検索でヒットしても「怒らせる」という検索ではヒットしません。

また「控えめな」でヒットしても「ひかえめな」ではヒットしません。

似た意味の日本語で同じようにヒットできるようにしたり、ひらがなでも漢字でもヒットするようにできれば良いですね。

何かアイデアがある方は、Twitter等で教えていただけると嬉しいです。

人気記事現役大学生が告白!大学生がブログを始めるのをオススメする理由

人気記事WordPress(ワードプレス)の始め方【初心者でも15分で開設】