吉田 正広

ルームの機能をモジュール化して、もうこれ以上コピペしなくて済むようにした話


はじめまして!

東京スタジオでエンジニアをしている、吉田と申します。

リリース後も、アップデートを繰り返していくオンラインゲーム。
安定したサービスを提供しつつ、常に新しい遊びを創造していくというのは、なかなか難儀なものです。

アップデートで機能追加をする度に、プログラマを悩ませるのが、

  • 安全のために、動作実績のある既存のプログラムはできるだけ変更したくない。
  • でも、コードの保守性や可読性のために、共通機能を関数化/クラス化するなどのリファクタもしたい。

こんなジレンマ。エンジニアのあなたも、身に覚えがありませんか?
それとも、「動いているコードは触ってはいけない」というルールを頑なに守って、リファクタを避けてはいないでしょうか?

でもそれだと、既存の仕組みに縛られて、新しい機能の実装が難しくなるし、アップデートと共に少しずつ大きくなって、今やなんの機能なのかわからないクラスなどがあると、バグの対応にも一苦労ですよね。

その上担当者も変わったりして、もう誰も中身を把握していない・・・、なんて状態になる前に、上手にリファクタしつつ、安全性も保ちながら、コードを保守していきたいものです。

先日4周年を迎えた「幻塔戦記グリフォン」(以下グリフォン)では、C++で書かれた、リアルタイム通信機能を担うサーバーがあります。
やはり4年もたつと、古いソースも多々あり、上記のような問題に常に直面しています。
今回は、新しい機能を実装するにあたって、私が行った取り組みについて紹介します。

現状のソース

グリフォンでは、いろんなタイプのクエストやバトルがあり、それぞれがクラスで表現されています。

CRoomBase          -共通で使う基本的な処理
  +- CQuestRoom         -シナリオなどのクエスト
  +- CPvPRoom           -定期的に開催されるPVP対戦
  +- CMockBattleRoom    -いつでもバトルが試せる、模擬戦
  +- CBattleRoyalRoom   -バトルロイヤル
  +- CColosseumRoom     -コロシアム

+- は継承を意味します。

CRoomBaseには、共通機能(主にゲーム内のオブジェクトの管理)が入っています。

それぞれの子クラスには、大まかに

  1. 入室までの処理
  2. ゲーム開始までの処理
  3. ゲームのルール処理
  4. ゲーム戦績の保存
  5. ゲーム終了後の処理

こんな機能が実装されています。

現状のソースコードを眺めてみると、こんな状態になっていました。

  • CPvPRoomCMockBattleRoomは、入室してゲーム開始するまでの流れが違うが、ゲームルールは一緒
  • CBattleRoyalRoomは、CMockBattleRoomをコピペし、一部機能を修正して作ったっぽい
  • その後、CMockBattleRoomにも独自の機能が追加されてるっぽい
  • その他、同じ様でちょっと違う処理が随所にみられる

だんだんつらくなってきた・・・もう既存のソースは見たくない・・ってなりました。
エンジニアの方なら、こんな気持ちをわかってくれるはず!!

そして今回

新しいゲームを提供するため、新たにルームクラスを作ることになりました。(仮にCNewBattleRoomとします)
その要件は、

  • 入室&ゲームスタートまでの流れはCBattleRoyalRoomとだいたい一緒
  • ゲームルール、戦績保存は、CQuestRoomとだいたい一緒
  • 加えて、今回初めて実装する機能

さてどうしたものか・・・
まず簡単に思いつく方法は、

  1. CBattleRoyalRoomをコピーして、CNewBattleRoomを作る。
  2. できたCNewBattleRoomのうち、いるものだけ残していらない物は削除する(もしくは使わないなら放置)
  3. CQuestRoomの一部機能をコピーしてCNewBattleRoomに入れる
  4. あと、足りない機能を入れる

こんな感じでしょうか。またコピペが増える・・・・
このアップデートをもって、サービス終了! ならこれでいいのですが、グリフォンはまだまだ続くのです・・!!

じゃあ、共通の機能を1つにまとめる? でも、共通化できそうな機能も、それぞれのルームで微妙に違ってたりするし(どうしてこうなったのか、もう分からないし)
これもつらいのです。

