カテゴリー: プログラミング

うにのトゲは刺さると痛い(´・ω・`)-16

Windows Updateとの苦闘を繰り返した間に、何をどれくらいやったか忘れた("・ω・゙)

まあでも内部的な変更とか追加ばっかりだったから、あんまりC#やUnityがどうこうって話は少ないかな…?


と思ったが、一つ二つUnity絡みで不可解な事象にハマっていたことを思い出した。

謎エラーに遭遇(´・ω・`)

或るタイミングで以下のエラーが表示されてUnityが起動しなくなった…んだったかな?

そのタイミングは確かVisual Studioの更新かなんかを促されて適当にやってしまった後だったような…

メッセージにある通り、"unity editor resources"がおかしいらしい。これの復旧方法が解らず、結局インストーラを再び叩いて再インストール(?)したら、このエラーは表示されなくなった。本当にVSの更新が原因かどうかは解らないが、何はともあれ再インストールというか上書きインストールすれば一応回避出来た。


謎例外に遭遇(´・ω・`)

結論から言えば、コードに問題があったのだが、なんでこの例外になるのかが解らなかった。

ハンドルされない例外が 0x000000014087332F (Unity.exe) で発生しました: 0xC0000005: 場所 0x0000000000000000 の読み取り中にアクセス違反が発生しました。 が発生しました

具体的にどういう状況かと言うと、GameManagerで

public Text MessageDisplay;

というモノを作り、Editor上でD&Dしておいて、メッセージを表示させる。→ ゲーム終了時にDestroy → タイトル画面に戻らずに(シーン遷移せずに)リスタートしようとした時に再び利用としていた。

まあ、Destoryしてるものを再び利用しようとしたらエラーになるのは解るのだが、例外が起きてUnity毎落ちてしまうのかが謎(´・ω・`)

もしかしたら、今まで遭遇して来なかっただけで、これが正しい挙動なのかな? 或るいはうちの環境だけの問題なのかも。まあ、Destroyしないで

MessageDisplay.gameObject.SetActive(false);

みたいな感じにしたら問題は起きなくなったので良いか。

何はともあれ、少なからずうちの環境でまたこれが起きたら、Destroyしたオブジェクトを触ろうとしていないかを疑えばいいのかな。


設定ダイアログ

