From a66a5c4bff04ca4d970d652190b64953d7cd812f Mon Sep 17 00:00:00 2001 From: Viktor Lidholt Date: Thu, 17 Sep 2015 15:31:15 -0700 Subject: [PATCH] Improvements to EffectLine in sprites --- ...ite_mesh.dart => example_effect_line.dart} | 28 ++++++-- packages/flutter_sprites/lib/effect_line.dart | 65 ++++++++++++++++--- .../flutter_sprites/lib/textured_line.dart | 12 +++- 3 files changed, 87 insertions(+), 18 deletions(-) rename examples/game/{test_sprite_mesh.dart => example_effect_line.dart} (84%) diff --git a/examples/game/test_sprite_mesh.dart b/examples/game/example_effect_line.dart similarity index 84% rename from examples/game/test_sprite_mesh.dart rename to examples/game/example_effect_line.dart index 5425682d6be..71aab60e088 100644 --- a/examples/game/test_sprite_mesh.dart +++ b/examples/game/example_effect_line.dart @@ -42,19 +42,20 @@ class TestApp extends App { List _labelTexts = [ "Colored", "Smoke", - "Electric" + "Electric", + "Rocket Trail" ]; Widget build() { ThemeData theme = new ThemeData( brightness: ThemeBrightness.light, - primarySwatch: Colors.purple + primarySwatch: Colors.blue ); return new Theme( data: theme, child: new Title( - title: 'Test drawAtlas', + title: 'EffectLine Demo', child: _buildColumn() ) ); @@ -114,11 +115,10 @@ class TestBed extends NodeWithSize { // Create a line with no texture and a color sequence _line = new EffectLine( texture: null, - colorSequence: new ColorSequence.fromStartAndEndColor(new Color(0xffff0000), new Color(0xff0000ff)), + colorSequence: new ColorSequence.fromStartAndEndColor(new Color(0xaaffff00), new Color(0xaaff9900)), widthMode: EffectLineWidthMode.barrel, - minWidth: 20.0, - maxWidth: 50.0, - animationMode: EffectLineAnimationMode.scroll, + minWidth: 10.0, + maxWidth: 15.0, fadeAfterDelay: 1.0, fadeDuration: 1.0 ); @@ -147,6 +147,20 @@ class TestBed extends NodeWithSize { maxWidth: 100.0, animationMode: EffectLineAnimationMode.random ); + } else if (lineType == "Rocket Trail") { + Texture baseTexture = new Texture(_images['assets/line_effects.png']); + Texture trailLineTexture = baseTexture.textureFromRect(new Rect.fromLTRB(0.0, 896.0, 1024.0, 1024.0)); + + _line = new EffectLine( + texture: trailLineTexture, + textureLoopLength: 300.0, + widthMode: EffectLineWidthMode.barrel, + minWidth: 20.0, + maxWidth: 40.0, + widthGrowthSpeed: 40.0, + fadeAfterDelay: 0.5, + fadeDuration: 1.5 + ); } addChild(_line); diff --git a/packages/flutter_sprites/lib/effect_line.dart b/packages/flutter_sprites/lib/effect_line.dart index 8e6ba70793f..a11658d5f02 100644 --- a/packages/flutter_sprites/lib/effect_line.dart +++ b/packages/flutter_sprites/lib/effect_line.dart @@ -15,12 +15,15 @@ class EffectLine extends Node { EffectLine({ this.texture: null, + this.transferMode: sky.TransferMode.dstOver, List points, this.widthMode : EffectLineWidthMode.linear, this.minWidth: 10.0, this.maxWidth: 10.0, + this.widthGrowthSpeed: 0.0, this.animationMode: EffectLineAnimationMode.none, this.scrollSpeed: 0.1, + double scrollStart: 0.0, this.fadeDuration: null, this.fadeAfterDelay: null, this.textureLoopLength: null, @@ -36,15 +39,20 @@ class EffectLine extends Node { new Color(0xffffffff), new Color(0xffffffff)); + _offset = scrollStart; + _painter = new TexturedLinePainter(points, _colors, _widths, texture); _painter.textureLoopLength = textureLoopLength; } final Texture texture; + final sky.TransferMode transferMode; + final EffectLineWidthMode widthMode; final double minWidth; final double maxWidth; + final double widthGrowthSpeed; final EffectLineAnimationMode animationMode; final double scrollSpeed; @@ -86,14 +94,25 @@ class EffectLine extends Node { _offset = randomDouble(); } - // Update age of line points, and remove if neccessary + // Update age of line points and remove if neccesasry if (fadeDuration != null && fadeAfterDelay != null) { + // Increase age of points for (int i = _points.length - 1; i >= 0; i--) { _pointAges[i] += dt; - if (_pointAges[i] > (fadeDuration + fadeAfterDelay)) { - _pointAges.removeAt(i); - _points.removeAt(i); + } + + // Check if the first/oldest point should be removed + while(_points.length > 0 && _pointAges[0] > (fadeDuration + fadeAfterDelay)) { + // Update scroll if it isn't the last and only point that is about to removed + if (_points.length > 1 && textureLoopLength != null) { + double dist = GameMath.pointQuickDist(_points[0], _points[1]); + _offset = (_offset - (dist / textureLoopLength)) % 1.0; + if (_offset < 0.0) _offset += 1; } + + // Remove the point + _pointAges.removeAt(0); + _points.removeAt(0); } } } @@ -101,8 +120,6 @@ class EffectLine extends Node { void paint(PaintingCanvas canvas) { if (points.length < 2) return; - //_painter.textureLoopLength = textureLoopLength; - _painter.points = points; // Calculate colors @@ -127,12 +144,14 @@ class EffectLine extends Node { // Calculate widths List widths = []; - for (double stop in stops) { + for (int i = 0; i < stops.length; i++) { + double stop = stops[i]; + double growth = math.max(widthGrowthSpeed * _pointAges[i], 0.0); if (widthMode == EffectLineWidthMode.linear) { - double width = minWidth + (maxWidth - minWidth) * stop; + double width = minWidth + (maxWidth - minWidth) * stop + growth; widths.add(width); } else if (widthMode == EffectLineWidthMode.barrel) { - double width = minWidth + math.sin(stop * math.PI) * (maxWidth - minWidth); + double width = minWidth + math.sin(stop * math.PI) * (maxWidth - minWidth) + growth; widths.add(width); } } @@ -148,12 +167,38 @@ class EffectLine extends Node { if (points.length > 0 && point.x == points[points.length - 1].x && point.y == points[points.length - 1].y) return; - if (simplify) { + if (simplify && points.length >= 2 && GameMath.pointQuickDist(point, points[points.length - 2]) < 10.0) { + // Check if we should remove last point before adding the new one + // Calculate the square distance from the middle point to the line of the + // new point and the second to last point + double dist2 = _distToSeqment2( + points[points.length - 1], + point, + points[points.length - 2] + ); + + // If the point is on the line, remove it + if (dist2 < 1.0) { + _points.removeAt(_points.length - 1); + } } // Add point and point's age _points.add(point); _pointAges.add(0.0); } + + double _sqr(double x) => x * x; + + double _dist2(Point v, Point w) => _sqr(v.x - w.x) + _sqr(v.y - w.y); + + double _distToSeqment2(Point p, Point v, Point w) { + double l2 = _dist2(v, w); + if (l2 == 0.0) return _dist2(p, v); + double t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2; + if (t < 0) return _dist2(p, v); + if (t > 1) return _dist2(p, w); + return _dist2(p, new Point(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y))); + } } diff --git a/packages/flutter_sprites/lib/textured_line.dart b/packages/flutter_sprites/lib/textured_line.dart index beabd4c69b3..ed11f21d6a7 100644 --- a/packages/flutter_sprites/lib/textured_line.dart +++ b/packages/flutter_sprites/lib/textured_line.dart @@ -75,6 +75,8 @@ class TexturedLinePainter { _calculatedTextureStops = null; } + sky.TransferMode transferMode = sky.TransferMode.srcOver; + Paint _cachedPaint = new Paint(); void paint(PaintingCanvas canvas) { @@ -85,6 +87,8 @@ class TexturedLinePainter { assert(_points.length == colors.length); assert(_points.length == widths.length); + _cachedPaint.transferMode = transferMode; + // Calculate normals List vectors = []; for (Point pt in _points) { @@ -216,7 +220,13 @@ Vector2 _computeMiter(Vector2 lineA, Vector2 lineB) { Vector2 miter = new Vector2(- (lineA[1] + lineB[1]), lineA[0] + lineB[0]); miter.normalize(); - double miterLength = 1.0 / dot2(miter, new Vector2(-lineA[1], lineA[0])); + double dot = dot2(miter, new Vector2(-lineA[1], lineA[0])); + if (dot.abs() < 0.1) { + miter = _vectorNormal(lineA).normalize(); + return miter; + } + + double miterLength = 1.0 / dot; miter = miter.scale(miterLength); return miter;