KLabGames Tech Blog

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

この記事は KLab Advent Calendar 2017 10日目の記事です。

こんにちは。このブログでは4度めまして、kakkun61 です。

この記事では、10月22日に開催された同人誌即売会技術書典3に KLab の有志で作った同人誌を頒布しましたので、その報告をします。

頒布した同人誌は電子版が無料で、この記事の下部にダウンロード用のリンクを張っています。

技術書典とは

技術書典は、プログラミングについての同人誌を作成しているサークル TechBooster達人出版会が主催する技術・科学系同人誌即売会で、今回含めナンバリングイベントが3回、超会議での超技術書典が1回開催されています。

今回は秋葉原のアキバ・スクエアで開催され、ユニークの来場者数では2750名とかなり大規模なイベントとなっています。

なぜ参加することにしたか

発案は筆者で、筆者は技術書典1と技術書典2そしてその間のコミックマーケット91とで Haskell の同人誌を頒布して同人誌を作る楽しさみたいなものを感じていたことと、KLab のアドベントカレンダーは過去2度ともすぐに枠が埋まってしまっていたので、社内に書きたいと思っている人がある程度いるのではないかと思い、会社に相談し会社として参加することになりました。

どう作ったか

TechBooster の『技術書をかこう!』にしたがって Re:VIEW を使い、GitHub 上で Pull Request 運用で作っていきました。

各章を1人で担当し、章ごとに PR を作成しレビュー後マージという運用をしました。ふだんのソースコードの開発と似せた方が分かりやすいかと思いこのようにしました。

どんな同人誌になったか

表紙

7名で執筆し、校正の協力に1名、表紙の協力に1名という体制のメンバーになりました。

内容は下記です。

  • 物理ベースレンダラーを Rust 実装して、表紙絵をレンダリングした話
  • Sprache を CPS 変換
  • Emscripten で動画再生する
  • テキストマクロプロセッサ「M4」のチューリング完全性
  • FPGA 初心者が試行錯誤しながら疑似乱数生成回路を作る
  • 家庭内ネットストーカーシステムを作った
  • とある KLab のスマホアプリのビルド事情

特にジャンルなどは指定せずみなが書きたいことを書いてもらったのですが、直接しごとと関係あるものは「とある KLab のスマホアプリのビルド事情」のみで、他は各々好き好きな内容になりました。

参加してどうだったか

さいわい印刷した冊数の7割ほど頒布することができました。印象としては KLab だからというよりは、内容で購入してくれていたように思いました。どれもコアな内容なので一部の人には刺さっていたようでよかったです。

メンバーでのふりかえりで、一般にも役に立ちそうなことを下記に抜粋します

ぱっと見てどんな内容の本か分からない

雑多な内容でタイトルに何も情報がないのでどんな本か分からなかったのは失敗でした。表紙に概要を書いたり、内容を書いたものを机に立てるなどしようと思います。

カラーのフェルトペンがあるとよかった

上の項目にも関連して、現地で札などを立てることになったときに黒ペンしかなかったので、見栄えが悪かったです。

人だかりができると立てたものは反対に見にくい

宣伝に札を立てたりしたのですが、人が近くで立って机を見たら上から見下ろすことになるので、そのときは紙を置く形の方がよかったです。机の前に人がいるいないで立てと寝かせとをうまく変えられるとすごくいいと思いました。

レビューに PR を使うことについて

今回は GitHub で PR を作る方式にしたのですが、いくつか問題に感じることがありました。

  • 書きかけの状態で全部の章を合わせてビルドするのが PR 方式だとやりにくい
    • マージしたブランチを作成しないといけないため
  • レビュワーが PR 上で指摘するのはリードタイムが長くなるので、明らかに問題のない修正はレビュワーが直接書きかえたかった

次回は、著者が直接 master にコミットし、レビュワーも直接コミットして訂正する方法を試そうと思います。

余談

調査不足でブースが隣りの Wantedly の書籍と名前かぶりしていました。「Tech Book ください」と言われて「どちらの?」となることがありすみませんでした。あちらの方が技術書典1からその書名を使っていらしたのでこちらがかぶせてしまいました。

電子版

ダウンロード

