mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 20:13:50 +08:00
Move files to src and comply with the dart package layout convention (#621)
* 👌 Use `Offset` type directly in `JoystickAction.update` calculations (#631) * Move files to src and comply with the dart package layout convention * Fixing widgets example Co-authored-by: Serge Matveenko <lig@countzero.co> Co-authored-by: Erick Zanardo <erickzanardoo@gmail.com>
This commit is contained in:
@ -1,295 +1 @@
|
||||
import 'dart:collection';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'assets/images.dart';
|
||||
import 'extensions/vector2.dart';
|
||||
import 'flame.dart';
|
||||
import 'game/game.dart';
|
||||
|
||||
extension SpriteBatchExtension on Game {
|
||||
/// Utility method to load and cache the image for a [SpriteBatch] based on its options
|
||||
Future<SpriteBatch> loadSpriteBatch(
|
||||
String path, {
|
||||
Color defaultColor = const Color(0x00000000),
|
||||
BlendMode defaultBlendMode = BlendMode.srcOver,
|
||||
RSTransform defaultTransform,
|
||||
}) {
|
||||
return SpriteBatch.load(
|
||||
path,
|
||||
defaultColor: defaultColor,
|
||||
defaultBlendMode: defaultBlendMode,
|
||||
defaultTransform: defaultTransform,
|
||||
images: images,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the scale value used in [BatchItem.matrix], we can't determine this from the [Batchitem.transform],
|
||||
/// but we also don't need to do so because it is already calculated inside the transform values.
|
||||
const _defaultScale = 0.0;
|
||||
|
||||
/// A single item in a SpriteBatch.
|
||||
///
|
||||
/// Holds all the important information of a batch item,
|
||||
///
|
||||
/// Web currently does not support `Canvas.drawAtlas`, so a BatchItem will
|
||||
/// automatically calculate a transform matrix based on the [transform] value, to be
|
||||
/// used when rendering on the web. It will initialize a [destination] object
|
||||
/// and a [paint] object.
|
||||
class BatchItem {
|
||||
/// The source rectangle on the [SpriteBatch.atlas].
|
||||
final Rect source;
|
||||
|
||||
/// The destination rectangle for the Canvas.
|
||||
///
|
||||
/// It will be transformed by [matrix].
|
||||
final Rect destination;
|
||||
|
||||
/// The transform values for this batch item.
|
||||
final RSTransform transform;
|
||||
|
||||
/// The background color for this batch item.
|
||||
final Color color;
|
||||
|
||||
/// Fallback matrix for the web.
|
||||
///
|
||||
/// Because `Canvas.drawAtlas` is not supported on the web we also
|
||||
/// build a `Matrix4` based on the [transform] values.
|
||||
final Matrix4 matrix;
|
||||
|
||||
/// Paint object used for the web.
|
||||
final Paint paint;
|
||||
|
||||
BatchItem({
|
||||
@required this.source,
|
||||
@required this.transform,
|
||||
@required this.color,
|
||||
}) : assert(source != null),
|
||||
assert(transform != null),
|
||||
assert(color != null),
|
||||
matrix = Matrix4(
|
||||
transform.scos, transform.ssin, 0, 0, //
|
||||
-transform.ssin, transform.scos, 0, 0, //
|
||||
0, 0, _defaultScale, 0, //
|
||||
transform.tx, transform.ty, 0, 1, //
|
||||
),
|
||||
paint = Paint()..color = color,
|
||||
destination = Offset.zero & source.size;
|
||||
}
|
||||
|
||||
/// The SpriteBatch API allows for rendering multiple items at once.
|
||||
///
|
||||
/// This class allows for optimization when you want to draw many parts of an
|
||||
/// image onto the canvas. It is more efficient than using multiple calls to [drawImageRect]
|
||||
/// and provides more functionality by allowing each [BatchItem] to have their own transform
|
||||
/// rotation and color.
|
||||
///
|
||||
/// By collecting all the necessary transforms on a single image and sending those transforms
|
||||
/// in a single batch to the GPU, we can render multiple parts of a single image at once.
|
||||
///
|
||||
/// **Note**: Currently web does not support `Canvas.drawAtlas`, which SpriteBatch uses under
|
||||
/// the hood, instead it will render each [BatchItem] using `Canvas.drawImageRect`, so there
|
||||
/// might be a performance hit on web when working with many batch items.
|
||||
class SpriteBatch {
|
||||
/// List of all the existing batch items.
|
||||
final _batchItems = <BatchItem>[];
|
||||
|
||||
/// The sources to use on the [atlas].
|
||||
final _sources = <Rect>[];
|
||||
|
||||
/// The sources list shouldn't be modified directly, that is why an
|
||||
/// [UnmodifiableListView] is used. If you want to add sources use the
|
||||
/// [add] or [addTransform] method.
|
||||
UnmodifiableListView<Rect> get sources {
|
||||
return UnmodifiableListView<Rect>(_sources);
|
||||
}
|
||||
|
||||
/// The transforms that should be applied on the [_sources].
|
||||
final _transforms = <RSTransform>[];
|
||||
|
||||
/// The transforms list shouldn't be modified directly, that is why an
|
||||
/// [UnmodifiableListView] is used. If you want to add transforms use the
|
||||
/// [add] or [addTransform] method.
|
||||
UnmodifiableListView<RSTransform> get transforms {
|
||||
return UnmodifiableListView<RSTransform>(_transforms);
|
||||
}
|
||||
|
||||
/// The background color for the [_sources].
|
||||
final _colors = <Color>[];
|
||||
|
||||
/// The colors list shouldn't be modified directly, that is why an
|
||||
/// [UnmodifiableListView] is used. If you want to add colors use the
|
||||
/// [add] or [addTransform] method.
|
||||
UnmodifiableListView<Color> get colors {
|
||||
return UnmodifiableListView<Color>(_colors);
|
||||
}
|
||||
|
||||
/// The atlas used by the [SpriteBatch].
|
||||
final Image atlas;
|
||||
|
||||
/// The default color, used as a background color for a [BatchItem].
|
||||
final Color defaultColor;
|
||||
|
||||
/// The default transform, used when a transform was not supplied for a [BatchItem].
|
||||
final RSTransform defaultTransform;
|
||||
|
||||
/// The default blend mode, used for blending a batch item.
|
||||
final BlendMode defaultBlendMode;
|
||||
|
||||
/// The width of the [atlas].
|
||||
int get width => atlas.width;
|
||||
|
||||
/// The height of the [atlas].
|
||||
int get height => atlas.height;
|
||||
|
||||
/// The size of the [atlas].
|
||||
Vector2 get size => Vector2Extension.fromInts(width, height);
|
||||
|
||||
SpriteBatch(
|
||||
this.atlas, {
|
||||
this.defaultColor = const Color(0x00000000),
|
||||
this.defaultBlendMode = BlendMode.srcOver,
|
||||
this.defaultTransform,
|
||||
}) : assert(atlas != null),
|
||||
assert(defaultColor != null);
|
||||
|
||||
/// Takes a path of an image, and optional arguments for the SpriteBatch.
|
||||
///
|
||||
/// When the [images] is omitted, the global [Flame.images] is used.
|
||||
static Future<SpriteBatch> load(
|
||||
String path, {
|
||||
Color defaultColor = const Color(0x00000000),
|
||||
BlendMode defaultBlendMode = BlendMode.srcOver,
|
||||
RSTransform defaultTransform,
|
||||
Images images,
|
||||
}) async {
|
||||
final _images = images ?? Flame.images;
|
||||
return SpriteBatch(
|
||||
await _images.load(path),
|
||||
defaultColor: defaultColor,
|
||||
defaultTransform: defaultTransform ?? RSTransform(1, 0, 0, 0),
|
||||
defaultBlendMode: defaultBlendMode,
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a new batch item using a RSTransform.
|
||||
///
|
||||
/// The [source] parameter is the source location on the [atlas]. You can position it
|
||||
/// on the canvas using the [offset] parameter.
|
||||
///
|
||||
/// The [color] paramater allows you to render a color behind the batch item, as a background color.
|
||||
///
|
||||
/// The [add] method may be a simpler way to add a batch item to the batch. However,
|
||||
/// if there is a way to factor out the computations of the sine and cosine of the
|
||||
/// rotation so that they can be reused over multiple calls to this constructor,
|
||||
/// it may be more efficient to directly use this method instead.
|
||||
void addTransform({
|
||||
@required Rect source,
|
||||
RSTransform transform,
|
||||
Color color,
|
||||
}) {
|
||||
final batchItem = BatchItem(
|
||||
source: source,
|
||||
transform: transform ??= defaultTransform ?? RSTransform(1, 0, 0, 0),
|
||||
color: color ?? defaultColor,
|
||||
);
|
||||
|
||||
_batchItems.add(batchItem);
|
||||
|
||||
_sources.add(batchItem.source);
|
||||
_transforms.add(batchItem.transform);
|
||||
_colors.add(batchItem.color);
|
||||
}
|
||||
|
||||
/// Add a new batch item.
|
||||
///
|
||||
/// The [source] parameter is the source location on the [atlas]. You can position it
|
||||
/// on the canvas using the [offset] parameter.
|
||||
///
|
||||
/// You can transform the sprite from its [offset] using [scale], [rotation] and [anchor].
|
||||
///
|
||||
/// The [color] paramater allows you to render a color behind the batch item, as a background color.
|
||||
///
|
||||
/// This method creates a new [RSTransform] based on the given transform arguments. If many [RSTransform] objects are being
|
||||
/// created and there is a way to factor out the computations of the sine and cosine of the rotation
|
||||
/// (which are computed each time this method is called) and reuse them over multiple [RSTransform] objects,
|
||||
/// it may be more efficient to directly use the more direct [addTransform] method instead.
|
||||
void add({
|
||||
@required Rect source,
|
||||
double scale = 1.0,
|
||||
Vector2 anchor,
|
||||
double rotation = 0,
|
||||
Vector2 offset,
|
||||
Color color,
|
||||
}) {
|
||||
anchor ??= Vector2.zero();
|
||||
offset ??= Vector2.zero();
|
||||
RSTransform transform;
|
||||
|
||||
// If any of the transform arguments is different from the defaults,
|
||||
// then we create one. This is to prevent unnecessary computations
|
||||
// of the sine and cosine of the rotation.
|
||||
if (scale != 1.0 ||
|
||||
anchor != Vector2.zero() ||
|
||||
rotation != 0 ||
|
||||
offset != Vector2.zero()) {
|
||||
transform = RSTransform.fromComponents(
|
||||
scale: scale,
|
||||
anchorX: anchor.x,
|
||||
anchorY: anchor.y,
|
||||
rotation: rotation,
|
||||
translateX: offset.x,
|
||||
translateY: offset.y,
|
||||
);
|
||||
}
|
||||
|
||||
addTransform(source: source, transform: transform, color: color);
|
||||
}
|
||||
|
||||
/// Clear the SpriteBatch so it can be reused.
|
||||
void clear() {
|
||||
_sources.clear();
|
||||
_transforms.clear();
|
||||
_colors.clear();
|
||||
_batchItems.clear();
|
||||
}
|
||||
|
||||
void render(
|
||||
Canvas canvas, {
|
||||
BlendMode blendMode,
|
||||
Rect cullRect,
|
||||
Paint paint,
|
||||
}) {
|
||||
paint ??= Paint();
|
||||
|
||||
if (kIsWeb) {
|
||||
for (final batchItem in _batchItems) {
|
||||
paint..blendMode = blendMode ?? paint.blendMode ?? defaultBlendMode;
|
||||
|
||||
canvas
|
||||
..save()
|
||||
..transform(batchItem.matrix.storage)
|
||||
..drawRect(batchItem.destination, batchItem.paint)
|
||||
..drawImageRect(
|
||||
atlas,
|
||||
batchItem.source,
|
||||
batchItem.destination,
|
||||
paint,
|
||||
)
|
||||
..restore();
|
||||
}
|
||||
} else {
|
||||
canvas.drawAtlas(
|
||||
atlas,
|
||||
_transforms,
|
||||
_sources,
|
||||
_colors,
|
||||
blendMode ?? defaultBlendMode,
|
||||
cullRect,
|
||||
paint,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export 'src/sprite_batch.dart';
|
||||
|
||||
Reference in New Issue
Block a user