2017年02月02日

[Unity] SimpleSQL(アセット)で日本語をINSERT/UPDATEできないときの対処

UnityでSQLを扱いたいときに、SimpleSQL(有料アセット)を使っています。
https://www.assetstore.unity3d.com/jp/#!/content/3845

無料の SQLiteUnityKit もあるのですが、
更新が止まっていることと、自分でカスタマイズできるものの色々と
手間がかかるしややこしいという点から、アセットを購入することにしました。

ですが、日本語をINSERTしようとしたときにエラーが出てしまいました。

"Insert" を使った場合:
-------------------------------------------
SQLiteException: Constraint
SimpleSQL.PreparedSqlLiteInsertCommand.ExecuteNonQuery (System.Object[] source)
SimpleSQL.SQLiteConnection.Insert (System.Object obj, System.String extra, System.Type objType, System.Int64& rowID)
SimpleSQL.SQLiteConnection.Insert (System.Object obj, System.Int64& rowID)
SimpleSQL.SimpleSQLManager.Insert (System.Object obj, System.Int64& rowID)
SimpleSQL.SimpleSQLManager.Insert (System.Object obj)
-------------------------------------------

"Execute" を使った場合:
-------------------------------------------
SQLiteException: unrecognized token: "'20"
SimpleSQL.SQLite3.Prepare2 (IntPtr db, System.String query)
SimpleSQL.SQLiteCommand.Prepare ()
SimpleSQL.SQLiteCommand.ExecuteNonQuery ()
SimpleSQL.SQLiteConnection.Execute (System.String query, System.Object[] args)
SimpleSQL.SimpleSQLManager.Execute (System.String query, System.Object[] args)
-------------------------------------------

サポートに問い合わせたら、SQL内で日本語を記述するところに「?」を書き、
Execute()の第2引数以降で渡せばよいと回答がありました。
フォーラムに過去のやりとりが記載されていました。

http://www.echo17.com/forum/index.php?topic=257.msg399#msg399

例)
SimpleSQL.SimpleSQLManager dbManager;
// ゲームオブジェクトにSimpleSQLManagerが適用されていること
dbManager = this.GetComponent();
string query = "INSERT INTO TEST(name) values(?)";
dbManager.Execute(query, "テスト");

これで無事、日本語を扱うことができるようになりました。
posted by be-style at 21:53| Comment(0) | Unity

2016年11月15日

[Unity] AWS Mobile SDK for Unity で、iPhoneで実行時エラー(NullReferenceException)が出る

AWSの一部のサービスをUnityから動かせるSDKが出ています。

AWS Mobile SDK for Unity:
https://docs.aws.amazon.com/mobile/sdkforunity/developerguide/

サンプルプログラムが添付されているので
これは便利だということで利用し始めました。

しかし、AWS S3 のサンプルを動かそうとしたときに
Unityエディタから動きませんでした。

Unityエディタで動かすには、

AWSConfigs.HttpClient = AWSConfigs.HttpClientOption.UnityWebRequest;

の一行が必要でした。

[参考] AWS Mobile SDK for Unityを使ってサーバーレスなアプリを開発する:
http://nil-one.com/blog/article/2016/04/14/AWS_Mobile_SDK_for_Unity/

これで安心、と思いきや、実機で実行したところ
NullReferenceException
が出てしまいました↓


NullReferenceException: A null value was found where an object instance was required.
at Amazon.Runtime.Internal.DownloadHandlerBufferWrapper..cctor () [0x00000] in :0
at Amazon.Runtime.Internal.UnityMainThreadDispatcher+d__7.MoveNext () [0x00000] in :0
at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in :0
at Amazon.Runtime.Internal.UnityMainThreadDispatcher.ProcessRequests () [0x00000] in :0
at Amazon.Runtime.Internal.UnityMainThreadDispatcher.Update () [0x00000] in :0
Rethrow as TypeInitializationException: The type initializer for 'Amazon.Runtime.Internal.DownloadHandlerBufferWrapper' threw an exception.
at Amazon.Runtime.Internal.UnityMainThreadDispatcher+d__7.MoveNext () [0x00000] in :0
at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in :0
at Amazon.Runtime.Internal.UnityMainThreadDispatcher.ProcessRequests () [0x00000] in :0
at Amazon.Runtime.Internal.UnityMainThreadDispatcher.Update () [0x00000] in :0
Amazon.Runtime.Internal.UnityMainThreadDispatcher:ProcessRequests()
Amazon.Runtime.Internal.UnityMainThreadDispatcher:Update()


