KLabGames Tech Blog

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

皆様は、メンターにマウスを禁止されたことはありますか!?

新卒入社1年目のshotaです!

私は恥ずかしながら最近までvimの存在を知らなかったのですが、とあるきっかけがあり3ヶ月程前にVimを触り始めてみました!その期間で、どのようにvimに歩み寄ったかを紹介します。

今までvimに触れたことが無く、これからvimを触りたい!と考えてる方の助けになればと思います。

第一印象

入社した当時はvimというエディタの存在を知りもしませんでした。

というのも学生時代はDirectXやOpenGLといったグラフィックAPIをVisualStudio等の統合開発環境で主に作業をしていたため、CLIに触れる機会も無くエディタに対して何一つ不自由を感じたことがなかったからです。

そんな自分が初めてvimを触れたのは入社後の研修中のことでした。その時の印象を箇条書きで書きますと

  • なんでマウス使わないの?
  • hjklでカーソル移動?誰得?
  • 普通に入力できないの?
  • モードとかややこしいだけじゃない?
  • 使いにくい。。。

と、正直な話最初はネガティブな印象しかないエディタでした。。

きっかけ

入社するまではUnix/Linuxのターミナルを触る事はありませんでしたし、今後も触るつもりがなかったので「別にvimで無くとも文字は書けるしこんな使いづらいエディタ使わなくてもいいかー」と高を括っていたのですが、親しい同僚達にはvimを利用している方が多く、魔法のように文字を入力している姿を見て衝撃を受けました。

「ここまで出来るようになればかっこいいなぁ」と感じて、自分もこの領域まで到達してやる!とvimを使う決意をしたのです。(要するに使いこなすとかっこよさそうだったから!)

3ヶ月間で取り組んだこと

エディタは必ずvimを使用する

vimを触る決意をしてまずはじめに取り組んだのが、何を書くにも日報を書くにもちょっとしたメモを残すにもとりあえずvimを使用するということです。

ちょうどその時期は案件に配属されてから、1週間を利用して簡単なゲームを制作するというタスクを課せられていました。

その時点ではインサートモードに入り文字を入力してノーマルモードに戻るぐらいの知識しかなく、vimに対する感想は随所でアピールしているのですが「漫画ドラゴンボールの悟空の胴着」です。この頃はどうにも通常のキーバインドのエディターの方が効率がよかったです。。

vimとの格闘の中で躓いたりわからないことがあればすぐに調べることで、vimに対する知識を実用レベルでどんどん吸収することができたので、これからvimを触る方にはぜひ実践していただきたいです。

マウスを極力触らないようにする

私はwindows用のvimを利用させていただいていたので、実はマウスが使えます。

vimというエディタはマウスがまだ一般的でなかった時代に作られたviと呼ばれるエディタを元に制作されたという話を聞いてマウスを極力触らないよう意識をするようになりました。

元々マウスを多用する方だったので少し気を抜くとマウスに手が伸びてしまい。この取り組みには大変苦労しました。

しかしこの苦労を乗り越えてh,j,k,lでの移動や移動に便利なコマンド等を活用できるようになり、今ではマウスに手を伸ばすこと自体が億劫になっています!

一日一つ便利なコマンド等を覚える

vimを触り始めてから最初の1ヶ月あたりは1日に1つずつ便利そうなコマンドを覚えていきました。

物覚えが悪い自分にはこの方法が実に効果的で、この時の知識が今でも業務で活躍しています。

その中から5つピックアップして紹介いたします。

「*」

as

単純ですがとても強力でした。

任意の単語の上で「*」を入力すると、その単語で検索を掛けてハイライトをしてくれます。その後「N」,「n」でそれぞれ前方後方の単語へジャンプできます。

「f,F,t,T」

fFtT

自分は主に行中の移動で利用しています。

「f」の後に任意の1文字を入力すると次に出てくる文字の上に、「t」の場合は文字の手前にカーソルがジャンプします。

それぞれ大文字の場合はジャンプの方向が前方になり、直近にジャンプした文字は「;」で次へ「,」で前へジャンプできます。

「◯i◯,◯a◯」

diw

動作+a or i+対象」という使い方をして、対象に対して動作を行います。

例えば「vi”」の場合。「区切り文字を含めない”で囲まれたテキストをビジュアルモードで選択する」というコマンドになります。

このとき「i」を利用すると対象に区切り文字を含めず、「a」を利用すると対象に区切り文字を含むことができます。

「i」や「a」は挿入モードに切り替えるiやaと違いそれぞれinnerと冠詞のaという意味を持つそうです。

また対象を「w」とすることで単語を対象にすることができます。

「J,gJ」

gJ

ビジュアルモードで選択した行を連結することができます。

「J」の場合連結時にスペースが挿入されますが、「gJ」を利用することでスペースを挿入せずに連結することができます。

「ctrl+v」

ctrlv