この記事は KLab Advent Calendar 2017 8日目の記事です。

こんにちは knsh14 です。 Unity で AnimationClip Editor を作った話をします。

KLab では様々な演出などで AnimationClip を使っていますが、Unity 標準の AnimationClip のエディタはどうにも使いづらいものになっています。

  • 各オブジェクトの並べ方が幅優先表示になっていて非常に見づらい
  • ショートカットがほとんど無い
  • 編集にじゃまなパーツを折りたたんで非表示にできない

などなどあります。 既存のアセット等でも対応できなさそうだったので、今回内製でエディタを作成することになりました。

実際の画面

sample

機能

エディタというのは単純だと思っていてもいろいろな機能があります。

エディタとしての機能

  • 新規作成、読み込み
  • 前回保存分を再開する
  • AnimationClip の情報の編集
  • AnimationCurve への KeyFrame の追加削除、値の編集
    • 変更はその場で反映される
  • キーのコピペ
  • キーを範囲選択して左右へ移動させる
  • 操作の Undo/Redo
  • データの保存

AnimationClip を編集するための機能

  • 今の状態を自動でプレビューできるようにする
  • シーン側でオブジェクトを動かしたりしたらエディタのキーフレームに反映される

基本設計

クラスやプロジェクト構成などは Clean Architecture を参考に作成しました。 Unity Editor 拡張で説明するより、 iOS/Android で解説されている資料のほうがより詳細にかかれていてわかりやすいと思うので、そちらを参考にしていただくのが一番いいと思います。

以下のような構成でできている仕組みです。

  1. Presentation 層

    1. View
      • UI を構築します。
      • UnityEditor.EditorWindow を継承し、アプリケーション全体の起動も担当します。
    2. ViewModel
      • Data 層の Entity を View で表示するためのクラスです
    3. Presenter (Controller)
      • View から受け取ったイベントを UseCase に流して処理してもらったり、データを取得したりする。
      • 本来なら Controller がイベントを受け取り UseCase に流す、 Presenter がデータを取得するという役割分担までするのですが、一旦まとめて実装しました。
  2. UseCase 層

    1. UseCase
      • 固有のロジックを実装します
    2. Translator
      • Entity を受け取り、Presentation 層で使う ViewModel に変換します。
      • Presentation から受け取ったものを Entity で扱えるようにしているところもあります。
  3. Data 層

    1. Repository

      • UseCase と DataStore の遣り取りをするための薄いインターフェース
    2. DataStore

      • Entity の集合で CRUD を実装します
      • DataStore をシリアライズして JSON に保存することで前回の状態から作業を再開できるような仕組みも実装できます。
    3. Entity

      • コアのデータ定義をします。
      • 例えば GameObject の ID、アニメーションするプロパティ、AnimationCurve のセットやショートカット名と実行するキーの組み合わせなどです

こうすることで以下のようなメリットがあります。

  1. UI と裏側のロジックを完全に分離できる

    • 特に Unity EditorWindow のイベントハンドリングは中々癖が強いので出来る限り一元管理できる仕組みにできたほうが都合がいいです。
  2. テストしやすい

    • 操作を UI から切り離しているので、操作ごとにテストを走らせるなんてこともできます
    • このエディタは一人で作っていたので簡単にテストできる仕組みは動作を安定させるために必須項目でした
  3. コードの再利用がしやすい

    • ショートカット機能では利用者が好きなようにショートカットの割当をできるのですが、これは別のウィンドウで行います。
    • そのために似た実装が増えるのは大変だし、共通化することでどちらかでバグが出たらすぐに気づけるようにしました。
  4. 実装にブレが出にくい

    • この領域では何をするかが割りときっちり決まっているので、実装にブレが出づらいです。
    • また後でメンテナンスする場合にも読みやすさが高くなります。

デメリットもあって、次のようなものです。

  1. ちゃんと理解してないとどこに何を書くのかわかりづらい

    • これは MVC などでも言えることだと思いますが、設計をしっかり理解してからじゃないとうまくかけないのはちょっとハードルが高いです。
  2. 当然ですがコードが長くなる

    • 強いて言えば程度のデメリットですが、当然たくさんコードを書かないといけません。

