Case study — Product
01 Screens
画面
ストア掲載用と同じ手順で、アプリ本体から書き出した実際の画面です。
-
01 カテゴリとアクションラボを選ぶ -
02 通常トレーニング(1問30秒) -
03 アクションラボの形式一覧 -
04 アクションラボ(数の天秤) -
05 結果とメダル判定 -
06 ニューラル・フロンティア -
07 ミッション(オービットとアクションラボ) -
08 研究ラボ(ブレインコアとNEURAL INDEX) -
09 プレイカレンダーと成長ログ -
10 設定(言語・プライバシー・購入) -
11 アクセシビリティ
02 Scope
担当範囲
企画からUI実装、質問データ、進捗保存、Firebase Analytics、広告とアプリ内課金、CI/CD、App Store・Google Play公開まで、一連のプロダクトライフサイクルを担当しています。
| Training | 通常トレーニングは5カテゴリ × 6レベルに日本語3,000問。1回10問・1問30秒を、成績に合わせて毎回組み直します。 |
|---|---|
| Action Lab | タップや配置で解く操作パズルを、1カテゴリ2形式の全10形式。通常トレーニングで貯めたポイントで挑戦します。 |
| Progress | 到達レベルと航路シグナル、デイリーのオービット、成長ログ、ブレインコアのXPを端末内に保存します。 |
| Monetization | 広告と、それを消す買い切りが1つ。購入は任意で、買わなくても全レベルを遊べます。 |
| Accessibility | 高コントラスト、モーション低減、文字拡大、効果音キャプション、色だけに依存しない表示を用意しています。 |
| Release | 一つのFlutterコードベースからiOS・Android・Webへビルドし、ストア用素材もテストから生成します。 |
03 Training
通常トレーニングの組み立て
10問はランダムに引いていません。カテゴリ×レベル×グループごとの正答率と、直近に出た問題の履歴から、始めるたびにその場で組み直しています。
| Bank | 5カテゴリ × 6レベル × 100問を日英それぞれに持っていて、日本語だけで3,000問。1レベルは10グループ × 10問で、このグループが成績を測る単位です。 |
|---|---|
| Mix | 10問の内訳は基準7問・復習2問・挑戦1問。難易度1〜5のうち、復習は1〜2、基準は2〜4、挑戦は4〜5から取ります。 |
| Order | 先に確保するのは復習の2問で、正答率の低いグループから順に選びます。得意な分野の問題に押し出されると、苦手なところがいつまでも出てこないためです。 |
| Spread | 1グループにつき1問を上限に、10のグループへ散らします。同じ切り口が続けて出るのは、在庫が足りず2周目に入ったときだけです。 |
| Repeat | 直近30問、3セッション分に出たIDは避けます。避けきれないときは重複を許すので、問題が尽きて始められなくなることはありません。 |
| Memory | 端末に残すのは、カテゴリ×レベル×グループごとの挑戦回数と正解数だけ。問題文も答えも保存していません。 |
| Report | 気になる問題は理由を選んで報告し、その場で別の問題に差し替えます。1回のチャレンジで2回まで。 |
| Clock | 30秒の締め切りは壁時計で持っています。アプリを離れても止まらず、戻った瞬間に時間切れが確定します。一方、計測に送る回答時間からは離れていた分を引きます。 |
04 Action Lab
操作で解くほうの遊び
4択だけでは測れない手つきを扱うために、タップと配置で解く形式を別立てで足しました。同じ5カテゴリを、選ぶのではなく動かして解きます。
| Formats | 1カテゴリにつき2形式、全10形式。数字をつないで合計を作る、天秤をつり合わせる、光った順にタップする、覚えた図形を戻す、動く対象から探す、2つの盤面の差を見つける、出来事を順に並べる、反対語を結ぶ、といったものです。 |
|---|---|
| Run | 1回は同じ形式を10問。制限時間はありません。形式ごとに途中経過とベスト記録を残します。 |
| Points | 通常トレーニングを最後まで終えると、正解1問につき10ポイント。新しい挑戦に20ポイント消費します。中断した挑戦を再開するときは追加で取りません。 |
| No shop | ポイントは売っていません。買える導線を用意していないので、遊んだ量だけが原資になります。 |
| Placement | ホームとミッションの両方から入れます。画面はタブの枠の外側に置いているので、解いている間にバナーは出ません。 |
05 Monetization
広告と課金
無料で全問遊べる状態は変えずに、広告と、その広告を消す買い切りの2本で組んでいます。課金は任意で、買っても買わなくても、出題数も機能も同じです。
| Banner | ホーム・フロンティア・ミッション・ラボの4画面の上部に、共通のスロットを1つだけ置いています。出題も結果もアクションラボもこの外側の画面なので、構造上バナーが出ません。 |
|---|---|
| Interstitial | 通常トレーニングを5回終えるごとに最大1本、記録を保存したあと結果を出す直前に挟みます。最初の4回には出ません。準備できていなければ最大3秒だけ待ち、間に合わなければ広告なしで結果へ進みます。 |
| Purchase | 「広告を非表示」の非消費型アイテムが1つ。期限も自動更新もなく、1回の支払いでバナーとインタースティシャルの両方が消えます。 |
| Entitlement | 権利はApp Store・Google Playのアカウントが持ちます。独自アカウントを作らないので、再インストールや機種変更でも起動時の問い合わせで戻り、設定の「購入を復元」が受け皿になります。 |
| Measurement | インタースティシャルの成否と待ち時間、広告1回ごとの推定収益をFirebase Analyticsへ送り、置き場所の判断に使っています。 |
| Web | ブラウザ版に決済はないので、購入の行そのものを出しません。広告もアプリ画面の外側のディスプレイ広告だけです。 |
06 Stack
技術構成
| UI / App | Flutter、Material 3、GoRouter |
|---|---|
| State | Provider、Flutter Bloc |
| Data | SharedPreferences、JSON question sets |
| Analytics / Ads | Firebase Analytics、Google Mobile Ads |
| Purchases | in_app_purchase、StoreKit、Google Play Billing |
| Quality | flutter_test、Mockito、レスポンシブ回帰テスト |
| Delivery | GitHub Actions、App Store Connect、Google Play Console |
07 Principles
設計の考え方
一つのコードベースからiOS・Android・Webへ配っているので、画面も規約も面ごとに作り分けない前提で組んでいます。分けた瞬間に、面の数だけ直す場所が増えるためです。
- 画面ではなく条件で組む。スマートフォンだけでなく、タブレットの縦横やデスクトップでも、画面幅と向きを見てレイアウトを切り替えます。端末ごとに別画面を持たないので、対応する面が増えても崩れません。
- 出題を決める部分に副作用を持たせない。10問を組む処理は、問題の束と成績と直近の履歴を受け取って10問を返すだけの関数にしています。保存も時計も内側に置いていないので、苦手なグループが先に来るか、同じ問題が続かないかを、乱数だけ固定してテストで確かめられます。
- 同意が済むまで何も始めない。年齢確認が完了する前に分析・広告リクエストを開始しない構成にし、子ども向け設定を最初のリクエストから適用しています。あとから設定を反映するのではなく、1回目から正しい状態で出します。
- 解いている手は止めない。広告は、ハブになる4画面の上部と、結果を出す直前だけに置いています。出題も結果もアクションラボもバナーを持つ枠の外側にあるので、解いている最中に割り込む形式は構造上使えません。全画面広告も毎回は出さず、5回終えるごとに1本までにしています。1回ごとに挟む作りは、アンインストールの理由になるほうだからです。収益の導線であっても、遊ぶ手が止まる位置には置きません。
- 買った証拠を自分で持たない。広告除去は非消費型アイテム1つで、権利はApp Store・Google Playのアカウントが持ちます。こちらに会員登録を作らないぶん、再インストールでも機種変更でも起動時の問い合わせで戻り、届かなかったときは設定の復元で拾えます。決済が繋がらなくても、ゲーム自体は止まりません。
- ストアに出す絵もコードから出す。日本語・英語、テーマ、文字倍率、ストア指定サイズを組み合わせたスクリーンショットを、テストから自動生成しています。画面を直すたびに撮り直す手間が要らず、ストアに並ぶ絵と実物がずれません。
08 Where to play