开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮

发表于 2022-06-17

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 23 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
  • 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
  • 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading
  • 【Flutter&Flame 游戏 - 贰陆】pinball 源码分析 - 游戏主菜单界面
  • 【Flutter&Flame 游戏 - 贰柒】pinball 源码分析 - 角色选择与玩法面板
  • 【Flutter&Flame 游戏 - 贰捌】pinball 源码分析 - 游戏主场景的构成
  • 【Flutter&Flame 游戏 - 贰玖】pinball 源码分析 - 视口与相机

第一季完结,谢谢支持 ~


1.在游戏中添加菜单组件

一般来说,休闲游戏并不会打开时立即进入游戏。会有一个菜单界面,让用户选择开始游戏,或通过设置按钮来打开配置界面,对游戏进行设置。而我们知道,Flame 的 “世界” 是通过 Ticker 不断触发更新的,但往往菜单是 静态 的,不需要一直更新。所以可以使用 Flutter 原生的组件来做菜单,再加上界面跳转也需要原生的路由。

其实本质上来说,Flame 所呈现的游戏界面也只是一个 Widget 而已,我们可以一视同仁。比如下面定义两个 GameWorld 组件,来表示游戏世界: 【22/01】

1
2
3
4
5
6
7
8
dart复制代码class GameWorld extends StatelessWidget {
const GameWorld({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return GameWidget(game: TolyGame());
}
}

由于需要界面路由跳转,所以这里使用 MaterialApp ,其内部集成了路由体系。并且这里使用 navigatorKey ,便于在无上下文的情况下,获取导航状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dart复制代码class GameApp extends StatelessWidget {
const GameApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
navigatorKey: Keys.navKey,
themeMode: ThemeMode.dark,
darkTheme: ThemeData(brightness: Brightness.dark),
home: const MainMenu(),
);
}
}

class Keys {
Keys._();
static GlobalKey<NavigatorState> navKey = GlobalKey(debugLabel: 'navKey');

static NavigatorState? get navigator => navKey.currentState;
}

比如现在先给个简单的菜单界面,如下所示,一个名字文本,两个按钮:

如下所示,定义一个 Flutter 常规的 MainMenu 组件,对内容进行展示即可,代码如下。其中 开始 按钮通过 Keys 中的 navKey 获取导航栏状态,通过 pushReplacement 方法,跳转到 GameWorld 游戏界面,并将当前的 MainMenu 界面弹栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
dart复制代码class MainMenu extends StatelessWidget {
const MainMenu({Key? key}) : super(key: key);

final TextStyle shadowStyle = const TextStyle(
fontSize: 30,
shadows: [Shadow(color: Colors.white,blurRadius: 10)]
);

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Wrap(
spacing: 20,
direction: Axis.vertical,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text('Adventurer', style: shadowStyle,),
ElevatedButton(onPressed: _doPlay, child: const Text('开 始')),
ElevatedButton(onPressed: _toOptions, child: const Text('设 置'))
],
),
),
);
}

void _doPlay() {
Keys.navigator?.pushReplacement(
MaterialPageRoute(builder: (ctx) => const GameWorld()),
);
}

void _toOptions() {}
}

2. 字体下载

有人觉得默认字体可能并不是很好看,想要引入别的字体,但很多字体不可以商用。其实google_fonts 中提供了大量可以商用的字体,我们可以在 fonts.google.com/ 中进行挑选。


在某个字体的 License 中,可以瞄一眼,比如 Ma Shan Zheng 是允许在- 项目-印刷或数字,商业或其他场景使用的。


点击下载,在 OFL 中也可以看到,字体证书是 STL ,允许商用:


你可以通过 线上和 本地 两种方式来加载字体。线上加载,可以使用 google_fonts 的字体库,所有的字体样式都可以通过 GoogleFonts 类通过静态方法获取,使用时会自动下载字体。

线上的缺点是必须依赖网络,而且需要下载时间,对于很大的字体,首次下载时间比较长,突然的字体改变,体验并不是很好。可以把字体下载到本地,这样就没有延迟的风险,而且在没有网络的情况下也能使用,缺点是会增加应用体积,大家可以酌情选择。本地字体使用也非常方便,只需要引入,在 pubspec.yaml 的 fonts 节点下引入即可:

如果想要指定全局字体,可以在主题数据 ThemeData ,指定对应的 fontFamily :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dart复制代码class GameApp extends StatelessWidget {
const GameApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.dark,
darkTheme: ThemeData(
brightness: Brightness.dark,
fontFamily: 'ZCOOLKuaiLe' //<--- 指定字体
),
home: const MainMenu(),
);
}
}

这样就可以对应用的 Text 组件的字体进行统一设置,效果如下:


3. 游戏的暂停和恢复

我们知道 Falme 中通过 GameLoop 维护一个持续触发的 Ticker 用于游戏的渲染更新。当然,游戏中也需要要有暂停和恢复的方法,如下案例中,通过按下空格键来切换游戏状态:

在 Game 类中提供了 resumeEngine 和 pauseEngine 两个方法,用于恢复和暂停游戏。此外 paused 属性可以得知游戏是否已经停止。由于 FlameGame 混入了 Game ,所以它有这些方法,如果在其他的构件中希望暂停或恢复游戏,可以通过混入 HasGameRef ,来得到 gameRef 对象触发这些方法。

1
2
3
4
5
6
7
dart复制代码void toggleGameState(){
if(paused){
resumeEngine();
}else{
pauseEngine();
}
}

4. 在 Flame 中展示浮层

有时我们有显示浮层的需求,比如暂停游戏时,显示暂停面板。不然用户不小心碰到了暂停键,有可能不知所措,显示一个浮层界面可以更好的引导交互。如下所示,在点击空格键时,显示浮层:代码详见 【22/02】


使用浮层需要三步:

  • 1.创建浮层中的内容组件

这里和开始菜单类似,就不贴代码了,详见源码。在其中定义了 Game 成员,在构造方法中初始化,这是为了方便在 PauseMenu 的继续按钮触发时,调用引擎的相关方法,继续游戏。当然,你也可以把事件回调出去,让使用者处理,其实都差不多,酌情考量即可。

另外,定义了一个 menuId 的静态常量,为了方便标识这个菜单,而不是在每处使用时,都写一个死的字符串。


  • 2.通过 GameWidget 的 overlayBuilderMap 参数指定 浮层id 和 组件内容 的映射关系:


  • 3.通过 浮层id 开启或隐藏浮层,其中 overlays 是 Game 中的公开成员:


本文介绍了,如何在 Flame 游戏中,让 Flutter 原生的组件发挥价值。其实 Flame 是在 Flutter 中的,你可以随时随地,使用 Flutter 中的任何知识。并没有必要把 Flame 和 Flutter 进行割裂,Flutter 的基础设施仍然可以使用,比如国际化、主题切换、状态管理等等。

  • @张风捷特烈 2022.06.17 未允禁转
  • 我的 公众号: 编程之王
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Flutter&Flame 游戏 - 贰壹】视差组件

发表于 2022-06-16

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 22 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
  • 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
  • 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading
  • 【Flutter&Flame 游戏 - 贰陆】pinball 源码分析 - 游戏主菜单界面
  • 【Flutter&Flame 游戏 - 贰柒】pinball 源码分析 - 角色选择与玩法面板
  • 【Flutter&Flame 游戏 - 贰捌】pinball 源码分析 - 游戏主场景的构成
  • 【Flutter&Flame 游戏 - 贰玖】pinball 源码分析 - 视口与相机

第一季完结,谢谢支持 ~


1. 什么是 Parallax

Parallax 译为 视差 。可以想想一下,在你做火车时,旁边的树会飞速向后退,而远处的云却在缓慢运动,这就是由于物体距观察者距离不同,而产生的 视差 。在某些游戏中我们也希望模拟这种视觉感受,这就是 ParallaxComponent 存在的意义。

