Line data Source code
1 : import 'dart:ui'; 2 : 3 : import '../extensions/vector2.dart'; 4 : import '../spritesheet.dart'; 5 : import 'position_component.dart'; 6 : 7 : /// This is just a pair of <int, int>. 8 : /// 9 : /// Represents a position in a matrix, or in this case, on the tilemap. 10 : class Block { 11 : /// x and y coordinates on the matrix 12 : int x, y; 13 : 14 0 : Block(this.x, this.y); 15 : 16 0 : @override 17 0 : String toString() => '($x, $y)'; 18 : } 19 : 20 : /// This component renders a tilemap, represented by an int matrix, given a 21 : /// tileset, in which the integers are the block ids. 22 : /// 23 : /// It can change the scale of each block by using the optional destTileSize 24 : /// property. 25 : class IsometricTileMapComponent extends PositionComponent { 26 : /// This is the tileset that will be used to render this map. 27 : SpriteSheet tileset; 28 : 29 : /// The positions of each block will be placed respecting this matrix. 30 : List<List<int>> matrix; 31 : 32 : /// Optionally provide a new tile size to render it scaled. 33 : Vector2? destTileSize; 34 : 35 : /// This is the vertical height of each block in the tile set. 36 : /// 37 : /// Note: this must be measured in the destination space. 38 : double? tileHeight; 39 : 40 0 : IsometricTileMapComponent( 41 : this.tileset, 42 : this.matrix, { 43 : this.destTileSize, 44 : this.tileHeight, 45 : Vector2? position, 46 : int? priority, 47 0 : }) : super(position: position, priority: priority); 48 : 49 : /// This is the size the tiles will be drawn (either original or overwritten). 50 0 : Vector2 get effectiveTileSize => destTileSize ?? tileset.srcSize; 51 : 52 : /// This is the vertical height of each block; by default it's half the tile size. 53 0 : double get effectiveTileHeight => tileHeight ?? (effectiveTileSize.x / 2); 54 : 55 0 : @override 56 : void render(Canvas c) { 57 0 : super.render(c); 58 : 59 0 : final size = effectiveTileSize; 60 0 : for (var i = 0; i < matrix.length; i++) { 61 0 : for (var j = 0; j < matrix[i].length; j++) { 62 0 : final element = matrix[i][j]; 63 0 : if (element != -1) { 64 0 : final sprite = tileset.getSpriteById(element); 65 0 : final p = getBlockPositionInts(j, i); 66 0 : sprite.render(c, position: p, size: size); 67 : } 68 : } 69 : } 70 : } 71 : 72 : /// Get the position in which a block must be in the isometric space. 73 : /// 74 : /// This does not include the (x,y) PositionComponent offset! 75 0 : Vector2 getBlockPosition(Block block) { 76 0 : return getBlockPositionInts(block.x, block.y); 77 : } 78 : 79 0 : Vector2 getBlockPositionInts(int i, int j) { 80 0 : final halfTile = effectiveTileSize / 2; 81 0 : final pos = Vector2(i.toDouble(), j.toDouble())..multiply(halfTile); 82 0 : return cartToIso(pos) - halfTile; 83 : } 84 : 85 : /// Converts a coordinate from the isometric space to the cartesian space. 86 0 : Vector2 isoToCart(Vector2 p) { 87 0 : final x = (2 * p.y + p.x) / 2; 88 0 : final y = (2 * p.y - p.x) / 2; 89 0 : return Vector2(x, y); 90 : } 91 : 92 : /// Converts a coordinate from the cartesian space to the isometric space. 93 0 : Vector2 cartToIso(Vector2 p) { 94 0 : final x = p.x - p.y; 95 0 : final y = (p.x + p.y) / 2; 96 0 : return Vector2(x, y); 97 : } 98 : 99 : /// Get what block is at isometric position p. 100 : /// 101 : /// This can be used to handle clicks or hovers. 102 0 : Block getBlock(Vector2 p) { 103 0 : final halfTile = effectiveTileSize / 2; 104 0 : final multiplier = 1 - halfTile.y / (2 * effectiveTileHeight); 105 0 : final delta = halfTile.clone()..multiply(Vector2(1, multiplier)); 106 0 : final cart = isoToCart(p - position + delta); 107 0 : final px = (cart.x / halfTile.x - 1).ceil(); 108 0 : final py = (cart.y / halfTile.y).ceil(); 109 0 : return Block(px, py); 110 : } 111 : 112 0 : void setBlockValue(Block pos, int block) { 113 0 : matrix[pos.y][pos.x] = block; 114 : } 115 : 116 0 : int blockValue(Block pos) { 117 0 : return matrix[pos.y][pos.x]; 118 : } 119 : 120 : /// Return whether the matrix contains a block in its bounds. 121 0 : bool containsBlock(Block block) { 122 0 : return block.y >= 0 && 123 0 : block.y < matrix.length && 124 0 : block.x >= 0 && 125 0 : block.x < matrix[block.y].length; 126 : } 127 : }