新規開発でやっちまった話

Unity お・と・なのLT大会 2019

自己紹介

  • 菅谷 琢磨(@tetsujp84)
  • Unityクライアント
  • 今年リードエンジニアとして1からアプリリリース

本当にあったかもしれないお話

今回のやらかしお品書き

  • Zenjectでのやらかし
  • 課金でのやらかし
  • 予約注文でのやらかし

Zenjectでのやらかし

Zenject導入

  • 設計に反省と興味
    「Unity Zenject完全に理解した」に参加

  • 社外への技術アピール

  • 初期メンバーも導入に賛同

Zenjectでのやらかし

  1. ProjectContextに突っ込みすぎ
  2. フィールドインジェクションの初期化順勘違い
  3. Zenjectに依存しすぎ
  4. メンバーへの要求技術力が高い

1. ProjectContextに突っ込みすぎ

  • ユーザーデータまで管理してしまった
  • リセットが難しい
  • どこで使われるか分からないUseCaseができた
    結局2シーンしか使われない

SceneContextに移動し解決

  1. どのSceneContextも参照するようなSceneContextを作る
    例:UserDataContext
  2. 各シーンではParentContractNameで指定する

 UserDataContextを破棄すればゲームのリセットが簡単に

ProjectContextはできるだけ軽く、Contextは細かく分離

Singleton化したもの

Zenject管理外からでも呼ばれるもの
細かい状態を持たないもの

  • SceneManager
  • ResourceManager
  • SoundManager

2. フィールドインジェクションの

初期化順勘違い

  • フィールドインジェクションは簡単に依存を解決
// 適切にBindされていれば勝手にインジェクトされる
[Inject] private IGetUseCase useCase;

public int Get() {
	// そのまま使える
	// 実体はInstaller側で決める
	return useCase.GetValue();
}

インジェクト順に注意

  • コンストラクタ→フィールドインジェクションの順で処理される
    コンストラクタでインジェクトを期待するとNULL例外が発生
  • 明示的にInitialize関数を用意して解決
  • メソッドインジェクションかコンストラクタインジェクションがベター
[Inject] private IGetUseCase useCase;
private int userValue;

// コンストラクタ内ではまだインジェクトされていないのでuseCaseはnull
private UserModel() {
    userValue = useCase.GetValue();
}

// 初期化関数を外から呼ぶ形式に
public void Initialize() {
	userValue = useCase.GetValue();
}

Zenjectに依存しすぎ

  • 最終的にInjectされる場合は中間クラスも
    Zenjectを経由しなければならない
    気軽なnewが使えない
  • UI以外のほぼ全てをFactory経由で生成する
    機能のプロトタイプでもFactoryが必要

メンバーへの要求技術力が高い

  • メンバーはZenjectを知らない
  • 開発締め切りは常に迫る
  • メリットや方法を理解してもらう
  • 責任を負う覚悟

Zenjectは有用だった?

  • 一部有用、一部負債
    トータルで見ると有用だった
  • 設計が強制される印象
  • 今後も導入するかは開発メンバーによる

課金でのやらかし

開発環境

  • Unity2019.1.5
  • UnityIAP最新版
  • 課金検証サーバーはGo言語

たまに課金が失敗する...

  • サーバーでのレシート検証に失敗
  • リトライすると成功する
  • 開発環境では発生しない

Unityのバグを踏んだ

  • UnityWebRequestでデータサイズが大きいと破損する
  • 課金通信が一番大きい通信だった
  • Unity2019.1.13で直ってた

検証に時間がかかった

  • 課金検証ビルドは最新のUnityバージョンで作っていた
  • UnityAnalyticsで見られるレシートも破損していた
    UnityAnalyticsにもUnityWebRequestで送られていた
  • 原因がサーバーかクライアント側かの特定に時間がかかった
    エラー検証の機構を入れられていなかった
    →Slackでエラーが通知されるたびに震える

Unityのバージョンと向き合う

  • 迷うならLTSを使う
  • 新機能を使いたい場合だけ最新版
  • リリースノート・バグレポートは要チェック
    意外なところが関係してくる
  • マイナーバージョンは常に最新

予約注文でのやらかし

App Storeの予約注文

  • リリースされたら自動でダウンロードしてくれる
  • リリース日は変えられる

リリース日前日の夜...

いよいよ明日がリリースですよ!

むっちゃドキドキしてきた…。

開発者の皆さん、今日くらいは検証は休んで明日に備えますよね?

 

夜中23時...

ユーザーが...増えてる!!
緊急リリーーーース!!!

何が起きた

  • App Storeの予約注文機能を使っていた
  • リリース日前日の23時に自動でリリースされた
  • 本番環境をメンテナンス状態にしていなかった
  • いつの間にかサービス開始
    →即メンテナンス
     メンテナンス機能の動作チェックに繋がる

なぜ起きたか

  • 予約注文は「自動で」リリースする機能
  • 「手動でリリース~」の設定は適用されない
  • 23時にリリースされたのはサマータイムの影響か?
  • 再度予約注文の日付を伸ばすとリリースが停止しダウンロードもできない
  • 1週間後、他社アプリでもやらかしてた

新規開発の経験から

  • 設計は議論し決めたら貫く
  • 決定に意思と責任を持つ
    使う技術・思想は誰かに依存することになる
  • 起きてしまったらしょうがない
    後悔に頭を使わず解決に頭を使え

シーン

Presenter - 1つ

Model - 1つ

View - 複数

通信処理・共通処理はUseCase化

MV(R)P+UseCase

ユーザーデータ

Repository

Entity

UseCase

UseCase経由でRepositoryにEntityを追加/取得/更新

おまけ

今回の設計はこうなりました

おまけ クラス図

新規開発でやっちまった話

By tetsujp

新規開発でやっちまった話

  • 2,667