クリアするとXEMがもらえるゲームを作る

Unityを使ってXEMを送金する記事を以前書きました。
今回は、ゲームをクリアするとXEMがもらえるサンプルや、そのような形式のコンテンツを配信する際の課題についてです。
ゲームを遊んでみたい方は下記からダウンロードしてください。
[Windows版](https://drive.google.com/open?id=16a9cpYQq-VzL2p5zyCU2XqH_IO9gDiTm)
[Mac版](https://drive.google.com/open?id=1u8kkGBQFbO5n0On2vGAbulapgKUwqasz)
※MainnetのXEMが極少量クリア時にもらえますが、一人一回だけです。

<h2>ゲームについて</h2>
Unity公式で用意されているチュートリアル「Roll-a-ball」を参考に簡単なゲームを作ります。基本的な流れは Roll-a-ball と同じです。プレイヤーはキーボードの十字キーでステージ内を転がり、ステージ内のアイテムを全て取得すればクリアです。
また、プレイする際に最初に自身のXEMアドレスの入力してもらうようにしました（しなくても遊べます）。これはクリアするときに0.1XEMを賞金として送るためです。


<h2>アドレスの入力</h2>
ゲーム開始前にはアドレスの入力をしてもらいます。
アドレスが有効かどうかは、`BeginGetAccountInfoFromAddress` で情報が取得できるかで判別を行いました。
本来は取引所のアドレスは無効等の条件を追加するべきですが、今回はそちらは行なっていません。

```AddressVerifier.cs
		// 確認開始
		var accountClient = new AccountClient(connection);
		ManualAsyncResult result = null;

		// ハイフンは除去
		var normalizedText = addressField.text.Replace("-", "");

		result = accountClient.BeginGetAccountInfoFromAddress(normalizedText);
・
・　省略
・
		try
		{
			var response = accountClient.EndGetAccountInfo(result);

			// 配布元の残高が1以下の場合は受け取れないようにする
			if (response.Account.Balance <= 1000000)
			{
				addressField.interactable = false;
				sendXemButton.gameObject.GetComponentInChildren<Text>().text = "残高不足につき配布終了です";
				sendXemButton.interactable = false;
			}
		}
		catch
		{
			// アドレスが無効の場合この処理が実行される.
			checkingText.text = failedText;
			yield break;
		}
```

一度ゲームをクリアしている場合や無効なアドレスの場合、以下のようにして無効であることを表しています。
![SampleGame 2017-12-11 01-22-49.png](https://qiita-image-store.s3.amazonaws.com/0/213908/83bd5d0a-03cb-8945-47fb-42512006c0db.png)

<h2>課題について</h2>

<h3>秘密鍵の管理</h3>
実際に運用する際、ウォレットの心臓たる秘密鍵は絶対にバレてはいけません。結論としてはマルチシグを使うのが良いでしょう。しかし、この辺りはUnityだけの実装では困難です。マルチシグ対応について案はありますが長くなるのでまたの機会に。
今回は時間の関係でノーガード戦法になっています。特にWindows版では、その気になればこの方式では簡単に秘密鍵を見られるでしょう。
IL2CPPでビルドしたり、 [Obfuscator](https://www.assetstore.unity3d.com/jp/#!/content/48919)等の難読化ツールを使うと気休め程度の効果は得られるかもしれませんが、数千数万単位のXEMを直で置くのはやはり精神的にもよろしくありません。なんども書きますが、マルチシグで対応しましょう。

<h3>手数料</h3>
今回はゲームクリアの賞金が0.1XEMですが、今回設定した短めのメッセージでも送金すると手数料は0.15XEMかかるようです。
賞金より手数料の方が高い、というのはある意味中々面白い状況なのでこのままでもいいかもしれませんが、実際にはゲームをある程度こなして内部的に賞金額を保存、賞金が一定額以上溜まったらまとめて送金みたいな感じにする必要がありそうです。都度送信は手数料もかかりますし、一定額以上とすれば複数アカウントで小銭稼ぎをするような人の対策にもなります。

<h3>本当はやりたかったこと</h3>
時間が足らず省略しましたが、ランキング機能をNEMAPIだけで実装する予定でした。今回はゲームクリア時にメッセージを送っていますが、メッセージにそのプレイヤーのスコアを記録しておけば自身のウォレットからの送信履歴を辿ればランキングも作れるという構想でした。実現可能かはわからずじまいですし実際には外部のDBを使った方が効率的でしょうけれど、メモとして残しておきます。

<h3>WebGLでハマったところ</h3>
今回は時間が足らなかったためあちこち作り込みが足りませんが、特にWebGL対応が鬼畜仕様でした（最終的にWebGLではなくWindows,Macでバイナリをダウンロードしてもらう形式にしたところからもわかるように、WebGLは間に合いませんでした…）。
前に書いた記事でも触れていましたが、XEMを課金してゲーム要素の購入等を行う場合 Android、iOS　ともに現実的な選択肢ではありません。おそらく審査時か後日リジェクトされるでしょう。ストアの用意した決済方法を介さないとGoogle, Appleに旨味がないのでまあこれは仕方ないでしょう。
そうなると残された現実的な選択肢はWebGLなのですがこれがUnityでは結構つらかったりします。
まずWebGLはブラウザによる縛りでマルチスレッド対応していません。また、[WebSocketがC#のものは使えません](https://docs.unity3d.com/jp/current/Manual/webgl-networking.html)。JavaScriptでプラグインを書いて対応する必要があります。ゲーム用途としてはすでにUNETがあるので、おそらく今後公式で対応する可能性はかなり低いです。そして、IMEの挙動が怪しいです。[公式でIMEプラグインは用意されています](https://www.assetstore.unity3d.com/jp/#!/content/64933)が、上手く動作しませんでした。これも自前で対応する必要がある気がしています。
こんな感じでかなりイバラの道でした。現在WebGLで動くようにNEMを用いたゲームを作ろうとしているところなのでより多くの地雷を踏むのだろうと思っていますが、特にNEMに関するものは今後も共有したいと思っています。

<h3>その他ハマったところ</h3>
以前の記事を書いた環境のまま進めていたのですが、どうもプラグインの手数料計算周りにバグがあったらしくエラーでMainnetでは送金できませんでした。これは執筆時点で三日前に出たCSharp2nem(2.0.15)に置き換えることで回避できました。
また、今回最初はWebGLを対象としていましたが最初はビルド自体に失敗していました。これはCSharp2nemが使っているメソッドの一部(System.Data以下の何か？)が IL2CPPを通らないことが原因だったようですが、これは執筆時点で六日前に出たバージョンのUnity(2017.2.0p4)に移行することで回避できました。
さらに、当初unityroomでの公開を予定していましたがMac環境ではアップロード用に指定された形式で書き出されないようでさらにさらにIMEが…とまあ色々ありました（白目

こんなところでしょうか。
本当は先ほど書いたマルチシグや、NEMで完結させるランキング機能やWebSocketをUnityWebGLで使う場合や収益化する場合どうすればいいのか等書きたいことは色々あったのですが、準備に十分な時間が取れず全て省略。
次以降記事を書くときにリベンジします…！