首先 ParallaxComponent 的特点是会以某个基础速度 运动,其次它支持多以不同速度运动的图层。没找到什么好的图片素材,Flame 官方的这个案例还不错,就直接拿来看了。如下选中的五张图片分别是从内向外依次是:

  • bg.png :背景
  • mountain-far.png : 远山
  • mountains.png : 近山
  • trees.png :远树
  • foreground-trees.png : 近树

通过 ParallaxComponent 进行叠合运动,就可以产生如下的效果:远处的图层运动慢,近处的图层运动快:代码详见 【21/01】

如下是代码实现,通过 loadParallaxComponent 方法加载 ParallaxComponent 对象。其中需要传入图层对于的图片数据,索引靠后的图层在前方。另外 baseVelocity 是图层运动的基础速度;velocityMultiplierDelta 是前一层和后一层的速度之比,也就相当于视差的倍率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dart复制代码class BasicParallaxExample extends FlameGame {

final _imageNames = [
ParallaxImageData('parallax/bg.png'),
ParallaxImageData('parallax/mountain-far.png'),
ParallaxImageData('parallax/mountains.png'),
ParallaxImageData('parallax/trees.png'),
ParallaxImageData('parallax/foreground-trees.png'),
];

@override
Future<void> onLoad() async {
final ParallaxComponent parallax = await loadParallaxComponent(
_imageNames,
baseVelocity: Vector2(20, 0),
velocityMultiplierDelta: Vector2(1.4, 1.0),
);
add(parallax);
}
}

2. 指定某层视差速度

通过 loadParallaxComponent 直接加载多个层,有个缺点:每层间的速度比值是恒定的。如果想要动态指定每层的速度该怎么办呢?我们可以通过创建一个个的层来指定不同的速度比:代码见 【21/02】


通过 loadParallaxLayer 方法,可以加载一个 ParallaxLayer 对象。如下通过 formLayerByMap 方法,把 Map 对象,转化成 ParallaxLayer 列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dart复制代码final _layersMeta = {
'parallax/bg.png': 1.0,
'parallax/mountain-far.png': 1.5,
'parallax/mountains.png': 2.3,
'parallax/trees.png': 3.8,
'parallax/foreground-trees.png': 6.6,
};

Future<List<ParallaxLayer>> formLayerByMap(Map<String,double> data) async{
List<ParallaxLayer> result = [];
for(String image in data.keys){
ParallaxLayer layer = await loadParallaxLayer(
ParallaxImageData(image),
velocityMultiplier: Vector2(data[image]!, 1.0),
);
result.add(layer);
}
return result;
}

在 ParallaxComponent 组件中,传入 Parallax 对象,该对象构造时,需要指定 ParallaxLayer 列表。这样就可以指定不同层的速度比值,更精确地控制视差的效果。

1
2
3
4
5
6
7
8
9
10
11
dart复制代码@override
Future<void> onLoad() async {
List<ParallaxLayer> layers = await formLayerByMap(_layersMeta);
final parallax = ParallaxComponent(
parallax: Parallax(
layers,
baseVelocity: Vector2(20, 0),
),
);
add(parallax);
}

3. 加载序列帧资源

如下在最外层添加一个序列帧动画,可以看出此时由于背景在向后运动,所以序列帧所在的层保持静止即可。在视觉上会有一种飞机在向前飞的错觉,这就是相对的参考系。代码见 【21/03】

如下是创建一个序列帧层的方式,使用 ParallaxAnimationData 加载。ParallaxLayer 创建完毕之后,添加到上面的 layers 列表中即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
dart复制代码final ParallaxLayer airplaneLayer = await loadParallaxLayer(
ParallaxAnimationData(
'parallax/airplane.png',
SpriteAnimationData.sequenced(
amount: 4,
stepTime: 0.2,
textureSize: Vector2(320, 160),
),
),
repeat: ImageRepeat.noRepeat,
velocityMultiplier: Vector2.zero(),
fill: LayerFill.none,
alignment: Alignment.center,
);

这里说明一下:只是介绍一下如何在 ParallaxComponent 中加入序列帧图层,像小人这种主角,一般还是作为一个 Component 加入游戏场景中的,这样方便操作。


4. 简单射击

结合前面学的知识,完成一个简单的设计场景应该不在话下,代码见 【21/04】

另外, ParallaxComponent 也可以抽离出来,单独作为一个构建来使用,没必要把太多逻辑写在 Game 实现类中。如下,通过 Background 构件加载 Parallax ,实现视差的背景效果。


接下来大家可以自己拓展一下,比如添加敌人、射击击中的检测、得分显示,和之前的小案例是一样的,这里就不过多引申了。其实对于游戏而言, UI 的资源也是一个很大的瓶颈。有些好想法,但由于设计素材缺乏,也很难实现。那本文就到这里,明天见 ~


番外: Flame 1.2.0 更新内容:

Falme 最近更新了一个版本,到了 1.2.0 ,我们来看一下目前进行了哪些变化:

首先,Compoment 的一个属性发生了变化:

1
dart复制代码Compoment#shouldRemove  ==>  bullet.isRemoving

对比 1.1.1 和 1.2.0 可以发现,新版本中对 text 进行了优化:之前文字通过一个 text.dart 文件进行实现,现在多了一个 text 的包,并且提供了 SpriteFontRenderer 来渲染精灵图字体。但目前源码看来并不是很好用,需要指定的参数非常麻烦,特别是 GlyphData ,感觉需要和特定的工具结合,加上解析才能用。


另外 1.2.0 中增加了 utils 包,其中有两个贝塞尔曲线相关的工具函数:


另外,对 Component 的事件支持做了优化,提供了 events 包:关于构件的事件通过 XXXCallback 进行处理,这是为了替换掉以前的 XXXable ,这点还是比较赞的。


effects 中提供了 AnchorEffect 可以对构件的锚点进行特效处理:


components/input 包中增加了 KeyboardListenerComponent ,这样监听键盘事件可以更方便了:


components/mixin 中增加了三个混入类 :

components 中增加了两个显示 fps 相关的构件:

最后 assets 包并入了 cache 包中:


  • @张风捷特烈 2022.06.16 未允禁转
  • 我的 公众号: 编程之王
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

JavaScript基础系列开篇 V8是如何运行JavaSc

发表于 2022-06-15

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

你就说吧,有没有资格,有没有资格,有没有资格,肯定有资格的。哈哈。因为我大致了解清楚了,也大致搞明白了一点了。

我们知道,机器是不能直接理解我们平常工作或者自己学习的代码的。所以,在执行程序之前,需要将代码翻译成机器能读懂的机器语言。按语言的执行流程,可以把计算机语言划分为编译型语言和解释型语言:

编译型语言:在代码运行前编译器直接将对应的代码转换成机器码,运行时不需要再重新翻译,直接可以使用编译后的结果。

解释型语言:需要将代码转换成机器码,和编译型语言的区别在于运行时需要转换。解释型语言的执行速度要慢于编译型语言,因为解释型语言每次执行都需要把源码转换一次才能执行。

Java 和 C++ 等语言都是编译型语言,而 JavaScript 是解释性语言,它整体的执行速度会略慢于编译型的语言。V8 是众多JavaScript引擎中性能表现最好的一个,并且它是 Chrome 的内核,Node.js 也是基于 V8 引擎研发的。

1.运行的整体过程

未命名文件 (4).png

2.英译汉翻译的过程

比如我们看到了google V8官网的一篇英文文章 v8.dev/blog/faster…,在阅读的过程中,可以就是要对每一个单词进行解析翻译成中文,然后多个单词进行语法的解析,再通过对整句话进行整个语句进行解析,那么这句话就翻译结束了。

下面我们就举例一句英文的翻译过程:I am a programmer。

  • 1、首先对输入的字符串I am a programmer。进行拆分便会拆分成 I am a programmer 。

相当于词法分析

  • 2、I 是一个主语, am 是一个谓语, a是一个形容词, programmer是个名词, 。标点符号。
  • 3、I 是我的意思, am 是是的意思, a是一个的意思, programmer是程序员的意思, 。是句号的意思。

2和3一起相当于语法分析

  • 4、对3中的语法分析进行拼接处理:我是一个程序员。当然这是非常简单的一个英译汉,一篇文章的话,就会复杂一些了。

