public static void TileDataField(Rect position, GUIContent label, SerializedProperty property, Tileset tileset) { Event e = Event.current; bool isLeftMouseReleased = e.type == EventType.MouseUp && e.button == 0; //NOTE: there is a bug with DrawTextureWithTexCoords where the texture disappears. It is fixed by overriding the Editor Script with a CustomEditor. Rect rVisualTile = new Rect(position.x, position.y, k_TilePreviewSize, k_TilePreviewSize); uint tileData = (uint)property.intValue; TilesetBrush brush = tileset.FindBrush(Tileset.GetBrushIdFromTileData(tileData)); if (brush) { tileData = brush.PreviewTileData(); } TilesetEditor.DoGUIDrawTileFromTileData(rVisualTile, tileData, tileset); if (isLeftMouseReleased && rVisualTile.Contains(e.mousePosition)) { EditorWindow wnd = EditorWindow.focusedWindow; TileSelectionWindow.Show(tileset); TileSelectionWindow.Instance.Ping(); wnd.Focus(); GUI.FocusControl(""); } EditorGUI.PropertyField(new Rect(position.x, position.y + k_TilePreviewSize, position.width, position.height - k_TilePreviewSize), property, label); }
public STETilemap GetDefaultTilemapForCurrentTileSelection() { if (SelectedTilemap && SelectedTilemap.Tileset) { if (SelectedTilemap.Tileset.SelectedTileId != Tileset.k_TileId_Empty) { return(GetDefaultTilemapForTileOrBrush(SelectedTilemap.Tileset.SelectedTileId, m_dicTileDefaultTilemap)); } else if (SelectedTilemap.Tileset.SelectedBrushId != Tileset.k_BrushId_Default) { return(GetDefaultTilemapForTileOrBrush(SelectedTilemap.Tileset.SelectedBrushId, m_dicBrushDefaultTilemap)); } else if (SelectedTilemap.Tileset.TileSelection != null && SelectedTilemap.Tileset.TileSelection.selectionData.Count > 0) { uint tileData = SelectedTilemap.Tileset.TileSelection.selectionData[0]; int brushId = Tileset.GetBrushIdFromTileData(tileData); if (brushId != Tileset.k_BrushId_Default) { return(GetDefaultTilemapForTileOrBrush(brushId, m_dicBrushDefaultTilemap)); } else { int tileId = Tileset.GetTileIdFromTileData(tileData); if (tileId != Tileset.k_TileId_Empty) { return(GetDefaultTilemapForTileOrBrush(tileId, m_dicTileDefaultTilemap)); } } } } return(null); }
public override uint[] GetSubtiles(Tilemap tilemap, int gridX, int gridY, uint tileData) { // Add animated tiles { int idx = CalculateIndex(tilemap, gridX, gridY, tileData); TilesetBrush brush = Tileset.FindBrush(Tileset.GetBrushIdFromTileData(TileIds[idx])); if (brush && brush.IsAnimated()) { TilemapChunk.RegisterAnimatedBrush(brush); } } return(null); }
/// <summary> /// Return a tile parameter (bool, int, float, string or UnityEngine.Object) from a tiledata. /// It takes first the parameter from a brush, if possible, then from a tile. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tileset"></param> /// <param name="tileData"></param> /// <param name="paramName"></param> /// <param name="defaultValue"></param> /// <returns></returns> static public T GetTileParameter <T>(Tileset tileset, uint tileData, string paramName, T defaultValue = default(T)) { Tile tile = tileset.GetTile(Tileset.GetTileIdFromTileData(tileData)); TilesetBrush brush = tileset.FindBrush(Tileset.GetBrushIdFromTileData(tileData)); T value = defaultValue; if (brush) { value = brush.Params.GetParam <T>(paramName, defaultValue); } if (tile != null && EqualityComparer <T> .Default.Equals(value, defaultValue)) { value = tile.paramContainer.GetParam <T>(paramName, defaultValue); } return(value); }
private void SetDefaultTilemapFromTileData(STETilemap tilemap, int gx, int gy, uint tileData) { int brushId = Tileset.GetBrushIdFromTileData(tileData); if (brushId != Tileset.k_BrushId_Default) { m_target.SetBrushDefaultTilemap(brushId, tilemap); } else { int tileId = Tileset.GetTileIdFromTileData(tileData); if (tileId != Tileset.k_TileId_Empty) { m_target.SetTileDefaultTilemap(tileId, tilemap); } } }
protected static bool s_refreshingLinkedBrush = false; //avoid infinite loop /// <summary> /// Returns a new tiledata after calling the refresh method of the linked brush. /// This is used to give support for tiles in a brush that are linked to another brush. Ex: a Road Brush with a tile linked to a random brush. /// </summary> /// <param name="tilemap"></param> /// <param name="gridX"></param> /// <param name="gridY"></param> /// <param name="tileData"></param> /// <returns></returns> public uint RefreshLinkedBrush(STETilemap tilemap, int gridX, int gridY, uint tileData) { if (s_refreshingLinkedBrush) { return(tileData); } int brushId = Tileset.GetBrushIdFromTileData(tileData); TilesetBrush brush = Tileset.FindBrush(brushId); if (brush) { s_refreshingLinkedBrush = true; tileData = ApplyAndMergeTileFlags(brush.Refresh(tilemap, gridX, gridY, tileData), tileData); s_refreshingLinkedBrush = false; } return(tileData); }
public override uint Refresh(STETilemap tilemap, int gridX, int gridY, uint tileData) { if (RandomTileList.Count > 0) { uint randomTileData = GetRandomTile(); if (RandomizeFlagMask != 0) { uint flags = ((uint)Random.Range(0, 8) << 29) & RandomizeFlagMask; randomTileData &= ~RandomizeFlagMask; randomTileData |= flags; } uint brushTileData = RefreshLinkedBrush(tilemap, gridX, gridY, randomTileData); // overwrite flags brushTileData &= ~Tileset.k_TileDataMask_Flags; brushTileData |= randomTileData & Tileset.k_TileDataMask_Flags; TilesetBrush brush = Tileset.FindBrush(Tileset.GetBrushIdFromTileData(brushTileData)); if (brush && brush.IsAnimated()) { // Set the animated brush (overwriting the random brush for this tile) brushTileData &= ~Tileset.k_TileDataMask_BrushId; brushTileData |= randomTileData & Tileset.k_TileDataMask_BrushId; } // Overwrite the brush id for the one in the selected random tile // Side Effect: the tile will loose the brush id for the random brush if (RemoveBrushIdAfterRefresh) { // Do nothing } // Overwrite the selected tile brush id for the random brush id // NOTE: animated tiles wont be animated else { // overwrite brush id brushTileData &= ~Tileset.k_TileDataMask_BrushId; brushTileData |= tileData & Tileset.k_TileDataMask_BrushId; } return(brushTileData); } return(tileData); }
public override uint Refresh(Tilemap tilemap, int gridX, int gridY, uint tileData) { if (RandomTileList.Count > 0) { uint randomTileData = GetRandomTile(); if (RandomizeFlagMask != 0) { uint flags = ((uint)Random.Range(0, 8) << 29) & RandomizeFlagMask; randomTileData &= ~RandomizeFlagMask; randomTileData |= flags; } uint brushTileData = RefreshLinkedBrush(tilemap, gridX, gridY, randomTileData); // overwrite flags brushTileData &= ~Tileset.k_TileDataMask_Flags; brushTileData |= randomTileData & Tileset.k_TileDataMask_Flags; TilesetBrush brush = Tileset.FindBrush(Tileset.GetBrushIdFromTileData(brushTileData)); if (brush && brush.IsAnimated()) { // Set the animated brush (overwriting the random brush for this tile) brushTileData &= ~Tileset.k_TileDataMask_BrushId; brushTileData |= randomTileData & Tileset.k_TileDataMask_BrushId; } // - Commented: // This code will make the Random Brush to work only once // Copy the tile will copy the tile placed the first time // - Uncommented: // This code will make Refresh Tilemap to update all random brushes // Copy the tile will copy a random brush tile /* * { * // overwrite brush id * brushTileData &= ~Tileset.k_TileDataMask_BrushId; * brushTileData |= tileData & Tileset.k_TileDataMask_BrushId; * }*/ return(brushTileData); } return(tileData); }
/// <summary> /// Get the parameter container from tileData if tileData contains a tile with parameters or Null in other case /// </summary> /// <param name="tilemap"></param> /// <param name="tileData"></param> /// <returns></returns> static public ParameterContainer GetParamsFromTileData(STETilemap tilemap, uint tileData) { int brushId = Tileset.GetBrushIdFromTileData(tileData); TilesetBrush brush = tilemap.Tileset.FindBrush(brushId); if (brush) { return(brush.Params); } else { int tileId = Tileset.GetTileIdFromTileData(tileData); Tile tile = tilemap.Tileset.GetTile(tileId); if (tile != null) { return(tile.paramContainer); } } return(null); }
public override uint[] GetSubtiles(STETilemap tilemap, int gridX, int gridY, uint tileData) { // Add animated tiles { int brushId = (int)((tileData & Tileset.k_TileDataMask_BrushId) >> 16); bool autotiling_N = AutotileWith(tilemap, brushId, gridX, gridY + 1); bool autotiling_E = AutotileWith(tilemap, brushId, gridX + 1, gridY); bool autotiling_S = AutotileWith(tilemap, brushId, gridX, gridY - 1); bool autotiling_W = AutotileWith(tilemap, brushId, gridX - 1, gridY); int idx = 0; if (autotiling_N) { idx = 1; } if (autotiling_E) { idx |= 2; } if (autotiling_S) { idx |= 4; } if (autotiling_W) { idx |= 8; } TilesetBrush brush = Tileset.FindBrush(Tileset.GetBrushIdFromTileData(TileIds[idx])); if (brush && brush.IsAnimated()) { TilemapChunk.RegisterAnimatedBrush(brush); } } return(null); }
public void SetTileData(int locGridX, int locGridY, uint tileData) { if (locGridX >= 0 && locGridX < m_width && locGridY >= 0 && locGridY < m_height) { int tileIdx = locGridY * m_width + locGridX; int tileId = (int)(tileData & Tileset.k_TileDataMask_TileId); Tile tile = Tileset.GetTile(tileId); int prevTileId = (int)(m_tileDataList[tileIdx] & Tileset.k_TileDataMask_TileId); Tile prevTile = Tileset.GetTile(prevTileId); int brushId = Tileset.GetBrushIdFromTileData(tileData); int prevBrushId = Tileset.GetBrushIdFromTileData(m_tileDataList[tileIdx]); if (brushId != prevBrushId || brushId == 0) //NOTE: because of the autotiling mode, neighbour tiles could be affected by this change, even if the tile is not a brush { if (!s_currUpdatedTilechunk) // avoid this is chunks is being Updated from FillMeshData { // Refresh Neighbors ( and itself if needed ) for (int yf = -1; yf <= 1; ++yf) { for (int xf = -1; xf <= 1; ++xf) { if ((xf | yf) == 0) { if (brushId > 0) { // Refresh itself tileData = (tileData & ~Tileset.k_TileFlag_Updated); } } else { int gx = (locGridX + xf); int gy = (locGridY + yf); int idx = gy * m_width + gx; bool isInsideChunk = (gx >= 0 && gx < m_width && gy >= 0 && gy < m_height); uint neighborTileData = isInsideChunk ? m_tileDataList[idx] : ParentTilemap.GetTileData(GridPosX + locGridX + xf, GridPosY + locGridY + yf); int neighborBrushId = (int)((neighborTileData & Tileset.k_TileDataMask_BrushId) >> 16); TilesetBrush neighborBrush = ParentTilemap.Tileset.FindBrush(neighborBrushId); if (neighborBrush != null && (neighborBrush.AutotileWith(ParentTilemap.Tileset, neighborBrushId, tileData) || neighborBrush.AutotileWith(ParentTilemap.Tileset, neighborBrushId, m_tileDataList[tileIdx]))) { neighborTileData = (neighborTileData & ~Tileset.k_TileFlag_Updated); // force a refresh if (isInsideChunk) { m_tileDataList[idx] = neighborTileData; } else { ParentTilemap.SetTileData(GridPosX + gx, GridPosY + gy, neighborTileData); } } } } } } } else if (brushId > 0) { // Refresh itself tileData = (tileData & ~Tileset.k_TileFlag_Updated); } m_needsRebuildMesh |= (m_tileDataList[tileIdx] != tileData) || (tileData & Tileset.k_TileDataMask_TileId) == Tileset.k_TileId_Empty; m_needsRebuildColliders |= m_needsRebuildMesh && ( (prevBrushId > 0) || (brushId > 0) || // there is a brush (a brush could change the collider data later) (tile != null && tile.collData.type != eTileCollider.None) || (prevTile != null && prevTile.collData.type != eTileCollider.None) // prev. or new tile has colliders ); if (ParentTilemap.ColliderType != eColliderType.None && m_needsRebuildColliders) { // Refresh Neighbors tilechunk colliders, to make the collider autotiling // Only if neighbor is outside this tilechunk for (int yf = -1; yf <= 1; ++yf) { for (int xf = -1; xf <= 1; ++xf) { if ((xf | yf) != 0) // skip this tile position xf = yf = 0 { int gx = (locGridX + xf); int gy = (locGridY + yf); bool isInsideChunk = (gx >= 0 && gx < m_width && gy >= 0 && gy < m_height); if (!isInsideChunk) { ParentTilemap.InvalidateChunkAt(GridPosX + gx, GridPosY + gy, false, true); } } } } } // Update tile data m_tileDataList[tileIdx] = tileData; if (!STETilemap.DisableTilePrefabCreation) { // Create tile Objects if (tile != null && tile.prefabData.prefab != null) { CreateTileObject(tileIdx, tile.prefabData); } else { DestroyTileObject(tileIdx); } } TilesetBrush brush = ParentTilemap.Tileset.FindBrush(brushId); if (brushId != prevBrushId) { TilesetBrush prevBrush = ParentTilemap.Tileset.FindBrush(prevBrushId); if (prevBrush != null) { prevBrush.OnErase(this, locGridX, locGridY, tileData, prevBrushId); } } if (brush != null) { tileData = brush.OnPaint(this, locGridX, locGridY, tileData); } } }
public override void OnEnable() { base.OnEnable(); m_brush = (RandomBrush)target; if (m_brush.Tileset != null) { m_brush.Tileset.OnTileSelected += OnTileSelected; m_brush.Tileset.OnBrushSelected += OnBrushSelected; } m_randTileList = new ReorderableList(serializedObject, serializedObject.FindProperty("RandomTileList"), true, true, true, true); m_randTileList.drawHeaderCallback += (Rect rect) => { EditorGUI.LabelField(rect, "Random Tiles", EditorStyles.boldLabel); }; m_randTileList.drawElementCallback += (Rect rect, int index, bool isActive, bool isFocused) => { Rect rTile = rect; rTile.width = rTile.height = m_brush.Tileset.VisualTileSize.y; uint tileData = m_brush.RandomTileList[index].tileData; int tileId = (int)(tileData & Tileset.k_TileDataMask_TileId); int brushId = Tileset.GetBrushIdFromTileData(tileData); TilesetBrush brush = m_brush.Tileset.FindBrush(brushId); if (brush) { GUI.Box(new Rect(rTile.position - Vector2.one, rTile.size + 2 * Vector2.one), ""); TilesetEditor.DoGUIDrawTileFromTileData(rTile, tileData, m_brush.Tileset, brush.GetAnimUV()); } else if (tileId != Tileset.k_TileId_Empty) { GUI.Box(new Rect(rTile.position - Vector2.one, rTile.size + 2 * Vector2.one), ""); TilesetEditor.DoGUIDrawTileFromTileData(rTile, tileData, m_brush.Tileset); } Rect rTileId = rect; rTileId.x += rTile.width + 10; rTileId.width -= rTile.width + 20; rTileId.height = rect.height / 2; if (brush) { GUI.Label(rTileId, "Brush Id(" + brushId + ")"); } else { GUI.Label(rTileId, "Id(" + tileId + ")"); } SerializedProperty randomTileDataProperty = m_randTileList.serializedProperty.GetArrayElementAtIndex(index); SerializedProperty probabilityFactorProperty = randomTileDataProperty.FindPropertyRelative("probabilityFactor"); Rect rProbabilityField = new Rect(rect.x + rTile.width + 10f, rect.y + EditorGUIUtility.singleLineHeight * 2.5f, rect.width - rTile.width - 10f, EditorGUIUtility.singleLineHeight); Rect rProbabilityLabel = new Rect(rProbabilityField.x, rProbabilityField.y - EditorGUIUtility.singleLineHeight, rProbabilityField.width, rProbabilityField.height); float sumProbabilityFactor = m_brush.GetSumProbabilityFactor(); float probability = sumProbabilityFactor >= 0 ? probabilityFactorProperty.floatValue * 100f / sumProbabilityFactor : 100f; EditorGUI.PrefixLabel(rProbabilityLabel, new GUIContent("Probability (" + Mathf.RoundToInt(probability) + "%)")); EditorGUI.PropertyField(rProbabilityField, probabilityFactorProperty, GUIContent.none); if (probabilityFactorProperty.floatValue == 0f) { serializedObject.ApplyModifiedProperties(); sumProbabilityFactor = m_brush.GetSumProbabilityFactor(); if (sumProbabilityFactor <= 0f) { probabilityFactorProperty.floatValue = 0.01f; } } if (GUI.Button(new Rect(rect.x + rect.width - 50f, rect.y, 50f, EditorGUIUtility.singleLineHeight), "Clear")) { m_brush.RandomTileList[index].tileData = Tileset.k_TileData_Empty; } }; m_randTileList.onSelectCallback += (ReorderableList list) => { TileSelectionWindow.Show(m_brush.Tileset); TileSelectionWindow.Instance.Ping(); }; m_randTileList.onAddCallback += (ReorderableList list) => { if (list.index >= 0) { list.serializedProperty.InsertArrayElementAtIndex(list.index); } else { list.serializedProperty.InsertArrayElementAtIndex(0); } list.index = Mathf.Max(0, list.index + 1); list.serializedProperty.serializedObject.ApplyModifiedProperties(); m_brush.RandomTileList[list.index].probabilityFactor = 1f; if (m_brush.Tileset.SelectedTile != null) { m_randTileList.GrabKeyboardFocus(); OnTileSelected(m_brush.Tileset, -1, m_brush.Tileset.SelectedTileId); } }; }
//Note: this is doing the same as FloodFill but not saving data in the tilemap, only saving the filled points and returning a list public static void FloodFillPreview(Tilemap tilemap, int gridX, int gridY, uint tileData, List <Vector2> outFilledPoints, uint maxPoints = uint.MaxValue) { if ( gridX >= tilemap.MinGridX && gridX <= tilemap.MaxGridX && gridY >= tilemap.MinGridY && gridY <= tilemap.MaxGridY ) { bool[] filledPoints = new bool[tilemap.GridWidth * tilemap.GridHeight]; LinkedList <Point> check = new LinkedList <Point>(); uint floodFrom = tilemap.GetTileData(gridX, gridY); outFilledPoints.Add(Vector2.Scale(new Vector2(gridX, gridY), tilemap.CellSize)); filledPoints[(gridY - tilemap.MinGridY) * tilemap.GridWidth + gridX - tilemap.MinGridX] = true; bool isBrush = Tileset.GetBrushIdFromTileData(floodFrom) != 0; if ( isBrush ? Tileset.GetBrushIdFromTileData(floodFrom) != Tileset.GetBrushIdFromTileData(tileData) : floodFrom != tileData ) { 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); if ( next.X >= tilemap.MinGridX && next.X <= tilemap.MaxGridX && next.Y >= tilemap.MinGridY && next.Y <= tilemap.MaxGridY ) { if (filledPoints[(next.Y - tilemap.MinGridY) * tilemap.GridWidth + next.X - tilemap.MinGridX]) { continue; // skip already filled points } uint nextTileData = tilemap.GetTileData(next.X, next.Y); if ( isBrush ? Tileset.GetBrushIdFromTileData(floodFrom) == Tileset.GetBrushIdFromTileData(nextTileData) : floodFrom == nextTileData ) { check.AddLast(next); filledPoints[(next.Y - tilemap.MinGridY) * tilemap.GridWidth + next.X - tilemap.MinGridX] = true; outFilledPoints.Add(Vector2.Scale(new Vector2(next.X, next.Y), tilemap.CellSize)); if (outFilledPoints.Count >= maxPoints) { return; } } } } } } } }
//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) { 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); tilemap.SetTileData(gridX, gridY, tileData[(gridX % patternW + patternW) % patternW, (gridY % patternH + patternH) % patternH]); bool isBrush = Tileset.GetBrushIdFromTileData(floodFrom) != 0; //Debug.Log(" Flood Fill Starts +++++++++++++++ "); if ( (patternW > 0 && patternH > 0) && isBrush? Tileset.GetBrushIdFromTileData(floodFrom) != Tileset.GetBrushIdFromTileData(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.MinGridX && next.X <= tilemap.MaxGridX && next.Y >= tilemap.MinGridY && next.Y <= tilemap.MaxGridY ) { if ( isBrush? Tileset.GetBrushIdFromTileData(floodFrom) == Tileset.GetBrushIdFromTileData(nextTileData) : floodFrom == nextTileData ) { check.AddLast(next); tilemap.SetTileData(next.X, next.Y, tileData[(next.X % patternW + patternW) % patternW, (next.Y % patternH + patternH) % patternH]); } } } 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"); }
// '°', '├', '═', '┤', | 0, 2, 10, 8, // '┬', '╔', '╦', '╗', | 4, 6, 14, 12, // '║', '╠', '╬', '╣', | 5, 7, 15, 13, // '┴', '╚', '╩', '╝', | 1, 3, 11, 9, public override uint[] GetSubtiles(STETilemap tilemap, int gridX, int gridY, uint tileData) { CalculateNeighbourData(tilemap, gridX, gridY, tileData); // tiles that need subtile division if (s_needsSubTiles) { uint[] aSubTileData = null; if (s_neighIdx == 0) //° { aSubTileData = new uint[] { TileIds[3], TileIds[9], TileIds[6], TileIds[12] }; } else if (s_neighIdx == 4)//┬ { aSubTileData = new uint[] { TileIds[6], TileIds[12], TileIds[6], TileIds[12] }; } else if (s_neighIdx == 5)//║ { aSubTileData = new uint[] { TileIds[7], TileIds[13], TileIds[7], TileIds[13] }; } else if (s_neighIdx == 1)//┴ { aSubTileData = new uint[] { TileIds[3], TileIds[9], TileIds[3], TileIds[9] }; } else if (s_neighIdx == 2)//├ { aSubTileData = new uint[] { TileIds[3], TileIds[3], TileIds[6], TileIds[6] }; } else if (s_neighIdx == 10)//═ { aSubTileData = new uint[] { TileIds[11], TileIds[11], TileIds[14], TileIds[14] }; } else if (s_neighIdx == 8)//┤ { aSubTileData = new uint[] { TileIds[9], TileIds[9], TileIds[12], TileIds[12] }; } // NOTE: this case '╬' cut the tiles different (using corner tiles). // If it is commented, and default case is used, instead or corner tiles, it will use the center tile '╬' // Depending on the graphics it could be interesting add a check box to choose between using this or not. else if (s_neighIdx == 15)// ╬ { aSubTileData = new uint[] { InteriorCornerTileIds[0], InteriorCornerTileIds[1], InteriorCornerTileIds[2], InteriorCornerTileIds[3] }; } else { aSubTileData = new uint[] { TileIds[s_neighIdx], TileIds[s_neighIdx], TileIds[s_neighIdx], TileIds[s_neighIdx] }; } for (int i = 0; i < s_showDiagonal.Length; ++i) { aSubTileData[i] = RefreshLinkedBrush(tilemap, gridX, gridY, aSubTileData[i]); if (s_showDiagonal[i]) { aSubTileData[i] = InteriorCornerTileIds[3 - i]; } // Add animated tiles { TilesetBrush brush = Tileset.FindBrush(Tileset.GetBrushIdFromTileData(aSubTileData[i])); if (brush && brush.IsAnimated()) { TilemapChunk.RegisterAnimatedBrush(brush, i); } } } return(aSubTileData); } // Add animated tiles { TilesetBrush brush = Tileset.FindBrush(Tileset.GetBrushIdFromTileData(s_tileData)); if (brush && brush.IsAnimated()) { TilemapChunk.RegisterAnimatedBrush(brush); } } return(null); }
private bool FillColliderMeshData() { //Debug.Log( "[" + ParentTilemap.name + "] FillColliderMeshData -> " + name); if (Tileset == null || ParentTilemap.ColliderType == eColliderType.None) { return(false); } System.Type collider2DType = ParentTilemap.Collider2DType == e2DColliderType.EdgeCollider2D ? typeof(EdgeCollider2D) : typeof(PolygonCollider2D); Component[] aColliders2D = null; if (ParentTilemap.ColliderType == eColliderType._3D) { int totalTiles = m_width * m_height; if (s_meshCollVertices == null) { s_meshCollVertices = new List <Vector3>(totalTiles * 4); s_meshCollTriangles = new List <int>(totalTiles * 6); } else { s_meshCollVertices.Clear(); s_meshCollTriangles.Clear(); } } else //if (ParentTilemap.ColliderType == eColliderType._2D) { m_has2DColliders = true; s_openEdges.Clear(); aColliders2D = GetComponents(collider2DType); } float halvedCollDepth = ParentTilemap.ColliderDepth / 2f; bool isEmpty = true; for (int ty = 0, tileIdx = 0; ty < m_height; ++ty) { for (int tx = 0; tx < m_width; ++tx, ++tileIdx) { uint tileData = m_tileDataList[tileIdx]; if (tileData != Tileset.k_TileData_Empty) { int tileId = (int)(tileData & Tileset.k_TileDataMask_TileId); Tile tile = Tileset.GetTile(tileId); if (tile != null) { #if ENABLE_MERGED_SUBTILE_COLLIDERS TilesetBrush brush = ParentTilemap.Tileset.FindBrush(Tileset.GetBrushIdFromTileData(tileData)); Vector2[] subTileMergedColliderVertices = brush ? brush.GetMergedSubtileColliderVertices(ParentTilemap, GridPosX + tx, GridPosY + ty, tileData) : null; #else Vector2[] subTileMergedColliderVertices = null; #endif bool hasMergedColliders = subTileMergedColliderVertices != null; TileColliderData tileCollData = tile.collData; if (tileCollData.type != eTileCollider.None || hasMergedColliders) { isEmpty = false; int neighborCollFlags = 0; // don't remove, even using neighborTileCollData, neighborTileCollData is not filled if tile is empty bool isSurroundedByFullColliders = true; for (int i = 0; i < s_neighborSegmentMinMax.Length; ++i) { s_neighborSegmentMinMax[i].x = float.MaxValue; s_neighborSegmentMinMax[i].y = float.MinValue; } System.Array.Clear(neighborTileCollData, 0, neighborTileCollData.Length); if (!hasMergedColliders) { if ((tileData & (Tileset.k_TileFlag_FlipH | Tileset.k_TileFlag_FlipV | Tileset.k_TileFlag_Rot90)) != 0) { tileCollData = tileCollData.Clone(); tileCollData.ApplyFlippingFlags(tileData); } for (int i = 0; i < 4; ++i) { uint neighborTileData; bool isTriggerOrPolygon = ParentTilemap.IsTrigger || ParentTilemap.ColliderType == eColliderType._2D && ParentTilemap.Collider2DType == e2DColliderType.PolygonCollider2D; switch (i) { case 0: // Up Tile neighborTileData = (tileIdx + m_width) < m_tileDataList.Count ? m_tileDataList[tileIdx + m_width] : isTriggerOrPolygon ? Tileset.k_TileData_Empty : ParentTilemap.GetTileData(GridPosX + tx, GridPosY + ty + 1); break; case 1: // Right Tile neighborTileData = (tileIdx + 1) % m_width != 0 ? //(tileIdx + 1) < m_tileDataList.Count ? m_tileDataList[tileIdx + 1] : isTriggerOrPolygon ? Tileset.k_TileData_Empty : ParentTilemap.GetTileData(GridPosX + tx + 1, GridPosY + ty); break; case 2: // Down Tile neighborTileData = tileIdx >= m_width ? m_tileDataList[tileIdx - m_width] : isTriggerOrPolygon ? Tileset.k_TileData_Empty : ParentTilemap.GetTileData(GridPosX + tx, GridPosY + ty - 1); break; case 3: // Left Tile neighborTileData = tileIdx % m_width != 0 ? //neighborTileId = tileIdx >= 1 ? m_tileDataList[tileIdx - 1] : isTriggerOrPolygon ? Tileset.k_TileData_Empty : ParentTilemap.GetTileData(GridPosX + tx - 1, GridPosY + ty); break; default: neighborTileData = Tileset.k_TileData_Empty; break; } int neighborTileId = (int)(neighborTileData & Tileset.k_TileDataMask_TileId); if (neighborTileId != Tileset.k_TileId_Empty) { Vector2 segmentMinMax; TileColliderData neighborTileCollider; neighborTileCollider = Tileset.Tiles[neighborTileId].collData; if ((neighborTileData & (Tileset.k_TileFlag_FlipH | Tileset.k_TileFlag_FlipV | Tileset.k_TileFlag_Rot90)) != 0) { neighborTileCollider = neighborTileCollider.Clone(); if ((neighborTileData & Tileset.k_TileFlag_FlipH) != 0) { neighborTileCollider.FlipH(); } if ((neighborTileData & Tileset.k_TileFlag_FlipV) != 0) { neighborTileCollider.FlipV(); } if ((neighborTileData & Tileset.k_TileFlag_Rot90) != 0) { neighborTileCollider.Rot90(); } } neighborTileCollData[i] = neighborTileCollider; isSurroundedByFullColliders &= (neighborTileCollider.type == eTileCollider.Full); if (neighborTileCollider.type == eTileCollider.None) { segmentMinMax = new Vector2(float.MaxValue, float.MinValue); //NOTE: x will be min, y will be max } else if (neighborTileCollider.type == eTileCollider.Full) { segmentMinMax = new Vector2(0f, 1f); //NOTE: x will be min, y will be max neighborCollFlags |= (1 << i); } else { segmentMinMax = new Vector2(float.MaxValue, float.MinValue); //NOTE: x will be min, y will be max neighborCollFlags |= (1 << i); for (int j = 0; j < neighborTileCollider.vertices.Length; ++j) { Vector2 v = neighborTileCollider.vertices[j]; { if (i == 0 && v.y == 0 || i == 2 && v.y == 1) //Top || Bottom { if (v.x < segmentMinMax.x) { segmentMinMax.x = v.x; } if (v.x > segmentMinMax.y) { segmentMinMax.y = v.x; } } else if (i == 1 && v.x == 0 || i == 3 && v.x == 1) //Right || Left { if (v.y < segmentMinMax.x) { segmentMinMax.x = v.y; } if (v.y > segmentMinMax.y) { segmentMinMax.y = v.y; } } } } } s_neighborSegmentMinMax[i] = segmentMinMax; } else { isSurroundedByFullColliders = false; } } } // Create Mesh Colliders if (isSurroundedByFullColliders && !hasMergedColliders) { //Debug.Log(" Surrounded! " + tileIdx); } else { float px0 = tx * CellSize.x; float py0 = ty * CellSize.y; Vector2[] collVertices = subTileMergedColliderVertices; if (!hasMergedColliders) { collVertices = tileCollData.type == eTileCollider.Full ? s_fullCollTileVertices : tileCollData.vertices; } for (int i = 0; i < collVertices.Length; ++i) { Vector2 s0 = collVertices[i]; Vector2 s1 = collVertices[i == (collVertices.Length - 1) ? 0 : i + 1]; if (hasMergedColliders) { ++i; // add ++i; in this case to go 2 by 2 because the collVertices for merged colliders will have the segments in pairs } // full collider optimization if ((tileCollData.type == eTileCollider.Full) && ( (i == 0 && neighborTileCollData[3].type == eTileCollider.Full) || // left tile has collider (i == 1 && neighborTileCollData[0].type == eTileCollider.Full) || // top tile has collider (i == 2 && neighborTileCollData[1].type == eTileCollider.Full) || // right tile has collider (i == 3 && neighborTileCollData[2].type == eTileCollider.Full) // bottom tile has collider ) ) { continue; } // polygon collider optimization else // if( tileCollData.type == eTileCollider.Polygon ) Or Full colliders if neighbor is not Full as well { Vector2 n, m; if (s0.y == 1f && s1.y == 1f) // top side { if ((neighborCollFlags & 0x1) != 0) // top tile has collider { n = s_neighborSegmentMinMax[0]; if (n.x < n.y && n.x <= s0.x && n.y >= s1.x) { continue; } } } else if (s0.x == 1f && s1.x == 1f) // right side { if ((neighborCollFlags & 0x2) != 0) // right tile has collider { n = s_neighborSegmentMinMax[1]; if (n.x < n.y && n.x <= s1.y && n.y >= s0.y) { continue; } } } else if (s0.y == 0f && s1.y == 0f) // bottom side { if ((neighborCollFlags & 0x4) != 0) // bottom tile has collider { n = s_neighborSegmentMinMax[2]; if (n.x < n.y && n.x <= s1.x && n.y >= s0.x) { continue; } } } else if (s0.x == 0f && s1.x == 0f) // left side { if ((neighborCollFlags & 0x8) != 0) // left tile has collider { n = s_neighborSegmentMinMax[3]; if (n.x < n.y && n.x <= s0.y && n.y >= s1.y) { continue; } } } else if (s0.y == 1f && s1.x == 1f) // top - right diagonal { if ((neighborCollFlags & 0x1) != 0 && (neighborCollFlags & 0x2) != 0) { n = s_neighborSegmentMinMax[0]; m = s_neighborSegmentMinMax[1]; if ((n.x < n.y && n.x <= s0.x && n.y == 1f) && (m.x < m.y && m.x <= s1.y && m.y == 1f)) { continue; } } } else if (s0.x == 1f && s1.y == 0f) // right - bottom diagonal { if ((neighborCollFlags & 0x2) != 0 && (neighborCollFlags & 0x4) != 0) { n = s_neighborSegmentMinMax[1]; m = s_neighborSegmentMinMax[2]; if ((n.x < n.y && n.x == 0f && n.y >= s0.y) && (m.x < m.y && m.x <= s1.x && m.y == 1f)) { continue; } } } else if (s0.y == 0f && s1.x == 0f) // bottom - left diagonal { if ((neighborCollFlags & 0x4) != 0 && (neighborCollFlags & 0x8) != 0) { n = s_neighborSegmentMinMax[2]; m = s_neighborSegmentMinMax[3]; if ((n.x < n.y && n.x == 0f && n.y >= s0.x) && (m.x < m.y && m.x == 0f && m.y >= s1.y)) { continue; } } } else if (s0.x == 0f && s1.y == 1f) // left - top diagonal { if ((neighborCollFlags & 0x8) != 0 && (neighborCollFlags & 0x1) != 0) { n = s_neighborSegmentMinMax[3]; m = s_neighborSegmentMinMax[0]; if ((n.x < n.y && n.x <= s0.y && n.y == 1f) && (m.x < m.y && m.x == 0f && m.y >= s1.x)) { continue; } } } } // Update s0 and s1 to world positions s0.x = px0 + CellSize.x * s0.x; s0.y = py0 + CellSize.y * s0.y; s1.x = px0 + CellSize.x * s1.x; s1.y = py0 + CellSize.y * s1.y; if (ParentTilemap.ColliderType == eColliderType._3D) { int collVertexIdx = s_meshCollVertices.Count; s_meshCollVertices.Add(new Vector3(s0.x, s0.y, -halvedCollDepth)); s_meshCollVertices.Add(new Vector3(s0.x, s0.y, halvedCollDepth)); s_meshCollVertices.Add(new Vector3(s1.x, s1.y, halvedCollDepth)); s_meshCollVertices.Add(new Vector3(s1.x, s1.y, -halvedCollDepth)); s_meshCollTriangles.Add(collVertexIdx + 0); s_meshCollTriangles.Add(collVertexIdx + 1); s_meshCollTriangles.Add(collVertexIdx + 2); s_meshCollTriangles.Add(collVertexIdx + 2); s_meshCollTriangles.Add(collVertexIdx + 3); s_meshCollTriangles.Add(collVertexIdx + 0); } else //if( ParentTilemap.ColliderType == eColliderType._2D ) { int linkedSegments = 0; int segmentIdxToMerge = -1; for (int edgeIdx = s_openEdges.Count - 1; edgeIdx >= 0 && linkedSegments < 2; --edgeIdx) { LinkedList <Vector2> edgeSegments = s_openEdges[edgeIdx]; if (edgeSegments.First.Value == edgeSegments.Last.Value) { continue; //skip closed edges } if (edgeSegments.Last.Value == s0) { if (segmentIdxToMerge >= 0) { LinkedList <Vector2> segmentToMerge = s_openEdges[segmentIdxToMerge]; if (s0 == segmentToMerge.First.Value) { for (LinkedListNode <Vector2> node = segmentToMerge.First.Next; node != null; node = node.Next) { edgeSegments.AddLast(node.Value); } s_openEdges.RemoveAt(segmentIdxToMerge); } /* Cannot join head with head or tail with tail, it will change the segment normal * else * for (LinkedListNode<Vector2> node = segmentToMerge.Last.Previous; node != null; node = node.Previous) * edgeSegments.AddLast(node.Value);*/ } else { segmentIdxToMerge = edgeIdx; edgeSegments.AddLast(s1); } ++linkedSegments; } /* Cannot join head with head or tail with tail, it will change the segment normal * else if( edgeSegments.Last.Value == s1 ) * else if (edgeSegments.First.Value == s0)*/ else if (edgeSegments.First.Value == s1) { if (segmentIdxToMerge >= 0) { LinkedList <Vector2> segmentToMerge = s_openEdges[segmentIdxToMerge]; if (s1 == segmentToMerge.Last.Value) { for (LinkedListNode <Vector2> node = edgeSegments.First.Next; node != null; node = node.Next) { segmentToMerge.AddLast(node.Value); } s_openEdges.RemoveAt(edgeIdx); } /* Cannot join head with head or tail with tail, it will change the segment normal * else * for (LinkedListNode<Vector2> node = edgeSegments.First.Next; node != null; node = node.Next) * segmentToMerge.AddFirst(node.Value);*/ } else { segmentIdxToMerge = edgeIdx; edgeSegments.AddFirst(s0); } ++linkedSegments; } } if (linkedSegments == 0) { LinkedList <Vector2> newEdge = new LinkedList <Vector2>(); newEdge.AddFirst(s0); newEdge.AddLast(s1); s_openEdges.Add(newEdge); } } } } } } } } } if (ParentTilemap.ColliderType == eColliderType._2D) { //+++ Process Edges //(NOTE: this was added to fix issues related with lighting, otherwise leave this commented) { // Remove vertex inside a line RemoveRedundantVertices(s_openEdges); // Split segments (NOTE: This is not working with polygon colliders) /*/ commented unless necessary for performance reasons * if (ParentTilemap.Collider2DType == e2DColliderType.EdgeCollider2D) * { * openEdges = SplitSegments(openEdges); * } * //*/ } //--- //Create Edges for (int i = 0; i < s_openEdges.Count; ++i) { LinkedList <Vector2> edgeSegments = s_openEdges[i]; bool reuseCollider = i < aColliders2D.Length; Collider2D collider2D = reuseCollider ? (Collider2D)aColliders2D[i] : (Collider2D)gameObject.AddComponent(collider2DType); collider2D.enabled = true; collider2D.isTrigger = ParentTilemap.IsTrigger; collider2D.sharedMaterial = ParentTilemap.PhysicMaterial2D; if (ParentTilemap.Collider2DType == e2DColliderType.EdgeCollider2D) { ((EdgeCollider2D)collider2D).points = edgeSegments.ToArray(); } else { ((PolygonCollider2D)collider2D).SetPath(0, edgeSegments.ToArray()); } } //Destroy unused edge colliders for (int i = s_openEdges.Count; i < aColliders2D.Length; ++i) { if (!s_isOnValidate) { DestroyImmediate(aColliders2D[i]); } else { ((Collider2D)aColliders2D[i]).enabled = false; } } } return(!isEmpty); }
private void DoPaintInspector() { Event e = Event.current; Tilemap tilemap = (Tilemap)target; if (DoToolBar() || DragAndDrop.objectReferences.Length > 0 || // hide brush when user is dragging a prefab into the scene EditorWindow.mouseOverWindow != SceneView.currentDrawingSceneView) // hide brush when it's not over the scene view { m_brushVisible = false; SceneView.RepaintAll(); return; } int controlID = GUIUtility.GetControlID(FocusType.Passive); HandleUtility.AddDefaultControl(controlID); EventType currentEventType = Event.current.GetTypeForControl(controlID); bool skip = false; int saveControl = GUIUtility.hotControl; try { if (currentEventType == EventType.Layout) { skip = true; } else if (currentEventType == EventType.ScrollWheel) { skip = true; } if (tilemap.Tileset == null) { return; } if (!skip) { if (e.type == EventType.KeyDown) { if (e.keyCode == ShortcutKeys.k_FlipH) { BrushBehaviour.GetOrCreateBrush(tilemap).FlipH(!e.shift); e.Use(); // Use key event } else if (e.keyCode == ShortcutKeys.k_FlipV) { BrushBehaviour.GetOrCreateBrush(tilemap).FlipV(!e.shift); e.Use(); // Use key event } else if (e.keyCode == ShortcutKeys.k_Rot90) { BrushBehaviour.GetOrCreateBrush(tilemap).Rot90(!e.shift); e.Use(); // Use key event } else if (e.keyCode == ShortcutKeys.k_Rot90Back) { BrushBehaviour.GetOrCreateBrush(tilemap).Rot90Back(!e.shift); e.Use(); // Use key event } } EditorGUIUtility.AddCursorRect(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), MouseCursor.Arrow); GUIUtility.hotControl = controlID; { Plane chunkPlane = new Plane(tilemap.transform.forward, tilemap.transform.position); Vector2 mousePos = Event.current.mousePosition; mousePos.y = Screen.height - mousePos.y; Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); float dist; if (chunkPlane.Raycast(ray, out dist)) { Rect rTile = new Rect(0, 0, m_tilemap.CellSize.x, m_tilemap.CellSize.y); rTile.position = tilemap.transform.InverseTransformPoint(ray.GetPoint(dist)); Vector2 tilePos = rTile.position; if (tilePos.x < 0) { tilePos.x -= m_tilemap.CellSize.x; } if (tilePos.y < 0) { tilePos.y -= m_tilemap.CellSize.y; } tilePos.x -= tilePos.x % m_tilemap.CellSize.x; tilePos.y -= tilePos.y % m_tilemap.CellSize.y; rTile.position = tilePos; Vector2 startPos = new Vector2(Mathf.Min(m_startDragging.x, m_endDragging.x), Mathf.Min(m_startDragging.y, m_endDragging.y)); Vector2 endPos = new Vector2(Mathf.Max(m_startDragging.x, m_endDragging.x), Mathf.Max(m_startDragging.y, m_endDragging.y)); Vector2 selectionSnappedPos = BrushUtil.GetSnappedPosition(startPos, m_tilemap.CellSize); Vector2 selectionSize = BrushUtil.GetSnappedPosition(endPos, m_tilemap.CellSize) - selectionSnappedPos + m_tilemap.CellSize; BrushBehaviour brush = BrushBehaviour.GetOrCreateBrush(tilemap); // Update brush transform m_localPaintPos = (Vector2)tilemap.transform.InverseTransformPoint(ray.GetPoint(dist)); Vector2 brushSnappedPos = BrushUtil.GetSnappedPosition(brush.Offset + m_localPaintPos, m_tilemap.CellSize); brush.transform.rotation = tilemap.transform.rotation; brush.transform.localScale = tilemap.transform.lossyScale; brush.transform.position = tilemap.transform.TransformPoint(new Vector3(brushSnappedPos.x, brushSnappedPos.y, -0.01f)); //--- int prevMouseGridX = m_mouseGridX; int prevMouseGridY = m_mouseGridY; if (e.isMouse) { m_mouseGridX = BrushUtil.GetGridX(m_localPaintPos, tilemap.CellSize); m_mouseGridY = BrushUtil.GetGridY(m_localPaintPos, tilemap.CellSize); } bool isMouseGridChanged = prevMouseGridX != m_mouseGridX || prevMouseGridY != m_mouseGridY; //Update Fill Preview if (GetBrushMode() == eBrushMode.Fill && isMouseGridChanged) { m_fillPreview.Clear(); TilemapDrawingUtils.FloodFillPreview(tilemap, brush.Offset + m_localPaintPos, brush.BrushTilemap.GetTileData(0, 0), m_fillPreview); } if ( (EditorWindow.focusedWindow == EditorWindow.mouseOverWindow) && // fix painting tiles when closing another window popup over the SceneView like GameObject Selection window (e.type == EventType.MouseDown || e.type == EventType.MouseDrag && isMouseGridChanged) ) { if (e.button == 0) { if (m_dblClick.IsDblClick && brush.BrushTilemap.GridWidth == 1 && brush.BrushTilemap.GridHeight == 1) { // Restore previous tiledata modified by Paint, because before the double click, a single click is done before tilemap.SetTileData(brush.Offset + m_localPaintPos, m_floodFillRestoredTileData); brush.FloodFill(tilemap, brush.Offset + m_localPaintPos, brush.BrushTilemap.GetTileData(0, 0)); } // Do a brush paint action else { switch (GetBrushMode()) { case eBrushMode.Paint: m_floodFillRestoredTileData = tilemap.GetTileData(m_mouseGridX, m_mouseGridY); brush.Paint(tilemap, brush.Offset + m_localPaintPos); break; case eBrushMode.Erase: brush.Erase(tilemap, brush.Offset + m_localPaintPos); break; case eBrushMode.Fill: brush.FloodFill(tilemap, brush.Offset + m_localPaintPos, brush.BrushTilemap.GetTileData(0, 0)); break; } } } else if (e.button == 1) { if (e.type == EventType.MouseDown) { m_isDragging = true; brush.BrushTilemap.ClearMap(); m_startDragging = m_endDragging = m_localPaintPos; } else { m_endDragging = m_localPaintPos; } } } else if (e.type == EventType.MouseUp) { if (e.button == 1) // right mouse button { m_isDragging = false; ResetBrushMode(); // Copy one tile if (selectionSize.x <= m_tilemap.CellSize.x && selectionSize.y <= m_tilemap.CellSize.y) { uint tileData = tilemap.GetTileData(m_localPaintPos); if (tileData == Tileset.k_TileData_Empty) { tilemap.Tileset.SelectedTileId = Tileset.k_TileId_Empty; brush.BrushTilemap.SetTileData(0, 0, Tileset.k_TileData_Empty); } else { int brushId = Tileset.GetBrushIdFromTileData(tileData); int tileId = Tileset.GetTileIdFromTileData(tileData); // Select the copied tile in the tileset, alternating between the brush and the tile drawn by the brush if (brushId > 0 && brushId != tilemap.Tileset.SelectedBrushId) { tilemap.Tileset.SelectedBrushId = brushId; } else { tilemap.Tileset.SelectedTileId = tileId; brush.BrushTilemap.SetTileData(0, 0, tileData & ~Tileset.k_TileDataMask_BrushId); // keep tile flags } } // Cut tile if key shift is pressed if (e.shift) { int startGridX = BrushUtil.GetGridX(startPos, m_tilemap.CellSize); int startGridY = BrushUtil.GetGridY(startPos, m_tilemap.CellSize); brush.CutRect(tilemap, startGridX, startGridY, startGridX, startGridY); } brush.BrushTilemap.UpdateMesh(); brush.Offset = Vector2.zero; } // copy a rect of tiles else { int startGridX = BrushUtil.GetGridX(startPos, m_tilemap.CellSize); int startGridY = BrushUtil.GetGridY(startPos, m_tilemap.CellSize); int endGridX = BrushUtil.GetGridX(endPos, m_tilemap.CellSize); int endGridY = BrushUtil.GetGridY(endPos, m_tilemap.CellSize); // Cut tile if key shift is pressed if (e.shift) { brush.CutRect(tilemap, startGridX, startGridY, endGridX, endGridY); } else { brush.CopyRect(tilemap, startGridX, startGridY, endGridX, endGridY); } brush.Offset.x = m_endDragging.x > m_startDragging.x ? -(endGridX - startGridX) * tilemap.CellSize.x : 0f; brush.Offset.y = m_endDragging.y > m_startDragging.y ? -(endGridY - startGridY) * tilemap.CellSize.y : 0f; } } } if (m_isDragging) { Rect rGizmo = new Rect(selectionSnappedPos, selectionSize); HandlesEx.DrawRectWithOutline(tilemap.transform, rGizmo, new Color(), Color.white); } else // Draw brush border { Rect rBound = new Rect(brush.BrushTilemap.MapBounds.min, brush.BrushTilemap.MapBounds.size); Color fillColor; switch (GetBrushMode()) { case eBrushMode.Paint: fillColor = new Color(0, 0, 0, 0); break; case eBrushMode.Erase: fillColor = new Color(1f, 0f, 0f, 0.1f); break; case eBrushMode.Fill: fillColor = new Color(1f, 1f, 0f, 0.2f); break; default: fillColor = new Color(0, 0, 0, 0); break; } HandlesEx.DrawRectWithOutline(brush.transform, rBound, fillColor, new Color(1, 1, 1, 0.2f)); } } } if (currentEventType == EventType.MouseDrag && Event.current.button < 2) // 2 is for central mouse button { // avoid dragging the map Event.current.Use(); } } } // Avoid loosing the hotControl because of a triggered exception catch (System.Exception ex) { Debug.LogException(ex); } SceneView.RepaintAll(); GUIUtility.hotControl = saveControl; }