- 1:简介
- 2:内置动效
- 2.1:动效
- 2.2:MoveByEffect
- 2.3:MoveToEffect
- 2.4:MoveAlongPathEffect
- 2.5:RotateAroundEffect
- 2.6:RotateEffect.by
- 2.7:RotateEffect.to
- 2.8:ScaleEffect.by
- 2.9:ScaleEffect.to
- 2.10:SizeEffect.by
- 2.11:SizeEffect.to
- 2.12:AnchorByEffect
- 2.13:AnchorToEffect
- 2.14:OpacityToEffect
- 2.15:OpacityByEffect
- 2.16:GlowEffect
- 2.17:SequenceEffect
- 2.18:RemoveEffect
- 2.19:ColorEffect
- 2.20:FunctionEffect
动效是一种特殊的组件,可以附加到其他组件上,以修改其属性或外观。
例如,假设正在制作一款包含可收集强化道具的游戏。希望这些强化道具在地图上随机生成,并在一段时间后消失。显然,可以为强化道具制作一个精灵组件,然后将该组件放置在地图上。
简介
效果器的功能是使某个组件的属性随时间发生变化。为了实现这一点,效果器必须知道该属性的初始值、最终值以及它如何随时间变化。初始值通常由效果器自动确定,最终值由用户明确提供,而随时间变化则由 EffectControllers 处理。
Flame 提供了多种效果器,也可以创建自己的效果器。包含以下效果:
- MoveByEffect
- MoveToEffect
- MoveAlongPathEffect
- RotateAroundEffect
- RotateEffect.by
- RotateEffect.to
- ScaleEffect.by
- ScaleEffect.to
- SizeEffect.by
- SizeEffect.to
- AnchorByEffect
- AnchorToEffect
- OpacityToEffect
- OpacityByEffect
- ColorEffect
- SequenceEffect
- RemoveEffect
- FunctionEffect
EffectController 是一个描述效果如何随时间演变的对象。如果将效果的初始值视为 0% 进度,将最终值视为 100% 进度,那么效果控制器的作用就是将以秒为单位的“物理”时间映射到从 0 到 1 变化的“逻辑”时间。
Flame 框架还提供了多种效果控制器:
- EffectController 动效控制器
- LinearEffecController 线性效果控制器
- ReverseLinearEffectController 反向线性效果控制器
- CurvedEffectController 曲线效果控制器
- ReverseCurvedEffectController 反向曲线效果控制器
- PauseEffectController 暂停效果控制器
- RepeatedEffectController 重复效果控制器
- InfiniteEffectController 无限效果控制器
- SequenceEffectController 顺序效果控制器
- SpeedEffectController 速度效果控制器
- DelayedEffectController 延迟效果控制器
- NoiseEffectController 噪声效果控制器
- RandomEffectController 随机效果控制器
- SineEffectController 正弦效果控制器
- ZigzagEffectController 锯齿效果控制器
内置动效
动效
基础 Effect 类本身不可使用(它是抽象的),但它提供了一些其他效果继承的通用功能。这些功能包括:
- 使用 effect.pause() 和 effect.resume() 暂停/恢复效果的功能。您可以使用 effect.isPaused 检查效果当前是否处于暂停。
- removeOnFinish 属性(默认为 true)将导致效果组件在效果完成后从游戏树中移除并被垃圾回收。如果您计划在效果完成后重复使用,请将其设置为 false。
- 可选的用户提供的 onComplete 函数,将在效果执行完毕但尚未从游戏中移除时调用。
- Completed Future,在效果完成后完成。
- reset() 方法将效果恢复到原始状态,使其再次运行。
MoveByEffect
此动效应用于 PositionComponent,并将其按指定的偏移量移动。此偏移量相对于目标的当前位置:
final effect = MoveByEffect(
Vector2(0, -10),
EffectController(duration: 0.5),
);
多个移动效果可以同时应用于一个组件。其结果将是所有单独效果的叠加。
MoveToEffect
此效果将 PositionComponent 从其当前位置沿直线移动到指定的目标点:
final effect = MoveToEffect(
Vector2(100, 500),
EffectController(duration: 3),
);
MoveAlongPathEffect
此效果使 PositionComponent 沿指定路径相对于组件当前位置移动。路径可以包含非线性段,但必须单向连接。建议从 Vector2.zero() 开始路径,以避免组件位置突然跳跃。
final effect = MoveAlongPathEffect(
Path()..quadraticBezierTo(100, 0, 50, -50),
EffectController(duration: 1.5),
);
- absolute: true 表示效果中的路径为绝对路径。也就是说,目标会在开始时“跳转”到路径的起点,然后沿着该路径移动,就像在画布上绘制曲线一样。
-
oriented: true 表示目标不仅会沿着曲线移动,还会在每个点处沿着曲线的方向旋转。使用此标志,效果将同时具有移动和旋转效果。
RotateAroundEffect
将目标绕指定中心点顺时针旋转指定角度(相对于其当前方向)。该角度以弧度为单位。例如,以下效果将使目标绕 (100, 100) 顺时针旋转 90º(= tau/4 弧度)。
final effect = RotateAroundEffect(
tau/4,
center: Vector2(100, 100),
EffectController(duration: 2),
);
RotateEffect.by
将目标相对于其当前方向顺时针旋转指定角度。该角度以弧度为单位。例如,以下效果将使目标顺时针旋转 90º(= tau/4 弧度):
final effect = RotateEffect.by(
tau/4,
EffectController(duration: 2),
);
RotateEffect.to
将目标顺时针旋转至指定角度。例如,以下代码将使目标旋转至朝东(0º 为北,90º=tau/4 为东,180º=tau/2 为南,270º=tau*3/4 为西):
final effect = RotateEffect.to(
tau/4,
EffectController(duration: 2),
);
ScaleEffect.by
此效果将按指定量改变目标的比例。例如,这将导致组件增大 50%:
final effect = ScaleEffect.by(
Vector2.all(1.5),
EffectController(duration: 0.3),
);
ScaleEffect.to
此效果与 ScaleEffect.by 类似,但设置了目标比例的绝对值。
final effect = ScaleEffect.to(
Vector2.all(0.5),
EffectController(duration: 0.5),
);
SizeEffect.by
此效果将改变目标组件相对于其当前尺寸的大小。例如,如果目标的尺寸为 Vector2(100, 100),则应用以下效果并运行后,新的尺寸将为 Vector2(120, 50):
final effect = SizeEffect.by(
Vector2(-15, 30),
EffectController(duration: 1),
);
PositionComponent 的尺寸不能为负数。如果效果尝试将尺寸设置为负值,尺寸将被限制为零。
请注意,要使此效果生效,目标组件必须实现 SizeProvider 接口,并在渲染时考虑其尺寸。只有少数内置组件实现了此 API,但您始终可以通过在类声明中添加 implements SizeEffect 来使自己的组件支持尺寸效果。SizeEffect 的替代方案是 ScaleEffect,它的作用更广泛,可以同时缩放目标组件及其子组件。
SizeEffect.to
将目标组件的尺寸更改为指定尺寸。目标尺寸不能为负数:
final effect = SizeEffect.to(
Vector2(90, 80),
EffectController(duration: 1),
);
AnchorByEffect
根据指定的偏移量更改目标锚点的位置。此效果也可以使用 AnchorEffect.by() 来实现。
final effect = AnchorByEffect(
Vector2(0.1, 0.1),
EffectController(speed: 1),
);
AnchorToEffect
改变目标锚点的位置。此效果也可以使用 AnchorEffect.to() 来实现。
final effect = AnchorToEffect(
Anchor.center,
EffectController(speed: 1),
);
OpacityToEffect
此效果会将目标的不透明度随时间推移更改为指定的 alpha 值。此效果仅适用于实现了 OpacityProvider 的组件。
final effect = OpacityEffect.to(
0.2,
EffectController(duration: 0.75),
);
不透明度值为 0 表示组件完全透明,不透明度值为 1 表示完全不透明。便捷构造函数 OpacityEffect.fadeOut() 和 OpacityEffect.fadeIn() 分别会将目标动画化为完全透明/完全可见。
OpacityByEffect
此效果将根据指定的 Alpha 值改变目标的不透明度。例如,以下效果将使目标的不透明度改变 90%:
final effect = OpacityEffect.by(
0.9,
EffectController(duration: 0.75),
);
GlowEffect
此效果将根据指定的发光强度在目标周围应用发光阴影。阴影颜色将为目标的油漆颜色。例如,以下效果将在目标周围应用强度为 10 的发光阴影:
final effect = GlowEffect(
10.0,
EffectController(duration: 3),
);
目前,此效果只能应用于具有 HasPaint mixin 的组件。
SequenceEffect
此效果可用于依次运行多个其他效果。组成效果的类型可能不同。
序列效果也可以交替运行(序列先向前运行,然后向后运行);也可以按预定次数或无限次重复。
final effect = SequenceEffect([
ScaleEffect.by(
Vector2.all(1.5),
EffectController(
duration: 0.2,
alternate: true,
),
),
MoveEffect.by(
Vector2(30, -50),
EffectController(
duration: 0.5,
),
),
OpacityEffect.to(
0,
EffectController(
duration: 0.3,
),
),
RemoveEffect(),
]);
RemoveEffect
这是一个简单的效果,可以附加到一个组件,使其在指定的延迟时间过去后从游戏树中移除:
final effect = RemoveEffect(delay: 3.0);
ColorEffect
此效果将改变油漆的基色,导致渲染的组件在提供的范围内被提供的颜色着色。
final effect = ColorEffect(
const Color(0xFF00FF00),
EffectController(duration: 1.5),
opacityFrom: 0.2,
opacityTo: 0.8,
);
opacityFrom 和 opacityTo 参数将决定应用于组件的颜色“量”。在本例中,效果将从 20% 开始,逐渐增加到 80%。
注意:由于此效果的实现方式以及 Flutter 的 ColorFilter 类的工作方式,此效果不能与其他 ColorEffect 混合使用。当向组件添加多个 ColorEffect 时,只有最后一个 ColorEffect 才会生效。
FunctionEffect
FunctionEffect 类是一个非常通用的 Effect 类,它几乎允许执行任何操作,而无需定义新的 Effect。它运行一个函数,该函数接受目标和 Effect 的进度作为参数,然后用户可以决定如何处理该输入。
例如,它可以用于实现游戏状态随时间的变化,但不一定像大多数其他 Effect 那样具有视觉效果。
在下面的示例中,我们有一个 PlayerState 枚举,我们希望它随时间变化。我们希望在进度超过 50% 时将状态更改为“打哈欠”,在进度超过 80% 时将其恢复为“空闲”。
enum PlayerState {
idle,
yawn,
}
final effect = FunctionEffect<SpriteAnimationGroupComponent<PlayerState>>(
(target, progress) {
if (progress > 0.5) {
target.current = PlayerState.yawn;
} else if(progress > 0.8) {
target.current = PlayerState.idle;
}
},
EffectController(
duration: 10,
infinite: true,
),
);