Adds missing Flutter Sprites API docs and improved README (#4022)

This commit is contained in:
Viktor Lidholt 2016-05-19 13:37:29 -07:00
parent 880f2f785e
commit 0bbb25b25a
23 changed files with 538 additions and 60 deletions

View File

@ -1,8 +1,224 @@
# Flutter Sprites # Flutter Sprites
Flutter Sprites is a toolkit for building complex, high performance animations and 2D games with Flutter. Your sprite render tree lives inside a SpriteWidget that mixes seamlessly with other Flutter and Material widgets. You can use Flutter Sprites to create anything from an animated icon to a full fledged game.
A sprite toolkit built on top of Flutter. This guide assumes a basic knowledge of Flutter and Dart. You can find an example of Flutter Sprites in the Flutter Gallery in the Weather demo, or in the flutter/game repository on Github.
## Getting Started ## Setting up a SpriteWidget
The first thing you need to do to use Flutter Sprites is to setup a SpriteWidget with a root node that is used to draw it's contents. Any sprite nodes that you add to the root node will be rendered by the SpriteWidget. Typically, your root node is part of your app's state. This is an example of how you can setup a custom stateful widget with Flutter Sprites:
For help getting started with Flutter, view our online import 'package:flutter/material.dart';
[documentation](http://flutter.io). import 'package:flutter_sprites/flutter_sprites.dart';
class MyWidget extends StatefulWidget {
@override
MyWidgetState createState() => new MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
NodeWithSize rootNode;
@override
void initState() {
super.initState();
rootNode = new NodeWithSize(const Size(1024.0, 1024.0));
}
@override
Widget build(BuildContext context) {
return new SpriteWidget(rootNode);
}
}
The root node that you provide the SpriteWidget is a NodeWithSize, the size of the root node defines the coordinate system used by the SpriteWidget. By default the SpriteWidget uses letterboxing to display its contents. This means that the size that you give the root node will determine how the SpriteWidget's contents will be scaled to fit. If it doesn't fit perfectly in the area of the widget, either its top and bottom or the left and right side will be trimmed. You can optionally pass in a parameter to the SpriteWidget for other scaling options depending on your needs.
When you have added the SpriteWidget to your app's build method it will automatically start running animations and handling user input. There is no need for any other extra setup.
## Adding objects to your node graph
Your SpriteWidget manages a node graph, the root node is the NodeWithSize that is passed in to the SpriteWidget when it's created. To render sprites, particles systems, or any other objects simply add them to the node graph.
Each node in the node graph has a transform. The transform is inherited by its children, this makes it possible to build more complex structures by grouping objects together as children to a node and then manipulating the parent node. For example the following code creates a car sprite with two wheels attached to it. The car is added to the root node.
Sprite car = new Sprite.fromImage(carImage);
Sprite frontWheel = new Sprite.fromImage(wheelImage);
Sprite rearWheel = new Sprite.fromImage(wheelImage);
frontWheel.position = const Point(100, 50);
rearWheel.position = const Point(-100, 50);
car.addChild(frontWheel);
car.addChild(rearWheel);
rootNode.addChild(car);
You can manipulate the transform by setting the position, rotation, scale, and skew properties.
## Sprites, textures, and sprite sheets
The most common node type is the Sprite node. A sprite simply draws an image to the screen. Sprites can be drawn from Image objects or Texture objects. A texture is a part of an Image. Using a SpriteSheet you can pack several texture elements within a single image. This saves space in the device's gpu memory and also make drawing faster. Currently Flutter Sprites supports sprite sheets in json format and produced with a tool such as TexturePacker. It's uncommon to manually edit the sprite sheet files. You can create a SpriteSheet with a definition in json and an image:
SpriteSheet sprites = new SpriteSheet(myImage, jsonCode);
Texture texture = sprites['texture.png'];
## The frame cycle
Each time a new frame is rendered to screen Flutter Sprites will perform a number of actions. Sometimes when creating more advanced interactive animations or games, the order in which these actions are performed may matter.
This is the order things will happen:
1. Handle input events
2. Run animation actions
3. Call update functions on nodes
4. Apply constraints
5. Render the frame to screen
Read more about each of the different phases below.
## Handling user input
You can subclass any node type to handle touches. To receive touches, you need to set the userInteractionEnabled property to true and override the handleEvent method. If the node you are subclassing doesn't have a size, you will also need to override the isPointInside method.
class EventHandlingNode extends NodeWithSize {
EventHandlingNode(Size size) : super(size) {
userInteractionEnabled = true;
}
@override handleEvent(SpriteBoxEvent event) {
if (event.type == PointerDownEvent)
...
else if (event.type == PointerMoveEvent)
...
return true;
}
}
If you want your node to receive multiple touches, set the handleMultiplePointers property to true. Each touch down or dragged touch will generate a separate call to the handleEvent method, you can distinguish each touch by its pointer property.
## Animating using actions
Flutter Sprites provides easy to use functions for animating nodes through actions. You can combine simple action blocks to create more complex animations.
To execute an action animation you first build the action itself, then pass it to the run method of a nodes action manager (see the Tweens section below for an example).
### Tweens
Tweens are the simplest building block for creating an animation. It will interpolate a value or property over a specified time period. You provide the ActionTween class with a setter function, its start and end value, and the duration for the tween.
After creating a tween, execute it by running it through a node's action manager.
Node myNode = new Node();
ActionTween myTween = new ActionTween(
(Point a) => myNode.position = a,
Point.origin,
const Point(100.0, 0.0),
1.0
);
myNode.actions.run(myTween);
You can animate values of different types, such as floats, points, rectangles, and even colors. You can also optionally provide the ActionTween class with an easing function.
### Sequences
When you need to play two or more actions in a sequence, use the ActionSequence class:
ActionSequence sequence = new ActionSequence([
firstAction,
middleAction,
lastAction
]);
### Groups
Use ActionGroup to play actions in parallel:
ActionGroup group = new ActionGroup([
action0,
action1
]);
### Repeat
You can loop any action, either a fixed number of times, or until the end of times:
ActionRepeat repeat = new ActionRepeat(loopedAction, 5);
ActionRepeatForever longLoop = new ActionRepeatForever(loopedAction);
### Composition
It's possible to create more complex actions by composing them in any way:
ActionSequence complexAction = new ActionSequence([
new ActionRepeat(myLoop, 2),
new ActionGroup([
action0,
action1
])
]);
## Handle update events
Each frame, update events are sent to each node in the current node tree. Override the update method to manually do animations or to perform game logic.
MyNode extends Node {
@override
update(double dt) {
// Move the node at a constant speed
position += new Offset(dt * 1.0, 0.0);
}
}
## Defining constraints
Constraints are used to constrain properties of nodes. They can be used to position nodes relative other nodes, or adjust the rotation or scale. You can apply more than one constraint to a single node.
For example, you can use a constraint to make a node follow another node at a specific distance with a specified dampening. The dampening will smoothen out the following node's movement.
followingNode.constraints = [
new ConstraintPositionToNode(
targetNode,
offset: const Offset(0.0, 100.0),
dampening: 0.5
)
];
Constraints are applied at the end of the frame cycle. If you need them to be applied at any other time, you can directly call the applyConstraints method of a Node object.
## Perform custom drawing
Flutter Sprites provides a default set of drawing primitives, but there are cases where you may want to perform custom drawing. To do this you will need to subclass either the Node or NodeWithSize class and override the paint method:
class RedCircle extends Node {
RedCircle(this.radius);
double radius;
@override
void paint(Canvas canvas) {
canvas.drawCircle(
Point.origin,
radius,
new Paint()..color = const Color(0xffff0000)
);
}
}
If you are overriding a NodeWithSize you may want to call applyTransformForPivot before starting drawing to account for the node's pivot point. After the call the coordinate system is setup so you can perform drawing starting at origo to the size of the node.
@override
void paint(Canvas canvas) {
applyTransformForPivot(canvas);
canvas.drawRect(
new Rect.fromLTWH(0.0, 0.0, size.width, size.height),
myPaint
);
}
## Add effects using particle systems
Particle systems are great for creating effects such as rain, smoke, or fire. It's easy to setup a particle system, but there are very many properties that can be tweaked. The best way of to get a feel for how they work is to simply play around with the them.
This is an example of how a particle system can be created, configured, and added to the scene:
ParticleSystem particles = new ParticleSystem(
particleTexture,
posVar: const Point(100, 100.0),
startSize: 1.0,
startSizeVar: 0.5,
endSize: 2.0,
endSizeVar: 1.0,
life: 1.5 * distance,
lifeVar: 1.0 * distance
);
rootNode.addChild(particles);

View File

@ -1,5 +1,10 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// Signature for callbacks used by the [ActionCallFunction].
typedef void ActionCallback(); typedef void ActionCallback();
/// Actions are used to animate properties of nodes or any other type of /// Actions are used to animate properties of nodes or any other type of
@ -34,19 +39,21 @@ abstract class Action {
_finished = false; _finished = false;
} }
/// The total time it will take to complete the action, in seconds.
double get duration => 0.0; double get duration => 0.0;
} }
/// Signature for callbacks for setting properties, used by [ActionTween].
typedef void SetterCallback(dynamic value); typedef void SetterCallback(dynamic value);
/// The abstract class for an action that changes properties over a time /// The abstract class for an action that changes properties over a time
/// interval, optionally using an easing curve. /// interval, optionally using an easing curve.
abstract class ActionInterval extends Action { abstract class ActionInterval extends Action {
/// Creates a new ActionInterval, typically you will want to pass in a
/// [duration] to specify how long time the action will take to complete.
ActionInterval([this._duration = 0.0, this.curve]); ActionInterval([this._duration = 0.0, this.curve]);
/// The duration, in seconds, of the action.
///
/// double myTime = myAction.duration;
@override @override
double get duration => _duration; double get duration => _duration;
double _duration; double _duration;
@ -84,9 +91,13 @@ abstract class ActionInterval extends Action {
} }
} }
/// An action that repeats an action a fixed number of times. /// An action that repeats another action a fixed number of times.
class ActionRepeat extends ActionInterval { class ActionRepeat extends ActionInterval {
/// The number of times the [action] is repeated.
final int numRepeats; final int numRepeats;
/// The action that is repeated.
final ActionInterval action; final ActionInterval action;
int _lastFinishedRepeat = -1; int _lastFinishedRepeat = -1;
@ -119,6 +130,8 @@ class ActionRepeat extends ActionInterval {
/// An action that repeats an action an indefinite number of times. /// An action that repeats an action an indefinite number of times.
class ActionRepeatForever extends Action { class ActionRepeatForever extends Action {
/// The action that is repeated indefinitely.
final ActionInterval action; final ActionInterval action;
double _elapsedInAction = 0.0; double _elapsedInAction = 0.0;
@ -325,6 +338,8 @@ abstract class ActionInstant extends Action {
_finished = true; _finished = true;
} }
/// Called when the action is executed. If you are implementing your own
/// ActionInstant, override this method.
void fire(); void fire();
} }
@ -546,6 +561,8 @@ class ActionController {
} }
} }
/// Steps the action forward by the specified time, typically there is no need
/// to directly call this method.
void step(double dt) { void step(double dt) {
for (int i = _actions.length - 1; i >= 0; i--) { for (int i = _actions.length - 1; i >= 0; i--) {
Action action = _actions[i]; Action action = _actions[i];

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
Point _cardinalSplineAt(Point p0, Point p1, Point p2, Point p3, double tension, double t) { Point _cardinalSplineAt(Point p0, Point p1, Point p2, Point p3, double tension, double t) {
@ -17,6 +21,7 @@ Point _cardinalSplineAt(Point p0, Point p1, Point p2, Point p3, double tension,
return new Point(x, y); return new Point(x, y);
} }
/// Signature for callbacks used by the [ActionSpline] to set a [Point] value.
typedef void PointSetterCallback(Point value); typedef void PointSetterCallback(Point value);
/// The spline action is used to animate a point along a spline definied by /// The spline action is used to animate a point along a spline definied by

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A sequence of colors representing a gradient or a color transition over /// A sequence of colors representing a gradient or a color transition over

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A constraint limits or otherwise controls a [Node]'s properties, such as /// A constraint limits or otherwise controls a [Node]'s properties, such as
@ -139,8 +143,13 @@ class ConstraintPositionToNode extends Constraint {
/// same parent, but they need to be added to the same [SpriteBox]. /// same parent, but they need to be added to the same [SpriteBox].
ConstraintPositionToNode(this.targetNode, {this.dampening, this.offset: Offset.zero}); ConstraintPositionToNode(this.targetNode, {this.dampening, this.offset: Offset.zero});
/// Target node to follow.
final Node targetNode; final Node targetNode;
/// Offset to the target node.
final Offset offset; final Offset offset;
/// Dampening used when following the [targetNode], value between 0.0 and 1.0.
final double dampening; final double dampening;
@override @override

View File

@ -1,18 +1,42 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// Used by [EffectLine] to determine how the width of the line is calculated.
enum EffectLineWidthMode { enum EffectLineWidthMode {
/// Linear interpolation between minWidth at the start and maxWidth at the
/// end of the line.
linear, linear,
/// Creates a barrel shaped line, with minWidth at the end points of the line
/// and maxWidth at the middle.
barrel, barrel,
} }
/// Used by [EffectLine] to determine how the texture of the line is animated.
enum EffectLineAnimationMode { enum EffectLineAnimationMode {
/// The texture of the line isn't animated.
none, none,
/// The texture of the line is scrolling.
scroll, scroll,
/// The texture of the line is set to a random position at every frame. This
/// mode is useful for creating flashing or electricity styled effects.
random, random,
} }
/// The EffectLine class is using the [TexturedLine] class to draw animated
/// lines. These can be used to draw things such as smoke trails, electricity
/// effects, or other animated types of lines.
class EffectLine extends Node { class EffectLine extends Node {
/// Creates a new EffectLine with the specified parameters. Only the
/// [texture] parameter is required, all other parameters are optional.
EffectLine({ EffectLine({
this.texture: null, this.texture: null,
this.transferMode: TransferMode.dstOver, this.transferMode: TransferMode.dstOver,
@ -49,22 +73,41 @@ class EffectLine extends Node {
_painter.textureLoopLength = textureLoopLength; _painter.textureLoopLength = textureLoopLength;
} }
/// The texture used to draw the line.
final Texture texture; final Texture texture;
/// The transfer mode used to draw the line, default is
/// [TransferMode.dstOver].
final TransferMode transferMode; final TransferMode transferMode;
/// Mode used to calculate the width of the line.
final EffectLineWidthMode widthMode; final EffectLineWidthMode widthMode;
/// The width of the line at its thinnest point.
final double minWidth; final double minWidth;
/// The width of the line at its thickest point.
final double maxWidth; final double maxWidth;
/// The speed at which the line is growing, defined in points per second.
final double widthGrowthSpeed; final double widthGrowthSpeed;
/// The mode used to animate the texture of the line.
final EffectLineAnimationMode animationMode; final EffectLineAnimationMode animationMode;
/// The speed of which the texture of the line is scrolling. This property
/// is only used if the [animationMode] is set to
/// [EffectLineAnimationMode.scroll].
final double scrollSpeed; final double scrollSpeed;
ColorSequence _colorSequence;
/// Color gradient used to draw the line, from start to finish.
ColorSequence get colorSequence => _colorSequence; ColorSequence get colorSequence => _colorSequence;
List<Point> _points; ColorSequence _colorSequence;
/// List of points that make up the line. Typically, you will only want to
/// set this at the beginning. Then use [addPoint] to add additional points
/// to the line.
List<Point> get points => _points; List<Point> get points => _points;
set points(List<Point> points) { set points(List<Point> points) {
@ -75,15 +118,26 @@ class EffectLine extends Node {
} }
} }
List<Point> _points;
List<double> _pointAges; List<double> _pointAges;
List<Color> _colors; List<Color> _colors;
List<double> _widths; List<double> _widths;
/// The time it takes for an added point to fade out. It's total life time is
/// [fadeDuration] + [fadeAfterDelay].
final double fadeDuration; final double fadeDuration;
/// The time it takes until an added point starts to fade out.
final double fadeAfterDelay; final double fadeAfterDelay;
/// The length, in points, that the texture is stretched to. If the
/// textureLoopLength is shorter than the line, the texture will be looped.
final double textureLoopLength; final double textureLoopLength;
/// True if the line should be simplified by removing points that are close
/// to other points. This makes drawing faster, but can result in a slight
/// jittering effect when points are added.
final bool simplify; final bool simplify;
TexturedLinePainter _painter; TexturedLinePainter _painter;
@ -168,6 +222,7 @@ class EffectLine extends Node {
_painter.paint(canvas); _painter.paint(canvas);
} }
/// Adds a new point to the end of the line.
void addPoint(Point point) { void addPoint(Point point) {
// Skip duplicate points // Skip duplicate points
if (points.length > 0 && point.x == points[points.length - 1].x && point.y == points[points.length - 1].y) if (points.length > 0 && point.x == points[points.length - 1].x && point.y == points[points.length - 1].y)

View File

@ -4,12 +4,18 @@
part of flutter_sprites; part of flutter_sprites;
/// The ImageMap is a helper class for loading and keeping references to
/// multiple images.
class ImageMap { class ImageMap {
/// Creates a new ImageMap where images will be loaded from the specified
/// [bundle].
ImageMap(AssetBundle bundle) : _bundle = bundle; ImageMap(AssetBundle bundle) : _bundle = bundle;
final AssetBundle _bundle; final AssetBundle _bundle;
final Map<String, ui.Image> _images = new Map<String, ui.Image>(); final Map<String, ui.Image> _images = new Map<String, ui.Image>();
/// Loads a list of images given their urls.
Future<List<ui.Image>> load(List<String> urls) { Future<List<ui.Image>> load(List<String> urls) {
return Future.wait(urls.map(_loadImage)); return Future.wait(urls.map(_loadImage));
} }
@ -20,6 +26,9 @@ class ImageMap {
return image; return image;
} }
/// Returns a preloaded image, given its [url].
ui.Image getImage(String url) => _images[url]; ui.Image getImage(String url) => _images[url];
/// Returns a preloaded image, given its [url].
ui.Image operator [](String url) => _images[url]; ui.Image operator [](String url) => _images[url];
} }

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// Labels are used to display a string of text in a the node tree. To align /// Labels are used to display a string of text in a the node tree. To align

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A [Node] that provides an intermediate rendering surface in the sprite /// A [Node] that provides an intermediate rendering surface in the sprite

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A NineSliceSprite is similar to a [Sprite], but it it can strech its /// A NineSliceSprite is similar to a [Sprite], but it it can strech its

View File

@ -1,7 +1,13 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// Converts degrees to radians.
double convertDegrees2Radians(double degrees) => degrees * math.PI/180.8; double convertDegrees2Radians(double degrees) => degrees * math.PI/180.8;
/// Converts radians to degrees.
double convertRadians2Degrees(double radians) => radians * 180.0/math.PI; double convertRadians2Degrees(double radians) => radians * 180.0/math.PI;
/// A base class for all objects that can be added to the sprite node tree and rendered to screen using [SpriteBox] and /// A base class for all objects that can be added to the sprite node tree and rendered to screen using [SpriteBox] and
@ -428,7 +434,8 @@ class Node {
return _transformMatrixBoxToNode; return _transformMatrixBoxToNode;
} }
Matrix4 inverseTransformMatrix() { /// The inverse transform matrix used by this node.
Matrix4 get inverseTransformMatrix {
if (_transformMatrixInverse == null) { if (_transformMatrixInverse == null) {
_transformMatrixInverse = new Matrix4.copy(transformMatrix); _transformMatrixInverse = new Matrix4.copy(transformMatrix);
_transformMatrixInverse.invert(); _transformMatrixInverse.invert();

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// An node that transforms its children using a 3D perspective projection. This /// An node that transforms its children using a 3D perspective projection. This

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// The super class of any [Node] that has a size. /// The super class of any [Node] that has a size.

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
class _Particle { class _Particle {
@ -41,6 +45,8 @@ class _ParticleAccelerations {
/// number of particles can never exceed the [maxParticles] limit. /// number of particles can never exceed the [maxParticles] limit.
class ParticleSystem extends Node { class ParticleSystem extends Node {
/// Creates a new particle system with the given properties. The only
/// required parameter is the texture, all other parameters are optional.
ParticleSystem(this.texture, ParticleSystem(this.texture,
{this.life: 1.5, {this.life: 1.5,
this.lifeVar: 1.0, this.lifeVar: 1.0,
@ -208,6 +214,8 @@ class ParticleSystem extends Node {
double _emitCounter; double _emitCounter;
int _numEmittedParticles = 0; int _numEmittedParticles = 0;
/// The over all opacity of the particle system. This value is multiplied by
/// the opacity of the individual particles.
double opacity = 1.0; double opacity = 1.0;
static Paint _paint = new Paint() static Paint _paint = new Paint()

View File

@ -1,7 +1,14 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// An audio asset loaded by the SoundEffectPlayer. /// An audio asset loaded by the SoundEffectPlayer.
class SoundEffect { class SoundEffect {
/// Creates a new sound effect with the given sound id. Normally,
/// SoundEffect objects are created through the [SoundEffectPlayer].
SoundEffect(this._soundId); SoundEffect(this._soundId);
int _soundId; int _soundId;
@ -9,6 +16,9 @@ class SoundEffect {
/// A sound being played by the SoundEffectPlayer. /// A sound being played by the SoundEffectPlayer.
class SoundEffectStream { class SoundEffectStream {
/// Creates a new SoundEffectStream. Typically SoundEffectStream objects are
/// created by the SoundEffectPlayer.
SoundEffectStream(SoundEffectPlayer player, int streamId, { SoundEffectStream(SoundEffectPlayer player, int streamId, {
double leftVolume, double leftVolume,
double rightVolume, double rightVolume,
@ -27,10 +37,12 @@ class SoundEffectStream {
SoundPoolProxy get _soundPool => _player._soundPool; SoundPoolProxy get _soundPool => _player._soundPool;
/// Stop the sound effect.
void stop() { void stop() {
_soundPool.ptr.stop(_streamId); _soundPool.ptr.stop(_streamId);
} }
/// True if the sound effect is paused.
bool get paused => _paused; bool get paused => _paused;
bool _paused; bool _paused;
set paused(bool value) { set paused(bool value) {
@ -42,6 +54,7 @@ class SoundEffectStream {
} }
} }
/// Left volume of the sound effect, valid values are 0.0 to 1.0.
double get leftVolume => _leftVolume; double get leftVolume => _leftVolume;
double _leftVolume; double _leftVolume;
set leftVolume(double value) { set leftVolume(double value) {
@ -49,6 +62,7 @@ class SoundEffectStream {
_soundPool.ptr.setVolume(_streamId, <double>[_leftVolume, _rightVolume]); _soundPool.ptr.setVolume(_streamId, <double>[_leftVolume, _rightVolume]);
} }
/// Right volume of the sound effect, valid values are 0.0 to 1.0.
double get rightVolume => _rightVolume; double get rightVolume => _rightVolume;
double _rightVolume; double _rightVolume;
set rightVolume(double value) { set rightVolume(double value) {
@ -56,6 +70,8 @@ class SoundEffectStream {
_soundPool.ptr.setVolume(_streamId, <double>[_leftVolume, _rightVolume]); _soundPool.ptr.setVolume(_streamId, <double>[_leftVolume, _rightVolume]);
} }
/// The pitch of the sound effect, a value of 1.0 plays back the sound effect
/// at normal speed. Cannot be negative.
double get pitch => _pitch; double get pitch => _pitch;
double _pitch; double _pitch;
set pitch(double value) { set pitch(double value) {
@ -64,7 +80,11 @@ class SoundEffectStream {
} }
} }
/// The SoundEffectPlayer loads and plays sound effects.
class SoundEffectPlayer { class SoundEffectPlayer {
/// Creates a new SoundEffectPlayer with a max number of simultaneous
/// streams specified.
SoundEffectPlayer(int maxStreams) { SoundEffectPlayer(int maxStreams) {
MediaServiceProxy mediaService = new MediaServiceProxy.unbound(); MediaServiceProxy mediaService = new MediaServiceProxy.unbound();
shell.connectToService("mojo:media_service", mediaService); shell.connectToService("mojo:media_service", mediaService);
@ -76,6 +96,7 @@ class SoundEffectPlayer {
bool _paused; bool _paused;
int _nextStreamId = 0; int _nextStreamId = 0;
/// Loads a sound effect.
Future<SoundEffect> load(MojoDataPipeConsumer data) async { Future<SoundEffect> load(MojoDataPipeConsumer data) async {
SoundPoolLoadResponseParams result = await _soundPool.ptr.load(data); SoundPoolLoadResponseParams result = await _soundPool.ptr.load(data);
if (result.success) if (result.success)
@ -84,6 +105,7 @@ class SoundEffectPlayer {
throw new Exception('Unable to load sound'); throw new Exception('Unable to load sound');
} }
/// Plays a sound effect.
Future<SoundEffectStream> play(SoundEffect sound, { Future<SoundEffectStream> play(SoundEffect sound, {
double leftVolume: 1.0, double leftVolume: 1.0,
double rightVolume: 1.0, double rightVolume: 1.0,
@ -106,6 +128,7 @@ class SoundEffectPlayer {
throw new Exception('Unable to play sound'); throw new Exception('Unable to play sound');
} }
/// Set to true to pause a sound effect.
bool get paused => _paused; bool get paused => _paused;
set paused(bool value) { set paused(bool value) {
@ -118,23 +141,44 @@ class SoundEffectPlayer {
} }
} }
/// Signature for callbacks used by [SoundTrack].
typedef void SoundTrackCallback(SoundTrack soundTrack); typedef void SoundTrackCallback(SoundTrack soundTrack);
/// Signature for callbacks used by [SoundTrack].
typedef void SoundTrackBufferingCallback(SoundTrack soundTrack, int index); typedef void SoundTrackBufferingCallback(SoundTrack soundTrack, int index);
/// A sound track is typically longer than a [SoundEffect]. Use sound tracks to
/// play back music or ambient sounds.
class SoundTrack { class SoundTrack {
MediaPlayerProxy _player; MediaPlayerProxy _player;
/// Called when the sound has finished playing.
SoundTrackCallback onSoundComplete; SoundTrackCallback onSoundComplete;
/// Called when a seek operation has finished.
SoundTrackCallback onSeekComplete; SoundTrackCallback onSeekComplete;
/// Called when buffering is being performed.
SoundTrackBufferingCallback onBufferingUpdate; SoundTrackBufferingCallback onBufferingUpdate;
/// If true, the sound track will automatically loop.
bool loop; bool loop;
/// The current playback time in seconds.
double time; double time;
/// The volume the sound track is currently played at, valid range is 0.0 to
/// 1.0.
double volume; double volume;
} }
SoundTrackPlayer _sharedSoundTrackPlayer; SoundTrackPlayer _sharedSoundTrackPlayer;
/// Loads and plays [SoundTrack]s.
class SoundTrackPlayer { class SoundTrackPlayer {
/// Creates a new [SoundTrackPlayer], typically you will want to use the
/// [sharedInstance] method to receive the player.
SoundTrackPlayer() { SoundTrackPlayer() {
_mediaService = new MediaServiceProxy.unbound(); _mediaService = new MediaServiceProxy.unbound();
shell.connectToService("mojo:media_service", _mediaService); shell.connectToService("mojo:media_service", _mediaService);
@ -144,10 +188,13 @@ class SoundTrackPlayer {
Set<SoundTrack> _soundTracks = new HashSet<SoundTrack>(); Set<SoundTrack> _soundTracks = new HashSet<SoundTrack>();
/// Retrives a singleton object of the SoundTrackPlayer, use this method
/// in favor for the constructor.
static SoundTrackPlayer sharedInstance() { static SoundTrackPlayer sharedInstance() {
return _sharedSoundTrackPlayer ??= new SoundTrackPlayer(); return _sharedSoundTrackPlayer ??= new SoundTrackPlayer();
} }
/// Loads a [SoundTrack].
Future<SoundTrack> load(Future<MojoDataPipeConsumer> pipe) async { Future<SoundTrack> load(Future<MojoDataPipeConsumer> pipe) async {
// Create media player // Create media player
SoundTrack soundTrack = new SoundTrack(); SoundTrack soundTrack = new SoundTrack();
@ -158,11 +205,13 @@ class SoundTrackPlayer {
return soundTrack; return soundTrack;
} }
/// Unloads a [SoundTrack] from memory.
void unload(SoundTrack soundTrack) { void unload(SoundTrack soundTrack) {
stop(soundTrack); stop(soundTrack);
_soundTracks.remove(soundTrack); _soundTracks.remove(soundTrack);
} }
/// Plays a [SoundTrack].
void play(SoundTrack soundTrack, { void play(SoundTrack soundTrack, {
bool loop: false, bool loop: false,
double volume: 1.0, double volume: 1.0,
@ -175,15 +224,19 @@ class SoundTrackPlayer {
_soundTracks.add(soundTrack); _soundTracks.add(soundTrack);
} }
/// Stops a [SoundTrack]. You may also want to call the [unload] method to
/// remove if from memory if you are not planning to play it again.
void stop(SoundTrack track) { void stop(SoundTrack track) {
track._player.ptr.pause(); track._player.ptr.pause();
} }
/// Pauses all [SoundTrack]s that are currently playing.
void pauseAll() { void pauseAll() {
for (SoundTrack soundTrack in _soundTracks) for (SoundTrack soundTrack in _soundTracks)
soundTrack._player.ptr.pause(); soundTrack._player.ptr.pause();
} }
/// Resumes all [SoundTrack]s that have been loaded by this player.
void resumeAll() { void resumeAll() {
for (SoundTrack soundTrack in _soundTracks) for (SoundTrack soundTrack in _soundTracks)
soundTrack._player.ptr.start(); soundTrack._player.ptr.start();

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A Sprite is a [Node] that renders a bitmap image to the screen. /// A Sprite is a [Node] that renders a bitmap image to the screen.
@ -84,6 +88,8 @@ class Sprite extends NodeWithSize with SpritePaint {
} }
} }
/// Defines properties, such as [opacity] and [transferMode] that are shared
/// between [Node]s that render textures to screen.
abstract class SpritePaint { abstract class SpritePaint {
double _opacity = 1.0; double _opacity = 1.0;

View File

@ -1,26 +1,36 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// Options for setting up a [SpriteBox]. /// Options for setting up a [SpriteBox]'s coordinate system.
///
/// * [nativePoints]: use the same points as the parent [Widget].
/// * [letterbox]: use the size of the root node for the coordinate system, and constrain the aspect ratio and trim off
/// areas that end up outside the screen.
/// * [stretch]: use the size of the root node for the coordinate system, and scale it to fit the size of the box.
/// * [scaleToFit]: similar to the letterbox option, but instead of trimming areas the sprite system will be scaled
/// down to fit the box.
/// * [fixedWidth]: use the width of the root node to set the size of the coordinate system, and change
/// the height of the root node to fit the box.
/// * [fixedHeight]: use the height of the root node to set the size of the coordinate system, and change
/// the width of the root node to fit the box.
enum SpriteBoxTransformMode { enum SpriteBoxTransformMode {
/// Use the same points as the parent [Widget].
nativePoints, nativePoints,
/// Use the size of the root node for the coordinate system, and constrain the
/// aspect ratio and trim off areas that end up outside the screen.
letterbox, letterbox,
/// Use the size of the root node for the coordinate system, and scale it to
/// fit the size of the box.
stretch, stretch,
/// Similar to the letterbox option, but instead of trimming areas the sprite
/// system will be scaled down to fit the box.
scaleToFit, scaleToFit,
/// Use the width of the root node to set the size of the coordinate system,
/// and change the height of the root node to fit the box.
fixedWidth, fixedWidth,
/// Use the height of the root node to set the size of the coordinate system,
/// and change the width of the root node to fit the box.
fixedHeight, fixedHeight,
} }
/// A [RenderBox] that draws a sprite world represented by a [Node] tree.
class SpriteBox extends RenderBox { class SpriteBox extends RenderBox {
// Setup // Setup
@ -71,31 +81,6 @@ class SpriteBox extends RenderBox {
// Member variables // Member variables
// Root node for drawing
NodeWithSize _rootNode;
set rootNode (NodeWithSize value) {
if (value == _rootNode) return;
// Ensure that the root node has a size
assert(_transformMode == SpriteBoxTransformMode.nativePoints
|| value.size.width > 0);
assert(_transformMode == SpriteBoxTransformMode.nativePoints
|| value.size.height > 0);
// Remove sprite box references
if (_rootNode != null)
_removeSpriteBoxReference(_rootNode);
// Update the value
_rootNode = value;
_actionControllers = null;
// Add new references
_addSpriteBoxReference(_rootNode);
markNeedsLayout();
}
// Tracking of frame rate and updates // Tracking of frame rate and updates
Duration _lastTimeStamp; Duration _lastTimeStamp;
double _frameRate = 0.0; double _frameRate = 0.0;
@ -126,14 +111,16 @@ class SpriteBox extends RenderBox {
List<Node> _constrainedNodes; List<Node> _constrainedNodes;
Rect _visibleArea; /// A rectangle that represents the visible area of the sprite world's
/// coordinate system.
Rect get visibleArea { Rect get visibleArea {
if (_visibleArea == null) if (_visibleArea == null)
_calcTransformMatrix(); _calcTransformMatrix();
return _visibleArea; return _visibleArea;
} }
Rect _visibleArea;
bool _initialized = false; bool _initialized = false;
// Properties // Properties
@ -143,6 +130,30 @@ class SpriteBox extends RenderBox {
/// var rootNode = mySpriteBox.rootNode; /// var rootNode = mySpriteBox.rootNode;
NodeWithSize get rootNode => _rootNode; NodeWithSize get rootNode => _rootNode;
NodeWithSize _rootNode;
set rootNode (NodeWithSize value) {
if (value == _rootNode) return;
// Ensure that the root node has a size
assert(_transformMode == SpriteBoxTransformMode.nativePoints
|| value.size.width > 0);
assert(_transformMode == SpriteBoxTransformMode.nativePoints
|| value.size.height > 0);
// Remove sprite box references
if (_rootNode != null)
_removeSpriteBoxReference(_rootNode);
// Update the value
_rootNode = value;
_actionControllers = null;
// Add new references
_addSpriteBoxReference(_rootNode);
markNeedsLayout();
}
@override @override
void performLayout() { void performLayout() {
size = constraints.biggest; size = constraints.biggest;

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A widget that uses a [SpriteBox] to render a sprite node tree to the screen. /// A widget that uses a [SpriteBox] to render a sprite node tree to the screen.

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A sprite sheet packs a number of smaller images into a single large image. /// A sprite sheet packs a number of smaller images into a single large image.

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A texture represents a rectangular area of an image and is typically used to draw a sprite to the screen. /// A texture represents a rectangular area of an image and is typically used to draw a sprite to the screen.
@ -65,6 +69,7 @@ class Texture {
/// myTexture.pivot = new Point(0.5, 0.5); /// myTexture.pivot = new Point(0.5, 0.5);
Point pivot; Point pivot;
/// Creates a new Texture from a part of the current texture.
Texture textureFromRect(Rect rect, [String name = null]) { Texture textureFromRect(Rect rect, [String name = null]) {
assert(rect != null); assert(rect != null);
assert(!rotated); assert(!rotated);
@ -73,6 +78,8 @@ class Texture {
return new Texture._fromSpriteFrame(image, name, rect.size, false, false, srcFrame, dstFrame, new Point(0.5, 0.5)); return new Texture._fromSpriteFrame(image, name, rect.size, false, false, srcFrame, dstFrame, new Point(0.5, 0.5));
} }
/// Draws the texture to a [Canvas] at a specified [position] and with the
/// specified [paint].
void drawTexture(Canvas canvas, Point position, Paint paint) { void drawTexture(Canvas canvas, Point position, Paint paint) {
// Get drawing position // Get drawing position
double x = position.x; double x = position.x;

View File

@ -1,10 +1,20 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// A [Node] that draws a polyline from a list of points using the provided
/// [Texture]. The textured line draws static lines. If you want to create an
/// animated line, consider using the [EffectLine] instead.
class TexturedLine extends Node { class TexturedLine extends Node {
/// Creates a new TexturedLine.
TexturedLine(List<Point> points, List<Color> colors, List<double> widths, [Texture texture, List<double> textureStops]) { TexturedLine(List<Point> points, List<Color> colors, List<double> widths, [Texture texture, List<double> textureStops]) {
painter = new TexturedLinePainter(points, colors, widths, texture, textureStops); painter = new TexturedLinePainter(points, colors, widths, texture, textureStops);
} }
/// The painter used to draw the line.
TexturedLinePainter painter; TexturedLinePainter painter;
@override @override
@ -13,26 +23,34 @@ class TexturedLine extends Node {
} }
} }
/// Draws a polyline to a [Canvas] from a list of points using the provided [Texture].
class TexturedLinePainter { class TexturedLinePainter {
TexturedLinePainter(this._points, this.colors, this.widths, [Texture texture, this.textureStops]) { TexturedLinePainter(this._points, this.colors, this.widths, [Texture texture, this.textureStops]) {
this.texture = texture; this.texture = texture;
} }
List<Point> _points; /// The points that makes up the polyline.
List<Point> get points => _points; List<Point> get points => _points;
List<Point> _points;
set points(List<Point> points) { set points(List<Point> points) {
_points = points; _points = points;
_calculatedTextureStops = null; _calculatedTextureStops = null;
} }
/// The color of each point on the polyline. The color of the line will be
/// interpolated between the points.
List<Color> colors; List<Color> colors;
List<double> widths;
Texture _texture;
/// The width of the line at each point on the polyline.
List<double> widths;
/// The texture this line will be drawn using.
Texture get texture => _texture; Texture get texture => _texture;
Texture _texture;
set texture(Texture texture) { set texture(Texture texture) {
_texture = texture; _texture = texture;
if (texture == null) { if (texture == null) {
@ -47,41 +65,51 @@ class TexturedLinePainter {
} }
} }
/// Defines the position in the texture for each point on the polyline.
List<double> textureStops; List<double> textureStops;
List<double> _calculatedTextureStops; /// The [textureStops] used if no explicit texture stops has been provided.
List<double> get calculatedTextureStops { List<double> get calculatedTextureStops {
if (_calculatedTextureStops == null) if (_calculatedTextureStops == null)
_calculateTextureStops(); _calculateTextureStops();
return _calculatedTextureStops; return _calculatedTextureStops;
} }
List<double> _calculatedTextureStops;
double _length; double _length;
/// The length of the line.
double get length { double get length {
if (_calculatedTextureStops == null) if (_calculatedTextureStops == null)
_calculateTextureStops(); _calculateTextureStops();
return _length; return _length;
} }
/// The offset of the texture on the line.
double textureStopOffset = 0.0; double textureStopOffset = 0.0;
double _textureLoopLength; /// The length, in points, that the texture is stretched to. If the
/// textureLoopLength is shorter than the line, the texture will be looped.
double get textureLoopLength => textureLoopLength; double get textureLoopLength => textureLoopLength;
double _textureLoopLength;
set textureLoopLength(double textureLoopLength) { set textureLoopLength(double textureLoopLength) {
_textureLoopLength = textureLoopLength; _textureLoopLength = textureLoopLength;
_calculatedTextureStops = null; _calculatedTextureStops = null;
} }
/// If true, the textured line attempts to remove artifacts at sharp corners
/// on the polyline.
bool removeArtifacts = true; bool removeArtifacts = true;
/// The [TransferMode] used to draw the line to the [Canvas].
TransferMode transferMode = TransferMode.srcOver; TransferMode transferMode = TransferMode.srcOver;
Paint _cachedPaint = new Paint(); Paint _cachedPaint = new Paint();
/// Paints the line to the [canvas].
void paint(Canvas canvas) { void paint(Canvas canvas) {
// Check input values // Check input values
assert(_points != null); assert(_points != null);

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;

View File

@ -1,6 +1,13 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of flutter_sprites; part of flutter_sprites;
/// Provides a virtual joystick that can easily be added to your sprite scene.
class VirtualJoystick extends NodeWithSize { class VirtualJoystick extends NodeWithSize {
/// Creates a new virtual joystick.
VirtualJoystick() : super(new Size(160.0, 160.0)) { VirtualJoystick() : super(new Size(160.0, 160.0)) {
userInteractionEnabled = true; userInteractionEnabled = true;
handleMultiplePointers = false; handleMultiplePointers = false;
@ -17,11 +24,15 @@ class VirtualJoystick extends NodeWithSize {
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
} }
Point _value = Point.origin; /// Reads the current value of the joystick. A point with from (-1.0, -1.0)
/// to (1.0, 1.0). If the joystick isn't moved it will return (0.0, 0.0).
Point get value => _value; Point get value => _value;
Point _value = Point.origin;
bool _isDown = false; /// True if the user is currently touching the joystick.
bool get isDown => _isDown; bool get isDown => _isDown;
bool _isDown = false;
Point _pointerDownAt; Point _pointerDownAt;
Point _center; Point _center;