—-そして悩んだ結果、
よし、今回だけは、コピペを許そう。ただし、今後はコピペしないで済むようにする。

と決めました。

そのために、各機能をモジュール化して、そのモジュールたちを付け替える事で、ルームを表現できるようにします。

新しいクラス構成は

CRoomBase               -共通で使う基本的な処理
  +- CModularRoomBase        -各モジュールを乗せるためのベースクラス
      +- CNewBattleModule    -新しいルーム特有の機能を実装したモジュール
      +- CPvPCommonModule    -対戦共通モジュール
      +- CCommonModule       -共通モジュール

今回だけは、既存ソースからコピペして、各モジュールを作ります。

そして、モジュールを組み合わせたルームを生成するためのテンプレートクラスを

template <typename... Modules>
class CModularRoom : public virtual CModularRoomBase, public Modules...
{
...
};

こんな風に定義します。

例えば、Common,PvPCommon,NewBattleの3つの機能を実装した CNewBattleRoom を作成するには、

typedef CModularRoom<
    CCommonModule,           //テンプレート引数に、入れたいモジュールを羅列する
    CPvPCommonModule,
    CNewBattleModule>
        CNewBattleRoom;

こんな感じです。

こうしておけば、例えば今後、新しいルームクラス CHogeBattleRoom を作るときには、CHogeBattleModuleを作り、CNewBattleModuleと差し替えて、

typedef CModularRoom<
    CCommonModule,
    CPvPCommonModule,
    CHogeBattleModule>       //←ここだけ差し替える
        CHogeBattleRoom;

とすればいい訳ですね。

もし、CHogeBattleRoomを作るなかで、「CNewBattleRoomのこの部分は共通化したい」となった場合は、新しい CFugaCommonModule を作って、機能を分離すれば良いのです。

typedef CModularRoom<
    CCommonModule,
    CPvPCommonModule,
    CFugaCommonModule,     //←CNewBattleModuleから一部機能を分離
    CNewBattleModule>
        CNewBattleRoom;

typedef CModularRoom<
    CCommonModule,
    CPvPCommonModule,
    CFugaCommonModule,     //←分離して作ったモジュールをこっちにも入れる
    CHogeBattleModule>
        CHogeBattleRoom;

こうなります。

ところで、class CModularRoom がいろんなモジュールを多重継承してるけど、ひし型継承問題が起こるんじゃ?と思いましたか?

そんな時のために、C++には仮想継承という機能があります。

各モジュールに

class CHogeModule : public virtual CModularRoomBase
{
};

こんな感じで virtual 指定で継承すれば、ひし形問題は起こらずに済むんですね。

終わりに

今回の記事では、アップデートを繰り返していると必ず直面する、安全性 vs 保守性 のジレンマに対する弊社での取り組みについて紹介させて頂きました。
予測不能?なプランナーさんの考える企画を、リスクを回避しつつ、最大限の効果が得られるように、どうやって実現するか? エンジニアの腕のみせどころです。
Happy Programming!


mewlist

uGUI 描画優先度のチートシート


皆様,こんにちは!
株式会社Aiming の 土井と申します!
リードソフトウェアエンジニアをやっております!

ここ数年は,業務で Unity の uGUI を使って UI 開発をする機会が多いのですが、
Order in Layer, Sorting Layer, ヒエラルキ上の並び順……(つд⊂)ゴシゴシ
Unity では重なり順を指定する項目が多いですよね。
これらの項目間の優先度が実際にどうなっているのか理解していなかったため、調査を兼ねてチートシートを作ってみました!

描画優先度を指定するものを列挙してみた

どうやらこれらの関係を全て調べ上げれば良さそうです。

チートシート

調べました。
画像は実際に Unity の GameView で表示されたもののキャプチャです。

これで、もう UI の重なり順で悩まない!

解説

カメラ毎の優先度

  • Canvas に設定された Camera による優先度
    • ScreenSpace: Overlay 設定のものが最優先
    • 次いで Depth 値が高いものが優先