相当于语义分析

3.V8运行的整个过程

3.1.准备一段JavaScript源代码

1
javascript复制代码let a = 10

3.2.词法分析:

一段源代码,就是一段字符串。编译器识别源代码的第一步就是要进行分词,将源代码拆解成一个个的token。所谓的token,就是不可再分的单个字符或者字符串。

3.3.token

通过 esprima.org/demo/parse.… 可以查看生成的tokens,也就是上面那段源代码生成的所有token。

Token类别: 关键字、标识符、字面量、操作符、数据类型(String、Numeric)等

image.png

3.4.语法分析

将上一步生成的 token 数据,根据语法规则转为 AST。通过astexplorer.net 可以查看生成AST抽象语法树。

3.5.AST

生成的AST如下图所示,生成过程就是先分词(词法分析),再解析(语法分析)

image.png
当然你也可以查看生成的AST的JSON结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
javascript复制代码{
"type": "Program",
"start": 0,
"end": 9,
"body": [
{
"type": "VariableDeclaration",
"start": 0,
"end": 9,
"declarations": [
{
"type": "VariableDeclarator",
"start": 4,
"end": 9,
"id": {
"type": "Identifier",
"start": 4,
"end": 5,
"name": "a"
},
"init": {
"type": "Literal",
"start": 8,
"end": 9,
"value": 1,
"raw": "1"
}
}
],
"kind": "let"
}
],
"sourceType": "module"
}

同样我在本地下载了v8,直接用v8来查看AST

1
javascript复制代码v8-debug  --print-ast hello.js

image.png

3.6.解释器

解释器会将AST生成字节码,生成字节码的过程也就是对AST抽象语法树进行遍历循环,并进行语义分析

3.7.字节码

在最开始的V8引擎中是没有字节码,是直接将AST转换生成为机器码。这种架构存在的问题就是内存消耗特别大,尤其是在移动设备上,编译出来的机器码占了整个chorme浏览器的三分之一,这样为代码运行时留下的内存就更小了。
于是后来在V8中加入了Ignition 解释器,引入字节码,主要就是为了减少内存消耗。
本地可以使用V8命令行查看生成的字节码

1
javascript复制代码v8-debug  --print-bytecode hello.js

image.png

3.8.热点代码

首先判断字节码是否为热点代码。通常第一次执行的字节码,Ignition 解释器会逐条解释执行。在执行的过程中,如果发现是热点代码,比如for 循环中的代码被执行了多次,这种就称之为热点代码。那么后台的TurboFan就会把该段热点代码编译为高效的机器码,然后再次执行这段被优化的代码时,只需要执行编译后的机器码就可以了, 这样就大大提升了代码的执行效率。

3.9.编译器

TurboFan编译器也可以说是JIT的即时编译器,也可以说是优化编译器。

Ignition 解释器: 可以将AST生成字节码,还可以解释执行字节码。

4、总结

  • 了解V8整个的运行机制
  • 学习JavaScript到底是怎么运行的
  • 对日后编写JavaScript代码有非常多的好处
  • 看完学习了,能提升我们的技术水平
  • 对于日后遇到问题,能够从底层去思考问题出在那里,更快速的定位和解决问题
  • 真的非常熟悉了,可以自己开发一门新的语言

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Flutter&Flame游戏 - 贰拾】构件特效 其

发表于 2022-06-15

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 21 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
  • 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
  • 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading
  • 【Flutter&Flame 游戏 - 贰陆】pinball 源码分析 - 游戏主菜单界面
  • 【Flutter&Flame 游戏 - 贰柒】pinball 源码分析 - 角色选择与玩法面板
  • 【Flutter&Flame 游戏 - 贰捌】pinball 源码分析 - 游戏主场景的构成
  • 【Flutter&Flame 游戏 - 贰玖】pinball 源码分析 - 视口与相机

第一季完结,谢谢支持 ~


1. 延迟特效控制器: DelayedEffectController

上一篇中,介绍了 DurationEffectController 一族的特效控制器,本节我们继续来认识一下其他的六种控制器。如下两侧的六个控制器有一个共同的特点,它们都会持有 子控制器 ,也就是说它们是在一个控制器的基础上,再施加的变化。本文代码均在 【20/01】


如下是 DelayedEffectController 的构造方法,其中必须传入 child 控制器,另外需要指定延时的秒数。以此达到:子控制器延时 delay 秒开始动画的效果。

如下案例,通过 DelayedEffectController 效果控制器, 让 Curved 效果延时 2s 触发。主需要将 Curved 效果作为 DelayedEffectController 的孩子,并指定 delay 为 2 即可:

