KLabGames Tech Blog

KLabは、多くのスマートフォン向けゲームを開発・提供しています。 スマートフォン向けゲームの開発と運用は、Webソーシャルゲームと比べて格段に複雑で、またコンシューマゲーム開発とも異なったノウハウが求められる領域もあります。 このブログでは、KLabのゲーム開発・運用の中で培われた様々な技術や挑戦とそのノウハウについて、広く紹介していきます。

カテゴリ: ノウハウ

〜エンジニア不要で納品をまわす仕組み〜

このエントリーは、KLab Advent Calendar 2016 の12/24の記事です。

KLabのとある新規アプリ開発プロジェクトでは、アプリ内で使用する制作物の納品フローを工夫して、エンジニアの手がどんどん不要になってきています。

現在稼働しているワークフローを紹介します。

納品フローに関わるプロジェクトチームは大きく5つの班に別れています。

  1. ゲーム内の各種パラメータを入力する「企画班」
  2. UIパーツを作成する「UI班」
  3. 演出を作成する「演出班」
  4. サウンドを作成する「サウンド班」
  5. プログラム作成の全般を担う「開発班」

以上の各班の作業内容がプロジェクトの最終成果物としてのipaやapkファイルに取り込まれるまでの流れです。

以下では単に「バイナリ」と書いた場合ipa、apkファイルのことを指します。

また、プロジェクトで使用しているGitレポジトリは

  • client-server ソースコード・パラメータTSV用
  • ui-effects 演出・UIアセット用
  • sound サウンド用

と分割されています(実際には細かいものがもっとあります)。

各レポジトリのメインブランチは、origin/masterを使用しており、バイナリ作成時に最終的に使われるブランチです。

そのため、いつでもバイナリ作成ができるようにメインブランチは常に正しく動作することが求められます。

workflow

ポイントは2つです。

  1. エンジニアの手を介さずにバイナリをビルドし動作確認ができる
  2. エンジニアの手を介さずにメインブランチが更新できる

1. エンジニアがいなくてもバイナリをビルドし動作確認ができる

1番目のポイントは、誰でもSlack上でBotに話しかけることで簡単にバイナリ作成が出来るので最新のメインブランチに自分の作業内容を取り込んでデバッグ用端末上ですぐに動作確認ができることです。

話しかける内容はこんな感じです。

@builder1.1
os=ios
branch=origin/master
ui-effects=origin/update-effect1
sound=origin/master
title=演出1更新確認
description=個人テスト用_演出1更新

自分が動作確認したいブランチとOSを指定してタイトル、説明文を書くだけです。

初めての人でも3分教えれば操作を覚えられます。

これが導入される前は、ビルドは週末のプロジェクトの正式ビルド時のみで、開発班以外にとって自分の作業内容がデバッグ用端末上で確認できるまで数日待つ必要がありました。

それが今では10分ほどに短縮されました。

また、メインブランチの状態が動作確認されるのも数日に1回程度だったので、メインブランチが正常に動作しない時にどこでバグが入りこんだのか調査するのに時間がかかっていました。

今では、30分に1回は誰かがビルドしているので、メインブランチに何かあればすぐに開発班以外からバグ報告が来るのでバグの発見速度があがっています。

さらに、Jenkins上のジョブは触っていけない項目も多く、開発以外には解放していないプロジェクトも多いかと思います。

そんな場合でも、SlackのBotでラップしてしまえば必要な項目だけをパラメータ化でき、自由度の高いバリデーションもかけられるので、安全に実行してもらうことができます。

仕組みの構築は1時間程度で出来るものなので、メリットが大きいです。

実装は簡単で、SlackのReal Time Messagin API を使用してBotを作成し社内のサーバーでデーモンとして起動しています。

このデーモンが、Slackからのメッセージを受取り、ビルドパラメータにパースして、社内のJenkinsビルドサーバーに対してパラメータとともにPOSTリクエストを投げます。

ビルドが完了するとEMランチャにアップロードされ、Slackチャンネルに通知が来る、という流れです。

2. エンジニアを介さずにメインブランチが更新できる

2つ目のポイントは、開発班以外でもエンジニアの手を介さずにメインブランチの更新ができることです。

※もちろん一部必要な箇所は手動で取り込みます。

先に記した通りメインブランチは常に正常に動作することが求められているので、不正な変更は取り除く必要があります。

それにもかかわらずなぜエンジニアの手を介さずに更新ができるのかを説明していきます。

開発班以外がメインブランチを更新する流れには大きく2つ存在しています。

  1. UI・サウンド班はSVNを更新し、cronジョブがメインブランチへ自動定期取り込み
  2. 企画・演出班はGitレポジトリのメインブランチに対してプルリクを投げる

2.1. UI・サウンド班

UI班が納品するのは主にテクスチャで、サウンド班はオーサリングツールで書き出したサウンドデータを納品します。

この2班はもともとSVNに慣れていてGitに移行するメリットがなかったのでそのままSVNを使ってもらっています。

cronジョブが必要に応じた頻度で走っていて、SVN側で更新があると納品物に対する自動テストが走り、テストにパスするとGitレポジトリの方にコミットされて取り込まれます。

そのため、Gitレポジトリの方で開発班がデータに触る段階ではすでに命名規則などの自動テストの項目をすべて満たした状態が保証されています。

UI・サウンド班がSVNにデータをコミットするだけで、開発が手をかけずともメインブランチが更新できてしまいます。

