Skip to content

Commit

Permalink
refactor: OpacityEffect now uses opacity instead of alpha internally (#…
Browse files Browse the repository at this point in the history
…2064)

Using opacity instead of integer alpha simplifies the code (and also reduces round-off errors which may accumulate for longer-running effects).
  • Loading branch information
st-pasha committed Oct 11, 2022
1 parent 1b97747 commit b3b6730
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 30 deletions.
25 changes: 11 additions & 14 deletions packages/flame/lib/src/effects/opacity_effect.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ import 'package:flame/src/effects/controllers/effect_controller.dart';
/// requires that any other effect or update logic applied to the same component
/// also used incremental updates.
class OpacityEffect extends ComponentEffect<HasPaint> {
int _alphaOffset;
double _roundingError = 0.0;
final String? paintId;

/// This constructor will set the opacity in relation to it's current opacity
/// over time.
OpacityEffect.by(
double offset,
super.controller, {
this.paintId,
super.onComplete,
}) : _alphaOffset = (255 * offset).round();
}) : _opacityOffset = offset;

/// This constructor will set the opacity to the specified opacity over time.
factory OpacityEffect.to(
Expand Down Expand Up @@ -62,16 +58,18 @@ class OpacityEffect extends ComponentEffect<HasPaint> {
);
}

double _opacityOffset;
double _roundingError = 0.0;
final String? paintId;

@override
void apply(double progress) {
final deltaProgress = progress - previousProgress;
final currentAlpha = target.getAlpha(paintId: paintId);
final deltaAlpha =
(_alphaOffset * deltaProgress) + _roundingError * deltaProgress.sign;
var nextAlpha = (currentAlpha + deltaAlpha).round();
_roundingError = (currentAlpha + deltaAlpha) - nextAlpha;
nextAlpha = nextAlpha.clamp(0, 255);
target.setAlpha(nextAlpha, paintId: paintId);
final currentOpacity = target.getOpacity(paintId: paintId) + _roundingError;
final deltaOpacity = _opacityOffset * deltaProgress;
final newOpacity = (currentOpacity + deltaOpacity).clamp(0, 1).toDouble();
target.setOpacity(newOpacity, paintId: paintId);
_roundingError = newOpacity - target.getOpacity(paintId: paintId);
}

@override
Expand Down Expand Up @@ -101,7 +99,6 @@ class _OpacityToEffect extends OpacityEffect {

@override
void onStart() {
_alphaOffset =
(_targetOpacity * 255 - target.getAlpha(paintId: paintId)).round();
_opacityOffset = _targetOpacity - target.getOpacity(paintId: paintId);
}
}
26 changes: 10 additions & 16 deletions packages/flame/test/effects/opacity_effect_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void main() {
const _epsilon = 0.004; // 1/255, since alpha only holds 8 bits

group('OpacityEffect', () {
flameGame.test('relative', (game) async {
testWithFlameGame('relative', (game) async {
final component = _PaintComponent();
await game.ensureAdd(component);

Expand All @@ -44,7 +44,7 @@ void main() {
expectDouble(component.getOpacity(), 0.6, epsilon: _epsilon);
});

flameGame.test('absolute', (game) async {
testWithFlameGame('absolute', (game) async {
final component = _PaintComponent();
await game.ensureAdd(component);

Expand All @@ -66,7 +66,7 @@ void main() {
expectDouble(component.getOpacity(), 0.4, epsilon: _epsilon);
});

flameGame.test('reset relative', (game) async {
testWithFlameGame('reset relative', (game) async {
final component = _PaintComponent();
await game.ensureAdd(component);

Expand All @@ -92,7 +92,7 @@ void main() {
}
});

flameGame.test('reset absolute', (game) async {
testWithFlameGame('reset absolute', (game) async {
final component = _PaintComponent();
await game.ensureAdd(component);

Expand All @@ -107,13 +107,11 @@ void main() {
// regardless of its initial opacity.
effect.reset();
game.update(1);
// TODO(spydon): This is not good, since it sometimes won't hit the
// minima.
expectDouble(component.getOpacity(), 0.0, epsilon: _epsilon);
expect(component.getOpacity(), 0.0);
}
});

flameGame.test('opacity composition', (game) async {
testWithFlameGame('opacity composition', (game) async {
final component = _PaintComponent();
component.setOpacity(0.0);
await game.ensureAdd(component);
Expand Down Expand Up @@ -151,7 +149,7 @@ void main() {
expect(component.children.length, 0);
});

flameGame.test(
testWithFlameGame(
'fade out',
(game) async {
final rng = Random();
Expand All @@ -176,7 +174,7 @@ void main() {
},
);

flameGame.test(
testWithFlameGame(
'infinite fade out',
(game) async {
final component = _PaintComponent();
Expand All @@ -198,7 +196,7 @@ void main() {
},
);

flameGame.test(
testWithFlameGame(
'on custom paint',
(game) async {
final component =
Expand Down Expand Up @@ -242,11 +240,7 @@ void main() {
game.update(dt);
}
game.update(1000 - totalTime);
// TODO(spydon): The loop above has an average of 100fps.
// It should change from 0-255 in 1s so it will change alpha with an
// average of 255/100=2.5 per tick, which should not result in a need of
// an epsilon value this high.
expectDouble(component.getOpacity(), 1.0, epsilon: 100 * _epsilon);
expectDouble(component.getOpacity(), 1.0, epsilon: _epsilon);
});
});
}

0 comments on commit b3b6730

Please sign in to comment.