FlutterのBonfireでタッチエフェクトを表示したい
BonfireやFlameの記事は少ないので、同じように詰まっている人の助けになれば…
Bonfireとは
BonfireはFlutterでRPGなどのゲームを作成できるフレームワークです。
Flutter公式のゲームエンジンであるFlameをベースとしているため、BonfireにFlameのコンポーネントを使用することもできます。
Bonfireの入口はBonfireWidgetというStatefulWidgetなので、FutterのWidgetと組み合わせることができます。
作りたかったもの
スマホゲームでよく見る、タップした箇所にエフェクトを表示するやつです。
実装
TouchEffectorの作成
class TouchEffector extends GameComponent with TapGesture {
/// コンストラクタ
TouchEffector() {
// 他のコンポーネントよりも上に表示する
renderAboveComponents = true;
}
/// タッチエフェクトの表示を行います。
void onTapDownScreen(GestureEvent event) {
add(_TouchEffectParticle(position: event.worldPosition));
super.onTapDownScreen(event);
}
// do nothing
void onTap() {}
/// GameComponentはデフォルトでは表示しないようになっているので表示
bool get isVisible => true;
}
/// タッチ時に表示するパーティクルです。
class _TouchEffectParticle extends ParticleSystemComponent with HasPaint {
_TouchEffectParticle({
required super.position,
}) : super(
anchor: Anchor.center,
) {
particle = CircleParticle(
paint: paint..color = Colors.cyan,
lifespan: 1.5,
);
}
Future<void> onLoad() async {
// フェードアウト
add(
OpacityEffect.fadeOut(
EffectController(
duration: 1.5,
curve: Curves.easeOutQuad,
),
),
);
// 縮小
add(
ScaleEffect.to(
Vector2.zero(),
EffectController(duration: 1.5),
),
);
return super.onLoad();
}
}
BonfireWidgetに追加
class TouchEffectSamplePage extends StatelessWidget {
/// コンストラクタ
const TouchEffectSamplePage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Touch Effect'),
),
body: BonfireWidget(
map: WorldMap.empty(),
backgroundColor: Colors.transparent,
components: [
TouchEffector(),
],
),
);
}
}
解説
追加したコンポーネント(TouchEffector)について
class TouchEffector extends GameComponent with TapGesture {
TouchEffector
はGameComponent
を継承しています。
Bonfire(その元になっているFlameも)は基本的にComponentを使用することでゲーム上のあらゆる機能を実現していく作りになっています。
BonfireではGameComponent
またはそれを継承したクラスを使用することで、Bonfireの様々なComponentを使用できます。
TapGesture
はこのコンポーネントにタップ機能を追加するために追加しています。
Flame(Bonfire)はこれに限らずmixinで機能を追加する形が多いため、車輪の再発明にならないようにドキュメントやリポジトリで探してみると意外に用意されていることがあるかもしれません。
TouchEffector() {
// 他のコンポーネントよりも上に表示する
renderAboveComponents = true;
}
Bonfireはコンポーネントごとにレイヤー(優先度)が設定されていますが、renderAboveComponents = true;
を使用して、他のレイヤーよりも上に表示できるようにしています。
void onTapDownScreen(GestureEvent event) {
add(_TouchEffectParticle(position: event.worldPosition));
super.onTapDownScreen(event);
}
onTapDownScreen
で画面上のタップした位置に_TouchEffectParticle
を追加しています。
追加にはaddを使用していますが、これはこのコンポーネントの子要素にadd
しています。
一方でgameRef.add()
も目にするかと思いますが、こちらはBonfireGame
の(内部で使用されている)ベースとなるコンポーネントに追加するものです。
ただし今回はレイヤー的に後ろに回り込んでしまうので renderAboveComponents
を設定したTouchEffector
にaddします
get isVisible => true;
bool
GameComponent
はデフォルトでは非表示となっているため、isVisible
をtrueにして、タッチエフェクトを画面上に表示できるようにしています。
_TouchEffectParticle
パーティクルについて
パーティクルは指定時間が経過すると自動的に消滅するコンポーネントです。これはFlame
の機能です。
class _TouchEffectParticle extends ParticleSystemComponent with HasPaint {
ParticleSystemComponent
を継承し、パーティクルとして扱えるようになります。
HasPaint
はBonfireで定義されているmixinですが、Component
を継承しているのでFlame
でも使えます。
後述するエフェクトを使用するために必要なPaintを生成してくれます。
_TouchEffectParticle({
required super.position,
}) : super(
anchor: Anchor.center,
) {
particle = CircleParticle(
paint: paint..color = Colors.cyan,
lifespan: 1.5,
);
}
このあたりはパラメータ(position
,anchor
,particle
)の設定です。
円形のParticleを設定しています。
Future<void> onLoad() async {
// フェードアウト
add(
OpacityEffect.fadeOut(
EffectController(
duration: 1.5,
curve: Curves.easeOutQuad,
),
),
);
// 縮小
add(
ScaleEffect.to(
Vector2.zero(),
EffectController(duration: 1.5),
),
);
return super.onLoad();
}
onLoadはゲーム上に追加されたときに一度だけ呼び出されるメソッドです。このパーティクルが生成されるタイミングでParticleSystemComponentにaddします。
onLoadでは透明度を変えるOpacityEffectと、大きさを変えるScaleEffect.toを使用しています。
参考
Effectを使わずupdateメソッドで自力で透明度(大きさ等も)を処理させる方法もありますが、Effectを使うほうがシンプルに感じたのでそちらを使っています。
void update(double dt) {
final double opacity = max(0.75 - progress * lifespan / lifespan, 0);
paint.color = paint.color.withOpacity(opacity);
super.update(dt);
}
追加したBonfireWidgetについて
body: BonfireWidget(
map: WorldMap.empty(),
backgroundColor: Colors.transparent,
components: [
TouchEffector(),
],
),
Bonfireを使用するにはBonfireWidget
を使用します。
今回はmap
を空で設定していますが、WorldMapByTiled
などでマップファイルを使用することができます。
最後にcomponents
には今回作成したTouchEffector
コンポーネントを設定しています。
まとめ
今回は、Bonfireを使用してタッチエフェクトを表示するために、GameComponent
、onTapDownScreen
、ParticleSystemComponent
を使用しました。
また、便利なmixinのTapGesture
,HasPaint
も確認しました。
もっと良い方法があるかもしれませんが、ドキュメントに掲載されていない内容も割とあったためこのやり方で実現しました。
なので詰まったときはリポジトリ(RafaelBarbosatec/bonfire)から読み解く必要があるなと感じるとともに、理解が進むと割とサクッとできるな、というのが現状のBonfireの感想です。
今後はGridViewやListViewなどと組み合わせられるのもやっていきたいです。
Discussion