public void CutRect(TileMap tilemap, int startGridX, int startGridY, int endGridX, int endGridY) { if (is_undo_enabled) { #if UNITY_EDITOR Undo.RecordObject(tilemap, TileMapConst.Undo_Operation_Name + tilemap.name); Undo.RecordObjects(tilemap.GetComponentsInChildren <TileMapChunk>(), TileMapConst.Undo_Operation_Name + tilemap.name); #endif } tilemap.is_undo_enabled = is_undo_enabled; for (int gridY = startGridY; gridY <= endGridY; ++gridY) { for (int gridX = startGridX; gridX <= endGridX; ++gridX) { tileMap.SetTileData(gridX - startGridX, gridY - startGridY, tilemap.GetTileData(gridX, gridY)); tilemap.SetTileData(gridX, gridY, TileSetConst.TileData_Empty); } } tileMap.UpdateMeshImmediate(); tilemap.UpdateMeshImmediate(); tilemap.is_undo_enabled = false; }
public void Erase(TileMap tileMap, Vector2 local_pos) { int minGridX = this.tileMap.min_grid_x; int minGridY = this.tileMap.min_grid_y; int maxGridX = this.tileMap.max_grid_x; int maxGridY = this.tileMap.max_grid_y; if (is_undo_enabled) { #if UNITY_EDITOR Undo.RecordObject(tileMap, TileMapConst.Undo_Operation_Name + tileMap.name); Undo.RecordObjects(tileMap.GetComponentsInChildren <TileMapChunk>(), TileMapConst.Undo_Operation_Name + tileMap.name); #endif } tileMap.is_undo_enabled = is_undo_enabled; int dstGy = TileSetBrushUtil.GetGridY(local_pos, tileMap.cell_size); for (int gridY = minGridY; gridY <= maxGridY; ++gridY, ++dstGy) { int dstGx = TileSetBrushUtil.GetGridY(local_pos, tileMap.cell_size); for (int gridX = minGridX; gridX <= maxGridX; ++gridX, ++dstGx) { tileMap.SetTileData(dstGx, dstGy, TileSetConst.TileData_Empty); } } tileMap.UpdateMeshImmediate(); tileMap.is_undo_enabled = false; }
public static void DrawLineMirrored(TileMap tilemap, int x0, int y0, int x1, int y1, uint[,] tileData, bool randomize = false) { int w = tileData.GetLength(0); int h = tileData.GetLength(1); TileMapDrawingUtil.Line(x0, y0, x1, y1, (x, y) => { int dataIdx0 = randomize ? Random.Range(0, w) : (x % w + w) % w; int dataIdx1 = randomize ? Random.Range(0, h) : (y % h + h) % h; tilemap.SetTileData(x, y, tileData[dataIdx0, dataIdx1]); dataIdx0 = randomize ? Random.Range(0, w) : ((x0 - x) % w + w) % w; dataIdx1 = randomize ? Random.Range(0, h) : ((y0 - y) % h + h) % h; tilemap.SetTileData(x0 - x, y0 - y, tileData[dataIdx0, dataIdx1]); return(true); } ); }
public void Paint(TileMap tileMap, Vector2 local_pos, bool skipEmptyTiles = false) { int min_grid_x = this.tileMap.min_grid_x; int min_grid_y = this.tileMap.min_grid_y; int max_grid_x = this.tileMap.max_grid_x; int max_grid_y = this.tileMap.max_grid_y; if (is_undo_enabled) { #if UNITY_EDITOR Undo.RecordObject(tileMap, TileMapConst.Undo_Operation_Name + tileMap.name); Undo.RecordObjects(tileMap.GetComponentsInChildren <TileMapChunk>(), TileMapConst.Undo_Operation_Name + tileMap.name); #endif } tileMap.is_undo_enabled = is_undo_enabled; int target_grid_y = TileSetBrushUtil.GetGridY(local_pos, tileMap.cell_size); bool is_do_paint_empty = this.tileMap.GridWidth == 1 && this.tileMap.GridHeight == 1 || // don't copy empty tiles brushPattern != null && brushPattern.GetLength(0) == 1 && brushPattern.GetLength(1) == 1; // unless the brush size is one is_do_paint_empty &= !skipEmptyTiles; for (int grid_y = min_grid_y; grid_y <= max_grid_y; ++grid_y, ++target_grid_y) { int target_grid_x = TileSetBrushUtil.GetGridX(local_pos, tileMap.cell_size); for (int grid_x = min_grid_x; grid_x <= max_grid_x; ++grid_x, ++target_grid_x) { uint tileData = this.tileMap.GetTileData(grid_x, grid_y); if ( is_do_paint_empty || tileData != TileSetConst.TileData_Empty ) { tileMap.SetTileData(target_grid_x, target_grid_y, tileData); } } } tileMap.UpdateMeshImmediate(); tileMap.is_undo_enabled = false; }
public static void DrawEllipse(TileMap tilemap, int x0, int y0, int x1, int y1, uint[,] tileData, bool isFilled, bool randomize = false) { int w = tileData.GetLength(0); int h = tileData.GetLength(1); int xf = 0; int yf = 0; //fix for cases where x1 x2 y1 or y2 are negative or x1 > x2 or y1 > y2 // NOTE: I tested this only for case x1 == y1 == 0 if (x0 > x1) { Swap <int>(ref x0, ref x1); } if (y0 > y1) { Swap <int>(ref y0, ref y1); } if (x0 < 0) { xf = x0; x0 = 0; x1 -= xf; } if (y0 < 0) { yf = y0; y0 = 0; y1 -= yf; } // TileMapDrawingUtil.Ellipse(x0, y0, x1, y1, isFilled, (x, y) => { int dataIdx0 = randomize ? Random.Range(0, w) : ((x + xf) % w + w) % w; int dataIdx1 = randomize ? Random.Range(0, h) : ((y + yf) % h + h) % h; tilemap.SetTileData(x + xf, y + yf, tileData[dataIdx0, dataIdx1]); return(true); } ); }
public void SetTileData(int local_grid_x, int local_grid_y, uint tileData) { if (local_grid_x >= 0 && local_grid_x < width && local_grid_y >= 0 && local_grid_y < height) { int tile_index = local_grid_y * width + local_grid_x; int tileId = (int)(tileData & TileSetConst.TileDataMask_TileId); Tile tile = tileSet.GetTile(tileId); int prev_tileId = (int)(tileData_list[tile_index] & TileSetConst.TileDataMask_TileId); Tile prev_tile = tileSet.GetTile(prev_tileId); int brushId = TileSetUtil.GetTileSetBrushIdFromTileData(tileData); int prev_brushId = TileSetUtil.GetTileSetBrushIdFromTileData(tileData_list[tile_index]); if (brushId != prev_brushId) { if (!current_updated_tileMapChunk) // avoid this is chunks is being Updated from FillMeshData { // Refresh Neighbors ( and itself if needed ) for (int y_offset = -1; y_offset <= 1; ++y_offset) { for (int x_offset = -1; x_offset <= 1; ++x_offset) { if ((x_offset | y_offset) == 0) { if (brushId > 0) { // Refresh itself tileData = (tileData & ~TileSetConst.TileFlag_Updated); } } else { int grid_x = (local_grid_x + x_offset); int grid_y = (local_grid_y + y_offset); int index = grid_y * width + grid_x; bool is_inside_chunk = (grid_x >= 0 && grid_x < width && grid_y >= 0 && grid_y < height); uint neighbor_tileData = is_inside_chunk ? tileData_list[index] : parent_tileMap.GetTileData(offset_grid_x + local_grid_x + x_offset, offset_grid_y + local_grid_y + y_offset); int neighbor_brushId = (int)((neighbor_tileData & TileSetConst.TileDataMask_TileSetBrushId) >> 16); TileSetBrush neighbor_tileSetBrush = parent_tileMap.tileSet.FindTileSetBrush(neighbor_brushId); //if (brush != null && brush.AutotileWith(brushId, neighborBrushId) || prevBrush != null && prevBrush.AutotileWith(prevBrushId, neighborBrushId)) if (neighbor_tileSetBrush != null && (neighbor_tileSetBrush.AutoTileWith(neighbor_brushId, brushId) || neighbor_tileSetBrush.AutoTileWith(neighbor_brushId, prev_brushId))) { neighbor_tileData = (neighbor_tileData & ~TileSetConst.TileFlag_Updated); // force a refresh if (is_inside_chunk) { tileData_list[index] = neighbor_tileData; } else { parent_tileMap.SetTileData(offset_grid_x + grid_x, offset_grid_y + grid_y, neighbor_tileData); } } } } } } } else if (brushId > 0) { // Refresh itself tileData = (tileData & ~TileSetConst.TileFlag_Updated); } is_need_rebuild_mesh |= (tileData_list[tile_index] != tileData) || (tileData & TileSetConst.TileDataMask_TileId) == TileSetConst.TileId_Empty; is_need_rebuild_colliders |= is_need_rebuild_mesh && ( (prev_brushId > 0) || (brushId > 0) || // there is a brush (a brush could change the collider data later) (tile != null && tile.tileColliderData.type != TileColliderType.None) || (prev_tile != null && prev_tile.tileColliderData.type != TileColliderType.None) // prev. or new tile has colliders ); if (parent_tileMap.tileMapColliderType != TileMapColliderType.None && is_need_rebuild_colliders) { // Refresh Neighbors tilechunk colliders, to make the collider autotiling // Only if neighbor is outside this tilechunk for (int y_offset = -1; y_offset <= 1; ++y_offset) { for (int x_offset = -1; x_offset <= 1; ++x_offset) { if ((x_offset | y_offset) != 0) // skip this tile position xf = yf = 0 { int grid_x = (local_grid_x + x_offset); int grid_y = (local_grid_y + y_offset); bool is_inside_chunk = (grid_x >= 0 && grid_x < width && grid_y >= 0 && grid_y < height); if (!is_inside_chunk) { parent_tileMap.InvalidateChunkAt(offset_grid_x + grid_x, offset_grid_y + grid_y, false, true); } } } } } // Update tile data tileData_list[tile_index] = tileData; if (!TileMap.Is_Disable_Tile_Prefab_Creation) { // Create tile Objects if (tile != null && tile.tilePrefabData.prefab != null) { CreateTileObject(tile_index, tile.tilePrefabData); } else { DestroyTileObject(tile_index); } } TileSetBrush brush = parent_tileMap.tileSet.FindTileSetBrush(brushId); if (brushId != prev_brushId) { TileSetBrush prevBrush = parent_tileMap.tileSet.FindTileSetBrush(prev_brushId); if (prevBrush != null) { prevBrush.OnErase(this, local_grid_x, local_grid_y, tileData, prev_brushId); } } if (brush != null) { tileData = brush.OnPaint(this, local_grid_x, local_grid_y, tileData); } } }
public static void DrawRect(TileMap tilemap, int x0, int y0, int x1, int y1, uint[,] tileData, bool isFilled, bool is9Sliced = false, bool randomize = false) { int w = tileData.GetLength(0); int h = tileData.GetLength(1); if (x0 > x1) { Swap <int>(ref x0, ref x1); } if (y0 > y1) { Swap <int>(ref y0, ref y1); } TileMapDrawingUtil.Rect(x0, y0, x1, y1, isFilled, (x, y) => { if (is9Sliced) { if (x == x0 && y == y0) { tilemap.SetTileData(x, y, tileData[0, 0]); } else if (x == x0 && y == y1) { tilemap.SetTileData(x, y, tileData[0, h - 1]); } else if (x == x1 && y == y0) { tilemap.SetTileData(x, y, tileData[w - 1, 0]); } else if (x == x1 && y == y1) { tilemap.SetTileData(x, y, tileData[w - 1, h - 1]); } else { int cw = w - 2; int ch = h - 2; int cx = cw >= 1 ? 1 + (x % cw + cw) % cw : (x % w + w) % w; int cy = ch >= 1 ? 1 + (y % ch + ch) % ch : (y % h + h) % h; if (x == x0) { tilemap.SetTileData(x, y, tileData[0, cy]); } else if (x == x1) { tilemap.SetTileData(x, y, tileData[w - 1, cy]); } else if (y == y0) { tilemap.SetTileData(x, y, tileData[cx, 0]); } else if (y == y1) { tilemap.SetTileData(x, y, tileData[cx, h - 1]); } else { tilemap.SetTileData(x, y, tileData[cx, cy]); } } } else { int dataIdx0 = randomize ? Random.Range(0, w) : (x % w + w) % w; int dataIdx1 = randomize ? Random.Range(0, h) : (y % h + h) % h; tilemap.SetTileData(x, y, tileData[dataIdx0, dataIdx1]); } return(true); } ); }
//https://social.msdn.microsoft.com/Forums/en-US/9d926a16-0051-4ca3-b77c-8095fb489ae2/flood-fill-c?forum=csharplanguage public static void FloodFill(TileMap tilemap, int gridX, int gridY, uint[,] tileData, bool randomize = false) { float timeStamp; timeStamp = Time.realtimeSinceStartup; //float callTimeStamp = timeStamp; int patternW = tileData.GetLength(0); int patternH = tileData.GetLength(1); LinkedList <Point> check = new LinkedList <Point>(); uint floodFrom = tilemap.GetTileData(gridX, gridY); int dataIdx0 = randomize ? Random.Range(0, patternW) : (gridX % patternW + patternW) % patternW; int dataIdx1 = randomize ? Random.Range(0, patternH) : (gridY % patternH + patternH) % patternH; tilemap.SetTileData(gridX, gridY, tileData[dataIdx0, dataIdx1]); bool isBrush = TileSetUtil.GetTileSetBrushIdFromTileData(floodFrom) != 0; //Debug.Log(" Flood Fill Starts +++++++++++++++ "); if ( (patternW > 0 && patternH > 0) && isBrush ? TileSetUtil.GetTileSetBrushIdFromTileData(floodFrom) != TileSetUtil.GetTileSetBrushIdFromTileData(tileData[0, 0]) : floodFrom != tileData[0, 0] ) { check.AddLast(new Point(gridX, gridY)); while (check.Count > 0) { Point cur = check.First.Value; check.RemoveFirst(); foreach (Point off in new Point[] { new Point(0, -1), new Point(0, 1), new Point(-1, 0), new Point(1, 0) }) { Point next = new Point(cur.X + off.X, cur.Y + off.Y); uint nextTileData = tilemap.GetTileData(next.X, next.Y); if ( next.X >= tilemap.min_grid_x && next.X <= tilemap.max_grid_x && next.Y >= tilemap.min_grid_y && next.Y <= tilemap.max_grid_y ) { if ( isBrush ? TileSetUtil.GetTileSetBrushIdFromTileData(floodFrom) == TileSetUtil.GetTileSetBrushIdFromTileData(nextTileData) : floodFrom == nextTileData ) { check.AddLast(next); dataIdx0 = randomize ? Random.Range(0, patternW) : (next.X % patternW + patternW) % patternW; dataIdx1 = randomize ? Random.Range(0, patternH) : (next.Y % patternH + patternH) % patternH; tilemap.SetTileData(next.X, next.Y, tileData[dataIdx0, dataIdx1]); } } } float timePast = Time.realtimeSinceStartup - timeStamp; if (timePast > k_timeToAbortFloodFill) { #if UNITY_EDITOR int result = UnityEditor.EditorUtility.DisplayDialogComplex("FloodFill is taking too much time", "Do you want to continue for another " + k_timeToAbortFloodFill + " seconds?", "Wait", "Cancel", "Wait and Don't ask again"); if (result == 0) { timeStamp = Time.realtimeSinceStartup; } else if (result == 1) { break; } else if (result == 2) { timeStamp = float.MaxValue; } #else check.Clear(); #endif } } } //Debug.Log("FloodFill Time " + (int)((Time.realtimeSinceStartup - callTimeStamp) * 1000) + "ms"); }