Line data Source code
1 : import 'dart:ui'; 2 : 3 : import 'extensions/vector2.dart'; 4 : import 'sprite.dart'; 5 : import 'sprite_animation.dart'; 6 : 7 : /// Utility class to help extract animations and sprites from a sprite sheet image. 8 : /// 9 : /// A sprite sheet is a single image in which several regions can be defined as individual sprites. 10 : /// For the purposes of this class, all of these regions must be identically sized rectangles. 11 : /// You can use the [Sprite] class directly if you want to have varying shapes. 12 : /// 13 : /// Each sprite in this sheet can be identified either by it's (row, col) pair or 14 : /// by it's "id", which is basically it's sequenced index if the image is put in a 15 : /// single line. The sprites can be used to compose an animation easily if they 16 : /// all the frames happen to be sequentially on the same row. 17 : /// Sprites are lazily generated but cached. 18 : class SpriteSheet { 19 : /// The src image from which each sprite will be generated. 20 : final Image image; 21 : 22 : /// The size of each rectangle within the image that define each sprite. 23 : /// 24 : /// For example, if this sprite sheet is a tile map, this would be the tile size. 25 : /// If it's an animation sheet, this would be the frame size. 26 : final Vector2 srcSize; 27 : 28 : final Map<int, Sprite> _spriteCache = {}; 29 : 30 : /// Creates a sprite sheet given the image and the tile size. 31 0 : SpriteSheet({ 32 : required this.image, 33 : required this.srcSize, 34 : }); 35 : 36 0 : SpriteSheet.fromColumnsAndRows({ 37 : required this.image, 38 : required int columns, 39 : required int rows, 40 0 : }) : srcSize = Vector2( 41 0 : image.width / columns, 42 0 : image.height / rows, 43 : ); 44 : 45 : /// Compute the number of columns the image has 46 : /// by using the image width and tile size. 47 0 : int get columns => image.width ~/ srcSize.x; 48 : 49 : /// Compute the number of rows the image has 50 : /// by using the image height and tile size. 51 0 : int get rows => image.height ~/ srcSize.y; 52 : 53 : /// Gets the sprite in the position (row, column) on the sprite sheet grid. 54 : /// 55 : /// This is lazily computed and cached for your convenience. 56 0 : Sprite getSprite(int row, int column) { 57 0 : return getSpriteById(row * columns + column); 58 : } 59 : 60 : /// Gets teh sprite with id [spriteId] from the grid. 61 : /// 62 : /// The ids are defined as starting at 0 on the top left and going 63 : /// sequentially on each row. 64 : /// This is lazily computed and cached for your convenience. 65 0 : Sprite getSpriteById(int spriteId) { 66 0 : return _spriteCache[spriteId] ??= _computeSprite(spriteId); 67 : } 68 : 69 0 : Sprite _computeSprite(int spriteId) { 70 0 : final i = spriteId % columns; 71 0 : final j = spriteId ~/ columns; 72 0 : return Sprite( 73 0 : image, 74 0 : srcPosition: Vector2Extension.fromInts(i, j)..multiply(srcSize), 75 0 : srcSize: srcSize, 76 : ); 77 : } 78 : 79 : /// Creates a [SpriteAnimation] from this SpriteSheet, using the sequence 80 : /// of sprites on a given row. 81 : /// 82 : /// [from] and [to] can be specified to create an animation 83 : /// from a subset of the columns on the row 84 0 : SpriteAnimation createAnimation({ 85 : required int row, 86 : required double stepTime, 87 : bool loop = true, 88 : int from = 0, 89 : int? to, 90 : }) { 91 0 : to ??= columns; 92 : 93 0 : final spriteList = List<int>.generate(to - from, (i) => from + i) 94 0 : .map((e) => getSprite(row, e)) 95 0 : .toList(); 96 : 97 0 : return SpriteAnimation.spriteList( 98 : spriteList, 99 : stepTime: stepTime, 100 : loop: loop, 101 : ); 102 : } 103 : }