そう言えば、ボタン用のテクスチャをコード内で作成するようにしようとして結構時間を費やしてしまったのを思い出した…(ヽ'ω`)

頑張った割にアンチエイリアスとかが上手く出来ていないのでアレな出来になってしまった。いずれもう少しブラッシュアップしたいところではあるが…

ゲームプレイ中に効果音とBGM、オーバーレイ表示を切り替えられるようにした。これも元々のコードがそんなことを想定していない感じで書いていたので、直すのに苦労した(ノ∀`)

切り替え用UIにはToggleを使用したが、大きさを変えるとチェックボックスが消えた(´・ω・`) 何処を設定すれば表示されるかは解らず、まあこのままでもいいかと放置w GUIContent等を利用してアイコンにノーチェック時とチェック時の画像をセットすればいいだけなのかもしれないけれども、そこまでチェックボックス自体は重要ではないかなと思ったので。


得点演出

カードがマッチした後に何にもないと寂しいので得点時演出を追加する。と言っても、得点とコンボ数、コンボ内容に応じた褒め言葉が上に上がって消えるだけ(ノ∀`)

得点体系がいまいちしっくり来ないのでこれも直さないとな。まあでも、正直なところ、この神経衰弱ゲームにおける得点という要素はあくまでもおまけなのでそんなにこだわらなくても良いような気がしないでもないw


得点結果

一応、得点結果の表示をするようにもした。得点もカウントアップするけれども、ちょっと時間を調整しないとすぐに終わってしまうな(´・ω・`)

色とかもどうしようかなぁ…(´・ω・`)


設定画面にしろ、得点時演出にしろ、痛感したことは

俺氏、この手のことに
ガチでセンスがねぇ( ;・´ω・`)ゴクリッ

思いっきり時間をかければ、いくらかはマシになるかもしれないけれども、パッと出せる俺氏のセンスではこんなのが精一杯だw なんつーか「ゲームってこーいう風だよね(・∀・)」って頭で、センスではなくロジックで作ったという感じの出来(ノ∀`)

まあこの辺は今は措いておくしかないw
この神経衰弱の本当の狙いはゲームとしての楽しさではなくもっと消極的時間潰しだし、習作故にゲームに必要な要素を一通り舐めることが目的なので、この辺は今は切り捨てておく。

本当は6月中に機能を全部載せて、7月にカードコンテンツを増やしてリリースするつもりだったが、PCに色々あったり、暑くなり死にかけ始めているので思うように進んでいない…_| ̄|○

うにのトゲは刺さると痛い(´・ω・`)-15

パソコンというかシステムがおかしくなったので、それの復旧をしていたり、他のことをしていたり、色々と改修したけど何を回収したのか忘れたりで前のエントリから大分経ってしまった…(´・ω・`)

進展はしてるけれども、リファクタリング的なことの方が多かったかな?


Unityの日本語化

取り敢えず復旧の際に2018.1にした。何かを調べていた時に日本語化が出来ることを知ったので日本語化した。ちょっと感動する(・∀・)

でも"RectTransform"等のコンポーネント名まで無理に"矩形トランスフォーム"のように訳さなくても良いんじゃなかろうかと思ったり。

参考:
【Unity】Unity2018.1を日本語化する方法


.NET 4.6 (C# 6.0)

確か、これもやったような気がする…("・ω・゙)

参考:
Unity2017 で .NET 4.6 (C# 6.0) を使う方法


Outline

こんな感じでOutlineを動的に追加したような…("・ω・゙)

var outlineComponent = textObject.AddComponent<Outline>();
outlineComponent.effectColor = new Color32(0, 80, 80, 255);
outlineComponent.effectDistance = new Vector2(1, -1);

参考:
Unity5のColor構造体は、0から1の範囲で、Color32構造体が0から255の範囲を取ります。


あとは内部的に作り変えたことばかり…のような……(´・ω・`)
えらく時間がかかって面倒くさかったけれども特筆する技術的な問題はなかった…かな?

ネパールとバチカン市国の旗が通常の国旗と形状や比率が異なることから、正しく表示されていなかったのを解消したくらい。なるべく文字のはみ出しが発生しないようにしてみたけれども、全部は確認していない(ノ∀`)

メニューの位置とか、表示情報の整理とかしないといけない(´・ω・`)メドイ

カード裏面を分けられるようにしてみたが、思っていた以上に単一言語モードではない日英モードはやっていて難しいというかきつい。記憶力だけの問題じゃないような気がしてきたw カード裏面を分けられるようになって気づいたが、シャッフルは出来ていても、妙にカードが偏ってしまう。もう少しその辺をチューンするか、無理矢理交互になるような仕組みにすべきなのかな?


取り敢えず今は得点系っぽいのを実装中。これも色々考えると面倒だなぁ…(´・ω・`)

うにのトゲは刺さると痛い(´・ω・`)-14 : LLM

※2018/05/19追記 ファイルが存在しない場合にDefaultLanguageを開くようにすることを忘れていたので追加して修正した(ノ∀`) 多分これでいいはず…(´・ω・`)

ついでにGitHubにも上げてみたけど、久しぶり過ぎて、まともに上がってるかどうかは不明(ノ∀`)
https://github.com/elekingmole/LocalizedLanguageManagerForUnity

またしても寄り道し過ぎてて、3日ほど時間を費やした…_| ̄|○


国際化

完成はまだ先だけど、そろそろタイトル画面で使用している文字列もローカリゼーションを適用させるかと、ぐぐる。色々と読む……

 

 

 

なんでUnityは国際化の仕組みを提供してないんだよヽ(`Д´)ノ

誰も作ってないのが不思議……もしかしたらアセットで配布されてんのかな(´・ω・`)?


しょうがないので、Javaのローカリゼーションを真似て作る。と言っても、Key=value形式でテキストファイルに格納する形にしただけだがw

コンセプト等は、

  • Key=value形式("title=タイトル"や"start=スタート"みたいな)を羅列した各言語ファイルを作る。BOMなしにしないとファイル頭に"EFBBBF"がひっついてlls[key]でtitleが取得出来なくなる(ノ∀`)ハマッタ
  • 各言語ファイルの拡張子はllf(Localized Language file)としたが、SetFileExtension()で書き換えられるようにしたので、何でもいいw
  • splitしたセットをDictionaryに格納。実際に使う時はLLM.GetLocalizedString("title")のようにkeyを引数にしてローカライズされた言語要素を取得する。
  • SetLanguage()でApplication.systemLanguageに対応するtargetFilenameを取得して拡張子を追加してコルーチンを呼び出す。最初は動的変更が出来るようにしようと思って、こういうメソッドにしたが、Application.systemLanguageは起動時に環境変数(?)を取得して、アプリが再起動されるまで保持し続けるようだから意味がなかった(ノ∀`) この処理はStart()でやるべきかな?
  • 本当はResources以下にllfファイルを入れてResources.Loadする方が楽なのだが、これだと開発者だけしかllfファイルの追加が出来ない。 ユーザーが独自のllfファイルを追加して使用出来るようにする為に、wwwによるアクセス方法を選択する必要があったので、通常時も同じようにwwwによってアクセスするStreamingAssetsにllfファイルを置くようにコーディングした。
  • Androidのパスは正直良く解らないというかバージョンによって変わってしまう酷いものなので、無理に全対応をすることは諦めた(ノ∀`) 取り敢えずはStreamingAssetsとインストール後のフォルダ(内部ストレージのAndroid\data\com.Com.Pr\files(?))とユーザー自らがパスを調べて手入力してもらうという力技でSDカード上のファイルを読み込めるようにしたw
  • 凄い適当実装なのと、そんなにテストしてないから、本当にまともに動くかは不明w 一応、日本語と英語は試したけども。
  • llfファイル名は基本的に各言語の最初の三文字。例外は日本とスロバキアとスロベニアと中国語繁体字と中国語簡体字かな?
  • 本当は開始時に各言語ファイル名群(jpnやeng)を文字配列に代入しておいて、switch (Application.systemLanguage)の箇所でその文字配列をtargetFilenameに代入する形にしようかと思ったが面倒くさいのでやめた。こうしておいて、その文字配列への代入メソッドを作って任意のファイルを読み込めるようにしておけば、各システム言語に対して任意の言語ファイルを割り当てられると思ったが、そこまでする意味があるのかわからなくなったから(ノ∀`) unKnownとかdefaultで処理しても良さげな分岐が順番通りに残っているのはその時の迷いの痕跡w
  • LoadTextData()は最初はコールバックのある形にしていたが、呼び出し側にコールバックを用意するのが面倒になって削除した(ノ∀`) www.isDoneとかを使うべきなのだが、面倒くさくてやってないw GetLocalizedString()でエラーを出さない為だけにisReadFinishedなんてのを用意したが、いつか直さないといけない気がしないでもない(´・ω・`)

テストは同じCanvasに下の二つのスクリプトを貼り、各言語ファイルの内容をちょっとだけ書き換え、StreamingAssetsとインストール後のフォルダとSDカードのルートにコピーして実行。あとPlayerSettingsで[Minimum API Level]はAndroid5.0以上にし、[Write Permission]は"External"にした。

LocalizationTestのStart()の

LLM.SetPathMode(LocalizedLanguageManager.PathMode.persistentData)
.SetLanguage();

の箇所を、StreamingAssetsの時は

LLM.SetLanguage();

に変える。

インストール後のフォルダの時は変更なし。

手入力の時は

LLM.SetPathMode(LocalizedLanguageManager.PathMode.manuallyInput)
.SetManuallyInputPath("(SDカードのパス)").SetLanguage();

に変える。SDカードのパスはESエクスプローラーで調べた(ノ∀`)


LocalizedLanguageManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System;
using UnityEngine.Events;

public class LocalizedLanguageManager : MonoBehaviour
{
    Dictionary&lt;string, string&gt; lls = new Dictionary&lt;string, string&gt;();
    TextReader textReader;
    public bool isReadFinished;

<pre><code>string defaultLanguage = &amp;quot;eng&amp;quot;;
string forcedLanguage = &amp;quot;&amp;quot;;
string fileExtension = &amp;quot;.llf&amp;quot;;
string manuallyInputPath = &amp;quot;&amp;quot;;
PathMode pathMode = PathMode.streamingAsset;

public LocalizedLanguageManager SetFileExtension(string extension)
{
    fileExtension = extension;
    return this;
}

public LocalizedLanguageManager SetPathMode(PathMode mode)
{
    pathMode = mode;
    return this;
}

public LocalizedLanguageManager SetManuallyInputPath(string path)
{
    manuallyInputPath = path;
    return this;
}

public LocalizedLanguageManager SetDefaultLanguage(string filename)
{
    defaultLanguage = filename;
    return this;
}

public LocalizedLanguageManager SetForcedLanguage(string filename)
{
    forcedLanguage = filename;
    return this;
}

public void SetLanguage()
{
    string targetFilename = &amp;quot;&amp;quot;;

    if (forcedLanguage.Length == 0)
    {
        switch (Application.systemLanguage)
        {
            case SystemLanguage.Afrikaans:
                targetFilename = &amp;quot;afr&amp;quot;;
                break;
            case SystemLanguage.Arabic:
                targetFilename = &amp;quot;ara&amp;quot;;
                break;
            case SystemLanguage.Basque:
                targetFilename = &amp;quot;bas&amp;quot;;
                break;
            case SystemLanguage.Belarusian:
                targetFilename = &amp;quot;bel&amp;quot;;
                break;
            case SystemLanguage.Bulgarian:
                targetFilename = &amp;quot;bul&amp;quot;;
                break;
            case SystemLanguage.Catalan:
                targetFilename = &amp;quot;cat&amp;quot;;
                break;
            case SystemLanguage.Chinese:
                targetFilename = &amp;quot;chi&amp;quot;;
                break;
            case SystemLanguage.Czech:
                targetFilename = &amp;quot;cze&amp;quot;;
                break;
            case SystemLanguage.Danish:
                targetFilename = &amp;quot;dan&amp;quot;;
                break;
            case SystemLanguage.Dutch:
                targetFilename = &amp;quot;dut&amp;quot;;
                break;
            case SystemLanguage.English:
                targetFilename = &amp;quot;eng&amp;quot;;
                break;
            case SystemLanguage.Estonian:
                targetFilename = &amp;quot;est&amp;quot;;
                break;
            case SystemLanguage.Faroese:
                targetFilename = &amp;quot;far&amp;quot;;
                break;
            case SystemLanguage.Finnish:
                targetFilename = &amp;quot;fin&amp;quot;;
                break;
            case SystemLanguage.French:
                targetFilename = &amp;quot;fre&amp;quot;;
                break;
            case SystemLanguage.German:
                targetFilename = &amp;quot;ger&amp;quot;;
                break;
            case SystemLanguage.Greek:
                targetFilename = &amp;quot;gre&amp;quot;;
                break;
            case SystemLanguage.Hebrew:
                targetFilename = &amp;quot;heb&amp;quot;;
                break;
            case SystemLanguage.Icelandic:
                targetFilename = &amp;quot;ice&amp;quot;;
                break;
            case SystemLanguage.Indonesian:
                targetFilename = &amp;quot;ind&amp;quot;;
                break;
            case SystemLanguage.Italian:
                targetFilename = &amp;quot;ita&amp;quot;;
                break;
            case SystemLanguage.Japanese:
                targetFilename = &amp;quot;jpn&amp;quot;;
                break;
            case SystemLanguage.Korean:
                targetFilename = &amp;quot;kor&amp;quot;;
                break;
            case SystemLanguage.Latvian:
                targetFilename = &amp;quot;lat&amp;quot;;
                break;
            case SystemLanguage.Lithuanian:
                targetFilename = &amp;quot;lit&amp;quot;;
                break;
            case SystemLanguage.Norwegian:
                targetFilename = &amp;quot;nor&amp;quot;;
                break;
            case SystemLanguage.Polish:
                targetFilename = &amp;quot;pol&amp;quot;;
                break;
            case SystemLanguage.Portuguese:
                targetFilename = &amp;quot;por&amp;quot;;
                break;
            case SystemLanguage.Romanian:
                targetFilename = &amp;quot;rom&amp;quot;;
                break;
            case SystemLanguage.Russian:
                targetFilename = &amp;quot;rus&amp;quot;;
                break;
            case SystemLanguage.SerboCroatian:
                targetFilename = &amp;quot;ser&amp;quot;;
                break;
            case SystemLanguage.Slovak:
                targetFilename = &amp;quot;svk&amp;quot;;
                break;
            case SystemLanguage.Slovenian:
                targetFilename = &amp;quot;svn&amp;quot;;
                break;
            case SystemLanguage.Spanish:
                targetFilename = &amp;quot;spa&amp;quot;;
                break;
            case SystemLanguage.Swedish:
                targetFilename = &amp;quot;swe&amp;quot;;
                break;
            case SystemLanguage.Thai:
                targetFilename = &amp;quot;tha&amp;quot;;
                break;
            case SystemLanguage.Turkish:
                targetFilename = &amp;quot;tur&amp;quot;;
                break;
            case SystemLanguage.Ukrainian:
                targetFilename = &amp;quot;ukr&amp;quot;;
                break;
            case SystemLanguage.Vietnamese:
                targetFilename = &amp;quot;vie&amp;quot;;
                break;
            case SystemLanguage.ChineseSimplified:
                targetFilename = &amp;quot;chs&amp;quot;;
                break;
            case SystemLanguage.ChineseTraditional:
                targetFilename = &amp;quot;cht&amp;quot;;
                break;
            case SystemLanguage.Unknown:
                break;
            case SystemLanguage.Hungarian:
                targetFilename = &amp;quot;hun&amp;quot;;
                break;
            default:
                break;
        }

        if (targetFilename.Length == 0)
        {
            targetFilename = defaultLanguage + fileExtension;
        }
        else
        {
            targetFilename += fileExtension;
        }
    }
    else
    {
        targetFilename = forcedLanguage + fileExtension;
    }

    StartCoroutine(LoadTextData(targetFilename));
}

public string GetLocalizedString(string key)
{
    if (isReadFinished)
    {
        if (lls.ContainsKey(key))
        {
            return lls[key];
        }
        else
        {
            string temp = &amp;quot;[key] &amp;quot;;
            foreach (string s in lls.Keys)
            {
                temp += s + &amp;quot;/&amp;quot;;
            }
            temp += &amp;quot;\n&lt;span class="f_l" style="color:#330000"&gt;※2018/05/19追記 ファイルが存在しない場合にDefaultLanguageを開くようにすることを忘れていたので追加して修正した(ノ∀`)&lt;/span&gt; &lt;span class="f_s"&gt;多分これでいいはず…(´・ω・`)&lt;/span&gt;
</code></pre>

<span class="f_s">ついでにGitHubにも上げてみたけど、久しぶり過ぎて、まともに上がってるかどうかは不明(ノ∀`)
<a href="https://github.com/elekingmole/LocalizedLanguageManagerForUnity" rel="noopener noreferrer" target="_blank">https://github.com/elekingmole/LocalizedLanguageManagerForUnity</a></span>

またしても寄り道し過ぎてて、3日ほど時間を費やした…_| ̄|○

<hr>

<span class="hd_01">国際化</span>

完成はまだ先だけど、そろそろタイトル画面で使用している文字列もローカリゼーションを適用させるかと、ぐぐる。色々と読む……

&nbsp;

&nbsp;

&nbsp;

<span class="f_l">なんでUnityは国際化の仕組みを提供してないんだよヽ(`Д´)ノ</span>

誰も作ってないのが不思議……もしかしたらアセットで配布されてんのかな(´・ω・`)?

<hr width="90%">

しょうがないので、Javaのローカリゼーションを真似て作る。<span class="f_s">と言っても、Key=value形式でテキストファイルに格納する形にしただけだがw</span>

コンセプト等は、

<ul>
    <li>Key=value形式("title=タイトル"や"start=スタート"みたいな)を羅列した各言語ファイルを作る。BOMなしにしないとファイル頭に"EFBBBF"がひっついてlls[key]でtitleが取得出来なくなる(ノ∀`)ハマッタ</li>
    <li>各言語ファイルの拡張子はllf(Localized Language file)としたが、SetFileExtension()で書き換えられるようにしたので、何でもいいw</li>
    <li>splitしたセットをDictionaryに格納。実際に使う時はLLM.GetLocalizedString("title")のようにkeyを引数にしてローカライズされた言語要素を取得する。</li>

    <li>SetLanguage()でApplication.systemLanguageに対応するtargetFilenameを取得して拡張子を追加してコルーチンを呼び出す。最初は動的変更が出来るようにしようと思って、こういうメソッドにしたが、Application.systemLanguageは起動時に環境変数(?)を取得して、アプリが再起動されるまで保持し続けるようだから意味がなかった(ノ∀`) この処理はStart()でやるべきかな?</li>
    <li>本当はResources以下にllfファイルを入れてResources.Loadする方が楽なのだが、これだと開発者だけしかllfファイルの追加が出来ない。 ユーザーが独自のllfファイルを追加して使用出来るようにする為に、wwwによるアクセス方法を選択する必要があったので、通常時も同じようにwwwによってアクセスするStreamingAssetsにllfファイルを置くようにコーディングした。</li>
    <li>Androidのパスは正直良く解らないというかバージョンによって変わってしまう酷いものなので、無理に全対応をすることは諦めた(ノ∀`) 取り敢えずはStreamingAssetsとインストール後のフォルダ(内部ストレージのAndroid\data\com.Com.Pr\files(?))とユーザー自らがパスを調べて手入力してもらうという力技でSDカード上のファイルを読み込めるようにしたw</li>
    <li>凄い適当実装なのと、そんなにテストしてないから、本当にまともに動くかは不明w 一応、日本語と英語は試したけども。</li>

    <li>llfファイル名は基本的に各言語の最初の三文字。例外は日本とスロバキアとスロベニアと中国語繁体字と中国語簡体字かな?</li>

    <li>本当は開始時に各言語ファイル名群(jpnやeng)を文字配列に代入しておいて、switch (Application.systemLanguage)の箇所でその文字配列をtargetFilenameに代入する形にしようかと思ったが面倒くさいのでやめた。こうしておいて、その文字配列への代入メソッドを作って任意のファイルを読み込めるようにしておけば、各システム言語に対して任意の言語ファイルを割り当てられると思ったが、そこまでする意味があるのかわからなくなったから(ノ∀`) unKnownとかdefaultで処理しても良さげな分岐が順番通りに残っているのはその時の迷いの痕跡w</li>

    <li>LoadTextData()は最初はコールバックのある形にしていたが、呼び出し側にコールバックを用意するのが面倒になって削除した(ノ∀`) www.isDoneとかを使うべきなのだが、面倒くさくてやってないw GetLocalizedString()でエラーを出さない為だけにisReadFinishedなんてのを用意したが、いつか直さないといけない気がしないでもない(´・ω・`)</li>

</ul>

<hr width="90%">

テストは同じCanvasに下の二つのスクリプトを貼り、各言語ファイルの内容をちょっとだけ書き換え、StreamingAssetsとインストール後のフォルダとSDカードのルートにコピーして実行。あとPlayerSettingsで[Minimum API Level]はAndroid5.0以上にし、[Write Permission]は"External"にした。

LocalizationTestのStart()の
[csharp]
LLM.SetPathMode(LocalizedLanguageManager.PathMode.persistentData)
.SetLanguage();

の箇所を、StreamingAssetsの時は

LLM.SetLanguage();

に変える。

インストール後のフォルダの時は変更なし。

手入力の時は

LLM.SetPathMode(LocalizedLanguageManager.PathMode.manuallyInput)
.SetManuallyInputPath("(SDカードのパス)").SetLanguage();

に変える。SDカードのパスはESエクスプローラーで調べた(ノ∀`)


LocalizedLanguageManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System;
using UnityEngine.Events;

public class LocalizedLanguageManager : MonoBehaviour
{
    Dictionary&lt;string, string&gt; lls = new Dictionary&lt;string, string&gt;();
    TextReader textReader;
    public bool isReadFinished;

<pre><code>string defaultLanguage = &amp;quot;eng&amp;quot;;
string forcedLanguage = &amp;quot;&amp;quot;;
string fileExtension = &amp;quot;.llf&amp;quot;;
string manuallyInputPath = &amp;quot;&amp;quot;;
PathMode pathMode = PathMode.streamingAsset;

public LocalizedLanguageManager SetFileExtension(string extension)
{
    fileExtension = extension;
    return this;
}

public LocalizedLanguageManager SetPathMode(PathMode mode)
{
    pathMode = mode;
    return this;
}

public LocalizedLanguageManager SetManuallyInputPath(string path)
{
    manuallyInputPath = path;
    return this;
}

public LocalizedLanguageManager SetDefaultLanguage(string filename)
{
    defaultLanguage = filename;
    return this;
}

public LocalizedLanguageManager SetForcedLanguage(string filename)
{
    forcedLanguage = filename;
    return this;
}

public void SetLanguage()
{
    string targetFilename = &amp;quot;&amp;quot;;

    if (forcedLanguage.Length == 0)
    {
        switch (Application.systemLanguage)
        {
            case SystemLanguage.Afrikaans:
                targetFilename = &amp;quot;afr&amp;quot;;
                break;
            case SystemLanguage.Arabic:
                targetFilename = &amp;quot;ara&amp;quot;;
                break;
            case SystemLanguage.Basque:
                targetFilename = &amp;quot;bas&amp;quot;;
                break;
            case SystemLanguage.Belarusian:
                targetFilename = &amp;quot;bel&amp;quot;;
                break;
            case SystemLanguage.Bulgarian:
                targetFilename = &amp;quot;bul&amp;quot;;
                break;
            case SystemLanguage.Catalan:
                targetFilename = &amp;quot;cat&amp;quot;;
                break;
            case SystemLanguage.Chinese:
                targetFilename = &amp;quot;chi&amp;quot;;
                break;
            case SystemLanguage.Czech:
                targetFilename = &amp;quot;cze&amp;quot;;
                break;
            case SystemLanguage.Danish:
                targetFilename = &amp;quot;dan&amp;quot;;
                break;
            case SystemLanguage.Dutch:
                targetFilename = &amp;quot;dut&amp;quot;;
                break;
            case SystemLanguage.English:
                targetFilename = &amp;quot;eng&amp;quot;;
                break;
            case SystemLanguage.Estonian:
                targetFilename = &amp;quot;est&amp;quot;;
                break;
            case SystemLanguage.Faroese:
                targetFilename = &amp;quot;far&amp;quot;;
                break;
            case SystemLanguage.Finnish:
                targetFilename = &amp;quot;fin&amp;quot;;
                break;
            case SystemLanguage.French:
                targetFilename = &amp;quot;fre&amp;quot;;
                break;
            case SystemLanguage.German:
                targetFilename = &amp;quot;ger&amp;quot;;
                break;
            case SystemLanguage.Greek:
                targetFilename = &amp;quot;gre&amp;quot;;
                break;
            case SystemLanguage.Hebrew:
                targetFilename = &amp;quot;heb&amp;quot;;
                break;
            case SystemLanguage.Icelandic:
                targetFilename = &amp;quot;ice&amp;quot;;
                break;
            case SystemLanguage.Indonesian:
                targetFilename = &amp;quot;ind&amp;quot;;
                break;
            case SystemLanguage.Italian:
                targetFilename = &amp;quot;ita&amp;quot;;
                break;
            case SystemLanguage.Japanese:
                targetFilename = &amp;quot;jpn&amp;quot;;
                break;
            case SystemLanguage.Korean:
                targetFilename = &amp;quot;kor&amp;quot;;
                break;
            case SystemLanguage.Latvian:
                targetFilename = &amp;quot;lat&amp;quot;;
                break;
            case SystemLanguage.Lithuanian:
                targetFilename = &amp;quot;lit&amp;quot;;
                break;
            case SystemLanguage.Norwegian:
                targetFilename = &amp;quot;nor&amp;quot;;
                break;
            case SystemLanguage.Polish:
                targetFilename = &amp;quot;pol&amp;quot;;
                break;
            case SystemLanguage.Portuguese:
                targetFilename = &amp;quot;por&amp;quot;;
                break;
            case SystemLanguage.Romanian:
                targetFilename = &amp;quot;rom&amp;quot;;
                break;
            case SystemLanguage.Russian:
                targetFilename = &amp;quot;rus&amp;quot;;
                break;
            case SystemLanguage.SerboCroatian:
                targetFilename = &amp;quot;ser&amp;quot;;
                break;
            case SystemLanguage.Slovak:
                targetFilename = &amp;quot;svk&amp;quot;;
                break;
            case SystemLanguage.Slovenian:
                targetFilename = &amp;quot;svn&amp;quot;;
                break;
            case SystemLanguage.Spanish:
                targetFilename = &amp;quot;spa&amp;quot;;
                break;
            case SystemLanguage.Swedish:
                targetFilename = &amp;quot;swe&amp;quot;;
                break;
            case SystemLanguage.Thai:
                targetFilename = &amp;quot;tha&amp;quot;;
                break;
            case SystemLanguage.Turkish:
                targetFilename = &amp;quot;tur&amp;quot;;
                break;
            case SystemLanguage.Ukrainian:
                targetFilename = &amp;quot;ukr&amp;quot;;
                break;
            case SystemLanguage.Vietnamese:
                targetFilename = &amp;quot;vie&amp;quot;;
                break;
            case SystemLanguage.ChineseSimplified:
                targetFilename = &amp;quot;chs&amp;quot;;
                break;
            case SystemLanguage.ChineseTraditional:
                targetFilename = &amp;quot;cht&amp;quot;;
                break;
            case SystemLanguage.Unknown:
                break;
            case SystemLanguage.Hungarian:
                targetFilename = &amp;quot;hun&amp;quot;;
                break;
            default:
                break;
        }

        if (targetFilename.Length == 0)
        {
            targetFilename = defaultLanguage + fileExtension;
        }
        else
        {
            targetFilename += fileExtension;
        }
    }
    else
    {
        targetFilename = forcedLanguage + fileExtension;
    }

    StartCoroutine(LoadTextData(targetFilename));
}

public string GetLocalizedString(string key)
{
    if (isReadFinished)
    {
        if (lls.ContainsKey(key))
        {
            return lls[key];
        }
        else
        {
            string temp = &amp;quot;[key] &amp;quot;;
            foreach (string s in lls.Keys)
            {
                temp += s + &amp;quot;/&amp;quot;;
            }
            temp += &amp;quot;\n[value] &amp;quot;;
            foreach (string s in lls.Values)
            {
                temp += s + &amp;quot;/&amp;quot;;
            }

            return temp;
        }
    }
    else
    {
        return &amp;quot;Not Ready&amp;quot;;
    }
}


public IEnumerator LoadTextData(string targetFilename)
{
    string path = &amp;quot;&amp;quot;;
    string textBuffer = &amp;quot;&amp;quot;;
</code></pre>

#if UNITY_EDITOR
        path = Application.streamingAssetsPath + &quot;&#92;&quot; + targetFilename;
        FileStream file = new FileStream(path,FileMode.Open,FileAccess.Read);
        textReader = new StreamReader(file);
        yield return new WaitForSeconds(0f);
#elif UNITY_ANDROID
        WWW www = null;

<pre><code>    switch(pathMode){
        case PathMode.streamingAsset:
            path = &amp;quot;jar:file://&amp;quot; + Application.dataPath + &amp;quot;!/assets&amp;quot; + &amp;quot;/&amp;quot; + targetFilename; 

            www = new WWW(path);
            yield return www;
            if(www.text.Length == 0){
                path = &amp;quot;jar:file://&amp;quot; + Application.dataPath + &amp;quot;!/assets&amp;quot; + &amp;quot;/&amp;quot; + defaultLanguage + fileExtension;
            }

            break;
        case PathMode.persistentData:
            if(!File.Exists(Application.persistentDataPath + &amp;quot;/&amp;quot; +targetFilename)){
               targetFilename = defaultLanguage + fileExtension;
            }

            path = &amp;quot;file://&amp;quot;+ Application.persistentDataPath + &amp;quot;/&amp;quot; +targetFilename;
            break;
        case PathMode.manuallyInput:
            if(!File.Exists(manuallyInputPath + &amp;quot;/&amp;quot;+ targetFilename)){
               targetFilename = defaultLanguage + fileExtension;
            }
            path = &amp;quot;file://&amp;quot;+ manuallyInputPath + &amp;quot;/&amp;quot;+ targetFilename;
            break;
    }

    www = new WWW(path);
    yield return www;
    textReader = new StringReader(www.text);
</code></pre>

#endif

<pre><code>    string[] tempString = null;
    while ((textBuffer = textReader.ReadLine()) != null)
    {
        if (textBuffer.Contains(&amp;quot;=&amp;quot;))
        {
            tempString = textBuffer.Split('=');

            if (tempString.Length &amp;gt; 2)
            {
                for (int i = 2; i &amp;lt; tempString.Length; i++)
                {
                    tempString[1] += &amp;quot;=&amp;quot; + tempString[i];
                }
            }

            lls.Add(tempString[0], tempString[1]);
        }
    }

    isReadFinished = true;
}

public enum PathMode
{
    streamingAsset = 0,
    persistentData = 1,
    manuallyInput = 2,
}
</code></pre>

}

LocalizationTest

using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System;
using System.Text;

public class LocalizationTest : MonoBehaviour
{
    public static string resultTxt = &quot;&quot;;
    private GUIStyle rectStyle = new GUIStyle();
    LocalizedLanguageManager LLM = null;

<pre><code>void Start()
{
    rectStyle.wordWrap = true;
    rectStyle.fontSize = 50;
    rectStyle.alignment = TextAnchor.MiddleLeft;
    LLM = GetComponentInParent&amp;lt;LocalizedLanguageManager&amp;gt;();
    LLM.SetPathMode(LocalizedLanguageManager.PathMode.persistentData).SetLanguage();
}

void Update()
{
    if (LLM.isReadFinished &amp;amp;&amp;amp; resultTxt.Length == 0)
    {
        resultTxt = &amp;quot;[title] &amp;quot; + LLM.GetLocalizedString(&amp;quot;title&amp;quot;) + &amp;quot;\n&amp;quot; + 
                    &amp;quot;[start] &amp;quot; + LLM.GetLocalizedString(&amp;quot;start&amp;quot;)+ &amp;quot;\n&amp;quot; +
                    &amp;quot;[splitTest] &amp;quot; + LLM.GetLocalizedString(&amp;quot;splitTest&amp;quot;);
    }
}

void OnGUI()
{
    GUI.TextArea(new Rect(10, Screen.height / 2, Screen.width, 350), resultTxt, rectStyle);
}
</code></pre>

}

これらはインストール後のフォルダに入れたファイル。他の場所に置いたファイルはそれぞれ"per"の部分を"StreamingAssets"やら"SD"に変えて表示時に区別がつくようにした。
jpn.llf

title=日本語タイトル per
start=ゲームスタート
splitTest=分割=文字列=

eng.llf

title=English per
start=Game Start
splitTest=splited=strings=

我ながら自分が説明ベタなのは知っているが、このアイディアを具現化した後すぐの、頭のとっちらかっている状態で書くエントリは本当に酷い(ノ∀) 自分でも後で読み直して理解出来るかどうか心配(´・ω・`) コードの方も直さなきゃって思うところがあるものの、もう面倒くさくてその気力が出て来ない(ノ∀) ソノウチネ

元々そんなにガチでやる必要はなかった国際化であったが、ユーザーが追加したファイルを読み込んで使えるようにすることはいずれ実装しようと思っていたので、ついつい或る程度の形になるまで粘ってしまった…(ヽ'ω`)

まあ取り敢えずこれで通常の神経衰弱ゲーム作成に戻れるから良しとするか…

参考:
今回使ったのか前に使ったのか覚えていないが、GUIStyle.wordWrapのお話。
Unity拡張:GUILayout.Label で自動折り返し表示

C#でDictionaryを使用したので、メモ。
C# の Dictionary<TKey, TValue> の使い方

シングルクォーテーションでやらなくてハマったんだったかな(´・ω・`)?
C# 文字列を分割 特定の文字で区切る Split エントリがなくなってる。
◇Unityでゲーム開発 -C#で文字列操作-

C#の言語仕様とかを理解していないのがいけないのかもしれないんだけども、Visual Studioが余計な自動変換とかしてエラーの原因になって鬱陶しいことがままある(´・ω・`)
Protection level error in dictionary [closed]

【Unity】テキストファイル読み込み?

パーミッションって内部が上位で内部を持っていたら外部もOKかと勘違いしてハマッた(ノ∀`)
逆だったかw
AndroidのpersistentDataPathがカオス
UnityでAndroid,Windowsにファイルを書き込む際の注意点
UnityにおけるAndroidアプリのパーミッション付与について ";
foreach (string s in lls.Values)
{
temp += s + "/";
}

            return temp;
        }
    }
    else
    {
        return &quot;Not Ready&quot;;
    }
}


public IEnumerator LoadTextData(string targetFilename)
{
    string path = &quot;&quot;;
    string textBuffer = &quot;&quot;;

#if UNITY_EDITOR
path = Application.streamingAssetsPath + "\" + targetFilename;
FileStream file = new FileStream(path,FileMode.Open,FileAccess.Read);
textReader = new StreamReader(file);
yield return new WaitForSeconds(0f);
#elif UNITY_ANDROID
WWW www = null;

    switch(pathMode){
        case PathMode.streamingAsset:
            path = &quot;jar:file://&quot; + Application.dataPath + &quot;!/assets&quot; + &quot;/&quot; + targetFilename; 

            www = new WWW(path);
            yield return www;
            if(www.text.Length == 0){
                path = &quot;jar:file://&quot; + Application.dataPath + &quot;!/assets&quot; + &quot;/&quot; + defaultLanguage + fileExtension;
            }

            break;
        case PathMode.persistentData:
            if(!File.Exists(Application.persistentDataPath + &quot;/&quot; +targetFilename)){
               targetFilename = defaultLanguage + fileExtension;
            }

            path = &quot;file://&quot;+ Application.persistentDataPath + &quot;/&quot; +targetFilename;
            break;
        case PathMode.manuallyInput:
            if(!File.Exists(manuallyInputPath + &quot;/&quot;+ targetFilename)){
               targetFilename = defaultLanguage + fileExtension;
            }
            path = &quot;file://&quot;+ manuallyInputPath + &quot;/&quot;+ targetFilename;
            break;
    }

    www = new WWW(path);
    yield return www;
    textReader = new StringReader(www.text);

#endif

    string[] tempString = null;
    while ((textBuffer = textReader.ReadLine()) != null)
    {
        if (textBuffer.Contains(&quot;=&quot;))
        {
            tempString = textBuffer.Split('=');

            if (tempString.Length &gt; 2)
            {
                for (int i = 2; i &lt; tempString.Length; i++)
                {
                    tempString[1] += &quot;=&quot; + tempString[i];
                }
            }

            lls.Add(tempString[0], tempString[1]);
        }
    }

    isReadFinished = true;
}

public enum PathMode
{
    streamingAsset = 0,
    persistentData = 1,
    manuallyInput = 2,
}

}
[/csharp]

LocalizationTest

using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System;
using System.Text;

public class LocalizationTest : MonoBehaviour
{
    public static string resultTxt = &quot;&quot;;
    private GUIStyle rectStyle = new GUIStyle();
    LocalizedLanguageManager LLM = null;

<pre><code>void Start()
{
    rectStyle.wordWrap = true;
    rectStyle.fontSize = 50;
    rectStyle.alignment = TextAnchor.MiddleLeft;
    LLM = GetComponentInParent&amp;lt;LocalizedLanguageManager&amp;gt;();
    LLM.SetPathMode(LocalizedLanguageManager.PathMode.persistentData).SetLanguage();
}

void Update()
{
    if (LLM.isReadFinished &amp;amp;&amp;amp; resultTxt.Length == 0)
    {
        resultTxt = &amp;quot;[title] &amp;quot; + LLM.GetLocalizedString(&amp;quot;title&amp;quot;) + &amp;quot;\n&amp;quot; + 
                    &amp;quot;[start] &amp;quot; + LLM.GetLocalizedString(&amp;quot;start&amp;quot;)+ &amp;quot;\n&amp;quot; +
                    &amp;quot;[splitTest] &amp;quot; + LLM.GetLocalizedString(&amp;quot;splitTest&amp;quot;);
    }
}

void OnGUI()
{
    GUI.TextArea(new Rect(10, Screen.height / 2, Screen.width, 350), resultTxt, rectStyle);
}
</code></pre>

}

これらはインストール後のフォルダに入れたファイル。他の場所に置いたファイルはそれぞれ"per"の部分を"StreamingAssets"やら"SD"に変えて表示時に区別がつくようにした。
jpn.llf

title=日本語タイトル per
start=ゲームスタート
splitTest=分割=文字列=

eng.llf

title=English per
start=Game Start
splitTest=splited=strings=

我ながら自分が説明ベタなのは知っているが、このアイディアを具現化した後すぐの、頭のとっちらかっている状態で書くエントリは本当に酷い(ノ∀) 自分でも後で読み直して理解出来るかどうか心配(´・ω・`) コードの方も直さなきゃって思うところがあるものの、もう面倒くさくてその気力が出て来ない(ノ∀) ソノウチネ

元々そんなにガチでやる必要はなかった国際化であったが、ユーザーが追加したファイルを読み込んで使えるようにすることはいずれ実装しようと思っていたので、ついつい或る程度の形になるまで粘ってしまった…(ヽ'ω`)

まあ取り敢えずこれで通常の神経衰弱ゲーム作成に戻れるから良しとするか…

参考:
今回使ったのか前に使ったのか覚えていないが、GUIStyle.wordWrapのお話。
Unity拡張:GUILayout.Label で自動折り返し表示

C#でDictionaryを使用したので、メモ。
C# の Dictionary<TKey, TValue> の使い方

シングルクォーテーションでやらなくてハマったんだったかな(´・ω・`)?
C# 文字列を分割 特定の文字で区切る Split エントリがなくなってる。
◇Unityでゲーム開発 -C#で文字列操作-

C#の言語仕様とかを理解していないのがいけないのかもしれないんだけども、Visual Studioが余計な自動変換とかしてエラーの原因になって鬱陶しいことがままある(´・ω・`)
Protection level error in dictionary [closed]

【Unity】テキストファイル読み込み?

パーミッションって内部が上位で内部を持っていたら外部もOKかと勘違いしてハマッた(ノ∀`)
逆だったかw
AndroidのpersistentDataPathがカオス
UnityでAndroid,Windowsにファイルを書き込む際の注意点
UnityにおけるAndroidアプリのパーミッション付与について

うにのトゲは刺さると痛い(´・ω・`)-13

細かい修正や追加をしていて余り進んでいないかも(´・ω・`)

タイトル画面でToggleやDropdownを増やして、基本的な設定変更はそこでやってもらう形にすることに。効果音の有無、BGMの有無、マッチ後のカードの自動消滅や裏面分けの有無。

あと結局、ややアルファを落として下のカードを視認出来る程度の透明度のでっかいオーバーレイ表示を行うことにした。やっぱり、小さいカードに情報を詰め込むのは無理くさい。というか出来るけれども読めないので意味がない(´・ω・`)


UIのDisable(グレーアウト)

トランプを選択した場合、

  • 情報系と違って長時間表示している意味がないので、デフォで自動消滅するようにして、Toggleをdisabledにする。
  • またオーバーレイ表示をする意味はないので、オーバーレイ表示をしないようにして、Toggleをdisabledにする。
  • トランプは図オンリーなので、カード内容を選択するdropdownをdisabledにする。

という風にしたかったので、最初はenabledにfalseを設定した。

そうすると操作は効かなくなっったが、色が変わらない(´・ω・`)
次にSetActive(false)をすると、表示自体しなくなった…

これはこれでいいかもと思ったが、そうするとDropdownのインスペクターに表示されてる"Disabled Color"ってなんだよ(´・ω・`)?っていう疑問に至り、ぐぐる。

(C#)UnityのButtonをDisabledにしたくて地味に躓いた

ああ、interactable をfalseにするんか(・∀・)

問題自体は解決したが更に、
Unityのボタンのenabled/setActive/interactableの違いというエントリを読むと、enabledとinteractableはGameObjectに使えず、setActiveはUIObjectに使えない模様。

※2018/5/25追記
よくよく考えてみるとComponent.gameObjectがあるので、

[UIコンポーネント].gameObject.SetActive(false);

とすれば非表示に出来る模様。


なんでCheckboxじゃなくてToggleって名前にしたんだろうね(´・ω・`)

Toggleのチェックマークが小さすぎるので、どうやって大きくするのかとToggle the Images on Toggle Button .やら他のエントリを一生懸命読んだりしたが、単純にチェックマークのイメージを大きくするだけでよかった(ノ∀`)

ToggleとかのGetIntとSetIntは三項演算子が楽なんだね(・∀・)
【Unity】チェックボックスとラジオボタン


BGM再生

今まではデフォでBGMを再生していたけれども、BGMのON/OFF設定を追加したので、その辺を修正した。【Unity開発】Audioまとめ(基本編)【ひよこのエッセンス】を参考にして最終的に

    void Update()
    {
        if (isPlayBGM && !(GetComponent<AudioSource>().isPlaying) )
        {
            GetComponent<AudioSource>().Play();
   }
    }

という感じにした。BGM再生の設定を読み込んだisPlayBGMだけだと毎回再生を始めようとするのかなんなのかわからないが、最初の数秒流れて音楽が止まってしまった。なのでisPlayingで再生していない場合だけ再生を開始するようにした。もしかしたら、もっと正しいやり方があるのかもしれないが、今のところ、これで良しとしよう。


タッチ

マッチ後のカードの自動消滅の設定を追加したので、位置やUIを限定した画面タッチではない画面タッチを拾う必要が出て来たが、どうすればいいのかわからず、ぐぐる。

タッチパネル対応を参考に

    void OnGUI()
    {

<pre><code>    if (Event.current.type == EventType.MouseDown) {
     //Do Something
</code></pre>

     }
    }

という感じに。意外と簡単だった(・∀・)
これで本当に正しいのかはわからないw


dropdownの画像指定

カードの裏面選択を画像から選べるようにしたいなぁと思い、ぐぐる。参考ページを読んだり、色々いじったりするもDropdownの仕組みがイマイチわかりにくかった(´・ω・`)

が、しばらくしてからようやく何に引っ掛かっていたのかがわかったw

もう既に知っていたことだったのだが、
1つのGameObjectに対してImageコンポーネントやTextコンポーネントは1つしかアタッチ出来ないという大前提を忘れていたのが原因(ノ∀`)

これ、実はToggleのCheckmarkのところでも同じことでハマっていたというか納得せずに悶々としていたw Graphicというプロパティ(?)に対してImageをD&Dしようとしても上手く行かず、GameObjectじゃないとD&D出来ない状況に、ドウシテダヨ,バーヤバーヤヽ(`Д´)ノと怒っていた俺氏であったが、先の大前提を踏まえれば、当然至極であった。

複数のImageやTextをアタッチしたいGameObjectは子Objectを作って、それにImageをアタッチしなきゃ駄目だからこういう形になるんだな。

取り敢えずDropdownの下にGameObjectを追加して、そのオブジェクトにImageコンポーネントを追加。そしてそのGameObjectをCaption ImageにD&Dしてサイズ調整をし、OptionsのImageの方にD&Dすれば意図していた画像選択が可能になった。

※追加し忘れていた(ノ∀`)

参考:
Dropdown
【Unity開発】uGUIのDropDownの使い方【ひよこエッセンス】


取り敢えず後は

  • 表示フォントサイズやどのくらい情報を表示するかの調整
  • 神経衰弱ゲームとしてのゲームギミック関連
  • カード(データ)・セットを増やす

辺りか。

どれも重い作業だけれども、まあ、ゴールが見えて来た……かなぁ…(ヽ'ω`)
カードセットの追加は後々継続的にしなければならない作業だし……