この記事は Flutter Advent Calendar 2025 14日目の記事です。
はじめに
最初は「FlutterSceneExampleを理解する」というお題目で書こうと思っていたのですが、Flutterのバージョンアップなどの影響もあり、公式のサンプルプログラムが動かないことや表示が崩れるということが発生しました。
そこで自分でサンプルプログラムを組むことにし、作る中で理解できた点や所感についてまとめます。
当記事では、FlutterGPUの概要や細かい技術仕様は省略します。
概要から把握されたい方は公式ドキュメントなどでご確認ください。
🚀作成したサンプルプログラム
最初に作成したサンプルプログラムを記載します。
モデルビューワー
- シンプルな3Dモデルビューワー
- モデル・カメラ操作のキホンを触りながら理解するためのサンプル
太陽系シミュレーター
- 複雑なモデル・カメラ操作を理解するためのサンプル
- モデルの大量配置(1000個)によるパフォーマンスの確認
奥スクロール避けシューティング
- モデル同士の衝突判定を理解するためのサンプル
リポジトリは公開しています。
カンタンに動作確認できるようしていますので、ぜひ手元で動かしながらご覧ください!
モデル・カメラ操作のキホン
サンプルプログラムの中でも一番わかりやすい「モデルビューワー」を例に記載していきます。
モデルの表示
下記の流れで定義~表示まで行います。
- glTF(.glb)形式のアセットを用意する
model_viewer ├ assets │ └glb │ └dash.glb -
hooks + flutter_scene_importerで利用可能なモデル形式に変換する
pubspec.yamlには上記で変換したモデルを利用するように定義します。hooks/build.dart
void main(List<String> args) async { await build(args, (config, output) async { buildModels( buildInput: config, inputFilePaths: [ // 用意したassetsを記載 'assets/glb/dash.glb', ], ); }); }pubspec.yamlflutter: uses-material-design: true assets: # hooksで作成したモデルを読み込むように指定 - build/models/ -
Node.fromAssetでモデルを読み込む@override void initState() { super.initState(); // モデルの読み込み Node.fromAsset('build/models/dash.model').then(model){ // ... }); }
モデルの操作
モデルの操作は Matrix4 (vector_math) を利用します。
回転、移動、スケールなどの操作が可能です。
// モデルの位置を座標指定
final translation = Vector3(
viewerState.modelPositionX,
viewerState.modelPositionY,
viewerState.modelPositionZ,
);
// モデルの角度をオイラー角で指定
final rotation = Quaternion.euler(
viewerState.modelRotationX,
viewerState.modelRotationY,
viewerState.modelRotationZ,
);
// モデルのスケールを指定
final scale = Vector3(
viewerState.modelScale,
viewerState.modelScale,
-viewerState.modelScale, // 表示面が逆転するので、Z軸は反転させる
);
// `globalTransform`を変更して反映する
dashModel.globalTransform = Matrix4.compose(translation, rotation, scale);
サンプルプログラムでそれぞれの数値をイジりながら体感できます。
他にもモデルに設定されたアニメーション操作やモデル内の部品を操作することもできます。
カメラの操作
カメラの操作で気にすることは以下の3点のみです。
- カメラの位置
- カメラの向き
- カメラの頂点
それぞれをx,y,zの3軸で指定して、3D空間にカメラを作れます。
final camera = PerspectiveCamera(
// カメラの位置 - 3D空間上のどこに置くかを座標指定する
position: Vector3(
viewerState.cameraPositionX,
viewerState.cameraPositionY,
viewerState.cameraPositionZ,
),
// カメラの向き - 3D空間上のどこを見るかを座標指定する
target: Vector3(
viewerState.cameraTargetX,
viewerState.cameraTargetY,
viewerState.cameraTargetZ,
),
// カメラの頂点 - カメラの頂点をどこに向けるか
// 寝かせたり逆さにしたりかたむけたりなど
up: switch (viewerState.cameraUp) {
CameraUp.up => Vector3(0, 1, 0),
CameraUp.down => Vector3(0, -1, 0),
CameraUp.left => Vector3(-1, 0, 0),
CameraUp.right => Vector3(1, 0, 0),
},
);
他にも環境光などの設定は可能ですが、上記3つのキホンだけ抑えれば、ほとんどやりたいことはジツゲンできると思います!
他2つのサンプルも上記の延長で作成することができます。
つまったポイント
次は公式サンプルを動かした時や自分で作った際に、私自身がつまったポイントをいくつかまとめます。
1️⃣公式ドキュメントの不足
プレビュー段階なのでアタリマエではありますが、公式ドキュメントが少ないです。
モデルの操作方法などは基本的に公式のサンプルプログラム頼りです。
ただ、Flutterのバージョンが進んだこともあり、公式のサンプルプログラムを動作しようとしてもエラーが発生し起動できないという状態でした。
Flutterのバージョンをあわせても良かったのですが、今回はForkして修正したものを利用することで解消しました。
2️⃣モデル操作を行うとZ軸の表示面が反転する
こちらは発生原因が究明しきれていません。
あくまで今回のサンプルプログラムを作った上で理解できた内容になります。
公式で用意されていたDashくん1のゲームを動かしたところ、Dashくんの表示が崩れていました。
| 反転したDashくん | いつものDashくん (かわいい) |
|---|---|
![]() |
![]() |
公式のサンプルでも発生していたので、原因がわかりきっていないのですが、正常に表示できている状態のモデルを確認してみると、読み込んだ直後の状態(Matrix4)が以下のようになっていました。
[0] [1.0,0.0,0.0,0.0]
[1] [0.0,1.0,0.0,0.0]
[2] [0.0,0.0,-1.0,0.0]
[3] [0.0,0.0,0.0,1.0]
3行目のZ軸のスケールに対して、-1と負数が設定されているようでした。
公式のサンプルでは新規のMatrix4を生成してモデルに設定していたため、生成時にZ軸のスケールに1が設定されてしまい、モデルのZ軸に対して反転スケールが適用されて表示が崩れていたようです。
前述した「モデル操作」の通り、Z軸のスケールに対しては負数を設定することでいつものかわいいDashくんが表示されました。
3️⃣標準機能は多くない
flutter_sceneは3Dモデルを扱いやすくするためのパッケージなので、モデルに対する操作は用意されたものを利用する必要があります。
今回、太陽系のサンプルを作成する際に 「光があふれる表現(ブルーム表現)」 を使ってみたいと思ったのですが、調べた限りflutter_sceneでは現状対応されていないようでした。
issueは上がっていますが、1年以上音沙汰がないので余裕があれば見てみたいと思っています。
時間の都合で確認できなかったのですが、ゲーム用パッケージであるflameでは2D上でジツゲンされていたので、flame_3dなどの他パッケージではジツゲンできるかもしれません。
こちらはまた調査したいと思います。
所感
開発体験はFlutterそのものなのに画面は経験したことないリッチなグラフィックをジツゲンでき、とにかく楽しい開発でした。
すべてをDartでジツゲンできるため、状態管理やその他のコンポーネントとの重ね合わせがやりやすかったです。
周辺パッケージ含めしばらく更新されておらず、Flutter本体も優先度の高い対応を残しているので、正式リリースされるまではまだ時間がかかるかもしれません。
「業務で」となるとまだ先の話ではありますが、少し未来のFlutterに触れるというトクベツな体験ができるので、みなさんもぜひ作ってみてください!
その際にこの記事が参考になれば幸いです。
-
Dart,Flutterの公式マスコットです。女の子です。 ↩

