public override Vector2[] GetMergedSubtileColliderVertices(STETilemap 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 < 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); }
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); }