1
2
3
4
5
6
7
8
dart复制代码void delayedEffectController(){
EffectController child = CurvedEffectController(2,Curves.ease);
EffectController ctrl = DelayedEffectController(child,delay: 2);
Effect effect = MoveByEffect(
Vector2(0, -100),
ctrl,
);
player.add(effect);

2. 重复控制器:Repeated 和 无限控制器 Infinite

顾名思义,RepeatedEffectController 可以让子效果的动画重复执行 repeatCount 次。

上一节介绍了几个震荡型变化的效果,可以结合 RepeatedEffectController 进行重复震荡。这样就可以完成震动、闪烁等特效,比如下面通过重复执行 5 次 SineEffectController ,实现简单的左右震动:

1
2
3
4
5
6
7
8
9
dart复制代码void repeatedEffectController(){
EffectController child = SineEffectController(period: 0.1);
EffectController ctrl2 = RepeatedEffectController(child,5);
Effect effect = MoveByEffect(
Vector2(-2, 0),
ctrl2,
);
player.add(effect);
}

InfiniteEffectController 和 RepeatedEffectController 类似,都可以让子效果 重复运行 。只不过 Infinite 表示重复次数无限,Repeated 可以指定重复次数。比如上面的抖动如果使用 InfiniteEffectController ,就会一直不停抖动。

1
2
3
4
5
6
7
8
9
dart复制代码void infiniteEffectController(){
EffectController child = SineEffectController(period: 0.1);
EffectController ctrl2 = InfiniteEffectController(child);
Effect effect = MoveByEffect(
Vector2(-2, 0),
ctrl2,
);
player.add(effect);
}

这时可能会有人疑问,那如何停止呢?Effect 本身是附在加角色之上的 构建 ,只要移除 Effect ,特效就会消失。这样可插拔的设计,正是特效的优势,比如下面代码可以移除 player 身上的所有特效:

1
dart复制代码player.removeAll(player.children.whereType<Effect>());

3.随机效果控制器: RandomEffectController

这里的随机值得是 时长随机 ,其中的 child 子效果必须是 DurationEffectController 一族的。而且,使用 RandomEffectController 后,子级设置的时长会被无效。


处理普通构造方法外,RandomEffectController 有两个 factory 构造,其中 uniform 使用的是 _UniformRandomVariable 随机数构造器。它是用于是生成在 min ~ max 之间的随机数:

通过源码很容易看出 _UniformRandomVariable 生成随机数的方式:


另外一个是 exponential 构造,随机数生成器是 _ExponentialRandomVariable ,其中传入一个 double 类型的 beta 小数:

源码中可以看出,其对随机数处理的逻辑。其中 log 函数是以 e 为底 。 1-_random.nextDouble() 小于 1 ,log(x) 的定义域取值范围是 0~1 ,值域是 -无穷 ~ 0 。这里是 -logx * beta ,所以取值范围是 0 ~ 无穷 , beta 起到放大效果。

另外吐槽一下,这里命名使用的是 exponential (指数) ,但从逻辑来看这里使用的是 logarithm (对数) ,感觉不怎么严谨。

另外如果有需要,我们也可以自定义 RandomVariable 实现特定的随机数生成器。


4. 速度效果控制器: SpeedEffectController

同样,SpeedEffectController中的 child 子效果必须是 DurationEffectController 一族,且子级设置的时长会被无效。构造时需要传入 double 型的 speed 参数,表示 每秒的变化量 。

如下案例,向上移动 100 ,速度是 10 ,就表示 10 s 移动到目的地。仔细思考一下,这个特效对于子弹来说是比较有用的,可以结合 Curve 和 沿曲线运动 来实现一些更好玩的子弹。

1
2
3
4
5
6
7
8
9
dart复制代码void speedEffectController(){
DurationEffectController child = LinearEffectController(2);
EffectController ctrl = SpeedEffectController(child,speed: 10);
Effect effect = MoveByEffect(
Vector2(0, -100),
ctrl,
);
player.add(effect);
}

5. 控制器序列:SequenceEffectController

SequenceEffectController 中可以传入控制器列表,注意这个序列是顺序的序列,不是各个控制器数值进行叠加。也就是说,会这些控制器会 依次 执行动画。


比如下面是 SequenceEffectController 中放入了三个控制器,角色会依次执行各个控制器的动画:

1
2
3
4
5
6
7
8
9
10
11
12
13
dart复制代码void sequenceEffectController(){
DurationEffectController child1 = LinearEffectController(2);
EffectController child2 = ZigzagEffectController(period: 2);
EffectController child3 = CurvedEffectController(2,Curves.ease);
EffectController ctrl = SequenceEffectController([
child1,child2,child3
]);
Effect effect = MoveByEffect(
Vector2(0, -100),
ctrl,
);
player.add(effect);
}

6. EffectController 构造

虽然 EffectController 是抽象类,但它有一个 factory 构造,集成了若干参数。仔细看一下就能发现,这些其实都是前面介绍的各个实现类中需要的属性,也就是说 EffectController 通过了一个大杂烩的构造,来减缓控制器的使用。


瞄一下源码就可以看出,本质上就是利用这些参数来实例化实现类进行返回而已。前面的知识理解后, EffectController 的构造方法可以说是迎刃而解。


到这里,通过四篇内容,介绍完了所以的 Effect 和所有的 EffectController 。这里只是针对每个实现类举个小例子,实际中,我们可以结合若干个效果或者控制器来实现一些组合特效。甚至去封装一些特定场景下的特效,以便复用。那本文就到这里,明天见 ~

  • @张风捷特烈 2022.06.15 未允禁转
  • 我的 公众号: 编程之王
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【若川视野 x 源码共读】第34期 tdesign-vu

发表于 2022-06-14

源码共读前言

为了能帮助到更多对源码感兴趣、想学会看源码、提升自己写作和前端技术能力的同学。 帮助读者夯实基础,查漏补缺,开阔眼界,拓宽视野,知其然知其所以然。

我倾力组织了每周一起学200行左右的源码共读活动。我写有《学习源码整体架构系列》20余篇。

欢迎点此扫码加我微信 ruochuan02 交流,参与 源码共读 活动,每周大家一起学习200行左右的源码,共同进步。可以持续关注我@若川。

从易到难推荐学习顺序

活动介绍和顺序具体看这里从易到难推荐学习顺序

提交笔记

提交笔记方式
简言之:看任务,看辅助文章、看源码,交流讨论,在掘金写笔记,写好后提交到本文评论区。

为了给大家谋福利,另外给大家的文章带来更多阅读量,便于搜索,从2022年3月27日起,笔记可以直接发布在掘金,以《标题自取》标题不限,可以取个好标题,容易被掘金推荐。

笔记文章开头加两句话:

  • 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
  • 这是源码共读的第xx期,链接:xxx。

笔记文章最后,建议写上总结、收获、感受等。

  • 开头第一句作用是:方便每月统计评优,掘金赞助送小礼物。顺便帮忙宣传推广,让更多人参与进来,一起学习。
  • 开头第二句作用是:加上是多少期,当前任务说明的链接,方便读者知道这是什么活动。

笔记写完后,到当前期活动的文章评论区留言自己的文章和笔记特点。方便大家查阅学习交流讨论。

往期所有笔记存放在语雀讨论区。

任务发布时间

6月13日 - 6月26日。可以按照自己节奏学习,提交笔记即可(不一定要严格按照我规定的时间)。往期共读也可以及时复习,笔记未完成可以继续完成。

语雀本期任务说明链接

语雀有树形菜单,更方便查看,所以也放下语雀的这一期链接

学习任务

  • github仓库地址 github.com/Tencent/tde…
  • github1s: github1s.com/Tencent/tde…
  • 克隆源码
1
2
3
4
5
bash复制代码git clone --recurse-submodules https://github.com/Tencent/tdesign-vue.git

cd tdesign-vue
# 开发预览
npm i # 可以用 yarn install yarn 相对更快
  • 主要学习这两个命令。可以查看贡献文档。
  • 新增组件: npm run init [组件名]
  • 删除组件:npm run init [组件名] del
  • 根据大家问卷反馈情况,多设置一些相对简单的,先让大家参与进来,让大家觉得源码也不难。
  • 最后大家没填问卷的,有空抽几分钟来填下源码共读活动问卷~你们的反馈至关重要wj.qq.com/s2/9304505/…
  • 如果出现克隆时没有权限问题,可以修改 根目录的 .gitmodules 的 url 为 https 的
1
2
3
bash复制代码[submodule "src/_common"]
path = src/_common
url = https://github.com/Tencent/tdesign-common.git

参考文章

和我这篇文章类似

每次新增页面复制粘贴?100多行源码的 element-ui 新增组件功能告诉你减少重复工作

  • 看文章,看源码,交流讨论,写笔记发布在掘金/语雀。再在这篇文章下评论放上提交笔记的链接。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Flutter&Flame游戏 - 拾玖】构件特效 了

发表于 2022-06-14

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 20 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
  • 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
  • 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading
  • 【Flutter&Flame 游戏 - 贰陆】pinball 源码分析 - 游戏主菜单界面
  • 【Flutter&Flame 游戏 - 贰柒】pinball 源码分析 - 角色选择与玩法面板
  • 【Flutter&Flame 游戏 - 贰捌】pinball 源码分析 - 游戏主场景的构成
  • 【Flutter&Flame 游戏 - 贰玖】pinball 源码分析 - 视口与相机

第一季完结,谢谢支持 ~


1. EffectController 及其衍生类

在 Effect 类中,持有一个 EffectController 对象,并在构造方法中被初始化。也就是说,任何 Effect 在构造时,都必须指定 EffectController 对象。


如下是 EffectController 类的定义,首先它是一个抽象类,其中维护了一些动画相关的 get 属性,包括动画时长 duration 、进度 progress 、动画是否已开启 started 、 动画是否已结束 completed 等。以及四个应用控制动画的抽象方法:


它作为抽象类,是无法直接作为运行时对象被实例化的,必定有其实现类对功能进行具体实现。在 effects/controllers 包中,有着很多不同功能的 EffectController 。


EffectController 衍生类目前有如下 14 个,主要分为两种:DurationEffectController 一族和直接继承自 EffectController 的子类。这些控制器本质上和 Flutter 原生的 AnimationController 是类似的,都是对数值的进行变换处理,从而让动画的变化效果。因为 Flame 在一个持续运转的 Ticker 中,所以 EffectController 相比于原生而言是更加简单的,不需要考虑 Ticker 的创建、销毁等处理。

根据名称也基本上能猜个大概功能。比如 DelayedEffectController 可以让动画延时播放、InfiniteEffectController 可以让动画无限时长、RepeatedEffectController 可以让动画重复播放、CurvedEffectController 可以支持变化曲线等。


2、线性效果控制器: LinearEffectController

如果看过 《Flutter 动画探索 - 流光幻影》,那么你对动画控制器的插值一定不会陌生。我们都知道 Ticker 每次触发回调的时间间隔基本恒定,其中每次触发被称为 1 帧 。如果某个量的属性在每一帧中都增加固定的值,在连续变化的过程中,该属性就会呈现一个 线性的 变化效果:

比如 LinearEffectController 中,每帧的 progress 属性是 运行时长 除以 总时长 ,也就是说该进度值的变化是线性的。

如下,使用 LinearEffectController 让角色在 2s 内向上平移 100 ,可见运动的位移变化是均匀的。

1
2
3
4
5
6
7
8
dart复制代码void linearEffectController(){
EffectController ctrl = LinearEffectController(2);
Effect effect = MoveByEffect(
Vector2(0, -100),
ctrl,
);
player.add(effect);
}

3. 曲线效果控制器:CurvedEffectController

曲线指的是在每次更新时,对属性值的变化量附加的变换效果。比如下面是附件 Curves.ease 的曲线变换,如果把属性值对应到小球的高度只,就可以通过该曲线变换实现开始运动快,后面逐渐减慢的效果。

如下, CurvedEffectController 中可以指定一个 Curve 对象,用于转换。这样角色在进行位移动画时就可以有速度变换的效果:

1
2
3
4
5
6
7
8
dart复制代码void curvedEffectController(){
EffectController ctrl = CurvedEffectController(2,Curves.ease);
Effect effect = MoveByEffect(
Vector2(0, -100),
ctrl,
);
player.add(effect);
}

简单瞄一下源码,可以看出 CurvedEffectController 中做的事,就是通过 Curve 对象的 transform 方法,对进度进行变换而已:

而 Curves.ease 是一个静态的 Cubic 对象,它的 transform 方法,就是按照三次贝塞尔曲线方程,对入参进行变换。也就是说,你可以通过 Cubic 对象,自定义任何的三次贝塞尔曲线,让速度按照该曲线进行。其中四个入参分别代表三次贝塞尔曲线的两个控制点坐标,另外起始坐标为 (0,0) ,终止坐标为 (1,1) 。

1
dart复制代码static const Cubic ease = Cubic(0.25, 0.1, 0.25, 1.0);


4. 反向控制器:ReverseLinearEffectController、ReverseCurvedEffectController

这两个看着名字老长的,本质上就是进度值被 1 减下而已,也就是说,默认一开始是 100% ,然后逐渐变为 0 ,即反方动画。如下图所示,如果是 ReverseLinearEffectController ,动画开启时,角色将从 (0,-100) 点向下匀速运动:

1
dart复制代码EffectController ctrl = ReverseLinearEffectController(2);

对于 ReverseCurvedEffectController 来说也一样, Curve 对象的 transform 方法转换的对象,也是被 1 减的进度。对应于图像来上,相当于反走曲线,所以对于 Curves.ease 来说,会产生先慢后快的效果。

1
dart复制代码EffectController ctrl = ReverseCurvedEffectController(2,Curves.ease);

5. 正弦曲线控制器: SineEffectController

这个也非常好理解, Cubic 是通过三次贝塞尔曲线,对数值进行变换;那这里就是通过正弦函数对数值进行变换。这点在源码中很容易被证实,可以看出在 period 时长下,属性值会完成 0~2pi 的正弦函数变换,效果如下图:

1
dart复制代码EffectController ctrl = SineEffectController(period: 2);

这有个不错的应用场景,可以结合 RepeatedEffectController 重复震荡,来达到抖动的效果,这在下一篇介绍 RepeatedEffectController 时再细说,效果如下所示:


6.噪音曲线控制器: NoiseEffectController

这里噪音曲线指的是在 0 附近随机震荡的的点,源码中通过 SimplexNoise 对象进行转换。这个比较适合做一些随机震动的场景,比如抖动:

1
dart复制代码EffectController ctrl = NoiseEffectController(frequency: 30,duration: 1);

7.锯齿形曲线控制器:ZigzagEffectController

这个控制器的变化是 0->1->0->-1->0,数值随时间的变化呈 锯齿形 ,如下图所示:

对应到角色的位移上,就是均匀的震荡,如下图所示:

1
dart复制代码EffectController ctrl = ZigzagEffectController(period: 2);

最后 PauseEffectController 的进度数值不会随时间进行任何变换,也就是说这个动画效果是不会动的效果。感觉使用场景非常优先,本身也非常简单,了解一下即可:


通过本文,我们认识了 8 个 DurationEffectController 一族的效果控制器。总的来看,这一族非常简单,就是利用不同的函数对进度数值进行转换而已,本身没有什么难点。通过这些控制器,就可以让动画不那么 “死板” ,可以让运动的速度产生变化,以及进行周期性的震荡。下一篇将继续通过小案例介绍其他的控制器,那本文就到这里,明天见 ~

  • @张风捷特烈 2022.06.14 未允禁转
  • 我的 公众号: 编程之王
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Flutter&Flame游戏 - 拾捌】构件特效 C

发表于 2022-06-13

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 19 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
  • 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
  • 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading
  • 【Flutter&Flame 游戏 - 贰陆】pinball 源码分析 - 游戏主菜单界面
  • 【Flutter&Flame 游戏 - 贰柒】pinball 源码分析 - 角色选择与玩法面板
  • 【Flutter&Flame 游戏 - 贰捌】pinball 源码分析 - 游戏主场景的构成
  • 【Flutter&Flame 游戏 - 贰玖】pinball 源码分析 - 视口与相机

第一季完结,谢谢支持 ~


1. ComponentEffect 一族特效的特点

上一节介绍了 移动 、旋转 、缩放 、移除 的特效,它们都是 Effect 的直接子类,施加一些变换效果。本文来看一下 ComponentEffect 一族,它们如何使用、有什么特点。


如下是 ComponentEffect 抽象类的源码实现,从中可以很清楚地看出 ComponentEffect 的特点。它可以支持一个 Component 类型的泛型,并且持有该类型的成员 target 。这个 target 对象使用 late 声明,会在 onMount 方法中被赋值。

从红框中的逻辑可以看出,会寻找上级第一个非 Effect 的构件,且该构件的类型是指定的 T 类型。也就是找到宿主构件为 target 成员赋值,注意如果宿主类型和指定的 T 类型会抛异常。


2. 尺寸特效:SizeEffect

同样 SizeEffect 也有 by 和 to 两个构造由于表示尺寸增加了多少,和尺寸变化到多少。如下,为了更方便演示操作,在左侧给出相关按键的效果信息,图中的是按下 8 和 9 的效果,更改宿主的尺寸。

在使用方式上合前面也是一样的,给出变化数据和控制器即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dart复制代码void addSizeEffectBy(){
Effect effect = SizeEffect.by(
Vector2(5,5*(37/50)),
EffectController(duration: 0.5),
);
player.add(effect);
}

void addSizeEffectTo(){
Effect effect = SizeEffect.to(
Vector2(50, 37),
EffectController(duration: 0.5),
);
player.add(effect);
}

这里可能会有人感觉 SizeEffect 似乎和 ScaleEffect 很像,都能实现把构件变大和变小。其实本质上还是有很大区别的。ScaleEffect 是变换,针对整体的图形变换,构件是被整体缩放的。可以用下面 ScaleEffect 效果对比一下,可以看出变换 ScaleEffect 也会波及其子构件。


而 SizeEffect 只是针对角色的 size 进行更改,不涉及变换,如下所示:


从 SizeEffect 的源码也能清楚地看出,本质是对 target 成员,也就是宿主构件的 size 属性进行变化:


3. 透明度效果: OpacityEffect

同样 OpacityEffect 也有 by 和 to 两个构造由于表示透明度增加了多少,和透明度变化到多少,注意透明度在 0~1 之间。下图中的是按下 q 和 w 的效果,更改宿主的透明度,其中 q 按键使用 by 每次增加 -0.1 透明度,w 按键将透明度变化到 1 。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dart复制代码 void addOpacityEffectBy(){
Effect effect = OpacityEffect.by(
-0.1,
EffectController(duration: 0.5),
);
player.add(effect);
}

void addOpacityEffectTo(){
Effect effect = OpacityEffect.to(
1,
EffectController(duration: 0.5),
);
player.add(effect);
}

另外 OpacityEffect 中还有 fadeIn 和 fadeOut 两个构造方法,本质上是基于 _OpacityToEffect 实现的。fadeIn 表示渐变到 1 而淡入,fadeOut 表示渐变到 0 而淡出。只是一个语义化的封装,没有什么特别之处,了解一下即可。


4. 颜色效果: ColorEffect

该效果可以为构件施加颜色滤镜,比如下面所示,在 1.5 s 之内,用蓝色颜色滤镜逐渐加到 40% 。这作为一个简单的冰冻效果还是不错的。

1
2
3
4
5
6
7
8
dart复制代码void addColorEffect(){
final effect = ColorEffect(
Colors.blue,
const Offset(0.0, 0.4),
EffectController(duration: 1),
);
player.add(effect);
}

什么的 Offset 对象表示颜色滤镜的程度变化范围,其中 0 表示不施加;1 表示把构建颜色完全变为该颜色。比如下图是取 1 的情况。


5. 沿路径运动: MoveAlongPathEffect

彩虹岛,是我童年的珍贵回忆,不知道大家有没有玩过。其中的小人可以发射一个彩虹桥,然后在弧线上走。这是一个 沿路径运动 的好场景。


如下,指定一条简单的二次贝塞尔曲线,通过添加 MoveAlongPathEffect 就可以让构件沿路径运动。另外关于 Path 对象,是绘制中一个非常重要的话题,在 《Flutter 绘制指南 - 妙笔生花》 小册中有详细的介绍,感兴趣的可以学习一下。

1
2
3
4
5
6
7
8
9
dart复制代码Path path = Path()..quadraticBezierTo(50, -50, 100, 0);

void addMoveAlongPathEffect() {
final Effect effect = MoveAlongPathEffect(
path,
EffectController(duration: 1.5),
);
player.add(effect);
}

简单瞄一眼 MoveAlongPathEffect 的源码,可以看出本质上还是通过 PathMetric 进行计算的,所以万变不离其宗。渲染的本质总是和绘制相关,路径、色彩、图像、变换的基本知识在哪里都是通用的。


本文介绍了一下 ComponentEffect 一族的特效,该族特点是会持有一个 Componemt 类型的 target 对象,在 onMount 中初始化。各个特效的实现类,本质上就是对 target 构建属性的补间变化,产生动画效果。下一篇,我们将认识一下 EffectController ,如何对动画特效进行控制。那么本文就到这里,明天见 ~

  • @张风捷特烈 2022.06.13 未允禁转
  • 我的 公众号: 编程之王
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Flutter&Flame游戏 - 拾柒】构件特效 了

发表于 2022-06-12

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 18 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
  • 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
  • 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading
  • 【Flutter&Flame 游戏 - 贰陆】pinball 源码分析 - 游戏主菜单界面
  • 【Flutter&Flame 游戏 - 贰柒】pinball 源码分析 - 角色选择与玩法面板
  • 【Flutter&Flame 游戏 - 贰捌】pinball 源码分析 - 游戏主场景的构成
  • 【Flutter&Flame 游戏 - 贰玖】pinball 源码分析 - 视口与相机

第一季完结,谢谢支持 ~


1. Effect 是什么

比如马里奥吃到蘑菇变大动画;僵尸被寒冰射手击中,整体颜色变成偏蓝色;当主角被击中时闪烁几下,或怪物被消灭时透明渐变消失。这些视觉上的效果处理,会增加游戏的动感,刚好地和用户交互。当你对一个构件进行移动、缩放、旋转、透明度、尺寸、颜色进行换行时,就可以考虑添加 Effect 。

本质上来说 Effect 是一个 Component ,它往往是被附加在其他构件上,实现功能。也就是说,一般并不单独展示 Effect 构件。另外,Effect 是一个抽象类,不能直接使用,Flame 中提供的 Effect 实现类在 effects 包下,如下所示:


Flame 提供的 Effect 有如下几个,主要分为两类:第一种是进行基本变换操作,及:移动 、旋转、缩放 等;另一种是 ComponentEffect ,为构件添加颜色、透明度、尺寸等变换特效。下面来结合粒子介绍一下。


2. 移动特效:MoveEffect

由 MoveEffect 支持移动特效,不过它是一个抽象类,不能作为运行时使用。它有两个子类:MoveByEffect 和 MoveToEffect ,分别应用 移动了 和 移动到 某位移。之前点击屏幕,让角色移动到指定位置,用的就是 MoveToEffect 。 下面通过两个一个小例子来介绍它们的使用方式和区别:本文代码均在 【18/01】

当按下数字 1 键时让角色 0.5 秒位移 Vector2(10, -10) 。实现创建 MoveByEffect 对象,设置偏移量和控制器,然后附加在角色构建上面即可。代码如下:

1
2
3
4
5
6
7
dart复制代码void addMoveByEffect(){
Effect effect = MoveByEffect(
Vector2(10, -10),
EffectController(duration: 0.5),
);
player.add(effect);
}

当按下数字 2 键时让角色 0.5 秒移动到屏幕中心。实现创建 MoveToEffect 对象,设置目标点和控制器,然后附加在角色构建上面即可。代码如下:

1
2
3
4
5
6
7
dart复制代码void addMoveToEffect(){
Effect effect = MoveToEffect(
size / 2,
EffectController(duration: 0.5),
);
player.add(effect);
}

另外注意一个小细节,MoveEffect 虽然是抽象类,但定义了 by 和 to 的工厂构造方法,来创建子类对象。本质上和直接使用 MoveByEffect 和 MoveToEffect 没有区别,可以根据自己的喜好来用。我更倾向于用 MoveEffect 的命名构造,感觉语义上好一些。


3. 旋转特效 :RotateEffect

从源码中来看 RotateEffect 也有 by 和 to 两个构造,含义上和移动类似。 by 表示旋转了多少度,to 表示旋转到多少度。


下面通过按下数字键 3 让角色旋转 15° ,通过下面的 addRotateEffectBy 实现;按下数字键 4 让角色旋转到 0° , 通过下面的 addRotateEffectTo 实现。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dart复制代码void addRotateEffectBy(){
Effect effect = RotateEffect.by(
15/180*pi,
EffectController(duration: 0.5),
);
player.add(effect);
}

void addRotateEffectTo(){
Effect effect = RotateEffect.to(
0,
EffectController(duration: 0.5),
);
player.add(effect);
}

4. 缩放特效:ScaleEffect

同样 ScaleEffect 也有 by 和 to 两个构造,和前面的也类似。 by 表示缩放了多少,to 表示缩放到多少。


下面通过按下数字键 5 让角色缩放 Vector2(1.2,1.2) ,通过下面的 addScaleEffectBy 实现;按下数字键 6 让角色旋转到 0° , 通过下面的 addScaleEffectTo 实现。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dart复制代码void addScaleEffectBy(){
Effect effect = ScaleEffect.by(
Vector2(1.2,1.2),
EffectController(duration: 0.5),
);
player.add(effect);
}

void addScaleEffectTo(){
Effect effect = ScaleEffect.to(
Vector2(1,1),
EffectController(duration: 0.5),
);
player.add(effect);
}

5. 移除特效: 移除特效

从 RemoveEffect 的源码中可以看出,这个特效非常简单,甚至连控制器都不需要指定。RemoveEffect 的作用是在 delay 秒后,移除附加的构件。如果时长为 0 ,那这和构件直接触发 removeFromParent 没有什么太大的区别。

如下测试中,点击数字键 7 ,显示边框信息示意,然后在 3 秒后消失。代码如下:

1
2
3
4
5
6
dart复制代码void addRemoveEffect(){
Effect effect = RemoveEffect(
delay:3
);
player.add(effect);
}

6. Effect 的结束回调事件

在 【第七篇】 人随指动的一文中,使用到了 MoveEffect ,那时有朋友问了一下 Effect 是否有执行完成的回调。这里来解答一下:


其实瞄一眼 Effect 的源码就很容易看到,有个 onFinishCallback 的方法可以监听结束回调:

使用方式也非常简单,直接设置 onFinishCallback 回调即可:

1
2
3
4
5
6
7
8
9
10
dart复制代码void addMoveByEffect(){
Effect effect = MoveByEffect(
Vector2(10, -10),
EffectController(duration: 0.5),
);
effect.onFinishCallback =(){
print('=====onFinishCallback========');
};
player.add(effect);
}

另外,注意一点:Effect 中有一个 removeOnFinish 方法,默认为 true ,就表示在特效完成时,会主动从宿主身上移除。如果不想移除可以把该属性设为 false ,可以触发 Effect#reset 方法来让特效在此执行。


本文介绍了 Effect 的体系和基本的用法,写了几个小例子认识变换相关的特效。下一篇我们将继续认识一下 ComponentEffect 系列的特效,特效好有个非常重要的 EffectController 概念,在后面也会逐步介绍。那本文就到这里,明天见 ~

  • @张风捷特烈 2022.06.12 未允禁转
  • 我的 公众号: 编程之王
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Flutter&Flame游戏 - 拾陆】粒子系统 粒

发表于 2022-06-11

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 17 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
  • 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
  • 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading
  • 【Flutter&Flame 游戏 - 贰陆】pinball 源码分析 - 游戏主菜单界面
  • 【Flutter&Flame 游戏 - 贰柒】pinball 源码分析 - 角色选择与玩法面板
  • 【Flutter&Flame 游戏 - 贰捌】pinball 源码分析 - 游戏主场景的构成
  • 【Flutter&Flame 游戏 - 贰玖】pinball 源码分析 - 视口与相机

第一季完结,谢谢支持 ~


1. 粒子种类总览

在上一篇中,我们知道了如何使用 ParticleSystemComponent 构建显示粒子。这篇文章将详细介绍一下 Flame 中内置的粒子种类。Particle 抽象类共有如下十几种实现类:

其中 SingleChildParticle 一族在构造时需要指定一个 Particle ,主要是为指定粒子进行动画变换,比如 移动 、旋转 、缩放 等。

另外,我们可以指定 Sprite 、 SpriteAnimation 、Image 等图片资源,作为粒子。也可以使用 ComponentParticle 将一个构建作为粒子,下面通过粒子来详细认识一下。


2. 通过粒子实现点击时序列帧

在 第七篇 中,我们实现了如下的点触序列帧播放,那时是通过自己维护 SpriteAnimationComponent 完成的。它在生成后,会在一定时间内消失,而且拖动时产生大量的单体,很适合通过粒子来处理。

如下,通过 SpriteAnimationParticle 携带 SpriteAnimation ,可以实现相同的效果。默认情况下 SpriteAnimationParticle 的生命时长和动画一致,也就是序列帧播放完毕就消失。代码详见 【16/01】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dart复制代码void createIndicator(Vector2 position) {
SpriteAnimation indicator = SpriteAnimation.spriteList(
sprites,
stepTime: 1 / 15,
loop: false,
);

// 创建 Particle 对象
Particle particle = SpriteAnimationParticle(
animation: indicator,
size: Vector2(30, 30),
);
// 创建 ParticleSystemComponent 构件
final ParticleSystemComponent psc = ParticleSystemComponent(
particle: particle,
position: position,
anchor: Anchor.center,
);
// 添加 ParticleSystemComponent 构件
add(psc);
}

3. 随机加速粒子

下面来看一下 AcceleratedParticle ,它可以指定速度和加速度让粒子进行运动。比如下面,在点击时,随机产生 第一象限 的 Verctor2 作为速度和加速度。这样就会产生在第一象限产生随机的加速粒子。代码详见 【16/02】

注 : Y 轴正方向向下,所以视右下方位第一象限。

Vector2 的 random 方法,可以生成 (0, 0) ~ (1, 1) 的随机点位,如下的 randomVector 的作用是生成 (0, 0) ~ (300, 300) 的随机点位:

1
2
3
4
5
6
dart复制代码final Random _random = Random();

Vector2 randomVector() {
Vector2 base = Vector2.random(_random);
return base * 300;
}

如下 createParticle 方法中,通过 Particle.generate 一次生成 10 个加速粒子,并且速度和加速度随机:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dart复制代码void createParticle(Vector2 position) {
CircleParticle circleParticle = CircleParticle(
radius: 1,
paint: Paint()..color = Colors.white,
);

Particle particle = Particle.generate(
count: 10,
lifespan: 1,
generator: (i) =>
AcceleratedParticle(
child: circleParticle,
acceleration: randomVector(),
speed: randomVector(),
position: position),
);

final ParticleSystemComponent psc =
ParticleSystemComponent(particle: particle);
add(psc);
}

4. 控制随机粒子的方向

虽然粒子是随机的,但是我们也可以进行控制。比如如何让粒子四散,也就是速度和加速度的矢量可以随机在四个象限中出现。另外加了一点小优化,粒子颜色每次都随机生成:【16/03】

其实这是一道简单的数学题, base 在 (0, 0) ~ (1, 1) ,表示只能在第一象限取值。如下左图,浅蓝色是随机点位取值范围;其实只要简单地对取值范围平移一下,就可以取 (-0.5, -0.5) ~ (0.5, 0.5) ,也就是取值范围可以包含四个象限的坐标。

1
2
3
4
5
6
dart复制代码Vector2 randomVector() {
Vector2 base = Vector2.random(_random); // (0, 0) ~ (1, 1)
Vector2 fix = Vector2(-0.5,-0.5);
base = base + fix; // (-0.5, -0.5) ~ (0.5, 0.5)
return base * 200;
}

如果只想生成 X 轴下方的矢量,向左偏移 0.5 即可:

1
2
3
4
dart复制代码Vector2 base = Vector2.random(_random); //  (0, 0) ~ (1, 1)
Vector2 fix = Vector2(-0.5,0);
base = base + fix; // (-0.5, 0) ~ (0.5, 1)
return base * 200;

SingleChildParticle 一族的使用方式非常类似,就是给一个例子添加变换的效果,这里就不一一介绍了。


5. 为主角子弹添加爆炸粒子

粒子可以用在子弹消失时,产生爆炸效果。比如下面所示,可以在主角子弹打到怪物身上消失后,产生上面的爆炸粒子的效果:代码详见 【16/04】

代码实现很简单,如下 tag1 处,是主角子弹碰到怪物时的场景。通过 createParticle 创建粒子即可:

1
2
3
4
5
6
7
8
9
10
11
dart复制代码@override
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
super.onCollision(intersectionPoints, other);
if (type == BulletType.hero && other is Monster) {
removeFromParent();
createParticle(position+Vector2(size.x/2,0)); // tag1
}
if (type == BulletType.monster && other is HeroComponent) {
removeFromParent();
}
}

