Open15

flutter flameメモ

kznrkznr

https://github.com/flame-engine/awesome-flame?tab=readme-ov-file
https://www.youtube.com/watch?v=eiZDbUBesqE
onLoad -> onMount -> onRemoveの順番

onLoadは初回に1度呼び出される。onMountはobject※が読み込まれたタイミングで呼ばれる。onRemoveはComponentTreeから削除されたタイミングで呼ばれる。

※このオブジェクトはonLoad(), onMount(), onRemove()をoverrideされており、Componentをextendsしている。

つまり、stateごとにアクションしたいオブジェクトはextends Componentしておけばいい。

update, renderはfpsごとに呼ばれる。
renderはcanvasオブジェクトを必要とし、canvasは描画を行う。描画にはPaintクラスを用いる。
renderではオブジェクトの位置情報を指定でき、update()で位置情報を更新するとrenderで描画される。

位置情報の更新幅にdtを入れてあげると、fpsごとに等速で動くので良い。

位置情報の更新にはPositionComponentが役にたつ。このコンポーネントはpotitionをメンバに持っているため、クラス内で変更できる。
またvector2を引数にとるが、これは位置の加算や情報のコピーなどの機能を持っていて便利基本vector2を用いて場所を変えるのが良い(x,yを直接変更すべきでない。)

_gravityを設定してあげることで重力を表現できる。onMountで初期位置を設定できる。

TapCallbacksを持つとtap時の挙動を制御できる。マイナスにすることでジャンプを表現できる。

kznrkznr

https://www.youtube.com/watch?v=nyUayMgZqDg

カメラについて
カメラはオブジェクトを追従する、表示幅がありその範囲でrenderされる(?)

FlameGameはデフォルトでcameraとworldを持っている。内部的にcamera.world = _worldとしておりcameraはworldを内包している。
GameWidgetをインスタンス化するときにcameraを定義しているが、この中でviewportを指定しているはずで、これはworldに対して表示範囲を定めるwindowの役割を果たす。viewwport内にhudComponentを追加した場合、これは画面の移動の影響を受けない。

カメラはworldを基準に表示している。
componentはworld.add()とすることがで、その場合addしたコンポーネントのカメラポジションは中央になる。

rectangleComponentは四角形をaddできる。これはaspect ratioを維持する

描画する範囲と内部的に持つ領域は異なる。
これはdebugModeで確認できる。必要に応じてcanvas.drawCircleで修正できる。

anchor 場所を指定できるっぽい。あとで調査

camera.followでオブジェクトをフォローできる。onMountやupdateなどで指定できる。positionの調整と似た方法で、follow条件は範囲をカスタムできる。その場合はupdateに書くことになる

kznrkznr

https://www.youtube.com/watch?v=Xb5ySgetpmU

各クラスはコンストラクタなどでkeyを設定できる。ComponentKey.named(Srtring)で設定したkeyを参照してオブジェクトを呼び出せる

参照する際はwith HasGameRef<MyGame>をmixinすると参照できる
呼び出す際は、gameRef.findByKeyを使う

オブジェクトの停止位置を制御(gravityをzeroにするなど)するためにはpositionOAnchor(Anker)を使う。(13:10-)

dartのassertはコンストラクタの後(superの前)につけることができる。

描画されるものと実際の判定には差がある。(2回目)
canvas.drawCircle((size/2).toOffset(),...)として合わせられる

with ParentIsA<T>で任意のクラスを親にできる。
設定したクラスは他のクラスから追加できない(?) link

描画したオブジェクトに対してEffectsでエフェクトをかけることができる。

画像の読み込みにはFuture asyncが必要

kznrkznr

https://www.youtube.com/watch?v=w6hEuWPnXQc

衝突検知を行う場合、game本体(MyGame)にHasCollisionDetection, コンポーネント(例えばPlayerクラス)にCollisionCallBacksを持たせる。CollisionCallBacksをextendしたクラスはonCollision、onCollisionEndをを実装できるこれらはは衝突したオブジェクトを引数に持つ。これはPositionComponent。

衝突検知を行うクラスはCirclehitBox()これをaddできる。これは設定した任意のオブジェクトがhitBoxの領域に踏み込んだときにcallされる。検知の方法はいくつかありCollisionTypeで指定する。(xxHitboxクラスの引数)。例えば検知が不要なタイミングでinactiveを利用することでパフォーマンスを向上できる。衝突検知したオブジェクトのうち、処理を行うのが1つだけの場合、他方のcomponentでonCollisionを呼ぶ必要はないのでpassiveにできる。

hitboxはポリゴンのように詳細に設定できる(略)

オブジェクトをremoveする方法はいくつかあり、その中にremoveFromParentがある。

memo
#30DaysMasterFlutter

kznrkznr

https://www.youtube.com/watch?v=ISSty1nQ-uQ

pause, resumeなどのUIはflamegameの外側で実装できる。通常のflutter widgetのonPressedなどでFlameGameの内部状態を管理する。(FlameGameクラスにpauseEngine(), resumeEngine()メソッドがあり、適宜呼び出せば良い)

HasDecoratorsをmixinするとDecoratorsできる。これで停止中の画面を暗くしたりできる。PositionComponentはデフォでmixinしているので前述以外でもブラーなどをかけたりもできる。mixinされた(した)クラスのdecorator=PaintDecorator.~すれば良い

HasTimeScale をmixinすると、ゲームの実行速度を高速化することができる。pauseEngineの代わりにmixinしたクラスでtimeScale=0としてもいいかも。

kznrkznr

https://docs.flame-engine.org/latest/flame/components.html

  • componentに優先度をつけられる。

  • componentに子componentをwrapできる。親がレンダリングされたとき、子もレンダリングが呼び出される。シンプルにaddするか、children: に記載する

  • ParentIsAで特定の親であることを保証できる。ここでしか使わないコンポーネントはつける方が良さそう。HasAncestorをmixinするとその親を確認できる。

  • componentにkeyをつけられる。

  • Query関連はパフォーマンスの向上によさそう

  • HUDを作成する場合、viewpointはPositionType.viewportにすることが多い

  • コントローラなどはPositionType.widgetとするかも

  • この設定はMroot class(MyGame)に追加されたときに反映される。

  • 表示/非表示でadd/removeできるが、await myChildComponent.removedなどでwatchするようにして

  • HasVisibilityをmixinするとremoveせずに非表示にできる

  • spriteアニメーションは複数の画像or spritesheetから作成できる。

  • spritesheet の場合はSpriteAnimationData.sequencedを使って読み込む

  • animationTickerで振る舞い、onCompleteでコールバックできる

  • spriteGroupComponentがある、複数のanimationを管理したいとき

    • Playerの歩く/攻撃/などの制御によさそう
  • SpawnComponentでarea内にcomponentをランダム生成できる。factory

  • 自分のユースケースだと不要かも

kznrkznr

ちょっとbloc実装大変そうなので、flame_riverpodを検討。
かなり分かりやすい。
blocはevent発火時の更新がfunctionalになるっぽいとも言われており、可読性も下がりそうかなという感じ

https://github.com/flame-engine/flame/blob/main/packages/flame_riverpod/example/lib/main.dart

上記の構成図(雑)

  • riverpodとflame_riberpodを両方使っている
  • riverpodをflameのライフサイクルで利用するには、RiverpodAwareGameWidget, RiverpodGameMixin, RiverpodGameMixinを設定する
  • watchは発火時に再構築する、listenは再構築せずにfunctionを実行する

参考: https://blog.markvideon.dev/the-genesis-and-rebirth-of-flame_riverpod/