UIは図3のフローです。

サウンドに関しては少し特殊で、目で見て分からない分動作確認の必要性が高いので、後述の企画・演出班と同様にUnityとGitの環境を構築しています。

サウンドは図1のフローです。

以下の流れになっています。

  1. サウンドデータをSVNにコミット
  2. soundレポジトリのorigin/stgにcronジョブで取り込まれる
  3. ローカル開発環境にorigin/stgをチェックアウトしUnityEditorで再生し確認
  4. Slackからorigin/stgを指定してバイナリビルド
  5. デバッグ用端末上で動作確認
  6. origin/stgをメインブランチにマージ

バイナリファイルや、大量のファイルは人間の目で確認するよりも機械的にチェックしたほうが不正なデータが取り込まれる可能性が減らせます。

2.2. 企画・演出班

図2のフローです。

企画班が納品するのはゲーム内の各種パラメータが記述されたTSVで、演出班は演出のプレファブとそれが使用するすべてのデータ (.prefab, .png, .mat, .anim, .controller, .fbx)を納品します。

企画班、演出班ともに開発班と同じフローで作業しています。

全員UnityとGitをインストールしており、各自の開発環境を構築しています。

サーバーは開発と同様に個人用サーバーにそれぞれ接続しています。

加えて、開発班以外は共通でGithubDesktopをインストールしています。

GithubDesktopを選んだ理由は主に3つです。

  • MacとWindowsに対応している。
  • Gitの複雑な概念を知らなくても使えるように複雑さを上手く隠蔽している。
  • 必要であればGitシェルを立ち上げて任意のGitコマンドが実行できる。

GithubDesktop独自の概念は例えば以下のようなものです。

  • 「Sync」- ローカルとリモートのブランチの内容をより新しいコミットの方に同期すること。
  • 「Update from master」- ローカルのmasterブランチの内容を作業ブランチにマージすること。
  • 「Publish」- 自分のローカルの作業ブランチをリモートにPushすること。

ブランチ切り替えや、PullReqもGithubDesktop上から出せます。

コンフリクトを発生させないために次のことに注意してもらっています。

  • 自分の作業内容をチームに周知し、同じファイルを編集しない
  • 作業開始前に必ずSyncで自分の環境を最新化する
  • 自動テストをパスしたPullReqはすぐにマージする
  • 自分のPullReqがマージされたら周知してそれぞれの環境でSyncさせる

また、Github EnterpriseのTeam機能を使って班ごとのTeamを作成し、アクセス権をコントロールしています。

例えば、演出Teamにはui-effectsレポジトリのみ読み書き権限を与え、その他のレポジトリは読み取りのみとしています。

これにより大雑把にレポジトリを守ることができます。

3. まとめ

納品フローに関する説明は以上になります。

この仕組みを作り上げるまでは当然開発の工数がそれなりに必要でした。

また、導入した当初は特にGit周りに関する質問が多く、開発班によるサポートが必要でした。

しかし今ではごくたまに発生するコンフリクト解消とシステム側のトラブル以外ではほとんどサポートが必要なくなってきています。

現時点での感想としては、

  1. 開発サイクルを速くまわす必要がある新規開発において早期の納品の仕組み化はおおきな価値があること
  2. 同じ作業を誰でもできるようにすることは想像を超える効果を生み出すこと

を感じています。

UI班から開発班へなどのファイルの受け渡しに関して当初は、プロジェクトの発足からしばらくは手渡しやファイル共有サーバーを使用していました。

そのため、単なる画像の差し替えやサウンドの差し替えにもいちいち開発班の手を介す必要がありました。

これは今振り返れば、開発サイクルを速くまわす必要がある新規開発においては時間的に大きなロスだったと感じます。

次の新規案件では最初の納品の前に仕組みを構築しておきたいものです。

Slackからのビルドシステムを作った当初の目的は、ビルドサーバーのJenkinsにアクセスしてパラメータを入力するのが面倒だったのでSlack上での会話の流れでビルドできたら素敵だよねというちょっとした遊び心でした。

それが今となっては各班が自分たちで作業と確認のサイクルをまわすためのコアとなっていることは驚きです。

繰り返し作業を単に自動化することと、それをさらに普段使い慣れたインターフェイスからすぐにアクセスできるようにすること、の間には大きな隔たりがあります。

前者は自分が作業を繰り返す回数を量的に変えるのに対し、後者は作業自体を質的に変える可能性をもっています。

今後もプロジェクトのみんなが幸せになる納品フローを常に模索していきたいです。

はじめに

オンラインゲームでは、金貨やコインといった ゲーム内で通貨のように利用可能なアイテム が必ずといって良いほど登場しますよね。
先日もこのTech Blogにて、ゲーム内で発行する仮想通貨のデータ分析業務についての記事が掲載されましたが、ゲーム内通貨の運用は各種法令等も絡むことからKLabでも特に注意して取り扱いを行っている業務の一つです。

そこで今日はゲーム内通貨の運用に関する業務やKLabとしての考え方を、会計上の観点からもう少し掘り下げてご紹介します。

法令におけるゲーム内の金貨等の位置づけ

オンラインゲーム内でアイテムの購入に利用できる金貨やコインといったトークン、上記ではゲーム内通貨と言いましたが、これは資金決済に関する法律(以下資金決済法、とします)で各種の規定がされています。そして上記のようなゲーム内の概念は、この法律の中では前払式支払手段という名前で定義付けがされています。

