はじめに
「ちょっとした操作なのに、なぜか気持ちいいアプリ」と、「ちゃんと動いているはずなのに、どこか触っていて楽しくないアプリ」。
この差は何なのか、と考えることがあります。
アニメーション?レスポンス速度?それとも設計の問題?
色々ありますが、その一つは
“インタラクションの作り込み”だと考えております。
今回、Flutter向けゲームエンジンであるFlameを使って、モバイルゲームのような操作体験を持つUIを試作しました。
この記事では単なるデモではなく、
入力処理の設計
フレーム更新ベースの状態管理
視覚・触覚フィードバックの設計
といった観点で、どのように構築したかを整理します。
【参考】Flutter(公式サイト)に基づき制作
背景と目的
当初はFluoriteの活用も検討していましたが、本記事では、まずFlame Engineを用いて「どこまで実現できるか」を検証します。
今回の目的は以下です:
- モバイルゲーム的な操作体験の実現
- Flutterでのインタラクション表現の拡張可能性の検証
- 将来的なフレームワーク選定のための知見蓄積
【参考】Fluorite(公式サイト)に基づき制作
なぜFlameを使うのか(設計視点)
Flutter単体でもGestureやAnimationは扱えますが、以下のようなケースでは設計が複雑になりがちです:
- 継続的な入力(ドラッグ・押しっぱなし)
- フレーム単位で変化する状態
- 複数オブジェクトの相互作用
Flameを使うことで、
- update(dt) による状態更新
- 明確なゲームループ
- 入力と描画の分離
が自然に実現できます。
特に「状態が時間に応じて変化する」処理は、Flameのモデルと非常に相性が良いと感じました。
【参考】Flame Engine(公式サイト)に基づき制作
全体アーキテクチャ
今回の構成は以下のように分離しています:
Game (FlameGame) ├── PlayerComponent ├── FieldComponent └── InputController
設計意図
- PlayerComponent
→ 移動・回転・内部状態を管理 - FieldComponent
→ フィールド状態および接触判定 - InputController
→ 入力イベントを状態として正規化
「入力 → 状態 → 表示」の流れを明確に分離することで、拡張性と可読性を担保しています。
処理内容について
入力処理:イベント駆動から状態駆動へ
Flutter標準のGestureはイベント駆動ですが、ゲーム的な挙動では「入力状態を保持する」方が扱いやすくなります。
- 実装方針
入力イベントを直接処理しない
状態として保持し、update内で参照する
class InputState {
Vector2 moveVector = Vector2.zero();
bool isLeftActive = false;
bool isRightActive = false;
}
@override
void update(double dt) {
position += input.moveVector * speed * dt;
}
これにより、フレーム単位での一貫した制御が可能になります。
仮想スティック:ベクトル正規化
ドラッグ量をそのまま使うと、入力がデバイスや操作量に依存してしまいます。
final direction = dragVector.normalized();
velocity = direction * maxSpeed;
入力を正規化することで、操作感の一貫性を保っています。
回転制御:状態遷移として扱う
画面下部の左右入力によって回転を制御し、非入力時には減衰させます。
if (input.isLeftActive) {
angularVelocity = -rotationSpeed;
} else if (input.isRightActive) {
angularVelocity = rotationSpeed;
} else {
angularVelocity *= 0.9;
}
入力を直接回転に反映するのではなく、「速度」として扱うことで自然な挙動になります。
フィードバック設計(視覚+触覚)
今回特に重視したのがフィードバック設計です。
HapticFeedback.selectionClick();
単一のフィードバックでは認知しづらいケースでも、視覚と触覚を組み合わせることで、ユーザーの理解を補助できます。
Flameを使って感じたメリット
1. updateループによる一元管理
Flutter単体ではTimerやAnimationControllerで分散しがちな処理を、1つのループで管理できます。
2. コンポーネント指向設計
Widgetツリーとは異なり、「振る舞い単位」で分割できるため、再利用性、テスト容易性が向上します。
3. 入力処理との親和性
Drag・Tap・Holdといった入力を、自然に状態へ落とし込めます。
ハマりどころ
Flutterとの責務分離
- UI(Flutter)
- ゲームロジック(Flame)
を明確に分けないと、設計が破綻しやすくなります。
Overlayなどを活用した分離が有効です。
状態管理の境界
RiverpodやBlocと併用する場合、
- アプリ状態
- ゲーム内状態
の責務を明確にする必要があります。
今後の展望
fluoriteが公開・成熟した場合には、実装コスト、表現力、保守性
といった観点で比較検証を行いたいと考えています。
特に、
- 入力の状態管理
- フィードバック設計
- インタラクションの抽象化
がどのように変化するかは注目しています。
まとめ
Flameを使うことで、
- フレームベースの状態管理
- 入力の状態化
- リッチなフィードバック設計
を比較的シンプルに実装できました。
Flutterは単なるUIフレームワークにとどまらず、インタラクション設計のプラットフォームとしても十分に強力だと感じています。
今回の内容はあくまでプロトタイプですが、
- 操作体験の向上
- UX改善
- ゲーミフィケーション
といった領域での応用可能性があると考えています。
Flutterでの表現の幅を広げる一つの選択肢として、Flameは有効なツールでした。