6. 其他类型粒子

还有几个粒子源码比较简单,就直接看下它们的源码实现。ComponentParticle 中必须传入一个 Component ,前面说过 Particle 最核心的就是对绘制的实现。如下,在 render 方法中,直接使用 Component 的 render 方法:


ComputedParticle 源码更简单,它将 Canvas 作为 ParticleRenderDelegate 的回调参数,暴露给使用者,在 render 方法中触发回调。也就是说 ComputedParticle 可以通过 renderer 方法把绘制权交给使用者。


ComposedParticle 中必须传入 Particle 列表,在 render 、update 中遍历子粒子列表,执行绘制和更新。

可以看出,这三者的源码都非常简单,都是借用它物来完成自身的功能,相当于一层包裹,简化使用。


ImageParticle 也非常简单,就是绘制 ui 包中的 Image 对象而已:


总的来看 Particle 就是一个 单体 ,它的核心是绘制方法。到这里,我们也对粒子系统有了较深的理解。粒子相对于构建来说,感觉更加轻量。比如一个 ParticleSystemComponent 可以渲染 100 个粒子,这对比与:把 100 个粒子都作为 Component 加入场景中,高低立判。那本文就到这里,明天见 ~

  • @张风捷特烈 2022.06.11 未允禁转
  • 我的 公众号: 编程之王
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Flutter&Flame游戏 - 拾伍】粒子系统 P