こちらのコマンドはビジュアルモードで矩形選択をするものです。

また矩形選択時に「I」,「A」どちらかで挿入モードに入り文字を入力したあとノーマルモードに戻ると選択した行すべてに同じ入力ができます。

前述の「J」,「gJ」を併用して改行区切りのデータを手軽にCSVに整形できるので、業務でもよく使っています。

.vimrcにキーバインドを記述する

2ヶ月目に入り操作にも慣れてきたころ、.vimrcというファイルに記述をすれば思い通りに設定ができることを思い出したので、前々から使い辛いと思っていた以下の4つのキーバインドを変更しました。

  1. 行頭へジャンプ:「0」→「ctrl+h」
  2. 行末へジャンプ:「$」→「ctrl+l」
  3. 画面上の先頭行へジャンプ:「H」→「ctrl+k」
  4. 画面上の末尾行へジャンプ:「L」→「ctrl+j」

最初にhjklのキーバインドに矯正したこともあり、こちらのバインドを設定するだけでカーソルのジャンプがとても直感的になりました。

何よりこのバインドではホームポジションからほとんど指を動かす必要が無くなるので素早く操作ができるようになります。

また新バインドのうち、1,2,4については元々別の機能がバインドされていますが、1,4はctrlを押さずに入力した場合と同じ機能がバインドされていて、2についても画面の再描画という今まで使ったことがない機能がバインドされています。

今のところ使う場面に出くわしていませんので、必要になれば使わなくなった「0」や「$」にバインドしようと考えています。

これから

3ヶ月間で標準のVimにはそこそこ慣れてきました。

ですが実はまだvimの本領ともいえるであろうプラグインによる拡張を試せていません。

同期の友人はプラグインでいろいろ拡張してとても便利にしているので、そろそろ3,4ヶ月目に入ることですしいろいろなプラグインを試してみたいなぁ。

しかし標準の状態でもちょっとコマンドを覚えるだけでコーディングの効率が上がりましたし、何より入力が楽しくなりました!

また、vimに慣れてきたといいましても、まだまだ手が覚えておらずやりたい動作を瞬時に入力できませんし知らないコマンドも山ほどあるので時間をかけてもっとお近づきになりたいと思います。

長文ですが最後までお付き合いいただきありがとうございました。

こんにちは。@kokukumaです。

みなさんは画像や音声データなど(以下、アセット)をどのように管理しているでしょうか?

簡単なversion管理ツールを使っている人もいれば、専用のアセット管理ツールを導入している人もいるでしょう。

私がいま在籍しているプロジェクトでは、他のクライアントコードと同様にgitで管理していました。

しかし、プロジェクトが進むにつれてアセットも増え、gitでは辛い場面も数多くでてきました。

  • cloneが遅すぎる。実行して一晩寝かせるレベル。
  • 容量がでかすぎる。12GBとかある...。消したい。
  • ふざけるな。git fetchが遅すぎる。

というような状態になり、やっぱりアセットをgitで管理するのは無理があるよねとなりました。

しかし、今から別のツールに移行するのも結構辛い。。

という事で、gitを使いつつ、もっと簡単にアセット管理をできる仕組みを考えてみました。

こんなのを作ることにした

  • クライアント-アセットの対応情報を取得できる

    クライアントとアセットの整合性が取れていなければ、動かない可能性があります。

    そのため、自分が開発しているクライアントに対応するアセットがどれかを知る必要があります。

    それを保存するためにDBとかを準備するのが面倒なので、gitレポジトリの中に保存してしまうことにしました。

    そして、この情報のみ頻繁にfetchするようにします。

  • 必要最小限のアセットだけfetchする

    一度に使うのは、特定のversion, 特定のplatform用のアセットです。

    そのため、上記の対応情報でわかったアセットだけを取得します。

    もちろん履歴はfetchしません。加えて、必要なplatformのディレクトリだけfetchする形にしました。

    これによって、実際のアセットを取得する際も、それほど時間をかけずに取得できます。

全体像を書くとこんな感じです。

図1. アセット管理の流れ

image

これで、fetchが重いとかローカルの容量が大きくなりすぎるといった問題を回避できそうです。

これらをどうやってgit上で行っているかを説明するために、gitの内部構造について軽く触れておきます。

gitの内部構造

『Pro Git』Chapter 10「Gitの内側」を読むと、Gitの中でどのように変更履歴やディレクトリ構造が保存されているのかがよくわかります。

要点をまとめると以下になります。

  • 変更履歴・ディレクトリ構造は、commit object, tree object, blob objectのグラフで表現されている。
  • tree objectには、ディレクトリ中にある、ディレクトリ名やファイル名が保存される。
  • blob objectには、ファイルのコンテンツが保存される。

つまり、dir1/hellodir1/worldfile2と3つのファイルが保存されている場合、以下の様に保存されることになります。

図2. gitの構造