前払式支払手段の定義について条文にはちょっと難しく書いてあるのですが、できるだけウソにならない範囲で簡単に書くと以下のようになります。

  1. 発行の際にその金額・数量等が紙の証票(例としてチケットなど)またはデジタルデータとして記録されていること
  2. 1が対価と引き替えに利用者に対して発行されていること、つまり有償であること
  3. 1の内容と紐付く、IDや番号などが発行されていること
  4. 物品の購入やサービスを受ける際などに利用できるものであること

※ただし上記を満たす場合でも使用期限が6ヶ月以内のものは原則として対象外

上記はゲーム内の金貨やコインだけでなく、デパートなどで発行される商品券や交通機関などで使える回数券やプリペイドカード、英会話教室の前売りチケットなども上記の特徴を持つことが多く、つまりこれらは法律上同じ立て付けのものということになります。

ところで今年5月に改正資金決済法が成立し、bitcoinといった近年出てきた新しい決済手段に関する条文が追加されました。そこで このbitcoinのような新しい概念は、条文の中では 「仮想通貨」 という名称で定義づけされました。
「仮想通貨」というとどこかで聞いたような名前ですが、ここでいう仮想通貨とは前述の特定のサービスやゲーム内でのみ利用可能な前払式支払手段とは法律上別物でして、ここでは不特定の相手に対して利用可能なものを指します(正確にはそのほかにも仮想通貨を構成する要件があります)。従って法令改正後も前述の前払式支払手段に関する条文はほぼそのまま残っています。

なお冒頭でご紹介した記事のように、我々KLab内部ではゲーム内の金貨やコイン=前払式支払手段のことは 仮想通貨 と呼んでいるのですが、上記の法令改正の絡みもあり誤解を招くためこの記事の中では、法令に則り正確に 前払式支払手段 と呼ぶことにします。

オンラインゲームでの前払式支払手段の会計処理

当たり前の話ですが、会計において一番大事なことは売上を正確に勘定することです。
会計上、売上を計上するタイミングは物販業においては引渡基準と言って 原則的には役務提供(顧客に対してサービス提供や商品の引き渡しを行うこと)を完了した時点とすること になっています。
例えば自動車の販売を行った場合、売上を立てるのは契約日や入金日ではなく、この引渡基準に基づいて登録日あるいは実際に顧客に対して車の引き渡しを行った日とする運用にするのが一般的です。

この点でオンラインゲームにおける前払式支払手段は次のように若干特殊な性格を持ちます。
※ゲームにおける金貨やコインなどは無償でも配布することもありますが、ここでは有償で販売された物で無償配布のものは含まない(本来の意味での前払式支払手段)前提で説明します

オンラインゲームにおける前払式支払手段の流れ

上記で示すようにオンラインゲームにおける前払式支払手段とは、iOSのApp StoreやAndroidのGoogle Playストアのようなスマートフォンでの「決済プラットフォーム」によるアプリ内課金や、その他クレジットカード決済などで販売される「商品」です。しかし、顧客がその「商品」(例えばコインや金貨)を購入する動機は、これそのものにあるわけではなく、これと引き替えに何かアイテムを購入したい、とかサービスを受けたい、というさらに別の目的にあるわけです。
逆にコンテンツ提供者=”前払式支払手段の提供者”の視点からすると、前払式支払手段を販売しただけの段階では商品を引き渡す義務がまだ残っている状態だと考えることもできます。
つまり前払式支払手段は債権を表章した有価証券のようなものと考えると、前払式支払手段販売時点ではゲーム内における役務提供は完了していないため、この時点では売上計上は まだ できない、と考えるのがもっとも合理的なのです。最初にプリペイドカードや英会話前売りチケットのことを書きましたが、これと同じと考えればわかりやすいと思います。

これを会計的に整理して言うと、顧客が「決済プラットフォーム」で代金を支払った時点でまもなくコンテンツ提供者への入金は発生するもののこの時点では役務提供の終了(=売上発生)とは見做さず、 前受金処理(負債として計上すること)を行う 、ということになります。つまり顧客が実際に前払式支払手段を利用してアイテムを引き替えた時が前受金を取り崩すタイミング=すなわち売上として計上を行うタイミングになるわけです。

帳簿イメージ

この項の最初に書いた通り、会計において一番重要なことは売上を正確に計上することです。従って前払式支払手段を利用した業務においては、販売・発行時の情報と同じくらい、 消費した際の情報が大事 だということがおわかり頂けると思います。

前払式支払手段の残高管理と売上計上

多くのゲームにおいて前払式支払手段はまとめ売りの際に値段を下げることがあります。
例えば、金貨100枚だと120円(@1.2円)だが1,200枚まとめて買うと1,000円(@1.0円)になってお得!……といった販促方法は一般的にもよく見られるものですよね。

どの価格で購入した前払式支払手段であっても同様に利用できるという前提であれば、アイテム引き替えの実装だけを考えた場合、その前払式支払手段の総残高さえ分かればよいことになります。つまりそれぞれの単価を気にする必要は無いわけです。
一方で、上記で述べたように「売上を正確に計上する」という観点ではその実装だけでは問題が発生します。

  • それぞれ「単価いくらの前払式支払手段が消費されたか?」を記録しておかないと売上が正確に計上できない
  • 単一アプリにおける同一の前払式支払手段でも、Google PlayストアとKindleアプリのように購入元の「決済プラットフォーム」が混在することがある(売上は「決済プラットフォーム」毎にそれぞれ勘定するのが妥当であるため)