原因は、実機で動かす時に必要な「link.xml(UnityプロジェクトのResourcesディレクトリに入れる)」
の記載が Unity5.4 から変更があったようでした。

[公式]AWS Mobile SDK for Unity セットアップの仕方:
https://docs.aws.amazon.com/mobile/sdkforunity/developerguide/setup-unity.html

公式ドキュメントにはlink.xmlは

<assembly fullname="UnityEngine">
<type fullname="UnityEngine.Experimental.Networking.UnityWebRequest" preserve="all" />
……

と記載されていますが、Unity5.4以降は「Experimental」を削除した

<assembly fullname="UnityEngine">
<type fullname="UnityEngine.Networking.UnityWebRequest" preserve="all" />
……

が正しいようです。

[参考]iOS: NullReferenceException: A null value was found where an object instance was required. #435:
https://github.com/aws/aws-sdk-net/issues/435

この修正をしたところ、実機でも無事AWS S3にファイルをアップロード/ダウンロードできるように
なりました。

公式ドキュメントを信じていたので、痛い目にあいました。
この解決方法を見つけた人って凄いなー。
posted by be-style at 08:00| Comment(0) | Unity

2016年08月28日

[Unity] AWS Lambda をUnityから実行してみた(iPhoneから実行も)

前回の [Web] AWS Lambda のHello Worldをやってみた の続きです。

前回は管理画面からAWS Lambdaを動かしました。
今回の目標はこの2つです。
・UnityからAWS Lambdaを動かしてみる
・Unityで作ったアプリをiPhoneで実行してAWS Lambdaを動かしてみる

[参考] AWS Mobile SDK for Unityを使ってサーバーレスなアプリを開発する:
http://nil-one.com/blog/article/2016/04/14/AWS_Mobile_SDK_for_Unity/

上記の参考ページにもありますが、"AWS Mobile SDK for Unity"を使います。

AWS Mobile SDK for Unity:
http://docs.aws.amazon.com/mobile/sdkforunity/developerguide/

SDKの対象にAWS Lambdaがありますね。
そして、Amazon Cognito も対象になっている点がポイントです(後述)。

はじめに、AWS Mobile SDK for Unityを上記のURLからダウンロードします。

lambda2_1.png


Unityで新規プロジェクトを作成します。
ここでは、LambdaTest という名前にして、2Dで作成します。

lambda2_3.png


Unityでプロジェクトが作成できたら、先ほど解凍した中から
AWSSDK.Lambda.3.1.4.7.unitypackage
をWクリックしてUnityにすべてインポートします。

lambda2_2.png

lambda2_4.png


UnityのBuildSettings(Ctrl+Shift+B)では、iOSにSwitchPlatformしておきます。

lambda2_5.png


ProjectウィンドウのAssets > Examples > LambdaExampleシーン をWクリックして
サンプルシーンを開きます。

lambda2_6.png


Gameウィンドウにプレビューが表示されるのですが、
カメラが配置されていないために警告(No cameras redering)が出ます。

lambda2_7.png


再生ボタンを押すと実行はされますが、気持ち悪いのでシーンにカメラを追加します。
Hierarchyウィンドウで右クリック > Camera でカメラを追加すると警告は消えます。

lambda2_8.png


カメラが追加された後のHierarcyウィンドウです。

lambda2_9.png


先ほど、再生ボタンを押せば実行されると書きましたが、実行はされますが
ボタンを押しても何も反応しません。
理由は、Lambdaオブジェクトに本来適用されているスクリプトが抜け落ちているからです!

lambda2_10.png