同じカメラに設定されたキャンバス同士

  • SortingLayer が高い Canvas が優先
    • 同じ SortingLayer 同士では、 Order In Layer の値が高い Canvas が優先
      • さらに同じ Order In Layer 同士では、Z 値が低い(カメラに近い) Canvas が優先

Mesh や パーティクルなど UI 以外の Renderer について

  • SortingLayer や Order In Layer の値による重なり順の制御ができる
  • ただし、SortingLayer と Order In Layer が同じ値を持つ Canvas がある場合、ヒエラルキ上の並び順による重なり順のソートは行われないので、 固有の Order In Layer 値を設定しよう

Render Queue について

  • UI の描画は、 RenderQueue = “Transparent” にて行われるため、独自のマテリアルを使用する場合は RenderQueue の値に 2501 以上を指定する必要がある

最後に

  • UI のレイヤーの間に 3D モデルを表示したりエフェクトを出したりといった要件のときには、まず全体の UI のレイヤー構成を整理して仕様化しましょう。行き当たりばったりで重なり順をいじっていると混迷を極めます
  • MeshRenderer などは標準でインスペクタ上から SortingLayer や Order In Layer が設定できません。
    • https://docs.unity3d.com/ja/current/ScriptReference/Renderer.html
    • 自前でスクリプトから設定する必要があります
      • (Order In Layer は sortingOrder というプロパティです)

おしまい。


oguma

ハートビーツさんと懇親会でLTをしました ’17 06/16


どうも、こんにちわ。

SNS幽霊部員ビリティ高めなOgumaです。
AimingではインフラをAll layerで担当しています。

先日6/16に、そのインフラに関係するMSP (Management Service Provider) である
ハートビーツ さんに 数名で 遊び 懇親会に行かせていただきました。

その際に後輩の趙くんと共にLTをやらせていただきました!!!

国内 (海外は担当が異なる) の公式サイトや、それらと連動するゲーム内から呼び出されるwebView向けのサーバなどをCephFS上に載せてみた話をしました。

さすがハートビーツさんっっ!!
いつもブログ等で勉強させていただいてる高いスキルをもった方々がいらっしゃいますね。
いろんな貴重なお話をさせていただいて、大変嬉しかったです。

また、発表した資料にさすがプロと言える鋭い質問をいくつも頂けました。

例えば

  • なぜCephなのか、Glusterではなかったのか
    • Glusterの方が高速に動いたからGlusterの方を選択した前例があったとのこと

など。 (お酒が回ってて、これ以上は思い出せませんw)

資料には書かれていないことなので、ここで補足という形で
我々がCephFSを採用した理由をまとめますと以下になります。

  • 今回の場合、I/O速度を求めなくて良いようなところに使用した
    • そのように設計した & なっている (I/0が必要なところはDB関係のみ)
  • CephがOpenStackで実績が積まれていて大部分が枯れている (LTSの安定に期待が持てた)
  • CephFSを使ってみたかった
    • 使ってみたら安定していた
  •  (今回の資料には書けていないところもありますが)高速化方法が色々把握できた
  • Kubernetes のPersistent diskでも対応している
  • スケーラビリティの高い分散ストレージClusterが必要
    • webViewや、イベントのアクセスが大量に来ることがあるため
  •  我々のGluster Storage (Redhatに買収されてからRHGSになった)の利用経験、実績がない

といった点です。

その他弊社からは
アプリケーションエンジニアより、昨今注目されているPrometheus についてLTがありました。

好評の触ってみたシリーズで、最後の刺さる点を共有してくれたところも勉強になりました。

hbstudyというITインフラ業界では有名な勉強会を開催している憧れの会社/エンジニアさんがいる会社で、スキル高いなーと再認識しました。
(人気が凄くてなかなか参加できていませんが、かなり前に個人的にもhbstudyに参加したことあります)

とても貴重で楽しい体験をさせていただけたハートビーツさんに
この場を借りて、心から感謝と敬意を表させていただきます。


菅野 明洋

社内でエンジニア読書会をやってみた!


はじめに

初めての人は初めまして!
前回の記事(第2回 Game Gatling LTに登壇してきました!)を見てくれてる人は、お久しぶりです!

