Unityで簡単かつ無料でアプリ内課金を実装できるライブラリ「SOOMLA」を使ってみた

先日リリースしたiPhoneアプリ「ワンパットゴルフ」では、アプリ内課金をしています。
このアプリはUnityで開発したのですが、Unity本体ではアプリ内課金をサポートしておらず、課金部分はネイティブで自作 or ライブラリを利用しないといけません。

ネイティブで自作はめんどくさそうだし、かといって Asset Store にあるライブラリはどれも有料。。どうしよう。。と悩んで色々ググってたら、SOOMLA というオープンソースライブラリを見つけました。

SOOMLAとは

アプリ内課金を簡単に実装することのできるオープンソースライブラリです。Unity以外にも、iOS、Android、cocos2d-xで利用することが出来ます。
今回は非消費型アイテムしか試していませんが、消費型アイテムにも対応しています。

インストール

下記ページの右側にある[unity3d-store]-[Download]から.unityPackageファイルをダウンロードしてインストールのが楽だと思います。その下にサンプルプロジェクトがあるので、一緒にダウンロードしておきましょう。
http://project.soom.la/

使い方

細かい実装方法に関してはサンプルプロジェクトを見ると分かると思うので、大まかな実装の流れを説明します。今回は非消費型アイテムの使い方になりますが、iOS版でしか実装していません。Android版に関しては別途必要な設定や処理が必要になるため、ドキュメントをご確認ください。

【Soomlaのプレハブをシーンに追加】
課金したいシーンに、Soomlaのプレハブを追加します。
Inspector パネルで、「Custom Secret」「Android Public Key」「Soom Sec」を設定してください。
設定内容は下記を参考にしてください。
https://github.com/soomla/unity3d-store

【課金アイテムクラスの作成】
サンプルプロジェクトの
/Assets/Soomla/Code/MuffinRushAssets.cs
を参考にして課金アイテムクラスを作成します。
プロダクトID、価格、アイテムの種類等を設定します。

【StoreController の初期化】
アプリのロード時に、StoreControllerを初期化します。
StoreController.Initialize(new YourStoreAssetsImplementation());
初期化は1度だけで、Awake()ではなく、Start()で実行しないといけません。
【追記】YourStoreAssetsImplementationの部分は、【課金アイテムクラスの作成】で作成したクラスを指定してください

【イベントの登録】
Events クラスに購入成功時などのイベント処理を登録します。
イベントの種類や具体的な登録方法は、サンプルプロジェクトの
/Assets/Soomla/Code/ExampleEventHandler.cs
を参考にしてください。
今回の実装では、StoreController.Initialize()の直後に登録処理を記述しました。

【購入処理】
StoreController.BuyMarketItem(プロダクトID);
購入に成功した場合や失敗した場合の処理は、Events.OnMarketPurchase や Events.OnUnexpectedErrorInStore に登録したメソッドで行います。

【復元処理】
StoreController.RestoreTransactions();

【(端末の)アプリで非消費型アイテムが購入済になっているか】
StoreInventory.NonConsumableItemExists(プロダクトID)

まとめ

SOOMLAの概要やUnityでの使い方について説明しました。
そんなSOOMLAを使っているアプリ「ワンパットゴルフ」は、App Store や Google Play で絶賛公開中です。
ぜひ遊んでみてください!

iPhone: https://itunes.apple.com/us/app/oneputtgolf/id791400827?l=ja&ls=1&mt=8
Android: https://play.google.com/store/apps/details?id=jp.shunsukeosawa.OnePuttGolf&hl=ja

非活性状態のGameObjectを活性状態にする

編集のために一部のGameObjectを非活性状態(active==false)にしておいて、起動時にすべて活性状態にしたい場合があったとします。

上の例では、Holeの子にHole1,Hole2,Hole3があり、Hole1とHole2は非活性状態です(Hole3を編集するために、不要なHole1,Hole2を非活性状態にしています)。
起動時にHole1,Hole2を活性状態にしたいのですが、GameObject.FindGameObjectWithTag()やGameObject.Find()だと非活性状態のGameObjectができず、SetActiveRecursively()がUnity4非奨励になって使えません。

そんなときは、活性状態にしたいGameObjectの親GameObjectのtransformから子を取得して活性状態にすることができます。

GameObject holeRootObj = GameObject.Find("Hole");
foreach( Transform child in holeRootObj.transform)
{
GameObject childGObj = child.gameObject;
childGObj.SetActive(true);
}

iPhoneやAndroidでGUI Textureがタップされたことを検知する方法

PCでGUI TextureがクリックされたときはOnMouseDown()を使えばよいのですが、iPhoneやAndroidだとそれに相当するイベントがないみたいです。なので、iPhoneやAndroidでGUI Textureがタップされたことを検知するためには自前でコードを書く必要があります。

ググると、Raycastを使う方法やiPhoneInputを使う方法など色々見つかりますが、うまく動かなかったり、Andoroidに対応できないという問題があったりしました。

最終的に、GUI TextureのComponentに以下のコードを追加することでうまく処理することが出来ました。

if (Input.touchCount == 1) {
Touch touch = Input.GetTouch (0);
if (touch.phase == TouchPhase.Ended) {
if (guiTexture.HitTest(touch.position, Camera.main)) {
//タップされた時の処理
}

このコードはタッチが終了したときにタップ処理を実行します。もしタッチの開始から終了までの間ずっと処理を実行したい場合は、
touch.phase == TouchPhase.Ended

touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary || touch.phase == TouchPhase.Ended
に変えるとよいです。

また、このままだとGUI Texture外でタッチを開始し、GUI Texture内でタッチを終了した時もタップ処理が実行されます。自分のアプリだと別にそれでもよいので上記のコードで対応しましたが、もっとちゃんと処理する場合は、以下のサイトを参考にしてください。

Unity3D:GUI Textureでボタンを作成(2)