发表于 2022-06-10

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 16 天,点击查看活动详情


前言

这是一套 张风捷特烈 出品的 Flutter&Flame 系列教程,发布于掘金社区。如果你在其他平台看到本文,可以根据对于链接移步到掘金中查看。因为文章可能会更新、修正,一切以掘金文章版本为准。本系列源码于 【toly_game】 ,如果本系列对你有所帮助,希望点赞支持,本系列文章一览:

  • 【Flutter&Flame 游戏 - 壹】开启新世界的大门
  • 【Flutter&Flame 游戏 - 贰】操纵杆与角色移动
  • 【Flutter&Flame 游戏 - 叁】键盘事件与手势操作
  • 【Flutter&Flame 游戏 - 肆】精灵图片加载方式
  • 【Flutter&Flame 游戏 - 伍】Canvas 参上 | 角色的血条
  • 【Flutter&Flame 游戏 - 陆】暴击 Dash | 文字构件的使用
  • 【Flutter&Flame 游戏 - 柒】人随指动 | 动画点触与移动
  • 【Flutter&Flame 游戏 - 捌】装弹完毕 | 角色武器发射
  • 【Flutter&Flame 游戏 - 玖】探索构件 | Component 是什么
  • 【Flutter&Flame 游戏 - 拾】探索构件 | Component 生命周期回调
  • 【Flutter&Flame 游戏 - 拾壹】探索构件 | Component 使用细节
  • 【Flutter&Flame 游戏 - 拾贰】探索构件 | 角色管理
  • 【Flutter&Flame 游戏 - 拾叁】碰撞检测 | CollisionCallbacks
  • 【Flutter&Flame 游戏 - 拾肆】碰撞检测 | 之前代码优化
  • 【Flutter&Flame 游戏 - 拾伍】粒子系统 | ParticleSystemComponent
  • 【Flutter&Flame 游戏 - 拾陆】粒子系统 | 粒子的种类
  • 【Flutter&Flame 游戏 - 拾柒】构件特效 | 了解 Effect 体系
  • 【Flutter&Flame 游戏 - 拾捌】构件特效 | ComponentEffect 一族
  • 【Flutter&Flame 游戏 - 拾玖】构件特效 | 了解 EffectController 体系
  • 【Flutter&Flame 游戏 - 贰拾】构件特效 | 其他 EffectControler
  • 【Flutter&Flame 游戏 - 贰壹】视差组件 | ParallaxComponent
  • 【Flutter&Flame 游戏 - 贰贰】菜单、字体和浮层
  • 【Flutter&Flame 游戏 - 贰叁】 资源管理与国际化
  • 【Flutter&Flame 游戏 - 贰肆】pinball 源码分析 - 项目结构介绍
  • 【Flutter&Flame 游戏 - 贰伍】pinball 源码分析 - 资源加载与 Loading
  • 【Flutter&Flame 游戏 - 贰陆】pinball 源码分析 - 游戏主菜单界面
  • 【Flutter&Flame 游戏 - 贰柒】pinball 源码分析 - 角色选择与玩法面板
  • 【Flutter&Flame 游戏 - 贰捌】pinball 源码分析 - 游戏主场景的构成
  • 【Flutter&Flame 游戏 - 贰玖】pinball 源码分析 - 视口与相机