エディタを実装する

全ての機能を紹介するのは大変なので一部だけ抜粋してどのように実装したか紹介します。

Undo/Redo

Undo/Redo は自前で実装するのは結構たいへんですが、Unity には割りと簡単に Undo をサポートする仕組みがあります。 Undo したいオブジェクトをシリアライズされる状態にして、UnityEngine.Object から辿れるようにします。 UnityEditor.Undo.RecordObject(UnityEngine.Object obj, string title) で操作を記憶してやると、その操作が Undo スタックに乗って、Unity のショートカットで Undo/Redo することができます。

範囲選択してコピペ

エディタといえばコピペですね! ペーストは clipboard にあるものを取り出してキーを追加する操作をすればいいので簡単なのですが、意外と大変なのがコピーです。

コピー操作はキーフレームを clipboard に入れることで実装できます。 ただコピーと一口に言ってもいろいろなコピー対象があります。

  • パーツについてるプロパティのキー全体をコピー
  • パーツについてるプロパティのキーの一部をコピー
  • パーツについてる一部のプロパティのキー全体をコピー
  • パーツについてる一部のプロパティのキーの一部をコピー
  • フレキシブルに選択した範囲をコピー

この種類を全部対応するのは大変そうだったので、最後の「事前に範囲選択をしてからその領域にあるキーフレームをコピー」だけを実装することにしました。 編集する対象によってこの辺は変わることが多いと思うのでどの仕組みにも対応できるように作っておけると楽になると思います。

ショートカット

ショートカットは利用していただいてるデザイナの方にも非常に好評で作ってよかった機能です。

仕組みは簡単で

  1. View にショートカットに対応しているメソッドを用意
  2. キーイベントからキー入力と突き合わせて一致したメソッド名を取得
  3. リフレクションで実行

というフローになっています。
ただこれには問題点があって、これだと1発のキー入力しかショートカットに登録できません。
Ctrl-X Ctrl-O のような2段階ショートカットに対応するには、一旦キー入力をキューに保存するなどの工夫が必要になります。

苦労したところ

UIレイアウトがなかなか直感的に書きづらい

全部 C# で書けるのは割りと楽なことも多いのですが、HTML で書いてこの要素にイベント仕込むだけならもっとUI作成も楽なのになあと思うことも多少あります。 またレイアウトではどうしても右寄せ左寄せなどの配置が難しいので、キーフレームの描画などは苦労することもありました。

またいろいろなデータを描画しようと思うと描画順で頭を悩ませることもありました。
処理的にはここでひとまとめに書くのがシンプルなんだけど描画を考えるとあとでわざわざ描くといった苦しいコードになることもありました。
depth 欲しいです。

Unity Editor 拡張のイベントハンドリングは辛い

Unity のイベントハンドリングは UnityEditor.Event.current を見てハンドリングするのですが、個別のUIに操作を割り当てるのがかなり苦手な感じを受けました。   ボタンやUIの値が変更された場合は割りと簡単に個別のハンドリングができるのですが、ドラッグやショートカット用のキーイベントなどになると途端にシンプルに書きづらくなるので、注意する必要があります。

終わりに

今回は Unity の Editor 拡張でエディタを作るという、Unity を使っているだけならなかなかやらないような話をしました。   ちゃんとしたエディタを一通り作るのは Unity の力を借りてもかなり大変でした。 普段は何気なくやっている操作も色々試行錯誤がある上にできていると思うと有り難みもましてきます。 世の中のテキストエディタなどを作っているエンジニアの方々の苦労が忍ばれます。
この文章を書いている Vim のコントリビューターの方々には尊敬の念を禁じ得ません。

Unity 2017 では標準の Timeline がかなり便利なので、そちらの UI を手軽に Unity 5 系でも使えたら嬉しいなあと思います。

明日は9日目です。hohean さんの記事です。お楽しみに

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

今年のISUCONはKLabが作問するということで、ゲームが題材だと予想していた方も多かったと思います。 その期待(?)に応えるべく、本選ではCookie Clickerを元にしたゲームを出題しました。 これを複数人で協力プレイできるようにして、WebSocketを使っていて、クッキーではなく椅子を増やし、椅子の数が多倍長整数になる、というゲームでした。

