2016年5月14日土曜日

【Unity】WebAPIに接続してデータを取得する

PHPでもAndroidでも、過去の開発経験からいって、WebAPIに接続してデータを取得するという課題は必ずつきまとうし、また初回はつまづきが大きい。
でも一度やり方が分かってしまうと、あとは作っておいたライブラリファイルに処理を丸投げして終わりとか、コードのコピペ で終わりとか、さらりと出来てしまう。

なので、UnityでWebAPIに接続するサンプルを作っていた。

それなりに出来たのでGitHubにあげました。
https://github.com/ishiitakeru/Unity_WebApiConnectSample

主要なスクリプトはこれ
https://github.com/ishiitakeru/Unity_WebApiConnectSample/blob/master/WebApiConnectSample/Assets/script/ConnectWebApiSample.cs

説明。
WebAPIに接続して値をプロパティに格納する機能を持つのがWWWクラスのインスタンス。
POSTデータを送信するために使うのがWWWFormクラスのインスタンス。
WWWクラスをnewするときに WWWFormのインスタンスを引数に渡してやることでPOSTデータを送信する。

留意ポイントは、 WWWクラスのインスタンスは通信によって状態が変わるということ。
つまり、newした直後はWebAPIからのレスポンスがまだ受け取れていないので、取得した値を取り出せない。
しばらくして通信が完了すると、WWWインスタンスのプロパティから通信で手に入れたデータを取り出せる。
ということは、WWWインスタンスを生成したあと、その状態を時間経過に従って監視し続ける仕組みが必要。

そこで、コルーチンを使う。
コルーチンになっているメソッドでは、毎フレーム、yield return から次の yield returnまでが実行される。

つまり、コルーチンメソッドのすごいところは、 ひとつのメソッド内で時間経過で状態が変わることを取り込んで実現できるということだ。

そこで、WWWインスタンスによってWebAPIに接続するコルーチンメソッドは以下のような作りになるはず。

----------------------------------------------------------------------
private IEnumerator コルーチンメソッド名(){

    //WebAPIにPOSTデータを送信するための WWWForm インスタンス
    WWWForm my_www_form = new WWWForm();
    //キーと値とのセットを登録
    my_www_form.AddField("key", "value");

    //WWWクラスのインスタンス作成
    WWW my_www = new WWW(
        "http://WebAPIのURL", //WebAPIのURL
        my_www_form  //POST送信値をセットしたWWWFormインスタンス
    );

    //↑ここまでは一度だけ実行されれば良い
    //↓ここからは通信によるデータ取得が完了するまで繰り返し実行される必要がある
    while(true){
        //1フレームで、yield return から次の yield return まで実行される。
        yield return null;

        //WWW.isDone プロパティによって通信が完了したかどうかを調べられる。
        if(my_www.isDone == true){
            //通信完了
            //WWWインスタンスからデータ取り出し
            Debug.Log("Apiレスポンス : " + my_www.text);
            break;

        }else{
            //まだ通信が完了してない
        }//if
    }//while
 }//function
----------------------------------------------------------------------

コツ :
■WWWインスタンスは一度作ればいいので毎フレーム繰り返されるループの外で作っておく。
■WWW.isDone プロパティの確認を毎フレーム繰り返されるループ内で行う。

Androidの場合、別スレッドで通信処理を開始して、データ取得が終わったらそれを検知するコールバックメソッドからデータを取り出して、みたいにかなり複雑なことをやった記憶がある。

[Androidの場合]
■通信用にメインの処理とは別の非同期処理スレッドを作成し、通信はそこでしなくてはならない。
■通信完了を検知するコールバックメソッドを使い、通信で手に入れたデータをそこで取り出さないといけない。

UnityはAndroidに作りが似ているのかなと思ったけど、どうも、WebAPIとの通信においては非同期スレッドもコールバックメソッドも使わない(あるいは意識しないで良い)ということみたい。

その代わり、毎フレーム「yield return 」から「yield return 」まで繰り返し実行されるコルーチンという仕組みをきちんと理解する必要があるようですな。

【Unity】コルーチンに登場するIEnumeratorとはなんぞや

Unityには処理をフレームごとに分割するコルーチン(Coroutine)という仕組みがある。

理解するためのキーワードが IEnumerator というクラス。
これは、yield return 文とセットで使われる。
これは、Listなどのコレクションに関連するクラスだが、コレクションそのものではなく、JavaでいうIterator のように「元となるコレクションがまずあり、それを反復処理するときに使う」クラス。

コルーチンのためのメソッドというものがまず作られる。
これは、返り値がIEnumeratorになるメソッド。

------------------------------------------------------------------------
using UnityEngine;
using System.Collections;

public class CoroutineTest : MonoBehaviour {
    //コルーチンで書き換える文字列
    private string text_to_show;

    //////////////////////////////
    /**
     * スタート
     */
    void Start () {
        //コルーチンをスタート
        StartCoroutine(
            SampleCoroutine()
        );
    }//Start

    //////////////////////////////
    /**
     * コルーチンのテスト
     * このメソッドはUnityで追加されたスクリプト(MonoBehaviour継承クラス)内に書かれる想定
     */
    private IEnumerator SampleCoroutine() {
        int i = 0;
        while(true){
            this.text_to_show = "test " + i;
            i++;
            //コンソールに出力
            Debug.Log("text_to_show : " + this.text_to_show);

            //2秒待つ
            //n秒待たせるには、yield return 文でnew WaitForSeconds(n)を戻させなくてはならない。
            yield return new WaitForSeconds(2);
        }//while

    }//function
}//class
------------------------------------------------------------------------