……ということがあるためです。そのため単に総残高を記録するだけでなく、前払式支払手段の種類別の 預入・払出のルールが厳密に運用 されていなければなりません。
このような要件は実は一般的な商品の在庫管理の考え方そのものでして、これに倣って前払式支払手段の残高を管理していくことになります。

在庫管理の基本的な方式は先入先出法、後入先出法、移動平均法といったものがありますが、「古いものから消費」というルールが顧客から見て最も自然でわかりやすいこと、預入・払出記録の1:1の突き合わせができるため詳細なトレース・分析が行いやすいなどの理由からKLabでは現状ほとんどのケースで先入先出法を採用しています。

さて、会計上一般的に在庫の管理を行う場合には 商品有高帳 という帳簿を作成します。前払式支払手段の内部管理においてもこれとほぼ同様のことを行うことになります。
例として、とあるゲームの金貨(=前払式支払手段)の預入・払出のサンプルを以下に示します。

金貨預入・払出サンプル

上記の例では、顧客であるユーザAは日を分けて金貨をそれぞれ100枚(単価 @1.2円)、1,200枚(単価 @1.0円)と購入し、その次の日にアイテムショップで500枚消費しています。ということで残高は (100+1,200)-500=800枚になる計算です。
肝心の内訳としては、先入先出で古いものから消費していくルールですから、単価1.2円の金貨全部と単価1.0円の金貨一部が取り崩され、後者が800枚だけ残ることになります。

このデータさえあれば売上を導出するのは簡単ですね。上記の「払出」に当たるログを全顧客分サマリーすれば良いわけです。
さらにここから前払式支払手段の残高も明らかなので、これをエビデンスとして資金決済法で定められた供託(後述します)を行うことができます。

その他資金決済法で定められた業務

これまで前払式支払手段の会計業務についてご説明してきましたが、KLabは資金決済法で定められる自家型の前払式支払手段発行者に該当しますので、上記の他にも法で定められた業務が存在します。
以下に簡単にご紹介します。

前払式支払手段の発行届出

資金決済法では基準日(毎年3月末・9月末)未使用残高が1,000万円を越える自家型発行者は、管轄の財務局長に前払式支払手段に関する情報(前払式支払手段の名称やその単価等)を書面で届出することが定められています。
KLabもこれに当たるため、関東財務局に前払式支払手段に関する届出を行っています。

発行保証金の供託

資金決済法では、保証金として発行済みの前払式支払手段の1/2以上の金銭を供託することが定められています。上述の発行届出と同様に、基準日残高でこれを計上します。
これは利用者保護のための運用で、万が一発行者が破綻してしまったような場合にはここから優先的に配当を行うことで、支払い済みの代金を丸損しないようにしているというわけです。

前払式支払手段の払戻し

銀行法や出資法という法律では、免許なしに事業として他人のお金を預かったり、その払戻しを行ったり、または送金をしたりすることを禁止しています(これらは社会的影響が大きな業務だからです)。
もし前払式支払手段がいつでも自由に払戻しができてしまうと、一旦それを購入後、好きなときに現金化ができるということになります。これは上述したお金の預入と実質同じことができることになってしまうため、発行済みの前払式支払手段を払戻しすることは資金決済法で禁止されているのです。この規定があるため、前払式支払手段の発行業者は滅多なことでは払戻しに応じることはありません。

一方で発行者が前払式支払手段を廃止した場合には、上記の例外として利用者に対して購入済みの前払式支払手段について払戻しの対応を行わなければならないことになっています。
利用者は発行者が設定した申し出期間(法令上最低60日間)内に申し出を行うことで払戻しが受けられることになります。
ちなみに、払戻しの期間を超えた場合は除斥(当該利用者を通常の払戻し手続きから除外して、相当する額の前払式支払手段残高を控除すること)することが可能になります。除斥された場合でも民法上の債権が消滅するわけではありませんが、この期間を過ぎてしまうとお金の回収が面倒になってしまいますので、未使用分の前払式支払手段があるサービスが終了してしまった場合は、事業者の告知を見たらぜひ早めに手続きをしてくださいね。

おわりに

アイテム課金型オンラインゲームというジャンルは、比較的新しい業態であるため日々利用者を保護するための新たな法令やガイドラインが検討されています。一方で、バックエンドの業務は通常の商品の在庫管理や売上管理等と考え方に大きな差はありません。
みなさんが前払式支払手段に関する業務を行うことがありましたら、この記事を何かの参考にしていただければ幸いです。


Shimanuki

分析基盤チームの高田です。

私のチームでは、各種ゲームのデータを集積するデータ分析システムの開発・運用を行なっています。

この記事では、モバイルオンラインゲームのクリティカルなポイントのひとつである仮想通貨の管理について、開発・運用という立場から気をつけていることを紹介したいと思います。

仮想通貨の扱いは、最終的には、法律・会計上の要請から決まってくるものだったりします。このため、開発者が単独で決められることは決して多くないです。しかし、ゲームの運用チームと会計部門の間に入ってルール作りを進めていくのは、経験上開発チーム主導で行なう方がうまくいくように思います。

重要なのは、「さまざまなユースケースを先に想定し、ルールを決めさせる」「システムの都合と、各部門の要件を整理する」というあたりです。要するに、「こういうケースがありえるんですが、この場合会計部門としてはどういうデータが必要ですか?」「運用チームはこのデータが見れれば問題ないでしょうか?」というのをどんどん聞いていくことが一番重要な仕事になります。