この記事では @hasi_t がISUCON7本選の作問でやったことを書きます。

プロトタイプ作成

まずプロトタイプ作成を担当しました。 Cookie Clickerを元にする、という案が出ていたので、自分がやりたい要素を入れたプロトタイプを作りました。

一つ目の要素は未来計算と操作遅延です。 サーバが未来の値を計算できるようなデータを返し、クライアント側で各時点での値を計算するようにする、という設計です。 そして操作を遅延させることで可能な限り同時刻での見え方が同じになるようにしました。 このような設計によって通信頻度を下げる、というのは、 CEDEC 2015で発表したことの実践で(参考)、 それを実現するための技術や開発体制について普段の業務で日頃考えていたりするのですが、 その実態を見せる機会と考え、この要素を入れました。

二つ目の要素は多倍長計算です。 以前、 ICFPC 2016 というプログラミングコンテストに参加したときに多倍長有理数を使って、 こういう多倍長計算を使った問題を作りたいと思っていた、というのがあります。 あと、メモリを消費させて、ただキャッシュするだけでは駄目なようにしたい、というのも考えていました。

ちなみに、作問チームのうち3人 (@mecha_g3, @___Johniel, @hasi_t) はここ4年ほど、この3人だけではないですが、DiamondPrincessというチームでそのICFPCに参加しています。そして今年は2位でした!

プロトタイプの時点ではWebSocketではなく、普通のHTTP通信で、サーバはPHPで雑に書いた状態でした。 ver0とver1を作ったのですが、ver0の時点ではミリ秒単位ではなく秒単位だったりしました。 あと、命名が雑すぎて抽象的な一文字変数名だらけになっていたので、本実装時に名前の調整が結構大変でした。

本実装作成

WebSocketを使うことになり、初期実装は他の人に任せて、 ゲーム画面を作るためにJavaScriptコードをロジックとビューに分割したり、 いい感じのフォントを探したりしていました。

ベンチマーカのバリデーション作成

未来計算の設計時点で、ベンチマーカから見て検証可能にすることを考えていました。 誤差を許容するには、許容範囲やそれに合わせた計算など、考えることが多く、それを避けた結果、厳密な検証になりました。 そのため、値が1でもずれるとfailするという厳しいコンテストになりました。

負荷走行前の検証はaddIsu, buyItemに対するレスポンスが必ず存在するので良かったのですが、 負荷走行後の検証はタイムアウトなどでレスポンスが無い場合を考える必要があり、 上限と下限を考えて検証する必要があってちょっと大変でした。

エラーメッセージを参加者にとってわかりやすくする、という作業をするのがぎりぎりになってしまい、 初期実装作成者には苦労をかけてしまいました。

ベンチマーカのチューニング

当然といえば当然ですが、初期実装そのままで検証していたら、 負荷走行後の検証の実行時間が長すぎる問題が発生し、高速化をしました。

やったこととしては、指数表記変換関数の高速化、item価格と生産速度のキャッシュ、1000回ループの回避、指数表記変換関数内の10のn乗のキャッシュ、addingの累積和を使う、などです。

実装は以下の通りです。指数表記変換関数(big2exp)はutil.goにあります。

とりあえず、Go言語のbig.Intは、つらい、という感想でした。

マスタデータ調整

いい感じのマスタデータにしないと多倍長使う意味が無い、と思っていたので、 1分間のベンチマークで10の10万乗ぐらいまで到達できるようにマスタデータを調整しました。 調子に乗って1個目の購入価格が10の10万乗ぐらいあるアイテムを作ったら、 初期実装の時点でそこが重すぎて、リハーサルやってみたけどまともにチューニングできない、 みたいなことも起きたりして申し訳ない感じだったりしました。

ちなみに、Excelで値を調整して、Pythonスクリプトで雑なシミュレーションをする、ということをしていました。 もちろん、値を直接扱えないので、対数をとって計算していました。

おわりに

参加者の皆様、運営の皆様、お疲れ様でした! いろいろご迷惑おかけしましたが、出題することができて嬉しく思っています。 ありがとうございました!

↑このページのトップヘ