while(true) があるから無限ループのように見える、もしくは (yield) return のところで処理が終わってそうに見える。
実際には、yield return のところで処理が呼び出し元に帰る。
そして、このメソッドの返り値であるIEnumeratorのインスタンスがMoveNext()を実行すると、前回 yield return した続きから処理が再開され、次の yield return まで処理が走る。
Unityの場合、この MoveNext() は明示的には書かれず、Unityが見えないところで自動で実行する。
その仕組が、Unityの持つ「StartCoroutine(コルーチンメソッド)」という命令。
これは、Unity によって毎フレーム「MoveNext()」が実行されるというもの。

IEnumerator とは、Iterator のような、コレクションを反復処理する際に使う反復子というインターフェイスの一種。
MoveNext() というメソッドを持っているのが特徴。

IEnumerator というのはつまり、元になるコレクションを持ってて(IEnumerator がコレクションから取り出されるとも言える)、MoveNext() によって次の値次の値とアクセスできるということ。
で、IEnumerator を返り値としてyield return するメソッドというのは、MoveNext()によって次の値を返す(次のyield returnの戻り値を返す)コレクションであると見ればいい。

IEnumeratorの元(持ち主)に2つの系統があると考える。
■Listなどのコレクションインスタンス
■yield return を持ち、IEnumerator を戻り値とするメソッド(コルーチン)

以下の変数とメソッドとは、似たようなことをやろうとしている。

------------------------------------------------------------------------
変数 test_list(コレクションのインスタンス)
------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;

public class IEnumeratorTest01 : MonoBehaviour {
    void Start () {
        //IEnumerator のインスタンス(コレクション変数)
        List<string> test_list = new List<string>();
        test_list.Add("test01");
        test_list.Add("test02");
        test_list.Add("test03");

        //コレクションからIEnumerator を取得
        IEnumerator test_ienumerator = test_list.GetEnumerator();
        while (test_ienumerator.MoveNext()) {
            Debug.Log(test_ienumerator.Current);
        }//while
    }//Start
}//class

------------------------------------------------------------------------
メソッド TestIenumeratorMethod()
------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;

public class IEnumeratorTest02 : MonoBehaviour {
    void Start () {
        //IEnumerator のインスタンス(yield return を持つメソッドからの戻り値)
        IEnumerator test_ienumerator_by_method = this.TestIenumeratorMethod("test");
        while (test_ienumerator_by_method.MoveNext()) {
            Debug.Log(test_ienumerator_by_method.Current);
        }//while

    }//Start

    //////////////////////////////
    /**
     * IEnumeratorの元となるコレクションに相当するメソッド
     */
    public IEnumerator TestIenumeratorMethod(string str){
        yield return str + "01";
        yield return str + "02";
        yield return str + "03";
    }//function
}//class

------------------------------------------------------------------------
IEnumerator.Current コレクションの中での現在地のオブジェクトを返す。
IEnumerator.MoveNext() boolean を返す。コレクションの現在地(Current)を次の要素に移動させ、次の要素がある場合はtrueを、ない場合はfalseを返す。

2016年5月10日火曜日

【ECcube3】さくらのレンタルサーバーの深い階層に設置したECcubeに独自ドメインを割り振ったところ、URL解決がややこしくなった

さくらのレンタルサーバーにはECキューブ(3系)のインストール機能がある。

マルチドメインで設定したディレクトリにECキューブをインストールした。

○○.sakura.ne.jp/△△/html

みたいになるように。

独自ドメインの移管の関係で独自ドメインを割り当てるのが遅れそうだったため、○○.sakura.ne.jp 以下のURLでECキューブサイトの構築作業をし、独自ドメインの操作ができるようになり次第URLを入れ替えればいいやと思っていた。

こんな感じ。

前:
http://○○.sakura.ne.jp/△△/html

後:
http://○○.com


ところが、独自ドメインを割り当てても、独自ドメインでアクセスした場合に正常に動作しない。

色々やったところ、

\app\config\eccube\path.yml

に記述されている内容を変更したら一応動くようになった。
「root」だとか「○○_urlpath」だとかの設定は、URLの定数設定らしいので、それらを変更した。
ただし、「○○_realdir」という名前の項目はURLではなくサーバー内のパス指定らしいのでこれらは変更してはいけないらしい。


path.yml の変更で一応は動くようになったんですが、URLに「index.php」が必須になってしまった。

独自ドメイン割り当て前の管理画面(例)
http://○○.sakura.ne.jp/△△/html/admin

独自ドメイン割り当て後の管理画面(例)
http://○○.com/index.php/admin

のように。
TOPページも「index.php」を省略するとエラーになってしまう。
.htaccess に DirectoryIndex を設定したりしたのだが、エラーが取れない。

どうも、mod_rewrite の設定が関係しているらしいのだけど、一旦保留。

独自ドメインのネームサーバーの設定が反映されない

作成しているWebサイトに独自ドメインを割り当てるネームサーバーの設定でちょっとつまづいたのでメモ。

ネームサーバーの設定を変更し、数時間経って設定が反映されたはずなのに以前と同じ表示のまま変更されない。

たまたま同じURLにスマホでアクセスしたら、ネームサーバー設定変更後のファイルが表示された。
ところが作業用PCでアクセスすると表示が昔のまま。

そこで、ブラウザのキャッシュを削除したらきちんと新しいファイルが表示された。

DNSの設定の話だからサーバーサイドのファイルだけが問題で、ローカルのキャッシュが影響を及ぼすとは思っていたなかったので気づくのが遅れた。