社内LT会でキャリアキーノートをやってきた
弊社では隔週で社内LTが開かれている。
テーマはなんでもいいということなので、今回は前職でやっていたキャリアキーノートをやることにした。
キャリアキーノートとは?というところは次のブログに全て記されている。
とはいえLT時間は5分なのでかなり端折ることとなり駆け足の発表になってしまった。
5分で話せる内容は本当にごく僅かで、何を話そうかとかなり取捨選択した結果、自分が大事にしているものが見えた気がしないでもない。
周りに流されずに個人で黙々と続けられる人は本当に尊敬しているし自分もそうなりたいけどそれは本当に苦手。とにかく人の影響を受けまくる。後輩にも「周りの影響受けやすいですよね」と指摘されたことはあり、実際その通りだなと自覚しているので、じゃあいい人がいる場所を探した方がいいんじゃない?というのが僕の考え方になっている。転職活動時もその辺に気を使っていたので入ってから後悔も特にはないのだった。
ちなみにスライド内にある阿部さんのゲームは2008年製だけどWin10で動いて感動した。流石.NET Framework
開発方針についての文書を書いていた
色々決めごとをするときは社内にissueなりwikiなり残すのですが、これは外に公開してもいいんじゃない?と思ったので公開します。 今回は「開発方針について」です。
目的
- クライアントアウトゲーム全体の開発方法、指針を把握する
- あとから参加した人もスムーズに着手できるようにする
お品書き
- アーキテクチャのお話
- Rxのお話
- DIのお話
- テストのお話
アーキテクチャ
なぜソフトウェアアーキテクチャが必要か
- 大規模開発、運用にはスケールを見通した設計が必要
- 秩序なき開発は人間が犠牲になる
- 設計ルールをその都度作っていくのは大変
- すでに先人たちが積み上げてきた効率の良い設計が存在する
- それがソフトウェアアーキテクチャ
- 一般化されている設計ルールは独自設計より学習しやすい
- 後から入った人も理解しやすい、むしろそれを前提とした採用もできる
プロジェクトの設計(アウトゲーム)
- MV(R)Pアーキテクチャを採用している
- MVPアーキテクチャの派生
- ViewとPresenterをRxで繋ぐ
- 経緯 ここにissueのリンクが入る
MV(R)P アーキテクチャ
Model
- いわゆるロジックと呼ばれる部分
- PureClassであり、MonoBehaviorではない
- 自身を更新したり、処理結果を返すインタフェースを公開している
- 自身のパラメータをReactivePropertyで公開もする
- Modelと一口に言っても内部で分類が色々ある。
- Presenterを知らない
- Viewを知らない
- ユニットテストが書けるようにする
View
- ButtonやImageなど、画面に表示されるもの
- MonoBehavior
- 複数の要素をまとめてViewクラスとするのも可
- Viewはイベントストリームを持っている
- クリックされた、選択されたetc..
- ストリームを公開して、外部から購読できるようにする
- Viewは外部から情報を更新できるインタフェースを公開している
- 任意のキャラの情報を表示など
- Presenterを知らない
- 自身の情報更新のため、Modelは知っている
- メンバ変数で持つのは推奨しない
Presenter
- ViewとModelの間に位置するもの
- MonoBehavior
- ViewとModelを知っている
- Viewのストリームを購読し、Modelに更新をかける
- Modelの変更ストリームを購読し、Viewに更新をかける
- Presenterは複数のViewを購読してもよい
- 大きくなりすぎたら別のPresenterに切り出す
Modelの詳細
Modelは多種多様なので、役割ごとに適切に層を分けないと開発に支障が出る。
- どこにファイル置けばいいの?と考える時間は少ないほうがいい
逆に層を分けすぎてもそれは分割の手間や思考の時間を奪う
- スパゲティコードに対してラザニアコードと呼ばれる
MV(R)Pにおいて、モデル下は規約は特に定まっていない。 が、スタンダードな考え方はいくつかある
プロジェクトで考えてるモデルの分け方
- Model
- Model/Entity
- Model/UseCase
- Model/Repository
Model
- いわゆるロジックと呼ばれるもの
- 現状はここに大半いる
- 色々な役割のクラスが混ざっているので適切な分割は必要
Entity
- ロジックをほぼ持たないデータのみのクラス
- キャラクタのパラメータとか
- 通信のレスポンスオブジェクトとか
Repository
- リソースを処理する存在
- リソースのGET,PUT,CREATE,DELETEのイメージ
- どうやって処理するのかを書く
- 通信?ローカル?etc...
- 外部からはリソースがどこに存在するのかは見えない
- キャッシュ機構を持つのもあり
UseCase
- ビジネスロジックと呼ばれるもの
- 「やりたいこと」単位でクラスを作る
UserNameChangeUseCase
TitleUserLoginUseCase
など
- Presenterは基本UseCaseを使ってモデルを触る
名前変更処理の一例(現在こうなっているわけではない)
インゲームのアーキテクチャについて
- 現状定めていない
- パフォーマンスチューニングなどが必要になるので、レイヤーの分割が適切に行えないことがある
- ViewとModelに分割はできるかも?くらいの認識
Rx
ReactiveExtentionsとは
- こちらをご参照ください https://www.slideshare.net/torisoup/unity-unirx
MV(R)PにおけるRxの役割
- Viewのイベントを購読する、モデルの更新を購読する
- Presenterがイベントを検知するために使う
- https://www.slideshare.net/torisoup/unirxmvrp
ここの Subscribe
OnNext
の関係を実装するのがRx
async/await とObservableの使い分け
- 単純に非同期を待ち合わせたいだけならasync/awaitが簡単
- それだけじゃない複雑なことをするならObservable
- 特にイベント処理はObservableのが楽
- とりさんのスライドで大体かいてる https://niconare.nicovideo.jp/watch/kn3081
DI
Dependency Injection
DIはもはやもうこれ見てもらったほうが早い・・
https://qiita.com/toRisouP/items/b3d3c43db40857ca4ad4
プロジェクトにおける主なZenjectの使い方
- staticなオブジェクトをなくす
- 環境によるモジュールの差し替え
staticなオブジェクトをなくす
- 実体に強く依存するため、差し替えづらい
- isTestみたいなフラグは持ちたくない
- ゲームはシングルトンによるstaticが生まれやすいイメージ
- マスタデータ、サウンドマネージャetc...
これらをDIContainerに管理させることで、疎結合なシングルトンに変更する
環境によるモジュールの差し替え
- テスト時には通信したくない
- ローカルと本番でリソース取得先を差し替えるなど
通常だとこれらのフラグ管理などが必要 or 手でDIしなくてはならないが、これらをDIContainerに任せられる
何をBindすべきか
- あらゆるメンバをBindするとかえって面倒になる
- 普通にコンストラクタで渡せるならそれのが楽
- 基本的にはシングルトンだったものが対象
- Presenter、Viewの層まではやってしまってよい感覚
Model層にContainerを渡さない
- 上記の通り、コンストラクタで渡せるものは
- Containerそのものに依存してしまうのはそもそも設計としてよくない
- Presenterから適切なオブジェクトだけをモデルに渡すのが良い
- ここは努力目標で・・
public class BadUseCase { [Inject] DiContainer container; // UseCaseがcontainerまで気にするのはおかしい public void Foo() { var foo = new BadFoo(container.Resolve<Bar>()); } }
public class BadUseCase { private Bar bar; // 素直に受け取る public BadUseCase(Bar b) { bar = b; } public void Foo() { var foo = new BadFoo(bar); } }
テスト
テストいろいろ
プロジェクトで書けるテストは2種類 - ユニットテスト - UIテスト
なぜ書くのか?
- 大体issueにまとめている
- 以前発表したスライドも参照
ユニットテストはどのくらい書く?
- 基本的にモデルはすべてユニットテストが書けると考えている
- 作成したメソッドのテストは書いてほしい
- タスクの完了条件に「テストを書いている」を足したい
- 現場の状況で書かない判断はしましょう
テストでよくある質問
Q.仕様変更はしょっちゅう起こるし、その都度落ちたテストを書き換えるのは手間では?
A.確かに手間です。が、どう落ちたのかが可視化されるのは大きなメリットです。影響範囲がある程度わかれば修正もやりやすくなるので結果的には開発速度は上がると考えます。
テストでよくある質問
Q.テスト書く時間をかけすぎて辛い。
A.テストが書けない理由を掘り下げることが大事です。
- 何をテストしたらいいかわからない => そのクラスに何をしてほしいのか明確になっていない?
- テストのための準備が多くて書きづらい => 1クラスの依存関係が多すぎる?
- テストの構文がわかってない => ググるぞ!
コードレビューについての文書を書いていた
色々決めごとをするときは社内にissueなりwikiなり残すのですが、これは外に公開してもいいんじゃない?と思ったので公開します。 今回は「コードレビューの導入する」というやつです
なぜコードレビューをするのか
大きく二つの効果があると思っています
コードの品質を保つ
一人でコードを書いたときに、それが対応として適切であるかどうかを判断するのは結構難しいです。 基本的には機能を実装するとき、以下の三つについて確認する必要があると考えています。
- コードの書き方が適切であるか
- 解決方法が適切であるか
- 修正箇所以外の影響への対応は適切であるか
下に行くほど考えるべき領域が広くなっていきます。 領域が広くなるほどに一人で対応できる難易度は上がっていくため、漏れが発生し不具合が起きてしまいます。 特に3に関しては自分が予想だにしていないところに影響を与えるなどありがちです。 これを防ぐためにテストコードを書いたり、レビューにより他人の視点、知識から問題を発見していくことが重要になります。
コードの属人化を減らす
ゲームは特に専門性の高い部分が多く、この部分はこの人以外対応できないという問題が発生しがちです(属人化)
短期開発における属人化は速度が出せるので有効ですが、長期の開発においての属人化はボトルネックになりがちです。 これを回避するためには全員がある程度コードを把握しておく必要があります。 コードレビューは自分が把握していないコードを見る有用な時間です。 品質を保つためのレビューだけではなく、自身がコードの内容を把握するためにレビューしていく習慣をつけることで属人化に対処できると考えています。
どうやっていくのか
PRのマージは2人のapprovedを必須にする
- 2人のapprovedを確認して、自分でマージしてもらいます。(書いた人が取り込みたいタイミングを理解しているはずなので)
- 人数は暫定です。多いか少ないかはやってみて動的に変えていこうと思います。
- 2人approvedもらえたら、そこからいつマージするかはPR出した人にお任せします。状況に応じてもっとレビューを求めるのも問題ありません。
非スクリプトファイルのみの変更はレビュー不要
- エンジニアが目視で見ても判断つかないため
- レビュイーがUIテストなどが通ってるかは確認しましょう(いずれ自動化)
- 「レビューポイント」のところにレビュー不要な理由を書きましょう(シーンファイルのみのためetc..)
インゲーム・アウトゲーム関係なく2人をランダムにレビュアーにする(ランダムな仕組みは後から作る)
- 属人化を防ぐため
- リード的な人のレビューを必須にするとボトルネックになってしまうため
- この人に見てもらわないと不安!みたいなシチュエーションの場合はその都度その旨書いて指定する
レビューを優先する
- レビューが終わらないのでマージできない!という状況を防ぐために、レビューを優先!という共通認識を持ちたい
- 忙しすぎてレビューが本当に無理!ということになったらタスクの積み方から一旦見直す
レビュイー(PR出す側)に意識してほしいこと
PRの粒度を意識する
PRのコミット粒度を小さくすることを心がけていきましょう。 体感ですが、スクリプトファイルの変更が二桁突破するともう辛くなります! コミット粒度を小さくするためには、エンジニアタスクの適切な分解が必要になってきます。
例えば「APIからとってきた情報を表示する新規の画面の追加」というタスクがあるとしたら、それは以下のタスクに分解できます。
- 画面に表示する情報を持つモデルを作成する
- 新規のAPIと通信をするメソッド実装
- 新規画面の追加(データは決め打ちのモック)
- 画面にモデルの情報を表示するように繋ぎこむ
これらをまとめてやってしまうと肥大化してレビューしにくいので、適切な分割を心がけましょう、
背景が伝わるように文章を書く
PRから背景が伝わるように文章を書いていきましょう。 なぜこのPRをやるのか、どのように解決するのか、特にどこをレビューしてほしいのかなのかがハッキリすればレビュアー側の負担を減らすことができます。JIRAチケットのURLを貼るのもいいでしょう。 前職では以下のようなテンプレートを採用していました。これに近いものを用意しようかと考えています。
https://gist.github.com/adarapata/40ec5f66e0c348a639a1aa6cb519aee6
レビュアーに意識してほしいこと
HRTの原則を守る
「レビューはBARで語らうように」という名言があります。レビューとは相手のやったことに対してコメントするので、ともすれば相手を傷つけてしまうような悪い空気になってしまいます。それが発生しないように謙虚(Humility) 尊敬(Respect) 信頼(Trust) の気持ちをもってコメントしましょう。
HRTの原則 ~ソフトウェア開発はバーでしっとり語り合うように ~
http://blog.livedoor.jp/lalha/archives/50496623.html
なぜを書く
「良い」「悪い」という基準は主観的な言葉なので、どういう観点から良い、悪いを判断したのかを書くように心がけましょう。
悪い例:「ここはBのようにしたほうが良いと思います」 いい例:「ここはBのようにするとネストが減り可読性が上がりそうです」
わからないところは質問していく
レビューは品質の保証だけでなくレビュアーが要件を理解する場でもあります。なので、こうしたほうがいいというコメントだけではなくて「このコードは何をしているんですか?」みたいな自身の理解を深めていくのも目的です。 なのでこの辺りがわからない、というのもどんどんコメントしましょう。
褒める
めっちゃええコードやんと思ったらそういうコメントもどんどんしてください。
テストコードについての文書を書いていた
色々決めごとをするときは社内にissueなりwikiなり残すのですが、これは外に公開してもいいんじゃない?と思ったので公開します。
今回は「Unityにテストコードを導入する」ときの文書です
なぜテストコードを導入するのか
往々にしてプロダクトの要件は常に変化します。一ヵ月前の仕様が変わることも珍しいことではありません。仕様が変わればコードの修正は必要です。つまり、コードは必ず変化するという前提で書く必要があります。
コードが変化すると、その周辺のコードに少なからず影響を与えます。場合によっては全然関係ないと思っていたところに飛び火することもあります。それらを予め人間がすべて把握するのは経験則からくる職人技に近いものなので、非常に難易度が高いです。
テストコードを書くと、変更を加えた際に起きる影響をある程度可視化してくれます。 プロダクトの規模が大きくなるほど開発速度は落ちていきますが、テストコードはその減速率を下げるのに役立ちます。
テストコードを書くメリットは以下の2点です
品質を把握する
テストそのものが品質を上げてくれるわけではありません。テストコードは現状のプロダクトの品質がどんなものなのかを可視化してくれます。この部分が適切に動いているのか、この部分に変更を加えるとどうなるのか、など。品質の可視化は機能追加、リファクタリングの判断材料となり、素早い意思決定が行えます。
テストがないコードは品質が悪いのではなく品質がわからないので、どこを直すべきか、どこの処理を手厚くすべきかという判断が難しくなり、結果的に開発効率が落ちてしまうと考えています。
精神的障壁の排除
影響範囲の見えないコードの修正はメンタル的によろしくないです。 根本原因を解決すべきとわかっていても、影響範囲が見えない場合、リスクを減らし最小工数で抑えるために一旦場当たり的な対応を取りがちです。これはもちろん有効に働く場合もありますが、上記のネガティブな理由から選択すると、未来で同じ問題にぶつかってしまいます。
テストコードにより影響範囲が可視化されていると、根本原因の解決はどのくらいの影響を与えるのかというのがある程度見えることで「なんだか大変そう・・」という精神的障壁を超えた上で判断ができるようになります。
導入するためには?
テストコードを書くためには、ビューとロジックが分離されている必要があります。(そうしないとめっちゃ書きづらい) これは別issueのMV(R)Pアーキテクチャ導入 #1125 によって実現できると考えてます。
また、シングルトンはテストするのが非常に面倒です。 refs シングルトンパターンの誘惑に負けない
この問題は、シングルトンをやめて依存性の注入(DI)を行うことで解決できます。UnityだとZenjectというライブラリが有名です。
ユニットテスト
Unityに依存しないPure Classのテストです。これはEditModeテストを導入します。実はいくつか実装済みです。 - 例 xxxxx
Zenjectを導入した場合、バインド処理が必要になるのでZenjectに付属の ZenjectUnitTestFixture
を継承して書く
refs - Unity で ユニットテストをする http://blog.kakeragames.com/2016/02/17/unity-test-unit.html
インテグレーションテスト
MonoBehaviorなどUnityに依存したコードのPlayModeテストを書く。やり方はもうちょっと詰めていく。
unity-uitestというライブラリがあったのでこれを使えばアウトゲームのシーンUIの自動テストは書けそうな気がするが要調査
FaceRigとボイスチェンジャーでバーチャルの肉体を手に入れた
30手前の男性二人がチャットでボイチェンで美少女声出してキャッキャしてたら肉体も欲しいなという気持ちになり、FaceRigを購入しておばあちゃんの肉体を手に入れた
バ美肉テスト pic.twitter.com/Ld9mvooAUA
— いも@efb~相手は死ぬ~ (@adarapata) July 8, 2018
無駄にバーチャルの肉体で配信できる環境を整えてしまった。
しかしそうなると今度は身体を動かしたいなという感じになってきたので誰か僕にHTC VIVEを買ってください。
社内LT会に参加してきた
社内でLTやろうぜ!という流れができたので参加してきた。
お昼に行われるやつで、なんと1000円相当の弁当が出るコスパ高いLT大会だった。今回は牛たん御膳を選択。
今日のお昼でした pic.twitter.com/1MPN8v3HES
— いも@efb~相手は死ぬ~ (@adarapata) June 19, 2018
僕は最近やってたUnityのでモッククライアントの実装など話していた。
前回話したテストの延長で、通信が絡むユニットテストの解決方法を探すという感じのお話。 これはいずれ汎用的に使えるやつとして公開したいなーというお気持ち。
その他にもC++の黒魔術があったりとバラエティ豊かな発表だった。
www.slideshare.net
全員基本的になんだかヤバそうなスキルを備えていて聞いてて飽きない話ばっかりだったので、こんな感じで表に出していけるような空気になっていけばいいなと思う。
試用期間が終わった
金曜日でちょうど三ヶ月だったので、無事に試用期間を終えることができたっぽい。
社内でやったこと
クライアント側の一部分にアーキテクチャ導入したり、テスト導入したり、その過程でDIツール入れたりなど。 割と開発基盤を整えていた感じある。 また、それとは別にふりかえりのファシリテートしたり、動きやすくするためのチームビルディングなどやっていた。 チームビルディングと敢えて言う内容でもなかったりするけど、行動に名前を付けることは大事なのでそう呼んでいる。
あと臨時会議室作るとかしてた
BARいも焼酎、席替えに伴いだいぶ土地を減らされました。
割と前職で得た知識や経験がそのまま適用できているので、業界関係なくエンジニアリングは大事だなあと感じた。特に人の巻き込み方に関してはそれはもうパクりまくっている。
最近はとりあえず考えてること全部垂れ流そうということでいもスレというスレッドを開発チャンネルに作ってる
コメントはないけど絵文字だけは付くので見てるんだと信じてる。
一方で、結構新しい概念や技術を立て続けに導入したため別の問題も発生しているので、それはそれで別途解決しないといけなさそう。今後優先する課題であった。
社外でやったこと
Gotanda.unityは定期的に登壇したりしてる。3月はZenjectのテストについて話してた。
入社3日目にして社名を間違える失態を犯したけど無事試用期間終わりました。
あと、「Unityテスト完全に理解した」という勉強会の会場提供したり発表したりしていた。
僕はテストを導入した話をやっとりました。
#Unityテスト理解した ハッシュタグがトレンド2位まで上がってたので驚いたのとともに、やっぱみんな気になってたんやな・・という気持ちになった。 懇親会でも、導入したいけどできなかったとか、一人で頑張ったけど燃え尽きたなど様々な苦労話を聞いたり。何か参考になればよさそう。
第二回やるときは是非とも各所で運用されているゲームの人たちに登壇してほしい。ほんと。
これからやること
有給が発生したので活用してぽん酒館行きたい