Assets > Examples > LambdaExample(C#ファイル) を先ほどのLambdaオブジェクトに
適用します。
すると、AWS Lambdaを使用するために必要なパラメーターが表示されます。

重要なのは、この3つです。
・Identity Pool Id
・Cognito Identity Region
・Lambda Region

lambda2_11.png


はじめに挙げた「AWS Mobile SDK for Unityを使ってサーバーレスなアプリを開発する」の
ページにもありますが、AWS Mobile SDK for Unity を使用するためには"Cognito"という
サービスを利用する必要があります。

Amazon Cognito:
https://aws.amazon.com/jp/cognito/

このユーザーのサインイン機能を使って、UnityからAWS Lambdaにアクセスするようです。
Unityの作業は一旦ここで止めて、Cognitoサービスを利用してみましょう。

AWSの管理画面のサービスから、Cognitoを選びます。
※管理画面の作業は、すべて"東京リージョン"での作業です!!

lambda2_12.png


"Manage Federated Identities"を選びます。
lambda2_13.png


"Identity pool name"が必須なので、ここではアプリの名前(AllZeroPuzzle)にしてみました。
ユーザー認証は使わないので、"Unauthenticated identities"の
"Enable access to unauthenticated identities"にチェックを入れておきます。
最後に"Create Pool"を押します。

lambda2_14.png


ユーザー認証あり/なし時のロールを作成する画面になります。
特に何もせず"許可"を押します。
lambda2_15.png


次に、Cognitoをどうやってプログラムから設定するかの説明ページが表示されるので、
Platform で Unity を選択します。
"Get AWS Credentials"のサンプルプログラムの中に記載されている

ap-northeast-1:xxxxxxxxxxxxxxxxxx

が Identity Pool ID です。
これを控えておきます。

控えておくものはこの2点です。
・Identity Pool ID
・リージョン名(Identity Pool IDの先頭"ap-northeast-1"のこと)

各リージョン名:
https://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/emr-plan-region.html

lambda2_16.png


それでは、Unityに戻って、以下の3つを埋めてみましょう。
・Identity Pool Id
・Cognito Identity Region
・Lambda Region

lambda2_17.png


再生ボタンを押してGameウィンドウの"List Functions"ボタンを押してみましょう。
すると、エラーが出ます。
これは、まだCognitoで作ったロールをLambdaに関連付けていないからです。

lambda2_18.png


再び、AWSの管理画面に戻ります。
サービス > AWS Lambda > Functions
を見ると、前回作った"helloWorld"があるので選択します。

lambda2_19.png


Configuration の"Existing role"が前回のまま(lambda_basic_execution)になっています。
この lambda_basic_execution を先ほどCognitoで作ったロールに置き換えたいのですが
選択肢にありません。
では、どうすればよいのでしょうか。

lambda2_20.png


管理画面のサービス > セキュリティ&アイデンティィ > IAM を選びます。

lambda2_21.png


左メニューの"ロール"を選ぶと、前回作ったロール(lambda_basic_execution)と
今回Cognitoで作ったロール(2つ)があります。
認証なし(Cognito_AllZeroPuzzleUnauth_Role)のロールを選びます。

lambda2_22.png


アクセス許可 > インラインポリシー > ロールポリシーの作成 を押します。

lambda2_23.png


"Policy Generator"の"選択"を押します。

lambda2_24.png


アクセス許可の編集画面になりますので、以下を設定します。
・AWSサービス = AWS Lambda
・アクション = InvokeFunction、ListFunctions ※Unityで使っている関数
・Amazonリソースネーム(ARN) = *(アスタリスク)

設定が終わったら"ステートメントを追加"を押します。

lambda2_25.png

lambda2_26.png


画面下にアクセス許可が表示されると"次のステップ"ボタンが有効になりますので、
押して次に進みます。

lambda2_27.png


ポリシーの確認画面です。
特に何もせずに"ポリシーの適用"を押します。

lambda2_28.png


画面が戻ると、インラインポリシーに先ほど作成したポリシーが追加されました。

lambda2_29.png


次に、前回作った lambda_basic_execution のロールを選びます。

lambda2_22.png


信頼関係を押すと、信頼されたエンティティとしてLambdaがあることが確認できます。
信頼関係の編集を押します。

lambda2_30.png


表示されるポリシードキュメントをコピーしておきます。

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}



ロール画面に戻り、認証しない方(Cognito_AllZeroPuzzleUnauth_Role)を選びます。
信頼関係の画面を見ると、信頼されたエンティティには

cognito-identity.amazonaws.com

だけが記載されています。
ここに、先ほどの"lambda_basic_executionと同じ信頼されたエンティティ"を追加します。
つまり、