注意事項:

  • この記事は開発者としての立場で書かれたものであり、仮想通貨に関する法律上の判断については責任を負いかねますのでご注意ください。

仮想通貨とは?

ここで仮想通貨と呼ぶのは、(1)ユーザーが対価を支払って購入し、(2)アイテムとの交換、行動力回復、ガチャなどに使用できるもののことです。

「仮想通貨」と呼ぶとあまりなじみがないかもしれませんが、モバイルオンラインゲームでは、特別なアイテム(「石」「ジェム」など)を販売し、ユーザーはそれらを使用することで体力回復やガチャなどの機能を使用するという仕組みをとるものが多いです。これらのアイテムも仮想通貨と呼ばれます。なお、この記事では、iOS、Googleのネイティブアプリの事例を想定しています。

日本では、利用者保護の観点から、仮想通貨の扱いは資金決済法という法律で厳しく制限されています。また、モバイルオンラインゲームの売上は仮想通貨の発行時ではなく、消費時に計上することが多いため、仮想通貨の発行・消費ログは会計上も重要な数値となります。

恐怖の仮想通貨

KLabの場合、各ゲームのログを、共通のデータ分析システムに集約し、そこから会計部門、運用チーム、分析者などに各種データを提供するという形をとっています(図参照)。仮想通貨に関するデータも、このデータ分析システムに集約されます。私のチームが担当しているのは、このデータ分析システムの開発・運用の部分です。

virtual-coin

仮想通貨の管理は、以下のような理由で運用難易度の高いシステムになりがちです。

  1. 会計などに直結するクリティカルな部分であり、データの間違いなどが致命的な問題になりえる。
  2. ゲームごとに仕様やデータの格納方法もかなり異なってくる。
  3. 会計部門、運用チーム、データ分析に関わる部署など、関係者が多く、それぞれの要件も複雑である。

詳細はあとで紹介しますが、細かい要件がたくさんあり、「こんなに考えることがたくさんあると思わなかった」というくらい複雑になっていきます。

また、多くの関係者が存在するため、うまく調整ができなければ悲惨な事故にもつながりかねません。発生しうる事故の具体例をあげてみましょう。取りまとめるチームからすると、悪夢のような状況です。

  • 会計部門の要件がゲームの開発チームに伝わっていなかった。必要なログがデータベースにも残っておらず管理上必要なデータを用意できない。
  • 大量のデバッグ用ポイントを発行してしまって、本来記録すべき仮想通貨の発行と区別できない。
  • 新しい販売アイテムが法務や会計部門のチェックなしに追加されてしまった。本来仮想通貨として管理すべきものだが、管理に必要な機能が実装されていない。

こうした事故を防ぐためには、各方面からの要望を整理し、ルールを決めて運用していくことが重要です。

リリース時のチェックリスト

仮想通貨の仕様は各ゲームごとにかなり異なります。新規ゲームのリリース時などによく問題になるポイントを以下にあげてみます。リリース前にこの種のチェックポイントを確認していき、とにかく考慮漏れや想定外の事態を発生させないことが重要です。開発段階で関係者を集めてレビューすることが望ましいでしょう。

  • (1)仮想通貨を無償で付与することはあるか?
  • (2)どの仮想通貨から消費するか?
  • (3)通貨単位はどうなっているか?
  • (4)仮想通貨の種類は何種類あるか?
  • (5)引き継ぎ時のプラットフォームの扱い
  • (6)時間の扱い
  • (7)補填・デバッグの扱い

(1)仮想通貨を無償で付与することはあるか?

多くのゲームでは、仮想通貨相当のアイテム(「石」「ジェム」)を無償でも付与します。こうした事例では、有償で発行した仮想通貨と無償で発行したものを区別し、仮想通貨として管理するのは有償発行のものに限定するといったポリシーを採用する場合も多いでしょう。会計上売上として計上されるのは、有償発行のものに限定されますし、本来仮想通貨として管理すべきものは、ユーザーが対価を支払って購入したものに限定されるからです。

(2)どの仮想通貨から消費するか?

無償発行のものと有償発行の仮想通貨の両方がある場合、消費される順序をルールとして定めなければなりません。代表的なものは、以下の3パターンでしょう。ゲーム内でどのパターンを利用するのかは関係者間でよく確認しておきましょう。

  1. 有償発行分から消費する
  2. 無償発行分から消費する
  3. 有償無償問わず、先に発行した分から消費する。

(3)通貨単位はどうなっているか?

海外向けに提供している場合など、価格は日本円で統一されているとはかぎりません。また、iOSの場合、価格の設定にはApple Tierという独自の単位が使用されます。いずれにしても、どのような単位を使用して集計するのか、各チームで合意しておかなければなりません。

(4)仮想通貨の種類は何種類あるか?

仮想通貨は一種類とはかぎりません。ガチャチケットなどを販売する場合、そちらも仮想通貨として扱わなければならない可能性もあります。

(5)引き継ぎ時のプラットフォームの扱い

iOS端末からAndroid端末に変更した際など、異なるプラットフォーム間でユーザーデータを引き継ぐことがあります。引き継ぎに関しては、ゲームのシステム上のルールとして、仮想通貨も引き継ぐことを認める場合と、引き継ぎを認めない場合があります。仮想通貨の引き継ぎを許容する場合、消費された仮想通貨がどのプラットフォームで発行されたものかを取得する必要が生じるかもしれません。

