public virtual uint Refresh(Tilemap tilemap, int gridX, int gridY, uint tileData) { return(tileData); }
public virtual uint[] GetSubtiles(Tilemap tilemap, int gridX, int gridY, uint tileData) { return(null); }
/// <summary> /// Get the world position for the center of a given grid cell position. /// </summary> /// <param name="gridX"></param> /// <param name="gridY"></param> /// <returns></returns> static public Vector3 GetGridWorldPos(Tilemap tilemap, int gridX, int gridY) { return(tilemap.transform.TransformPoint(new Vector2((gridX + .5f) * tilemap.CellSize.x, (gridY + .5f) * tilemap.CellSize.y))); }
public virtual Vector2[] GetMergedSubtileColliderVertices(Tilemap tilemap, int gridX, int gridY, uint tileData) { return(null); }
public override Vector2[] GetMergedSubtileColliderVertices(Tilemap tilemap, int gridX, int gridY, uint tileData) { uint[] subTiles = GetSubtiles(tilemap, gridX, gridY, tileData); if (subTiles != null) { s_mergedColliderVertexList.Clear(); for (int i = 0; i < subTiles.Length; ++i) { uint subTileData = subTiles[i]; Tile tile = tilemap.Tileset.GetTile(Tileset.GetTileIdFromTileData(subTiles[i])); if (tile != null && tile.collData.type != eTileCollider.None) { TileColliderData tileCollData = tile.collData; if ((subTileData & (Tileset.k_TileFlag_FlipH | Tileset.k_TileFlag_FlipV | Tileset.k_TileFlag_Rot90)) != 0) { tileCollData = tileCollData.Clone(); tileCollData.ApplyFlippingFlags(subTileData); } Vector2[] vertices = tile.collData.GetVertices(); if (vertices != null) { for (int v = 0; v < vertices.Length; ++v) { Vector2 v0, v1; if (v < tile.collData.vertices.Length - 1) { v0 = vertices[v]; v1 = vertices[v + 1]; } else { v0 = vertices[v]; v1 = vertices[0]; } if (i == 0 || i == 2) //left side { if (v0.x >= .5f && v1.x >= .5f) { continue; } float newY = v0.y + (.5f - v0.x) * (v1.y - v0.y) / (v1.x - v0.x); if (v0.x > .5f) { v0.y = newY; v0.x = .5f; } else if (v1.x > .5f) { v1.y = newY; v1.x = .5f; } } else // right side { if (v0.x <= .5f && v1.x <= .5f) { continue; } float newY = v0.y + (.5f - v0.x) * (v1.y - v0.y) / (v1.x - v0.x); if (v0.x < .5f) { v0.y = newY; v0.x = .5f; } else if (v1.x < .5f) { v1.y = newY; v1.x = .5f; } } if (i == 0 || i == 1) //bottom side { if (v0.y >= .5f && v1.y >= .5f) { continue; } float newX = v0.x + (.5f - v0.y) * (v1.x - v0.x) / (v1.y - v0.y); if (v0.y > .5f) { v0.x = newX; v0.y = .5f; } else if (v1.y > .5f) { v1.x = newX; v1.y = .5f; } } else // top side { if (v0.y <= .5f && v1.y <= .5f) { continue; } float newX = v0.x + (.5f - v0.y) * (v1.x - v0.x) / (v1.y - v0.y); if (v0.y < .5f) { v0.x = newX; v0.y = .5f; } else if (v1.y < .5f) { v1.x = newX; v1.y = .5f; } } s_mergedColliderVertexList.Add(v0); s_mergedColliderVertexList.Add(v1); } } } } return(s_mergedColliderVertexList.ToArray()); } return(null); }
//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; LinkedList <Point> check = new LinkedList <Point>(); uint floodFrom = tilemap.GetTileData(gridX, gridY); tilemap.SetTileData(gridX, gridY, tileData); bool isBrush = Tileset.GetBrushIdFromTileData(floodFrom) != 0; //Debug.Log(" Flood Fill Starts +++++++++++++++ "); 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); 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); } } } 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"); }
private ReorderableList CreateTilemapReorderableList() { ReorderableList reordList = new ReorderableList(serializedObject, serializedObject.FindProperty("m_tilemaps"), true, true, true, true); reordList.displayAdd = reordList.displayRemove = true; reordList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, "Tilemaps", EditorStyles.boldLabel); Texture2D btnTexture = reordList.elementHeight == 0f ? EditorGUIUtility.FindTexture("winbtn_win_max_h") : EditorGUIUtility.FindTexture("winbtn_win_min_h"); if (GUI.Button(new Rect(rect.width - rect.height, rect.y, rect.height, rect.height), btnTexture, EditorStyles.label)) { reordList.elementHeight = reordList.elementHeight == 0f ? EditorGUIUtility.singleLineHeight : 0f; reordList.draggable = reordList.elementHeight > 0f; } }; reordList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { var element = reordList.serializedProperty.GetArrayElementAtIndex(index); if (Event.current.type == EventType.Repaint) { m_reordListRectsDic[m_reordIdx] = rect; } else if (Event.current.type == EventType.Layout) { m_reordListRectsDic.TryGetValue(m_reordIdx, out rect); } m_reordIdx++; rect.y += 2; Tilemap tilemap = element.objectReferenceValue as Tilemap; SerializedObject tilemapSerialized = new SerializedObject(tilemap); SerializedObject tilemapObjSerialized = new SerializedObject(tilemapSerialized.FindProperty("m_GameObject").objectReferenceValue); GUILayout.BeginArea(rect); EditorGUILayout.BeginHorizontal(); tilemap.IsVisible = EditorGUILayout.Toggle(tilemap.IsVisible, GUILayout.Width(16)); EditorGUILayout.PropertyField(tilemapObjSerialized.FindProperty("m_Name"), GUIContent.none); if (TilemapEditor.EditMode == TilemapEditor.eEditMode.Collider) { SerializedProperty colliderTypeProperty = tilemapSerialized.FindProperty("ColliderType"); string[] colliderTypeNames = new List <string>(System.Enum.GetNames(typeof(eColliderType)).Select(x => x.Replace('_', ' '))).ToArray(); EditorGUI.BeginChangeCheck(); colliderTypeProperty.intValue = GUILayout.SelectionGrid(colliderTypeProperty.intValue, colliderTypeNames, colliderTypeNames.Length, GUILayout.MaxHeight(0.9f * EditorGUIUtility.singleLineHeight)); if (EditorGUI.EndChangeCheck()) { tilemapSerialized.ApplyModifiedProperties(); tilemap.Refresh(false, true); } } else { // Sorting Layer and Order in layer EditorGUI.BeginChangeCheck(); EditorGUIUtility.labelWidth = 1; EditorGUILayout.PropertyField(tilemapSerialized.FindProperty("m_sortingLayer"), new GUIContent(" ")); EditorGUIUtility.labelWidth = 40; EditorGUILayout.PropertyField(tilemapSerialized.FindProperty("m_orderInLayer"), new GUIContent("Order"), GUILayout.MaxWidth(90)); EditorGUIUtility.labelWidth = 0; tilemapSerialized.FindProperty("m_orderInLayer").intValue = (tilemapSerialized.FindProperty("m_orderInLayer").intValue << 16) >> 16; // convert from int32 to int16 keeping sign if (EditorGUI.EndChangeCheck()) { tilemapSerialized.ApplyModifiedProperties(); tilemap.RefreshChunksSortingAttributes(); SceneView.RepaintAll(); } //--- } EditorGUILayout.EndHorizontal(); GUILayout.EndArea(); if (GUI.changed) { tilemapObjSerialized.ApplyModifiedProperties(); } }; reordList.onReorderCallback = (ReorderableList list) => { var targetObj = target as TilemapGroup; int sibilingIdx = 0; foreach (Tilemap tilemap in targetObj.Tilemaps) { tilemap.transform.SetSiblingIndex(sibilingIdx++); } Repaint(); }; reordList.onSelectCallback = (ReorderableList list) => { serializedObject.FindProperty("m_selectedIndex").intValue = reordList.index; serializedObject.ApplyModifiedProperties(); GUI.changed = true; TileSelectionWindow.RefreshIfVisible(); TilePropertiesWindow.RefreshIfVisible(); }; reordList.onAddCallback = (ReorderableList list) => { var targetObj = target as TilemapGroup; Undo.RegisterCompleteObjectUndo(targetObj, "New Tilemap"); GameObject obj = new GameObject(); Undo.RegisterCreatedObjectUndo(obj, "New Tilemap"); Tilemap newTilemap = obj.AddComponent <Tilemap>(); obj.transform.parent = targetObj.transform; obj.name = GameObjectUtility.GetUniqueNameForSibling(obj.transform.parent, "New Tilemap"); Tilemap copiedTilemap = targetObj.SelectedTilemap; if (copiedTilemap) { UnityEditorInternal.ComponentUtility.CopyComponent(copiedTilemap); UnityEditorInternal.ComponentUtility.PasteComponentValues(newTilemap); obj.SendMessage("_DoDuplicate"); obj.name = GameObjectUtility.GetUniqueNameForSibling(obj.transform.parent, copiedTilemap.name); } }; reordList.onRemoveCallback = (ReorderableList list) => { var targetObj = target as TilemapGroup; Undo.DestroyObjectImmediate(targetObj.SelectedTilemap.gameObject); //NOTE: Fix argument exception if (m_tilemapReordList.index == targetObj.Tilemaps.Count - 1) { serializedObject.FindProperty("m_selectedIndex").intValue = m_tilemapReordList.index = m_tilemapReordList.index - 1; } }; return(reordList); }
// '°', '├', '═', '┤', | 0, 2, 10, 8, // '┬', '╔', '╦', '╗', | 4, 6, 14, 12, // '║', '╠', '╬', '╣', | 5, 7, 15, 13, // '┴', '╚', '╩', '╝', | 1, 3, 11, 9, public override uint[] GetSubtiles(Tilemap 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) { 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); }
public void DoPaintDragged(Tilemap tilemap, Vector2 localPos, EventModifiers modifiers = default(EventModifiers)) { //Debug.Log("DoPaintDragged (" + TilemapUtils.GetGridX(tilemap, localPos) + "," + TilemapUtils.GetGridY(tilemap, localPos) + ")"); if (m_paintMode == eBrushPaintMode.Pencil) { Paint(tilemap, localPos); } else { if (m_isDragging) { BrushTilemap.ClearMap(); Vector2 brushLocPos = tilemap.transform.InverseTransformPoint(transform.position); Vector2 startPos = BrushUtil.GetSnappedPosition(m_pressedPosition, BrushTilemap.CellSize) + BrushTilemap.CellSize / 2f - brushLocPos; Vector2 endPos = BrushUtil.GetSnappedPosition(localPos, BrushTilemap.CellSize) + BrushTilemap.CellSize / 2f - brushLocPos; bool isCtrl = (modifiers & EventModifiers.Control) != 0; bool isShift = (modifiers & EventModifiers.Shift) != 0; switch (m_paintMode) { case eBrushPaintMode.Line: if (isCtrl) { TilemapDrawingUtils.DrawLineMirrored(BrushTilemap, startPos, endPos, m_brushPattern); } else { TilemapDrawingUtils.DrawLine(BrushTilemap, startPos, endPos, m_brushPattern); } break; case eBrushPaintMode.Rect: case eBrushPaintMode.FilledRect: case eBrushPaintMode.Ellipse: case eBrushPaintMode.FilledEllipse: if (isShift) { Vector2 vTemp = endPos - startPos; float absX = Mathf.Abs(vTemp.x); float absY = Mathf.Abs(vTemp.y); vTemp.x = (absX > absY) ? vTemp.x : Mathf.Sign(vTemp.x) * absY; vTemp.y = Mathf.Sign(vTemp.y) * Mathf.Abs(vTemp.x); endPos = startPos + vTemp; } if (isCtrl) { startPos = 2f * startPos - endPos; } if (m_paintMode == eBrushPaintMode.Rect || m_paintMode == eBrushPaintMode.FilledRect) { TilemapDrawingUtils.DrawRect(BrushTilemap, startPos, endPos, m_brushPattern, m_paintMode == eBrushPaintMode.FilledRect, (modifiers & EventModifiers.Alt) != 0); } else if (m_paintMode == eBrushPaintMode.Ellipse || m_paintMode == eBrushPaintMode.FilledEllipse) { TilemapDrawingUtils.DrawEllipse(BrushTilemap, startPos, endPos, m_brushPattern, m_paintMode == eBrushPaintMode.FilledEllipse); } break; } BrushTilemap.UpdateMeshImmediate(); } } }
static public Texture2D CreateTexture2DFromTilemap(Tilemap tilemap) { MakeTextureReadable(tilemap.Tileset.AtlasTexture); int tilePxSizeX = (int)tilemap.Tileset.TilePxSize.x; int tilePxSizeY = (int)tilemap.Tileset.TilePxSize.y; Texture2D output = new Texture2D(tilemap.GridWidth * tilePxSizeX, tilemap.GridHeight * tilePxSizeY, TextureFormat.ARGB32, false); output.filterMode = FilterMode.Point; output.SetPixels32(new Color32[output.width * output.height]); output.Apply(); System.Action <Tilemap, int, int, uint> action = (Tilemap source, int gridX, int gridY, uint tileData) => { gridX -= source.MinGridX; gridY -= source.MinGridY; Tile tile = tilemap.Tileset.GetTile(Tileset.GetTileIdFromTileData(tileData)); if (tile != null) { Texture2D atlasTexture = tilemap.Tileset.AtlasTexture; int tx = Mathf.RoundToInt(tile.uv.x * atlasTexture.width); int ty = Mathf.RoundToInt(tile.uv.y * atlasTexture.height); int tw = tilePxSizeX; int th = tilePxSizeY; Sprite prefabSprite = null; if (tile.prefabData.prefab) { SpriteRenderer spriteRenderer = tile.prefabData.prefab.GetComponent <SpriteRenderer>(); if (spriteRenderer && spriteRenderer.sprite) { prefabSprite = spriteRenderer.sprite; MakeTextureReadable(spriteRenderer.sprite.texture); atlasTexture = spriteRenderer.sprite.texture; tx = Mathf.RoundToInt(spriteRenderer.sprite.textureRect.x); ty = Mathf.RoundToInt(spriteRenderer.sprite.textureRect.y); tw = Mathf.RoundToInt(spriteRenderer.sprite.textureRect.width); th = Mathf.RoundToInt(spriteRenderer.sprite.textureRect.height); } } bool flipH = (tileData & Tileset.k_TileFlag_FlipH) != 0; bool flipV = (tileData & Tileset.k_TileFlag_FlipV) != 0; bool rot90 = (tileData & Tileset.k_TileFlag_Rot90) != 0; Color[] srcTileColors = atlasTexture.GetPixels(tx, ty, tw, th); if (flipH) { Color[] tempArr = new Color[0]; for (int i = 0; i < th; ++i) { tempArr = tempArr.Concat(srcTileColors.Skip(tw * i).Take(tw).Reverse()).ToArray(); } srcTileColors = tempArr; } if (flipV) { Color[] tempArr = new Color[0]; for (int i = th - 1; i >= 0; --i) { tempArr = tempArr.Concat(srcTileColors.Skip(tw * i).Take(tw)).ToArray(); } srcTileColors = tempArr; } if (rot90) { Color[] tempArr = new Color[tw * th]; for (int x = tw - 1, i = 0; x >= 0; --x) { for (int y = 0; y < th; ++y, ++i) { tempArr[i] = srcTileColors[y * tw + x]; } } srcTileColors = tempArr; int temp = tw; tw = th; th = temp; } if (prefabSprite) { Vector2 tileSize = prefabSprite.textureRect.size; Vector2 pivot = prefabSprite.pivot - prefabSprite.textureRectOffset; if (flipV) { pivot.y = -pivot.y + prefabSprite.textureRect.height; } if (flipH) { pivot.x = -pivot.x + prefabSprite.textureRect.width; } if (rot90) { pivot = new Vector2(pivot.y, tileSize.x - pivot.x); tileSize.x = prefabSprite.textureRect.size.y; tileSize.y = prefabSprite.textureRect.size.x; } Vector2 offset = pivot - tilemap.Tileset.TilePxSize / 2;// sprite.pivot + sprite.textureRect.position - sprite.textureRectOffset; BlitPixels(output, gridX * tilePxSizeX - Mathf.RoundToInt(offset.x), gridY * tilePxSizeY - Mathf.RoundToInt(offset.y), Mathf.RoundToInt(tileSize.x), Mathf.RoundToInt(tileSize.y), srcTileColors); } else { output.SetPixels(gridX * tilePxSizeX, gridY * tilePxSizeY, tw, th, srcTileColors); } } }; TilemapUtils.IterateTilemapWithAction(tilemap, action); output.Apply(); return(output); }
/// <summary> /// /// Gets the grid X position for a given tilemap and camera where the mouse is over. /// </summary> /// <param name="tilemap"></param> /// <param name="camera"></param> /// <returns></returns> static public int GetMouseGridY(Tilemap tilemap, Camera camera) { Vector2 locPos = camera.ScreenToWorldPoint(Input.mousePosition); return(GetGridY(tilemap, tilemap.transform.InverseTransformPoint(locPos))); }
/// <summary> /// Gets the grid Y position for a given tilemap and local position. To convert from world to local position use tilemap.transform.InverseTransformPoint(worldPosition). /// Avoid using positions multiple of cellSize like 0.32f if cellSize = 0.16f because due float imprecisions the return value could be wrong. /// </summary> /// <param name="tilemap"></param> /// <param name="locPosition"></param> /// <returns></returns> static public int GetGridY(Tilemap tilemap, Vector2 locPosition) { return(BrushUtil.GetGridY(locPosition, tilemap.CellSize)); }
//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; } } } } } } } }