mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 03:15:43 +08:00
Adding more docs, dont need to commit doc/api folder (its generated)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@
|
||||
.pub/
|
||||
packages
|
||||
pubspec.lock
|
||||
doc/api/
|
||||
|
||||
@ -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<Sprite> 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());
|
||||
}
|
||||
|
||||
@ -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<String, File> 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<List<File>> loadAll(List<String> fileNames) async {
|
||||
return Future.wait(fileNames.map(load));
|
||||
}
|
||||
|
||||
/// Load a single [fileName] to the cache.
|
||||
///
|
||||
/// Also retruns a [Future] to access that file.
|
||||
Future<File> load(String fileName) async {
|
||||
if (!loadedFiles.containsKey(fileName)) {
|
||||
loadedFiles[fileName] = await _fetchToMemory(fileName);
|
||||
@ -43,13 +61,22 @@ class Audio {
|
||||
return loadedFiles[fileName];
|
||||
}
|
||||
|
||||
Future<AudioPlayer> 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<AudioPlayer> 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<AudioPlayer> 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<AudioPlayer> loop(String fileName, {double volume = 1.0}) async {
|
||||
File file = await load(fileName);
|
||||
AudioPlayer player = new AudioPlayer();
|
||||
player.setCompletionHandler(
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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<Size> 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);
|
||||
|
||||
Reference in New Issue
Block a user