(6)時間の扱い

時間の定義も問題になることがあります。海外向けに提供しているゲームの場合、タイムゾーンに気をつけなければならないのはもちろんですし、国内限定でも、取引日時を「ユーザーが端末上で操作した日時」とするのか「サーバー上でDBにレコードが作成された日時」とするのかが問題になることがあります。端末上の時間に合わせていると、過去の取引データが遅れて送信されてくることがあるからです。

例えば以下のようなケースを考えてみてください。

  1. 10/01 23:00 - ユーザーが仮想通貨を購入。通信エラーのため、データの送信がしばらく延期される。
  2. 10/02 01:00 - 仮想通貨集計バッチが10/01のデータを集計。
  3. 10/02 02:00 - 1のデータが再送されてくる。ところが10/01のデータはすでに集計済みである。

ここで再送されたデータを10/01のログとして集計しようとすると、集計から漏れてしまいます。

(7)補填・デバッグの扱い

障害時などに、通常の購入処理とは異なる形でユーザーに仮想通貨を付与することがあります。また運用中、購入処理などのデバッグが必要になる場面も想定されます。こうしたユースケースでは、デバッグのためにテスト用の仮想通貨を発行することがありえます。

こうしたイレギュラーなデータがログから漏れないよう、あらかじめ付与方法などについてもルールを決めておくことが必要です。

集計するログの種類

以下、KLabで仮想通貨管理のために集計しているログを参考のためにあげてみます。データの詳細はゲームごとの仕様によっても異なりますが、原則的には全ゲームで統一のフォーマットを使用して集計しています。

A. 仮想通貨発行ログ

仮想通貨の発行ログです。発行した個数や販売した仮想通貨商品(仮想通貨セット)のIDなどを集計します。モバイルゲームでは、App Store、Google Playストアなどのプラットフォームを通じて、仮想通貨をセット販売するので、セット単位で集計することになります。

項目 詳細
集計するタイミング デイリー
集計内容 ユーザーID、発行額、価格、通貨(JPY/USDなど)、日時、仮想通貨商品ID、プラットフォーム、仮想通貨の種類

B. 用途別仮想通貨消費ログ

消費用途別の仮想通貨消費ログです。発行した仮想通貨が、いつ、何の目的で、いくつ消費されたのかを集計します。

項目 詳細
集計するタイミング デイリー
集計内容 ユーザーID、消費額、日時、仮想通貨の種類、消費用途

C. セット別仮想通貨消費ログ

用途別の消費ログと似ていますが、発行時の仮想通貨商品ID(仮想通貨セット)別に消費を追ったものです。なぜこれが必要かというと、仮想通貨1個あたりの金額がセットごとに異なる場合があるからです。多くの場合、まとめ買いの方が得になるように商品価格が設定されています。また、途中で値段の変更を行なった場合などにも、この種のログが必要になります。

項目 詳細
集計するタイミング デイリー
集計内容 ユーザーID、消費額、日時、仮想通貨の種類、仮想通貨商品ID、発行時プラットフォーム

D. 仮想通貨残高

ここまでのログが正確に集計できていれば、そこから未使用の残高を計算することもできるはずです。基本的には、残高は、発行額合計 - 消費額合計になるはずです。

KLabでは、チェックのために、月に1回の頻度で、未使用仮想通貨の残高と上記のログから計算した残高を付き合わせています。

項目 詳細
集計するタイミング マンスリー
集計内容 仮想通貨個数、日時、仮想通貨の種類、発行時プラットフォーム

まとめ

以上、モバイルオンラインゲームの仮想通貨管理に関して、KLabで気をつけていることを紹介してきました。仮想通貨の扱いは、健全なゲームの運営やユーザーの利益保護という観点からきわめて重要なポイントになってきます。参考になれば幸いです。

KG SDKについて

KG SDKでは、KLabのノウハウを活かし、仮想通貨管理ライブラリやKPIレポートへの対応も行なっています。KG SDKの概要についてはこちら を、お問い合わせにつきましてはこちら をご覧下さい。

はじめに

こんにちは
KLab Advent Calendar8日目の記事です。
KLabGames事業部エンジニアの@knsh14です。

みなさんはUnityで開発をする際にどのようにしてアプリのパフォーマンスを計測していますか?
今回はある案件で今までと違うメモリ監視の方法を使ってみたら、デバッグが捗った話を紹介しようと思います。

KLabgamesでは幾つかのゲームでUnityを用いて開発を行っています。
よりよい品質でユーザ様に遊んでいただくために、常にパフォーマンス・チューニングを行っています。
その中でも各シチュエーションでのメモリ使用量を計測し、改善することは低スペック端末などで動作させるために必須です。
ですが、Unity標準のProfilerでは適切にメモリ使用量を計測できない問題がありました。

今までの方法

これまでは以下の様なコードでメモリ使用量を取得して画面に表示していました。

uint totalUsed = Profiler.GetTotalAllocatedMemory();
uint totalSize = Profiler.GetTotalReservedMemory();

System.Text.StringBuilder text = new System.Text.StringBuilder();

text.Append((totalUsed / (1024f * 1024f)).ToString("0.0"));
text.Append("/");
text.Append((totalSize / (1024f * 1024f)).ToString("0.0"));
text.Append(" MB(");
text.Append((100f * totalUsed / totalSize).ToString("0"));
text.Append("%) ");