ID プロバイダー lambda.amazonaws.com

を追加します。
ここでも、信頼関係の編集を押します。

先ほどの信頼関係の"Statement"の内容を、",(カンマ)"で区切ってから追記します。

lambda2_31.png


すると、信頼されたエンティティに"ID プロバイダー lambda.amazonaws.com"が追加されました。

lambda2_32.png


Lambdaの画面に戻ります。
ExistingRoleに、先ほど選択肢にはなかった"Cognito_AllZeroPuzzleUnauth_Role"が追加されているので
選択します。

lambda2_33.png


画面上の"Save and test"を押してみましょう。
特に問題なく実行されるはずです。

次に、Unityに移り、再生を押してから"List Functions"を押すと、
Lambdaで作った関数 helloWorld が表示されます。

lambda2_34.png


続いて、"Invoke"を押すと、"Function Name:"の入力欄に記載されている関数を呼びます。
記入欄には"helloWorld"とあるので、Lambda上のhelloWorldが呼ばれます。

lambda2_35.png


これで、PCのUnityからAWS Lambdaに接続し、関数を実行することができました。


最後に、iPhoneで動作するようにします。

サンプルに添付してあった
Assets > Examples > readme.md
を開くと、IL2CPP で実行するには
Resources ディレクトリを作り、その中に link.xml を追加する必要があるようです。

lambda2_36.png


link.xmlに記述する内容は
https://github.com/aws/aws-sdk-net/blob/master/Unity.README.md#unity-sdk-fundamentals
に記載されていますが、こちらにも転機しておきます。
※上記には、コメントアウトしていない部分があったため
<linker>
<assembly fullname="UnityEngine">
<type fullname="UnityEngine.Experimental.Networking.UnityWebRequest" preserve="all" />
<type fullname="UnityEngine.Experimental.Networking.UploadHandlerRaw" preserve="all" />
<type fullname="UnityEngine.Experimental.Networking.UploadHandler" preserve="all" />
<type fullname="UnityEngine.Experimental.Networking.DownloadHandler" preserve="all" />
<type fullname="UnityEngine.Experimental.Networking.DownloadHandlerBuffer" preserve="all" />
</assembly>

<assembly fullname="mscorlib">
<namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>

<assembly fullname="System">
<namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>

<assembly fullname="AWSSDK.Core" preserve="all">
<namespace fullname="Amazon.Util.Internal.PlatformServices" preserve="all"/>
</assembly>
<assembly fullname="AWSSDK.CognitoIdentity" preserve="all"/>
<assembly fullname="AWSSDK.SecurityToken" preserve="all"/>
</linker>

readmeでは
<assembly fullname="AWSSDK.Lambda" preserve="all"/>

を追加するように記載がありましたが、追記しなくても動作しました。

Unityでビルドするときは、
"Setting for iOS"で"Bundle Identifier"を自分用に変更しておきましょう。

ここでは、be-styleのアプリということで

com.jpn.bestyle.lambdatest

としておきました。

lambda2_37.png


ビルドする前は、Examples/LambdaExample のシーンが登録されていることを確認しましょう。

lambda2_38.png


UnityでビルドしてできたXcodeのプロジェクトファイルを開きます。
特に何もせずに実行します。

アプリが起動して、"List Functions"と"Invoke"が動作すれば成功です!

iPhoneでの実行時にハマった点がありました。
Credentials is expired、つまりCredentialsが有効期限切れになっているというのです。

事象としては、iPhoneの時計が日本時間なのに、サーバーの時間がアメリカ時間になっていて
その時差によって有効期限切れでAWS Lambdaが使えないというものでした。

解決方法は、iPhoneの時間を"自動設定"にすることでした。
このときにiPhoneの"位置情報"を有効にすることにポイントがありました。
そうしたら、接続先のサーバー(東京リージョン)と端末の時計が合い、Lambdaにアクセスできました。


今回が初めてなので、途中で不要な処理が入っているかもしれません。
それはこれから経験を積んで精査していきたいと思います。


次は、AWS Lambda から DynamoDB にアクセスしてみます。
[Web] AWS Lambda から DynamoDB にアクセスしてアイテムを更新してみた
posted by be-style at 23:09| Comment(0) | Unity