image

クライアント-アセットの対応情報を取得する

gitの内部がこのような形であると分かれば、レポジトリに保存されているディレクトリ構造と関係なく、データを保存して置けることがわかります。

gitの配管コマンドを利用すれば、ファイルを作成・編集しなくても、indexファイルにデータを書き込みcommit objectを作成することが出来ます。

そして、tree構造を作ってしまえば、gitのコマンドを使って簡単に保存した値を取得することができます。

つまり、tree objectの構造をkeyとして、blob objectにvalueを保存する、簡単なkey value storeとしてgitレポジトリを利用することができるわけです。

以下、bashでこの操作をやるとこんな感じです。

# set_value (レコード名) (key) (value) .gitディレクトリで実行。
set_value refs/heads/ppack_index/record1 assets/iphone ppack_assets_3dd0_iphone

# get_value (レコード名) (key) .gitディレクトリで実行。
get_value refs/heads/ppack_index/record1 assets/iphone
# => ppack_assets_3dd0_iphone

function set_value(){
    export GIT_INDEX_FILE='tmp_index'; # まっさらなindex

    local RECORD=$1
    local KEY=$2
    local VALUE=$3
    local PARENT=""

    # 指定したレコードが既にあればそれをindexに読み込む。
    if [[ ! -z $(git show-ref $RECORD) ]]; then
        git read-tree $RECORD^{tree}
        PARENT="-p $RECORD"
    fi

    BLOB_HASH=$(echo ${VALUE} | git hash-object -w --stdin)       # valueを保存したblob objectを作成。
    git update-index --add --cacheinfo 100644 $BLOB_HASH $KEY     # keyをファイル名としてindexに登録。
    TREE_HASH=$(git write-tree)                                   # 今のindex内容でtree objesctを作成する。
    COMMIT_HASH=$(git commit-tree $TREE_HASH -m $RECORD $PARENT)  # treeにcommit objectを紐付ける。
    git cat-file -p $COMMIT_HASH^{tree}

    # RECORDブランチを作成
    git update-ref $RECORD $COMMIT_HASH

    rm tmp_index
    unset GIT_INDEX_FILE

}
function get_value(){
    local PPACK_INDEX=$1
    local KEY=$2
    VALUE=$(git cat-file -p $PPACK_INDEX:$KEY 2> /dev/null)  # あるcommitのファイルの中身を
    echo $VALUE
}

この中に、clientレポジトリのコミットと、それに対応する必要最小限のアセットを指定するコミットを紐づけるデータを保存しておきます。

1つのレコードには、以下の情報を登録しておきました。

key value
client_branch クライアントのブランチ名
client_commit クライアントのコミットhash
asset_commit assetのコミットhash
assets/iphone iphoneのアセットだけ取得するためのコミット
assets/android androidのアセットだけ取得するためのコミット

次に、iphone/androidだけを取得するコミットをどうやって作るかを説明します。

必要最小限のアセットだけfetchする

私の案件では、platform毎にディレクトリが分けられ、アセットが保存されていました。

そのため、必要最小限のアセットだけをfetchするためには、特定のディレクトリだけfetchする方法が必要です。

それを実現するために、clone/fetchするobjectがどのようにして選ばれるかを調べ、利用しました。

まず、通常cloneすると、図2のようにブランチが示すcommit objectから辿れる全てのオブジェクトを取得します。

一方、shallow cloneを実行すると、図3のように、履歴をたどらずに取得することができます。

図3. shallow cloneしたとき(赤黄緑だけcloneする)

image

ここから、clone/fetchする対象は、指定されたcommit objectとから辿ることが出来る範囲にしぼられることが分かります。

そのため、ディレクトリ単位でfetchするためには、cloneしたいディレクトリのtree objectを指すcommit objectを作ってやり、それをcloneの対象として指定すればよいことになります。

図4. ディレクトリ単位でgit cloneする時の図(赤黄緑だけcloneする)

image

以下、bashでこの操作をやるとこんな感じです。

# clone元レポジトリで事前にやっておくこと。
declare BASE_HASH="692c6c9"
declare TARGET_DIR="directory_name"

TREE_HASH=$(git rev-parse $BASE_HASH:$TARGET_DIR)
COMMIT_HASH=$(git commit-tree $TREE_HASH -m 'clone')
git tag -a clone_tag -m 'clone_tag' $COMMIT_HASH

# cloneするときの操作
git clone -b clone_tag (clone元レポジトリのurl)

このアセットfetch用のcommit objectを、クライアントのコードに対応するアセットとして情報を持っておけば、必要最小限のアセットを取得することができるようになります。

まとめ

gitの構造を利用してやることで、ファイル数が多すぎて重いレポジトリでも、負荷なく使える環境を作ることができました。

今後、もう少し汎用的なツールにまとめていきたいです。

はじめに

先日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

↑このページのトップヘ