これをOnGUIで表示するなり、NGUIのラベルに出力するなりしていました。

シンプルですね。
ここで使っているメソッドを調べてみましょう。
Profiler.GetTotalAllocatedMemory()
Profiler.GetTotalReservedMemory()

公式のリファレンスにもなにも書いてない...
実際に動かした結果とプロファイラを見比べてみると
device-screenshot
unity-profiler-screenshot
instruments-screenshot

全ての計測値にかなり開きがありますね。これでは計測の目安とするには不十分です。

ネイティブプラグインを書いてみる

ではどうすればいいのでしょうか?

僕らが見たいメモリ使用量は各OSから見てUnityAppがどれほどのメモリを使っているかというものです。
なのでUnityから利用できるネイティブプラグインを書いてそこから各OSに問い合わせればいいわけです。

こうして書いたネイティブプラグインコードがこちらです。

iOSの場合

#import <Foundation/Foundation.h>
#import <mach/mach.h>

unsigned int getUsedMemorySize() {
    struct task_basic_info basic_info;
    mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
    kern_return_t status;

    status = task_info(current_task(), TASK_BASIC_INFO, (task_info_t)&basic_info, &t_info_count);

    if (status != KERN_SUCCESS)
    {
        NSLog(@"%s(): Error in task_info(): %s", __FUNCTION__, strerror(errno));
        return 0;
    }

    return (unsigned int)basic_info.resident_size;
}

このコードをAssets/Plugins/iOSに適切に配置して読み込むとiOSからUnityAppが使用している物理メモリ量を取得することができます。
ネイティブプラグインを読み込む方法は世の中にたくさんあるのでここでは割愛します。

task_info関数は公式リファレンスによると、現在のtaskに関する情報の配列を返すとあります。
第2引数のflavorにTASK_BASIC_INFOを指定すると第3引数に与えたtask_infoに以下のような構造体が入ります。

struct task_basic_info {
        integer_t       suspend_count;  /* suspend count for task */
        vm_size_t       virtual_size;   /* virtual memory size (bytes) */
        vm_size_t       resident_size;  /* resident memory size (bytes) */
        time_value_t    user_time;      /* total user run time for
                                           terminated threads */
        time_value_t    system_time;    /* total system run time for
                                           terminated threads */
    policy_t    policy;     /* default policy for new threads */
};

この中のresident_sizeがtaskが使っているメモリ量です。
iOSではtask = プロセス = アプリなのでこれでiOSから見たUnityアプリがどれ位メモリを食っているかを確認することができました。

Androidの場合

Androidの方ではtask_infoは使えませんが、代わりにAndroid自体に取得できる機能があったのでそちらを使いました。

import android.os.Debug;
import android.os.Process;
import android.app.ActivityManager;
import android.content.Context;

/**
 * @return 現在使用しているメモリ量(KB)
 */
private static long getUsedMemorySize() {
    final Context context = UnityPlayer.currentActivity.getApplication().getApplicationContext();
    final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    final int[] pids = new int[]{ Process.myPid() };
    final Debug.MemoryInfo[] memoryInfos = activityManager.getProcessMemoryInfo(pids);
    long sumMemories = 0;

    for (Debug.MemoryInfo mi : memoryInfos) {
        sumMemories += mi.getTotalPss();
    }

    return sumMemories;
}

Android公式リファレンスを見るとDebug.MemoryInfoがいろいろな情報を持っているのでこちらを利用します。
プロセス毎にメモリ使用量が取れるのでそれを合算して表示します。

改修後

両プラットフォームともネイティブコードを書いたところで実際にプロファイラと見比べてみましょう

iOS

device-screenshot
instruments-screenshot

Instrumentsから得られた物理メモリ使用量と画面に表示しているメモリ使用量が一致していますね!

Android

device-screenshot

プロファイラからは以下のようになりました

kamata% adb shell dumpsys meminfo | grep gopher
    50409 kB: com.example.gopher (pid XXXXX / activities)
               50409 kB: com.example.gopher (pid XXXXX / activities)

50409 / 1024 = 49.2MB

この通りメモリ使用量がプロファイラから見たものと一致していますね!
こうしてUnityApp上で各OSからみたメモリ使用量を可視化することができました。
これで、実際の端末上で気軽にメモリ使用量を計測することができますね!

最後に

いかがでしたか。

このように簡単なコードを一手間加えるだけでプロファイリングが格段に楽になりましたね。
時には標準Profiler以外の方法も試してみると、よりUnityと上手く付き合っていけるのでは無いでしょうか。
この記事を見てくださった方のメモリ監視が楽になることをサンタさんにお願いして終わりたいと思います。

なお、今回作成したサンプルで使われているGopherのモデルはGitHubにあるこちらのモデルを利用しました。
Gopherの原著作者はRenée Frenchさんです。

次回は@hnwさんの「Autotools三兄弟の末っ子libtoolとは何者なのか」です。
僕の記事より面白そうなので今から楽しみです!

はじめに

先日KLabではパブリッシングパートナー会社様及び共同開発パートナー会社様向けに、 「モバイルオンラインゲーム開発SDK」として、社内で開発・利用を推進しているライブラリ群の提供を開始しました。
今日はこのSDKに含まれているストア課金処理ライブラリおよび仮想通貨管理ライブラリについて、その提供の背景と概要についてお話しします。

ストア課金処理ライブラリ・仮想通貨管理ライブラリ提供の背景

