From 65b0193cb695382f5b413ee1634cfe5b2ed3dba0 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Mon, 25 May 2020 01:43:08 +0200 Subject: [PATCH] Sequence effect with example --- .../effects/sequence_effect/.gitignore | 70 ++++++++++++ .../effects/sequence_effect/.metadata | 10 ++ .../effects/sequence_effect/README.md | 3 + .../effects/sequence_effect/lib/main.dart | 78 ++++++++++++++ .../effects/sequence_effect/lib/square.dart | 24 +++++ .../effects/sequence_effect/pubspec.yaml | 17 +++ lib/effects/effects.dart | 29 +++-- lib/effects/move_effect.dart | 2 + lib/effects/rotate_effect.dart | 4 +- lib/effects/sequence_effect.dart | 101 ++++++++++++++++++ 10 files changed, 328 insertions(+), 10 deletions(-) create mode 100644 doc/examples/effects/sequence_effect/.gitignore create mode 100644 doc/examples/effects/sequence_effect/.metadata create mode 100644 doc/examples/effects/sequence_effect/README.md create mode 100644 doc/examples/effects/sequence_effect/lib/main.dart create mode 100644 doc/examples/effects/sequence_effect/lib/square.dart create mode 100644 doc/examples/effects/sequence_effect/pubspec.yaml create mode 100644 lib/effects/sequence_effect.dart diff --git a/doc/examples/effects/sequence_effect/.gitignore b/doc/examples/effects/sequence_effect/.gitignore new file mode 100644 index 000000000..07488ba61 --- /dev/null +++ b/doc/examples/effects/sequence_effect/.gitignore @@ -0,0 +1,70 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Visual Studio Code related +.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.packages +.pub-cache/ +.pub/ +/build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/doc/examples/effects/sequence_effect/.metadata b/doc/examples/effects/sequence_effect/.metadata new file mode 100644 index 000000000..6a633e088 --- /dev/null +++ b/doc/examples/effects/sequence_effect/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7fc14a55af64462763d28abfb4e610086c6e0f39 + channel: dev + +project_type: app diff --git a/doc/examples/effects/sequence_effect/README.md b/doc/examples/effects/sequence_effect/README.md new file mode 100644 index 000000000..4b404b376 --- /dev/null +++ b/doc/examples/effects/sequence_effect/README.md @@ -0,0 +1,3 @@ +# Infinite effects + +A Flame game showcasing how to create infinite alternating effects. diff --git a/doc/examples/effects/sequence_effect/lib/main.dart b/doc/examples/effects/sequence_effect/lib/main.dart new file mode 100644 index 000000000..cfda120d9 --- /dev/null +++ b/doc/examples/effects/sequence_effect/lib/main.dart @@ -0,0 +1,78 @@ +import 'package:flame/effects/move_effect.dart'; +import 'package:flame/effects/scale_effect.dart'; +import 'package:flame/effects/rotate_effect.dart'; +import 'package:flame/effects/sequence_effect.dart'; +import 'package:flame/gestures.dart'; +import 'package:flame/position.dart'; +import 'package:flame/flame.dart'; +import 'package:flame/game.dart'; +import 'package:flutter/material.dart'; + +import 'dart:math'; + +import './square.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Flame.util.fullScreen(); + runApp(MyGame().widget); +} + +class MyGame extends BaseGame with TapDetector { + Square greenSquare; + Square redSquare; + Square orangeSquare; + + MyGame() { + final green = Paint()..color = const Color(0xAA338833); + greenSquare = Square(green, 100, 100); + add(greenSquare); + } + + @override + void onTapUp(TapUpDetails details) { + final dx = details.localPosition.dx; + final dy = details.localPosition.dy; + + greenSquare.clearEffects(); + + final move1 = MoveEffect( + destination: Position(dx, dy), + speed: 250.0, + curve: Curves.bounceInOut, + isInfinite: false, + isAlternating: false, + ); + + final move2 = MoveEffect( + destination: Position(dx, dy + 150), + speed: 150.0, + curve: Curves.easeIn, + isInfinite: false, + isAlternating: true, + ); + + final scale = ScaleEffect( + size: Size(dx, dy), + speed: 250.0, + curve: Curves.easeInCubic, + isInfinite: false, + isAlternating: false, + ); + + final rotate = RotateEffect( + radians: (dx + dy) % pi, + speed: 2.0, // Radians per second + curve: Curves.decelerate, + isInfinite: false, + isAlternating: false, + ); + + final sequence = SequenceEffect( + effects: [move1, scale, move2, rotate], + //effects: [rotate], + //effects: [scale], + isInfinite: true, isAlternating: true); + greenSquare.addEffect(sequence); + } +} diff --git a/doc/examples/effects/sequence_effect/lib/square.dart b/doc/examples/effects/sequence_effect/lib/square.dart new file mode 100644 index 000000000..a74ef7c8d --- /dev/null +++ b/doc/examples/effects/sequence_effect/lib/square.dart @@ -0,0 +1,24 @@ +import 'package:flame/anchor.dart'; +import 'package:flame/components/component.dart'; + +import 'dart:ui'; + +class Square extends PositionComponent { + final Paint _paint; + + Square(this._paint, double x, double y, {double angle = 0.0}) { + width = 100; + height = 100; + this.x = x; + this.y = y; + this.angle = angle; + anchor = Anchor.center; + } + + @override + void render(Canvas canvas) { + prepareCanvas(canvas); + final rect = Rect.fromLTWH(0, 0, width, height); + canvas.drawRect(rect, _paint); + } +} diff --git a/doc/examples/effects/sequence_effect/pubspec.yaml b/doc/examples/effects/sequence_effect/pubspec.yaml new file mode 100644 index 000000000..59cc1f3bb --- /dev/null +++ b/doc/examples/effects/sequence_effect/pubspec.yaml @@ -0,0 +1,17 @@ +name: infinite_effects +description: Flame sample game showcasing infinite effects + +version: 1.0.0+1 + +environment: + sdk: ">=2.1.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + flame: + path: ../../../../ + +dev_dependencies: + flutter_test: + sdk: flutter diff --git a/lib/effects/effects.dart b/lib/effects/effects.dart index 810dd6941..4ddcf125e 100644 --- a/lib/effects/effects.dart +++ b/lib/effects/effects.dart @@ -4,6 +4,7 @@ import '../components/component.dart'; export './move_effect.dart'; export './scale_effect.dart'; export './rotate_effect.dart'; +export './sequence_effect.dart'; abstract class PositionComponentEffect { bool _isDisposed = false; @@ -15,19 +16,24 @@ abstract class PositionComponentEffect { bool isInfinite; double percentage; double travelTime; - double _curveTime = 0.0; - int _curveDirection = 1; + double currentTime = 0.0; + double driftTime = 0.0; + int curveDirection = 1; PositionComponentEffect(this.isInfinite, this.isAlternating); void update(double dt) { - _curveTime += dt * _curveDirection; - if (isAlternating) { - _curveDirection = isMax() ? -1 : (isMin() ? 1 : _curveDirection); - } else if (isInfinite && isMax()) { - _curveTime = 0.0; + currentTime += dt * curveDirection; + if (hasFinished() && !isDisposed) { + driftTime = isAlternating ? currentTime.abs() : currentTime - travelTime; } - percentage = min(1.0, max(0.0, _curveTime / travelTime)); + + if (isAlternating) { + curveDirection = isMax() ? -1 : (isMin() ? 1 : curveDirection); + } else if (isInfinite && isMax()) { + currentTime = 0.0; + } + percentage = min(1.0, max(0.0, currentTime / travelTime)); } void dispose() => _isDisposed = true; @@ -40,4 +46,11 @@ abstract class PositionComponentEffect { isDisposed; bool isMax() => percentage == null ? false : percentage == 1.0; bool isMin() => percentage == null ? false : percentage == 0.0; + + void reset() { + _isDisposed = false; + percentage = null; + currentTime = 0.0; + curveDirection = 1; + } } diff --git a/lib/effects/move_effect.dart b/lib/effects/move_effect.dart index 93b740331..07a2a3653 100644 --- a/lib/effects/move_effect.dart +++ b/lib/effects/move_effect.dart @@ -44,6 +44,8 @@ class MoveEffect extends PositionComponentEffect { _yDirection = _direction(destination.y, component.y); final totalDistance = sqrt(pow(_xDistance, 2) + pow(_yDistance, 2)); + print(totalDistance); + print(speed); travelTime = totalDistance / speed; } diff --git a/lib/effects/rotate_effect.dart b/lib/effects/rotate_effect.dart index 9804dff79..99be93f9b 100644 --- a/lib/effects/rotate_effect.dart +++ b/lib/effects/rotate_effect.dart @@ -13,7 +13,7 @@ class RotateEffect extends PositionComponentEffect { double _direction; RotateEffect({ - @required this.radians, // The angle to rotate to + @required this.radians, // As many radians as you want to rotate @required this.speed, // In radians per second this.curve, isInfinite = false, @@ -26,7 +26,7 @@ class RotateEffect extends PositionComponentEffect { _originalAngle = component.angle; _peakAngle = _originalAngle + radians; _direction = _peakAngle.sign; - travelTime = (_peakAngle / speed).abs(); + travelTime = (radians / speed).abs(); } @override diff --git a/lib/effects/sequence_effect.dart b/lib/effects/sequence_effect.dart new file mode 100644 index 000000000..171d86336 --- /dev/null +++ b/lib/effects/sequence_effect.dart @@ -0,0 +1,101 @@ +import 'package:flutter/animation.dart'; +import 'package:meta/meta.dart'; + +import 'dart:ui'; +import 'dart:math'; + +import './effects.dart'; +import '../position.dart'; + +class SequenceEffect extends PositionComponentEffect { + final List effects; + int _currentIndex = 0; + PositionComponentEffect _currentEffect; + bool _currentWasAlternating; + double _driftModifier = 0.0; + + SequenceEffect({ + @required this.effects, + isInfinite = false, + isAlternating = false, + }) : super(isInfinite, isAlternating); + + @override + set component(_comp) { + super.component = _comp; + final originalSize = _comp.toSize(); + final originalPosition = _comp.toPosition(); + Position currentSize = _comp.toSize(); + Position currentPosition = _comp.toPosition(); + effects.forEach((effect) { + effect.reset(); + _comp.setBySize(currentSize); + _comp.setByPosition(currentPosition); + effect.component = _comp; + if (effect is MoveEffect) { + currentPosition = effect.destination; + } else if (effect is ScaleEffect) { + currentSize = Position.fromSize(effect.size); + } + }); + travelTime = effects.fold(0, (time, effect) => time + effect.travelTime * + (effect.isAlternating ? 2 : 1)); + effects.forEach((element) {print("$element ${element.travelTime}");}); + component.setBySize(originalSize); + component.setByPosition(originalPosition); + _currentEffect = effects.first; + _currentWasAlternating = _currentEffect.isAlternating; + } + + @override + void update(double dt) { + super.update(dt); + if (hasFinished()) { + return; + } + + _currentEffect.update(dt + _driftModifier); + _driftModifier = 0.0; + if (_currentEffect.hasFinished()) { + _driftModifier = _currentEffect.driftTime; + _currentEffect.isAlternating = _currentWasAlternating; + _currentIndex++; + final iterationSize = isAlternating ? effects.length * 2 : effects.length; + if (isInfinite && + _currentIndex != 0 && + _currentIndex % iterationSize == 0) { + reset(); + return; + } + final orderedEffects = + curveDirection.isNegative ? effects.reversed.toList() : effects; + _currentEffect = orderedEffects[_currentIndex % effects.length]; + print("Index: ${_currentIndex % effects.length}"); + print(_currentIndex); + print(curveDirection); + print(_currentEffect); + print(orderedEffects); + _currentWasAlternating = _currentEffect.isAlternating; + if (isAlternating && + !_currentEffect.isAlternating && + curveDirection.isNegative) { + // Make the effect go in reverse + _currentEffect.isAlternating = true; + _currentEffect.percentage = 1.0; + } + } + } + + @override + void reset() { + super.reset(); + _currentIndex = 0; + component = component; + } + + @override + bool hasFinished() { + return super.hasFinished() && + effects.every((effect) => effect.hasFinished()); + } +}