From fe9dbb7ff251baf56159aa5613f098eee8d1fbbb Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Sat, 26 May 2018 21:59:37 -0300 Subject: [PATCH] Adding more docs, dont need to commit doc/api folder (its generated) --- .gitignore | 1 + lib/animation.dart | 41 ++++++++++++++++++++++++++++++++--- lib/audio.dart | 33 +++++++++++++++++++++++++--- lib/components/component.dart | 2 +- lib/flame.dart | 16 ++++++++++++++ lib/util.dart | 32 +++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 451d0956d..7ed665dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ .pub/ packages pubspec.lock +doc/api/ diff --git a/lib/animation.dart b/lib/animation.dart index ade4306ab..b49a6e244 100644 --- a/lib/animation.dart +++ b/lib/animation.dart @@ -1,18 +1,43 @@ import 'dart:math' as math; import 'sprite.dart'; +/// Represents an animation, that is, a list of sprites that change with time. class Animation { + + /// The sprites that compose this animation. List sprites; + + /// Time that it takes to go to the next sprite on the list, in seconds. double stepTime = 0.1; + + /// Current life time (total time) of this animation. + /// + /// It's ticked by the update method. double lifeTime = 0.0; + + /// Wether the animation loops after the last sprite of the list, going back to the first, or keeps returning the last when done. bool loop = true; + /// Creates an empty animation Animation() { this.sprites = []; } - Animation.spriteList(this.sprites, {this.stepTime, this.lifeTime}); + /// Creates an animation based on the parameters + Animation.spriteList(this.sprites, {this.stepTime, this.lifeTime, this.loop}); + /// Automatically creates a sequenced animation, that is, an animation based on a sprite sheet. + /// + /// From a single image source, it creates multiple sprites based on the parameters: + /// [amount]: how many sprites this animation is composed of + /// [textureX]: x position on the original image to start (defaults to 0) + /// [textureY]: y position on the original image to start (defaults to 0) + /// [textureWidth]: width of each frame (defaults to null, that is, full width of the sprite sheet) + /// [textureHeight]: height of each frame (defaults to null, that is, full height of the sprite sheet) + /// + /// For example, if you have a spritesheet where each row is an animation, and each frame is 32x32 + /// new Animation.sequenced('sheet.png', 8, textureY: 32.0 * i, textureWidth: 32.0, textureHeight: 32.0); + /// This will create the i-th animation on the 'sheet.png', given it has 8 frames. Animation.sequenced( String imagePath, int amount, { @@ -33,21 +58,31 @@ class Animation { } } + /// Current step of the animation, that is, which frame should be shown (ignoring boundary conditions) int get currentStep => (lifeTime / stepTime).round(); + /// Gets tha current [Sprite] that should be shown, given the [currentStep] and the boundary conditions. + /// + /// In case [currentStep] is greater than the sprites length: + /// * If [loop] is true, it will return the last sprite. Otherwise, it will go back to the first. Sprite getSprite() { int i = currentStep; return sprites[loop ? i % sprites.length : math.min(i, sprites.length - 1)]; } + /// If [loop] is false, returns wether the animation is done (fixed in the last Sprite). + /// + /// Always returns false otherwise. bool done() { return loop ? false : currentStep >= sprites.length; } - void update(double t) { - this.lifeTime += t; + /// Updates this animation, ticking the lifeTime by an amount [dt] (in seconds). + void update(double dt) { + this.lifeTime += dt; } + /// Wether all sprites composing this animation are loaded. bool loaded() { return !sprites.any((sprite) => !sprite.loaded()); } diff --git a/lib/audio.dart b/lib/audio.dart index 4f121b5dc..fb4138706 100644 --- a/lib/audio.dart +++ b/lib/audio.dart @@ -7,17 +7,29 @@ import 'dart:io'; import 'package:audioplayers/audioplayer.dart'; import 'package:path_provider/path_provider.dart'; +/// This class represents a cache for audio files to be played. +/// +/// It uses the [AudioPlayer] from the audioplayers lib to play. +/// Flutter can only play audios on device folders, so this first copies files to a temporary folder and the plays then. +/// You can pre-cache your audio, or clear the cache, as desired. class Audio { + + /// A reference to the loaded files. Map loadedFiles = {}; + /// Clear the cache of the file [fileName]. + /// + /// Does nothing if there was already no cache. void clear(String fileName) { loadedFiles.remove(fileName); } + /// Clear the whole cache. void clearCache() { loadedFiles.clear(); } + /// Disable [AudioPlayer] logs (enable only if debuggin, otherwise they can be quite overwhelming). void disableLog() { AudioPlayer.logEnabled = false; } @@ -32,10 +44,16 @@ class Audio { .writeAsBytes((await _fetchAsset(fileName)).buffer.asUint8List()); } + /// Load all the [fileNames] provided to the cache. + /// + /// Also retruns a list of [Future]s for those files. Future> loadAll(List fileNames) async { return Future.wait(fileNames.map(load)); } + /// Load a single [fileName] to the cache. + /// + /// Also retruns a [Future] to access that file. Future load(String fileName) async { if (!loadedFiles.containsKey(fileName)) { loadedFiles[fileName] = await _fetchToMemory(fileName); @@ -43,13 +61,22 @@ class Audio { return loadedFiles[fileName]; } - Future play(String fileName, {volume: 1.0}) async { + /// Plays the given [fileName]. + /// + /// If the file is already cached, it plays imediatelly. Otherwise, first waits for the file to load. + /// It creates a new instance of [AudioPlayer], so it does not affect other audios playing. + /// The instance is returned, to allow later access. + Future play(String fileName, {double volume = 1.0}) async { File file = await load(fileName); return await new AudioPlayer() ..play(file.path, isLocal: true, volume: volume); } - Future loop(String fileName, {volume: 1.0}) async { + /// Like [play], but loops the audio (starts over once finished). + /// + /// It adds a completion handler that starts to play again once finished. + /// The instance of [AudioPlayer] created is returned, so you can use it to stop the playback as desired. + Future loop(String fileName, {double volume = 1.0}) async { File file = await load(fileName); AudioPlayer player = new AudioPlayer(); player.setCompletionHandler( @@ -57,4 +84,4 @@ class Audio { return await player ..play(file.path, isLocal: true); } -} +} \ No newline at end of file diff --git a/lib/components/component.dart b/lib/components/component.dart index a3f25e271..74e86e924 100644 --- a/lib/components/component.dart +++ b/lib/components/component.dart @@ -30,7 +30,7 @@ abstract class Component { /// Wether this component has been loaded yet. If not loaded, [BaseGame] will not try to render it. /// /// Sprite based components can use this to let [BaseGame] know not to try to render when the [Sprite] has not been loaded yet. - /// Note that for a more consistent experience, you can pre-load all your assets beforehand with [Flame.images.loadAll]. + /// Note that for a more consistent experience, you can pre-load all your assets beforehand with Flame.images.loadAll. bool loaded() => true; /// Wether this should be destroyed or not. diff --git a/lib/flame.dart b/lib/flame.dart index e9e9f8439..387cd8b49 100644 --- a/lib/flame.dart +++ b/lib/flame.dart @@ -9,20 +9,36 @@ import 'audio.dart'; import 'images.dart'; import 'util.dart'; +/// This class holds static references to some useful objects to use in your game. +/// +/// You can access shared instances of [Audio], [Images] and [Util]. +/// Most games should need only one instance of each, and should use this class to manage that reference. class Flame { + + /// Access a shared instance of the [Audio] class. static Audio audio = new Audio(); + + /// Access a shared instance of the [Images] class. static Images images = new Images(); + + /// Access a shared instance of the [Util] class. static Util util = new Util(); + /// TODO verify if this is still needed (I don't think so) static void initialize() { FlameBiding.ensureInitialized(); } + /// TODO verify if this is still needed (I don't think so) static void initializeWidget() { WidgetsFlutterBinding.ensureInitialized(); } } +/// This class never needs to be used. +/// +/// It only exists here in order for [BindingBase] to setup Flutter services. +/// TODO: this could possibly be private, verify if it'd work. class FlameBiding extends BindingBase with GestureBinding, ServicesBinding { static FlameBiding instance; diff --git a/lib/util.dart b/lib/util.dart index ac319488e..2f26fb6c6 100644 --- a/lib/util.dart +++ b/lib/util.dart @@ -7,11 +7,24 @@ import 'package:flutter/services.dart'; import 'position.dart'; +/// Some utilities that did not fit anywhere else. +/// +/// To use this class, access it via [Flame.util]. class Util { + /// Sets the app to be fullscreen (no buttons bar os notifications on top). + /// + /// Most games should probably be this way. void fullScreen() { SystemChrome.setEnabledSystemUIOverlays([]); } + /// Waits for the initial screen dimensions to be avaliable. + /// + /// Because of flutter's issue #5259, when the app starts the size might be 0x0. + /// This waits for the information to be properly updated. + /// + /// A best practice would be to implement there resize hooks on your game and components and don't use this at all. + /// Make sure your components are able to render and update themselves for any possible screen size. Future initialDimensions() async { // https://github.com/flutter/flutter/issues/5259 // "In release mode we start off at 0x0 but we don't in debug mode" @@ -29,6 +42,17 @@ class Util { }); } + /// Returns a [material.TextPainter] that allows for text rendering and size measuring. + /// + /// Rendering text on the Canvas is not as trivial as it should. + /// This methods puts all exposes all possible parameters you might want to pass to render text, with sensible defaults. + /// Only the [text] is mandatory. + /// It returns a [material.TextPainter]. that have the properties: paint, width and height. + /// Example usage: + /// + /// final tp = Flame.util.text('Score: $score', fontSize: 48.0, fontFamily: 'Awesome Font'); + /// p.paint(c, Offset(size.width - p.width - 10, size.height - p.height - 10)); + /// material.TextPainter text( String text, { double fontSize: 24.0, @@ -55,10 +79,14 @@ class Util { return tp; } + /// TODO verify if this is still needed (I don't think so) void enableEvents() { window.onPlatformMessage = BinaryMessages.handlePlatformMessage; } + /// This properly binds a gesture recognizer to your game. + /// + /// Use this in order to get it to work in case your app also contains other widgets. void addGestureRecognizer(GestureRecognizer recognizer) { GestureBinding.instance.pointerRouter.addGlobalRoute((PointerEvent e) { if (e is PointerDownEvent) { @@ -67,6 +95,10 @@ class Util { }); } + /// Utility method to render stuff on a specific place. + /// + /// Some render methods don't allow to pass a offset. + /// This method translate the canvas, draw what you want, and then translate back. void drawWhere(Canvas c, Position p, void Function(Canvas) fn) { c.translate(p.x, p.y); fn(c);