多くのスマートフォン用アプリには、アプリの中から追加で課金を行うことでロックされた機能を有効化したり、新たなコンテンツを追加したりする機能があります。
これを実現しているのがアプリ内課金の機能です。オンラインゲームでは仮想通貨の購入やゲーム内機能をアンロックする目的で利用されるケースが多いようです。

このアプリ内課金は基本的にアプリ(スマートフォン端末)とGoogle PlayやiTunesなどの決済サーバ間で完結します。
つまり運営者が独自にサーバを用意することは仕様上必須ではありませんが、一方でオンラインゲームの運営サーバ(以降、リモートサーバ)側ではコンテンツの付与などをそのサーバ上で間違いなく行う必要があるため、アプリとリモートサーバとの連携処理を適切に実装する必要があります。

構成概念図

このようなリモートサーバにおける課金処理で特に注意しなければならないのが、購入情報の検証すなわち不正対策と、そこで付与されるコンテンツ=仮想通貨の管理に関する実装です。

決済プラットフォームが提供している仕様書の内容だけでは、オンラインゲームに特化したベストプラクティスといったことまではあまり読み取ることができません。
つまりこれらの処理は運用ノウハウといえる領域であり、各社独自に工夫を凝らしている状況ではないかと思います。

前置きが長くなりましたがこのストア課金処理ライブラリおよび仮想通貨管理ライブラリの目的は、このリモートサーバ側の「購入情報の検証」および「仮想通貨の管理」に関するKLabの運用ノウハウを透過的に、つまり容易に利用可能にすることでオンラインゲーム開発者が本来の作業に集中できるようにすることにあります。
なお、これらの機能はモジュール化されており自由に組み合わせることが可能な設計としています。

購入情報の検証 (ストア課金処理ライブラリ)

オンラインゲームのように購入情報をアプリからインターネットを介してリモートサーバに送信し、リモートサーバ上でコンテンツ付与を行う際に注意しなければならないのが不正対策です。
具体的に言うとアプリ内課金に関する処理を迂回するようにアプリそのものを改ざんされたりすることもありますし、偽の購入情報を送りつけるようなツールも横行していますので、このような不正利用者によるただ乗りがないよう考慮して検証を実装する必要があるわけです。

ただしオンラインゲームに関して言うと、アプリ内課金に必ずリモートサーバが介在するためスタンドアロンアプリ=リモートサーバを必要としないアプリに比べると不正対策は比較的容易です。
というのもアプリについてはバイナリ自体やメモリ改ざんが技術的に可能でも、サーバ側を乗っ取ってプログラムを書き換えるようなことはそれと比較してずっと難しいからです。
つまりオンラインゲームにおいてはサーバサイドでの検証を適切に漏れなく行うことで、不正利用をほぼ100%防ぐことができます。

ストア課金処理ライブラリでは次のようなチェックを実施することで不正対策を行っています。

  • 署名あるいは検証サーバを利用した検証の実施
  • 購入情報内の決済IDによる一意性の担保
  • 購入情報に含まれる製品IDや商品IDの存在チェック
  • などなど……

上記でおわかりのように一つひとつの処理は技術的に難易度が高いものではありません。
ただしこのような手続きはアプリ毎の依存部分があまりないことや、実際には検証サーバへの接続処理やリトライ処理等エラーハンドリング、各種ログ実装(忘れがちになる部分ですが運用では重要です)など煩雑な対応も付随して多く発生するためライブラリ化するメリットが大きいところといって良いと思います。

仮想通貨の管理 (仮想通貨管理ライブラリ)

日本国内向けに有償で発行が行われた仮想通貨は、発行額や発行形態に応じて資金決済に関する法律(資金決済法)という法律の適用を受けることがあります。
目的は利用者保護にあり、この場合事業者および発行する仮想通貨の名称や単価等を監督省庁に届け出る必要がありますし、事業者に万が一のこと(破産など)があったときに備えて仮想通貨発行額に応じた発行保証金の供託を行わなければならないことになっています。

これを実現するには一貫したルールで仮想通貨の付与・利用を行う必要がありますし、さらにこれらの入出力ログは厳密に管理されなければなりません。
このためKLabでは仮想通貨は種別毎に先入先出法で管理することで入出力を厳格にルール付けした上で残高管理を行っているのですが、このような実装は一定の会計知識も必要ですし、やや複雑な手続きになりがちな上に万が一不具合があった場合にはその損害も計り知れません。
そこでこのような処理をカプセル化し、付随する固有の業務処理におけるトランザクション管理やロック機構がテンプレート化されたライブラリとして提供することで仮想通貨の正確な管理・運用を支援しています。

さいごに

このライブラリの検証処理機能としては現在 python向けにIn-App Purchase(App Store)用とIn-app Billing(Google Playストア)用のものを提供しており、以後ニーズを見ながら他の決済プラットフォーム向けのものを提供予定です(動作環境等詳細につきましては までお問い合わせください)。
ライブラリの開発チームでは常に最新の情報を把握して、プログラムへの反映を継続して行うようにしています。

KLabではこのようにアプリの開発や運用で得られた知見を、各種ライブラリやフレームワークといった共通のコンポーネントとして集約することにより開発業務の効率化を進めています。

9/17から開催される東京ゲームショウでも当社コーナーを出展していますので、興味を持たれた方はぜひ遊びに来てくださいね。


Shimanuki

↑このページのトップヘ