第一季完结,谢谢支持 ~


1. Flame 中的粒子系统

在 Flame 中,一切的呈现都依赖于构件,粒子系统也不例外。目前和粒子相关的有两个构件,其中 ParticleComponent 已经过时了,不推荐使用。所以 Flame 中的粒子系统主要使用的是 ParticleSystemComponent 构件。


首先说一下什么是粒子,举个观的例子:现实中烟花的爆炸,会生成大量细小的颗粒,维持短暂的时间后消失。像这样的大量的,有存在时间的显示物就可以通过粒子系统来呈现。Flame 的官方案例中有一些案例,但放在一块有些杂乱,这里将用 2 篇文章,由详细介绍一下 ParticleSystemComponent 的使用。

image-20220607121048917.png


2. 通过 ParticleSystemComponent 显示一个粒子

如下,使用 ParticleSystemComponent 构件,显示一个生命时长为 1 s 的圆形粒子:代码详见 【15/01】

使用 ParticleSystemComponent 有三个步骤:

  • 1. 创建 Particle 粒子对象
  • 2. 创建 ParticleSystemComponent 构件对象
  • 3. 将构建加入到树中,进行显示

其中 2、3 步骤非常简单,所以对于粒子系统而言,如何创建 Particle 对象是重中之重。这里显示一个圆形的粒子,可以使用 Particle 的衍生类 CircleParticle 。其中有三个入参:paint 表示粒子的绘制画板;radius 表示粒子的半径;lifespan 表示粒子的存在多少秒后消失。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dart复制代码void createParticle(){
// 创建 Particle 对象
Paint circlePaint = Paint()..color = Colors.white;
Particle particle = CircleParticle(
paint: circlePaint,
radius: 20,
lifespan: 1,
);
// 创建 ParticleSystemComponent 构件
final ParticleSystemComponent psc = ParticleSystemComponent(
particle: particle,
);
// 添加 ParticleSystemComponent 构件
add(psc);
}