大阪スタジオ インフラチームの菅野明洋です。
業務では、主に大阪スタジオのサービスインフラを担当させていただいております。

今回は、読書会を開いてみましたので、その話をまとめました。

読書会について

読書会は、集団で読書や読書に関するコミュニケーションを図るイベントです。
弊社では興味がある話題や本ごとに幾つかの読書会が開かれております。

開催してみた読書会について

今回は、SQLアンチパターンと言う本を題材にしております。
この本の読書会をするキッカケとしては、弊社の若手のエンジニアが最近読み始めたと言うことで話題に上がったことと、他のエンジニアと意見交換出来たら面白いと言う事で開いてみました。

読書会のルールについて

基本的に参加自由

  • 参加者は8〜12人程

開催時間

  •  定時後の45〜60分
    • 子育てで忙しい人も参加しやすくするために短めに設定
    • 経験上、1ページ2分換算で調整
  • 時間内(15〜20分程度)に章の全てを読みきれない場合は2日に分ける
    • 2日に分ける場合の例
      • 1日目にアンチパターンの問題点を黙読し他班に説明
      • 2日目にアンチパターンの解決策を黙読し他班に説明

用意したもの(書籍以外)

  • 付箋、筆記用具
    • 質問や意見を記入
  • ホワイトボード
    • 書籍の内容の説明や上記の付箋を並べるのに使用
  • キッチンタイマー(スマホアプリ)
    • 時間調整に使用

やり方

読書会の流れ

事前にやること

  • 読書会の日時、場所を決める

ステップ1(班分け)

  • 参加者を2班に分ける
    • 現在の決め方は座った席を順で分ける

ステップ2(読書フェーズ:アンチパターンの確認)

  • 各自担当箇所の章を黙読
    • 最近は読む際に付箋を配り、気になった点や質問したい内容を記入
    • ページ数に応じて変更しますが、大体15〜20分を目安にしています

ステップ3(説明フェーズ)

  • 読んだ箇所を別の班に説明
    • 説明する際は、図説する際はホワイトボードも使用
    • 章ごとに10分程度(計20分)の説明する

ステップ4(質問・討論フェーズ)

  • 全体を通して質問、討論を行う
    • 気になる点、分からない点を確認し解消する
    • 討論時にはアンチパターンに対してどのようにアプローチすべきなのか、他に方法が無いのかなど参加者の想いや考えなどのやり取り
    • 過去の話とかで盛り上がることもあります

読書会の様子

弊社の藤野がSQLアンチパターンの5章EAVについて説明している様子

読書会中に書き留めた付箋を集めてホワイトボードに貼っている様子(5,6章)

読書会中に書き留めた付箋を集めてホワイトボードに貼っている様子(3,4章)

ものすごく盛り上がっている時はホワイトボードにびっしり文字や図説が書かれていることもあったのですが、写真を取っていなかったため、現在記事を書きながら後悔しております。

読書会での試みと効果(参加者のフィードバック)

課題1:短時間で効果的に理解を深めたい

  • 対応策
    • 黙読後、読んでいない人に説明する
  • 効果
    • 説明する際に一度情報を整理するため、理解を深めやすくなった

課題2:質疑応答の敷居を下げる。意見を拾い上げやすくする

  • 対応策
    • 付箋を配布し、記入してもらう
  • 効果
    • 気軽に質問しやすくなった
    • 付箋を並べることで、他の参加者の質問(悩み)が見えやすくなる
    • 類似する意見があることで、他の参加者も同じ疑問を思っている等の安心感を覚える
    • 最初から質問・意見の数がわかるため進行の調整が楽になった

最後に

読書会を通して他のエンジニアと意見が交換できたので、とても参考になりました。
技術的な話のみだけではなく読書会の運用方法なども含め様々な意見を頂き、とても勉強になりました。今回の読書会では、まだ未完成な部分もあり進行が安定していないため、ブラッシュアップをしていかなくてはいけないと考えております。
毎回試行錯誤の繰り返している状態ではありますが、今後もこのようなイベントを続けて、より完成度を高めていきたいです。