創造を続け、成長を加速させましょう!「ナゲッツデイリーニュープラン・6月アップデートチャレンジ」に参加して6日目です。クリックしてイベント詳細をご覧ください。
序文
これは、 Zhang Fengjietlieによって作成され、Nuggetsコミュニティで公開されているFlutter&Flame
一連です。他のプラットフォームでこの記事を表示している場合は、ナゲッツに移動してリンクに従って表示できます。記事は更新および改訂される可能性があるため、ナゲッツの記事のバージョンが優先されます。このシリーズの記事のリスト:
- 【Flutter&FlameGame-One】新しい世界への扉を開く
- 【Flutter&Flameゲーム-Ⅱ】ジョイスティックとキャラクターの動き
- 【Flutter&FlameGame-3】キーボードイベントとジェスチャー操作
- 【Flutter&Flameゲーム-4】スプライト画像の読み込み方法
- 【Flutter&FlameGame-Wu】CanvasParticipation | Character's Health Bar(
本文
)
1.異国の地で古くからの友人に会った-キャンバス
小冊子「フラッタードローイングガイド-素敵な筆と花」は、Canvas
ドローイング。実際、ゲームの本質は、図面を継続的に更新することです。Flame
エンジンでは、レンダリング方法も公開されるため、ユーザーは図面をカスタマイズできます。これは、これまでに蓄積した描画スキルをに適用できることを意味Flame
します。
上に示したように、render
メソッドはComponent
クラスため、誰でも构件
オーバーライドしてCanvas
ビルド。さらに、それ自体にいくつかのライフサイクルメソッドがあるComponent
こと。以前onLoad
に、、、update
およびonGameResize
メソッドを見てきました。Component
のライフサイクルについては、今は気にしないでください。後で特別な記事を書きます。
2.単純なストローク
次のようにMonster
クラスのメソッドをオーバーライドしrender
て、Canvas
オブジェクトに白い円を描きます。ここでのキャンバスの原点は、コンポーネントの左上隅にあることがわかります。コード[05/01]を参照してください。
---->[component/Monster]----
@override
void render(Canvas canvas){
super.render(canvas);
canvas.drawCircle(Offset.zero, 10, Paint()..color=Color(0xffffffff));
}
复制代码
注意すべき点の1つsuper.render
は、レンダリングメソッドが親クラスに代わって実行されることです。メソッドがオーバーライドされていない場合render
、Monster
描画はデフォルトで親をトリガーします。render
メソッドをオーバーライドすると、メソッドは実行されますMonster
。呼び出さないとsuper.render
、親の描画は有効になりません。つまり、モンスターはレンダリングされません。これは、オブジェクト指向の最も基本的な知識です。
さらに、描画も同じ后者居上
です。つまり、オーバーラップがある場合、以下に示すように、後で描画されるパターンが最初に描画されるパターンをカバーします。
3.血液バーを描画します
モンスターが登場した今、ブラッドバーは当然欠かせません。次のように、Monster
クラスです。コードについては[05/02]を参照してください。
以下は、描画の単純なロジックです。主なロジックは、外枠とヘルスバーの2つのRect
長方形です。外枠の白いバーの長方形は、ヘルスバーがここで中央に配置されると予想されるため、中心点の幅と高さによって決定され、長さは比率でwidthRadio
制御。白い棒の長方形を決定した後、左下隅の点を決定することができます。このとき、2点で長方形を決定する方が便利です。グラフィック情報を決定する最も簡単な方法を選択する方法は、描画の小さな詳細です。
Paint _outlinePaint = Paint();
Paint _fillPaint = Paint();
@override
void render(Canvas canvas) {
super.render(canvas);
Color lifeColor = Colors.red; // 血条颜色
Color outlineColor = Colors.white; // 外框颜色
final double offsetY = 10; // 血条距构件顶部偏移量
final double widthRadio = 0.8; // 血条/构件宽度
final double lifeBarHeight = 4; // 血条高度
final double lifeProgress = 0.8; // 当前血条百分百
Rect rect = Rect.fromCenter(
center: Offset(size.x / 2, lifeBarHeight / 2 - offsetY),
width: size.x * widthRadio,
height: lifeBarHeight);
Rect lifeRect = Rect.fromPoints(
rect.topLeft + Offset(rect.width * (1 - lifeProgress), 0),
rect.bottomRight);
_outlinePaint
..color = outlineColor
..style = PaintingStyle.stroke
..strokeWidth = 1;
_fillPaint.color = lifeColor;
canvas.drawRect(lifeRect, _fillPaint);
canvas.drawRect(rect, _outlinePaint);
}
复制代码
这里为了方便演示,只画了最简洁的血条,大家也可以发挥自己的绘画天赋,在网上找一些好看的血条画画看。
4. 代码复用的好帮手 -mixin
我们刚才只在 Monster
类中覆写的 render
,绘制血条。那主角 Adventurer
也需要要血条,笨方法是把 Monster
中的绘制拷一份到 Adventurer
中。如果一个游戏中有非常多需要需要血条的构件,这样做显然是不可行的。相同的绘制逻辑分散在各个类中,不利于维护和拓展。
反过来我们可以想一下,为什么每个构件都可以很简单地使用手势事件,答案是 mixin
。通过混入的方式可以拓展一个类的功能,所以混入一个 Liveable
来让构件显示血条,自然也不是什么难事。如下所示,是【05/03】 的案例效果 :
如下,Liveable
依赖于 PositionComponent
类,因为绘制时需要构件的尺寸。其中 initPaint
方法中,用于初始化一些配置参数用于自定义,比如血条颜色、外框颜色、生命上限等。这里只是简单演示,满足最基本的需求,你也可以提供一些其他的配置参数,或者定义一个配置信息类,简化传参流程。
在这里只要覆写 render
方法,执行刚才写的绘制逻辑即可。
---->[05/03/mixins]----
mixin Liveable on PositionComponent {
final Paint _outlinePaint = Paint();
final Paint _fillPaint = Paint();
late double lifePoint; // 生命上限
late double _currentLife; // 当前生命值
void initPaint({
required double lifePoint,
Color lifeColor = Colors.red,
Color outlineColor = Colors.white,
}) {
_outlinePaint
..color = outlineColor
..style = PaintingStyle.stroke
..strokeWidth = 1;
_fillPaint.color = lifeColor;
this.lifePoint = lifePoint;
_currentLife = lifePoint;
}
// 当前血条百分百
double get _progress => _currentLife / lifePoint;
@override
void render(Canvas canvas) {
super.render(canvas);
final double offsetY = 10; // 血条距构件顶部偏移量
final double widthRadio = 1.5; // 血条/构件宽度
final double lifeBarHeight = 4; // 血条高度
Rect rect = Rect.fromCenter(
center: Offset(size.x / 2, lifeBarHeight / 2 - offsetY),
width: size.x / 2 * widthRadio,
height: lifeBarHeight);
Rect lifeRect = Rect.fromPoints(
rect.topLeft + Offset(rect.width * (1 - _progress), 0),
rect.bottomRight);
canvas.drawRect(lifeRect, _fillPaint);
canvas.drawRect(rect, _outlinePaint);
}
}
复制代码
使用起来就非常简单,只要是 PositionComponent
一族的构件,都可以混入刚才自定义的 Liveable
,然后只要在 onLoad
方法中通过 initPaint
方法初始化数据即可。
这样就可以在 Adventurer
的头上加一个血条,生命值上限是 1000
。
通过这里可以看出 mixin
对于独立逻辑的封装,还是非常有优势的。混入类中可以拓展 属性
和 方法
,只要 A
混入了 B
,就说明 A
可以视为 B
的子类,即可访问 B
中的所有非私有 属性
和 方法
。
对于 mixin
的理解,是 Dart
中非常重要,也是很多新手所忽略的。在 Flutter
框架的源码中 mixin
有着非常多的使用场景。在 《Flutter 渲染机制 - 聚沙成塔》的第十二章,结合源码中的实际使用对 mixin
有详细的介绍。网上很多文章简单写两个 demo
,是很难真正理解 mixin
的价值的。
5. 血条的减少
有了血条不让它减少有点可惜了,如下案例中,通过点击事件让怪物的血量减少:代码见 【05/04】
血量是在 Liveable
类中定义的,所以也在此维护血量值。如下在 Liveable
中定义 loss
方法,对生命值进行减少。并在生命值小于 0
时,触发 onDied
方法。混入类可以覆写这个方法来监听角色的死亡。
---->[05/04/Liveable]
void loss(double point) {
_currentLife -= point;
if (_currentLife <= 0) {
_currentLife = 0;
onDied();
}
}
void onDied() {
}
复制代码
然后在 TolyGame
中使用 TapDetector
监听点击事件,每次点击让 monster
减少 50
点生命值。游戏中让射手发出弓箭,检测命中后,让 monster
生命值减少,或通过体力药水或辅助角色恢复生命值。
class TolyGame extends FlameGame with KeyboardEvents,TapDetector {
@override
void onTap() {
monster.loss(50);
}
复制代码
ヘルス値がである場合に削除するmonster
方法を見てみましょう。混合クラスでメソッドコールバック0
は上記でLiveable
定義されています。ライフ値がゼロ以下のイベントを監視できます。次に、メソッド。onDied
onDied
removeFromParent
class Monster extends SpriteAnimationComponent with Liveable {
@override
void onDied() {
removeFromParent();
}
复制代码
この記事では、キャラクターのヘルスバーのカスタム描画に合格Component#render
し、Canvas
オブジェクトを取得してカスタム描画操作を実行できるコールバックメソッドについて学習しました。さらにmixin
、。この記事は以上です、また明日〜
@张风捷特烈 2022.05.30 未允禁转
我的 公众号: 编程之王
我的 掘金主页
:張鳳凰寺我的 B站主页
:張鳳凰寺我的 github 主页
:toly1994328
\