3. 区分 ParticleSystemComponent 和 Particle

在进一步介绍粒子系统之前,有必要先认识一下 ParticleSystemComponent 和 Particle 两个类之间的区别与联系。从如下 ParticleSystemComponent 源码中可以看出,它是一个 PositionComponent ,构造时必须传入 Particle 对象。另外构造时还可以指定一些 PositionComponent 的大小、位置、锚点等属性。


而 Particle 本身只是抽象类,不是 Component 的衍生类,它有自己的衍生体系。

如下,在 Flame 的 particles 包中定义了很多实现类以供使用:


瞄一下 CircleParticle 的源码可以看出,其实现还是非常简单的,就是画个圈而已。如果有需要的话,我们也可以根据需求来自己定义 Particle 。核心就是在 render 方法了进行绘制,有了 Canvas ,就可画万物。


4. 移动粒子 MovingParticle

在 Particle 的衍生类中可以看到有很多支持传入 child 的例子,这些实现类往往可以基于某个粒子,是些特点的功能。比如 MovingParticle 可以让粒子进行运动,如下所示:可以让粒子在 lifespan 时间内,按照 curve 的变化曲线,从起点移动到终点。 【15/02】

1
2
3
4
5
6
7
8
9
10
11
12
dart复制代码// 创建 Particle 对象
Paint circlePaint = Paint()..color = Colors.white;
Particle particle = MovingParticle(
lifespan: 3,
curve: Curves.easeIn,
from: Vector2.zero(),
to: Vector2(200,0),
child: CircleParticle(
radius: 20.0,
paint: circlePaint,
),
);

5.批量生成粒子

Particle 有个 generate 的方法可以生成多个粒子,下面是 Flame 官方的一个小例子,感觉挺有趣,这里来看一下。代码详见 【15/03】

这里通过 Particle.generate 生成多个粒子,其中 count 表示粒子生成粒子的数量,generator 是根据索引生成 Particle 的函数;再使用 MovingParticle 实现粒子的移动;另外使用 PaintParticle 通过调节 paint 的 blendMode ,产生叠合效果。

其实这里不用 PaintParticle ,直接设置 CircleParticle#paint 的 blendMode 也可以得到相同的效果。可能是案例想多介绍一种 Particle 吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
dart复制代码final List<Color> colors = [
const Color(0xffff0000),
const Color(0xff00ff00),
const Color(0xff0000ff),
];

final List<Vector2> positions = [
Vector2(-10, 10),
Vector2(10, 10),
Vector2(0, -14),
];

// 创建 Particle 对象
Particle particle = Particle.generate(
count: 3,
lifespan: 3,
generator: (i) => PaintParticle(
paint: Paint()..blendMode = BlendMode.difference,
child:MovingParticle(
curve: Curves.easeIn,
from: positions[i],
to: i == 0 ? positions.last : positions[i - 1],
child: CircleParticle(
radius: 20.0,
paint: Paint()..color = colors[i],
),
),
));

本问简单介绍了一下 ParticleSystemComponent 的使用,并了解了 CircleParticle 、MovingParticle、PaintParticle 和 Particle.generate 的使用。下一篇我们将进一步认识 Particle 一族,并结合随机数来完成某些粒子效果。那本文就到这里,明天见 ~

  • @张风捷特烈 2022.06.10 未允禁转
  • 我的 公众号: 编程之王
  • 我的 掘金主页 : 张风捷特烈
  • 我的 B站主页 : 张风捷特烈
  • 我的 github 主页 : toly1994328

\

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…929394…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%