private List<Poly2Tri.DelaunayTriangle> GetTriangleListFromClipperSolution(ClipperLib.PolyTree solution) { Func<ClipperLib.IntPoint, Poly2Tri.PolygonPoint> xfToPolygonPoint = (p) => new Poly2Tri.PolygonPoint(p.X, p.Y); Poly2Tri.PolygonSet polygonSet = new Poly2Tri.PolygonSet(); ClipperLib.PolyNode node = solution.GetFirst(); while (node != null) { // Only interested in closed paths if (!node.IsOpen) { if (node.IsHole) { if (polygonSet.Polygons.Count() > 0) { // Add hole to last polygon entered var polyPoints = node.Contour.Select(xfToPolygonPoint).ToArray(); Poly2Tri.Polygon hole = new Poly2Tri.Polygon(polyPoints); Poly2Tri.Polygon polygon = polygonSet.Polygons.Last(); polygon.AddHole(hole); } } else { // Add a new polygon to the set var polyPoints = node.Contour.Select(xfToPolygonPoint).ToList(); Poly2Tri.Polygon polygon = new Poly2Tri.Polygon(polyPoints); polygonSet.Add(polygon); } } node = node.GetNext(); } // Now triangulate the whole set Poly2Tri.P2T.Triangulate(polygonSet); // Combine all the triangles into one list List<Poly2Tri.DelaunayTriangle> triangles = new List<Poly2Tri.DelaunayTriangle>(); foreach (var polygon in polygonSet.Polygons) { triangles.AddRange(polygon.Triangles); } return triangles; }
private bool AreNormalsEquivalent(ClipperLib.DoublePoint n0, ClipperLib.DoublePoint n1) { const double epsilon = 1.0f / 1024.0f; double ax = Math.Abs(n0.X - n1.X); double ay = Math.Abs(n0.Y - n1.Y); return (ax < epsilon) && (ay < epsilon); }
public void AddSlit(Point3d from, Point3d to, double thickness, double deeper) { var cut = new LineCurve(from, to); var points = Curves.SelectMany(o => SlitPoints(o, cut)).OrderBy(o => o.T).ToList(); var yUnit = cut.Line.UnitTangent; var xUnit = Vector3d.CrossProduct(yUnit, Plane.Normal); for (int i = 1; i < points.Count; i += 1) { var slitA = points[i - 1]; var slitB = points[i++]; var a = slitA.Point; var b = slitB.Point; var mid = a + (b - a) / 2 + deeper * yUnit; var end = a - (b - a) / 2; var p00 = mid - thickness / 2 * xUnit; var p10 = p00 + thickness * xUnit; var p01 = end - thickness / 2 * xUnit; var p11 = p01 + thickness * xUnit; var slit = new[] { p00, p10, p11, p01, p00 }.ToPolygon(Plane, Unit); Slits.Add(slit); } }
private void AddPolygonCollider2DElements_Convex(ClipperLib.PolyTree solution, List<XElement> xmlList) { // This may generate many convex polygons as opposed to one "complicated" one var polygons = LayerClipper.SolutionPolygons_Simple(solution); // Each PointF array is a polygon with a single path foreach (var pointfArray in polygons) { string data = String.Join(" ", pointfArray.Select(pt => String.Format("{0},{1}", pt.X * Tiled2Unity.Settings.Scale, pt.Y * Tiled2Unity.Settings.Scale))); XElement pathElement = new XElement("Path", data); XElement polyColliderElement = new XElement("PolygonCollider2D", pathElement); xmlList.Add(polyColliderElement); } }
private void AddPolygonCollider2DElements_Complex(ClipperLib.PolyTree solution, List<XElement> xmlList) { // This should generate one "complicated" polygon which may contain holes and concave edges var polygons = ClipperLib.Clipper.ClosedPathsFromPolyTree(solution); if (polygons.Count == 0) return; // Add just one polygon collider that has all paths in it. List<XElement> pathElements = new List<XElement>(); foreach (var path in polygons) { string data = String.Join(" ", path.Select(pt => String.Format("{0},{1}", pt.X * Tiled2Unity.Settings.Scale, pt.Y * Tiled2Unity.Settings.Scale))); XElement pathElement = new XElement("Path", data); pathElements.Add(pathElement); } XElement polyColliderElement = new XElement("PolygonCollider2D", pathElements); xmlList.Add(polyColliderElement); }
public void Clip(double x0, double x1, double y0, double y1) { var p00 = new Point3d(x0, y0, 0.0); var p01 = new Point3d(x0, y1, 0.0); var p11 = new Point3d(x1, y1, 0.0); var p10 = new Point3d(x1, y0, 0.0); var clip = new[] { p00, p10, p11, p01, p00 }.ToPolygon(Plane.WorldXY, Unit); var clipper = new Clipper(); clipper.AddPaths(Polygons, PolyType.ptSubject, true); clipper.AddPath(clip, PolyType.ptClip, true); var solution = new List<List<IntPoint>>(); clipper.Execute(ClipType.ctIntersection, solution, PolyFillType.pftEvenOdd, PolyFillType.pftNonZero); Polygons = solution; Curves = Polygons.ToCurves(Plane, Unit); }
private static void ClipperPolyTreeToPolygonListRecursively(ClipperLib.PolyNode node, HashSet<ClipperLib.IntPoint> sharpPoints, HashSet<ClipperLib.IntPoint> allPoints, List<PolygonClosedD2D> polygonList, IDictionary<ClipperLib.PolyNode, PolygonClosedD2D> dictClipperNodeToNode) { if (node.Contour != null && node.Contour.Count != 0) { var pointsInThisPolygon = node.Contour.Select(clipperPt => new PointD2D(clipperPt.X / 65536.0, clipperPt.Y / 65536.0)); var sharpPointsInThisPolygon = node.Contour.Where(clipperPt => sharpPoints.Contains(clipperPt)).Select(clipperPt => new PointD2D(clipperPt.X / 65536.0, clipperPt.Y / 65536.0)); var polygon = new PolygonClosedD2D(pointsInThisPolygon.ToArray(), new HashSet<PointD2D>(sharpPointsInThisPolygon)); polygon.IsHole = node.IsHole; polygonList.Add(polygon); if (node.IsHole) { polygon.Parent = dictClipperNodeToNode[node.Parent]; } dictClipperNodeToNode.Add(node, polygon); } if (0 != node.ChildCount) { foreach (var childNode in node.Childs) { ClipperPolyTreeToPolygonListRecursively(childNode, sharpPoints, allPoints, polygonList, dictClipperNodeToNode); } } }
public static List<List<Vector2>> GenerateClipperPathPoints(TileLayer tileLayer, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { ClipperLib.Clipper clipper = new ClipperLib.Clipper(); List<List<ClipperLib.IntPoint>> pathsList = new List<List<ClipperLib.IntPoint>>(); List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>(); List<List<Vector2>> points = new List<List<Vector2>>(); for (int x = 0; x < tileLayer.Tiles.Width; x++) { for (int y = 0; y < tileLayer.Tiles.Height; y++) { Tile t = tileLayer.Tiles[x, y]; if (t == null || t.TileSet == null || t.TileSet.TilesObjects == null) continue; if (t.TileSet.TilesObjects.ContainsKey(t.OriginalID)) { List<TileObject> tileObjs = t.TileSet.TilesObjects[t.OriginalID]; foreach (var tileObj in tileObjs) { pathsList.Add(tileObj.GetPath(x, y, t.SpriteEffects, tileLayer.BaseMap.MapRenderParameter.TileWidth, tileLayer.BaseMap.MapRenderParameter.TileHeight)); } } } } // Add the paths to be merged to ClipperLib clipper.AddPaths(pathsList, ClipperLib.PolyType.ptSubject, true); // Merge it! //clipper.PreserveCollinear = false; //clipper.ReverseSolution = true; clipper.StrictlySimple = simpleTileObjectCalculation; if (!clipper.Execute(ClipperLib.ClipType.ctUnion, solution)) return points; clipper.Execute(ClipperLib.ClipType.ctUnion, solution); // Now solution should contain all vertices of the collision object, but they are still multiplied by TileObject.ClipperScale! #region Implementation of increase and decrease offset polygon. if (simpleTileObjectCalculation == false) { // Link of the example of ClipperLib: // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/_Body.htm ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset(clipperMiterLimit, clipperArcTolerance); foreach (List<ClipperLib.IntPoint> item in solution) { co.AddPath(item, clipperJoinType, clipperEndType); } solution.Clear(); co.Execute(ref solution, clipperDeltaOffset * TileObject.ClipperScale); } #endregion for (int i = 0; i < solution.Count; i++) { if (solution[i].Count < 1) continue; points.Add(new List<Vector2>()); for (int j = 0; j < solution[i].Count; j++) { points[i].Add( new Vector2( solution[i][j].X / (float)TileObject.ClipperScale, solution[i][j].Y / (float)TileObject.ClipperScale ) ); } } return points; }
/// <summary> /// Generate 3D Colliders based on Tile Collisions /// </summary> /// <param name="isTrigger">True for Trigger Collider, false otherwise</param> /// <param name="generateClosedPolygon">True to generate a Polygon Collider. False will generate Edge Collider.</param> /// <param name="tag">Tag for the generated GameObjects</param> /// <param name="physicsLayer">Physics Layer for the generated GameObjects</param> /// <param name="physicsMaterial3D">Physics Material for 3D collider</param> /// <param name="zDepth">Z Depth of the collider.</param> /// <param name="colliderWidth">Width of the collider, in Units</param> /// <param name="innerCollision">If true, calculate normals facing the anchor of the collider (inside collisions), else, outside collisions.</param> /// <param name="simpleTileObjectCalculation">true to generate simplified tile collisions</param> /// <param name="clipperArcTolerance">Clipper arc angle tolerance</param> /// <param name="clipperMiterLimit">Clipper limit for Miter join type</param> /// <param name="clipperJoinType">Clipper join type</param> /// <param name="clipperEndType">Clipper Polygon end type</param> /// <param name="clipperDeltaOffset">Clipper delta offset</param> /// <returns></returns> public static GameObject[] GenerateTileCollisions3D(this Map map, bool isTrigger = false, bool generateClosedPolygon = true, string tag = "Untagged", int physicsLayer = 0, PhysicMaterial physicsMaterial3D = null, float zDepth = 0, float colliderWidth = 1, bool innerCollision = false, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { List<GameObject> tileCollisions = new List<GameObject>(); // Iterate over each Tile Layer, grab all TileObjects inside this layer and use their Paths with ClipperLib to generate one polygon collider foreach (var layer in map.Layers) { if (layer is TileLayer) { tileCollisions.AddRange(GenerateTileCollision3DFromLayer(map, layer as TileLayer, isTrigger, generateClosedPolygon, tag, physicsLayer, physicsMaterial3D, zDepth, colliderWidth, innerCollision, simpleTileObjectCalculation, clipperArcTolerance, clipperMiterLimit, clipperJoinType, clipperEndType, clipperDeltaOffset)); } } return tileCollisions.ToArray(); }
/// <summary> /// Generate Colliders based on Tile Collisions /// </summary> /// <param name="used2DColider">True to generate a 2D collider, false to generate a 3D collider.</param> /// <param name="isTrigger">True for Trigger Collider, false otherwise</param> /// <param name="generateClosedPolygon">True to generate a Polygon Collider. False will generate Edge Collider.</param> /// <param name="tag">Tag for the generated GameObjects</param> /// <param name="physicsLayer">Physics Layer for the generated GameObjects</param> /// <param name="physicsMaterial3D">Physics Material for 3D collider</param> /// <param name="physicsMaterial2D">Physics Material for 2D collider</param> /// <param name="zDepth">Z Depth of the collider.</param> /// <param name="colliderWidth">Width of the collider, in Units</param> /// <param name="innerCollision">If true, calculate normals facing the anchor of the collider (inside collisions), else, outside collisions.</param> /// <param name="simpleTileObjectCalculation">true to generate simplified tile collisions</param> /// <param name="clipperArcTolerance">Clipper arc angle tolerance</param> /// <param name="clipperMiterLimit">Clipper limit for Miter join type</param> /// <param name="clipperJoinType">Clipper join type</param> /// <param name="clipperEndType">Clipper Polygon end type</param> /// <param name="clipperDeltaOffset">Clipper delta offset</param> /// <returns>A GameObject containing all generated mapObjects</returns> public static GameObject[] GenerateTileCollisions(this Map map, bool used2DColider = true, bool isTrigger = false, bool generateClosedPolygon = true, string tag = "Untagged", int physicsLayer = 0, PhysicMaterial physicsMaterial3D = null, PhysicsMaterial2D physicsMaterial2D = null, float zDepth = 0, float colliderWidth = 1, bool innerCollision = false, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { if (used2DColider) return GenerateTileCollisions2D(map, isTrigger, generateClosedPolygon, tag, physicsLayer, physicsMaterial2D, zDepth, simpleTileObjectCalculation, clipperArcTolerance, clipperMiterLimit, clipperJoinType, clipperEndType, clipperDeltaOffset); else return GenerateTileCollisions3D(map, isTrigger, generateClosedPolygon, tag, physicsLayer, physicsMaterial3D, zDepth, colliderWidth, innerCollision, simpleTileObjectCalculation, clipperArcTolerance, clipperMiterLimit, clipperJoinType, clipperEndType, clipperDeltaOffset); }
public static GameObject[] GenerateTileCollision3DFromLayer( this Map map, string layer, bool isTrigger = false, bool generateClosedPolygon = true, string tag = "Untagged", int physicsLayer = 0, PhysicMaterial physicsMaterial = null, float zDepth = 1, float colliderWidth = 1, bool innerCollision = false, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { return GenerateTileCollision3DFromLayer(map, map.GetTileLayer(layer), isTrigger, generateClosedPolygon, tag, physicsLayer, physicsMaterial, zDepth, colliderWidth, innerCollision, simpleTileObjectCalculation, clipperArcTolerance, clipperMiterLimit, clipperJoinType, clipperEndType, clipperDeltaOffset); }
public static GameObject[] GenerateTileCollision2DFromLayer( this Map map, TileLayer layer, bool isTrigger = false, bool generateClosedPolygon = true, string tag = "Untagged", int physicsLayer = 0, PhysicsMaterial2D physicsMaterial = null, float zDepth = 1, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { if (layer == null) return null; if (layer.LayerTileCollisions == null) { layer.LayerTileCollisions = new GameObject(layer.Name + " Tile Collisions"); Transform t = layer.LayerTileCollisions.transform; if (layer.BaseMap != null) { t.parent = layer.BaseMap.MapGameObject.transform; } t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; layer.LayerTileCollisions.isStatic = true; } layer.LayerTileCollisions.tag = tag; layer.LayerTileCollisions.layer = physicsLayer; List<GameObject> newSubCollider = new List<GameObject>(); List<List<Vector2>> points = GenerateClipperPathPoints(layer, simpleTileObjectCalculation, clipperArcTolerance, clipperMiterLimit, clipperJoinType, clipperEndType, clipperDeltaOffset); for (int i = 0; i < points.Count; i++) { newSubCollider.Add(new GameObject("Tile Collisions " + layer.Name + "_" + i)); newSubCollider[i].transform.parent = layer.LayerTileCollisions.transform; newSubCollider[i].transform.localPosition = new Vector3(0, 0, zDepth); newSubCollider[i].transform.localScale = Vector3.one; newSubCollider[i].transform.localRotation = Quaternion.identity; newSubCollider[i].tag = tag; newSubCollider[i].layer = physicsLayer; // Add the last point equals to the first to close the collider area // it's necessary only if the first point is diffent from the first one if (points[i][0].x != points[i][points[i].Count - 1].x || points[i][0].y != points[i][points[i].Count - 1].y) { points[i].Add(points[i][0]); } Vector2[] pointsVec = points[i].ToArray(); for (int j = 0; j < pointsVec.Length; j++) { pointsVec[j] = map.TiledPositionToWorldPoint(pointsVec[j]); } if (generateClosedPolygon) { PolygonCollider2D polyCollider = newSubCollider[i].AddComponent<PolygonCollider2D>(); polyCollider.isTrigger = isTrigger; polyCollider.points = pointsVec; if (physicsMaterial != null) polyCollider.sharedMaterial = physicsMaterial; } else { EdgeCollider2D edgeCollider = newSubCollider[i].AddComponent<EdgeCollider2D>(); edgeCollider.isTrigger = isTrigger; edgeCollider.points = pointsVec; if (physicsMaterial != null) edgeCollider.sharedMaterial = physicsMaterial; } } return newSubCollider.ToArray(); }
// Put the closed path polygons into an enumerable collection of an array of points. // Each array of points in a separate convex polygon public static IEnumerable<PointF[]> SolutionPolygons_Simple(ClipperLib.PolyTree solution) { ConvexPolygonSet convexPolygonSet = new ConvexPolygonSet(); convexPolygonSet.MakeConvextSetFromClipperSolution(solution); foreach (var polygon in convexPolygonSet.Polygons) { var pointfs = polygon.Select(pt => new PointF(pt.Xf, pt.Yf)); yield return pointfs.ToArray(); } }
// Put the closed path polygons into an enumerable collection of an array of points. // Each array of points in a path in a "complex" polygon that supports convace edges and holes public static IEnumerable<PointF[]> SolutionPolygons_Complex(ClipperLib.PolyTree solution) { foreach (var points in ClipperLib.Clipper.ClosedPathsFromPolyTree(solution)) { var pointfs = points.Select(pt => new PointF(pt.X, pt.Y)); yield return pointfs.ToArray(); } }
public void MakeConvextSetFromClipperSolution(ClipperLib.PolyTree solution) { var triangles = GetTriangleListFromClipperSolution(solution); MakeConvexSetFromTriangles(triangles); }
/// <summary> /// Create a Tiled Map using TextAsset as parameter /// </summary> /// <param name="mapText">Map's TextAsset</param> /// <param name="mapPath">Path to XML folder, so we can read relative paths for tilesets</param> /// <param name="parent">This map's gameobject parent</param> /// <param name="baseTileMaterial">Base material to be used for the Tiles</param> /// <param name="sortingOrder">Base sorting order for the tile layers</param> /// <param name="mapPath">Path to XML folder, so we can read relative paths for tilesets</param> /// <param name="makeUnique">array with bools to make unique tiles for each tile layer</param> /// <param name="onMapFinishedLoading">Callback for when map finishes loading</param> /// <param name="simpleTileObjectCalculation">true to generate simplified tile collisions</param> /// <param name="tileObjectEllipsePrecision">Tile collisions ellipsoide approximation precision</param> /// <param name="clipperArcTolerance">Clipper arc angle tolerance</param> /// <param name="clipperDeltaOffset">Clipper delta offset</param> /// <param name="clipperEndType">Clipper Polygon end type</param> /// <param name="clipperJoinType">Clipper join type</param> /// <param name="clipperMiterLimit">Clipper limit for Miter join type</param> public Map(TextAsset mapText, string mapPath, GameObject parent, Material baseTileMaterial, int sortingOrder, bool makeUnique, Action<Map> onMapFinishedLoading = null, int tileObjectEllipsePrecision = 16, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { _tileObjectEllipsePrecision = tileObjectEllipsePrecision; _simpleTileObjectCalculation = simpleTileObjectCalculation; _clipperArcTolerance = clipperArcTolerance; _clipperDeltaOffset = clipperDeltaOffset; _clipperEndType = clipperEndType; _clipperJoinType = clipperJoinType; _clipperMiterLimit = clipperMiterLimit; NanoXMLDocument document = new NanoXMLDocument(mapText.text); _mapName = mapText.name; Parent = parent; DefaultSortingOrder = sortingOrder; GlobalMakeUniqueTiles = makeUnique; BaseTileMaterial = baseTileMaterial; _mapPath = mapPath; OnMapFinishedLoading = onMapFinishedLoading; Initialize(document); }
/// <summary> /// Create a Tiled Map loading the XML from a StreamingAssetPath or a HTTP path (in the pc or web) /// </summary> /// <param name="wwwPath">Map's path with http for web files or without streaming assets path for local files</param> /// <param name="parent">This map's gameobject parent</param> /// <param name="baseTileMaterial">Base material to be used for the Tiles</param> /// <param name="sortingOrder">Base sorting order for the tile layers</param> /// <param name="makeUnique">Make unique tiles for all tile layers.</param> /// <param name="onMapFinishedLoading">Callback for when map finishes loading</param> /// <param name="simpleTileObjectCalculation">true to generate simplified tile collisions</param> /// <param name="tileObjectEllipsePrecision">Tile collisions ellipsoide approximation precision</param> /// <param name="clipperArcTolerance">Clipper arc angle tolerance</param> /// <param name="clipperDeltaOffset">Clipper delta offset</param> /// <param name="clipperEndType">Clipper Polygon end type</param> /// <param name="clipperJoinType">Clipper join type</param> /// <param name="clipperMiterLimit">Clipper limit for Miter join type</param> public Map(string wwwPath, GameObject parent, Material baseTileMaterial, int sortingOrder, bool makeUnique, Action<Map> onMapFinishedLoading = null, int tileObjectEllipsePrecision = 16, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { _tileObjectEllipsePrecision = tileObjectEllipsePrecision; _simpleTileObjectCalculation = simpleTileObjectCalculation; _clipperArcTolerance = clipperArcTolerance; _clipperDeltaOffset = clipperDeltaOffset; _clipperEndType = clipperEndType; _clipperJoinType = clipperJoinType; _clipperMiterLimit = clipperMiterLimit; _mapName = Path.GetFileNameWithoutExtension(wwwPath); _mapExtension = Path.GetExtension(wwwPath); if (string.IsNullOrEmpty(_mapExtension)) _mapExtension = ".tmx"; Parent = parent; DefaultSortingOrder = sortingOrder; GlobalMakeUniqueTiles = makeUnique; BaseTileMaterial = baseTileMaterial; if (!wwwPath.Contains("://")) _mapPath = string.Concat(Application.streamingAssetsPath, Path.AltDirectorySeparatorChar); else { // remove _mapName from wwwPath _mapPath = wwwPath.Replace(string.Concat(_mapName, _mapExtension), ""); } OnMapFinishedLoading = onMapFinishedLoading; new Task(LoadFromPath(wwwPath), true); }
public static GameObject[] GenerateTileCollision3DFromLayer( this Map map, TileLayer layer, bool isTrigger = false, bool generateClosedPolygon = true, string tag = "Untagged", int physicsLayer = 0, PhysicMaterial physicsMaterial = null, float zDepth = 1, float colliderWidth = 1, bool innerCollision = false, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { if (layer == null) return null; if (layer.LayerTileCollisions == null) { layer.LayerTileCollisions = new GameObject(layer.Name + " Tile Collisions"); Transform t = layer.LayerTileCollisions.transform; if (layer.BaseMap != null) { t.parent = layer.BaseMap.MapGameObject.transform; } t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; layer.LayerTileCollisions.isStatic = true; } layer.LayerTileCollisions.tag = tag; layer.LayerTileCollisions.layer = physicsLayer; layer.LayerTileCollisions.transform.localScale = Vector3.one; List<GameObject> newSubCollider = new List<GameObject>(); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); List<List<Vector2>> points = GenerateClipperPathPoints(layer, simpleTileObjectCalculation, clipperArcTolerance, clipperMiterLimit, clipperJoinType, clipperEndType, clipperDeltaOffset); for (int i = 0; i < points.Count; i++) { newSubCollider.Add(new GameObject("Tile Collisions " + layer.Name + "_" + i)); newSubCollider[i].transform.parent = layer.LayerTileCollisions.transform; newSubCollider[i].transform.localPosition = new Vector3(0, 0, zDepth); newSubCollider[i].transform.localScale = Vector3.one; newSubCollider[i].transform.localRotation = Quaternion.identity; newSubCollider[i].tag = tag; newSubCollider[i].layer = physicsLayer; vertices.Clear(); triangles.Clear(); Mesh colliderMesh = new Mesh(); colliderMesh.name = "TileCollider_" + layer.Name + "_" + i; MeshCollider mc = newSubCollider[i].AddComponent<MeshCollider>(); mc.isTrigger = isTrigger; GenerateVerticesAndTris(map, points[i], vertices, triangles, zDepth, colliderWidth, innerCollision, true, true); // Connect last point with first point (create the face between them) triangles.Add(vertices.Count - 1); triangles.Add(1); triangles.Add(0); triangles.Add(0); triangles.Add(vertices.Count - 2); triangles.Add(vertices.Count - 1); if (generateClosedPolygon) FillFaces(points[i], triangles); colliderMesh.vertices = vertices.ToArray(); colliderMesh.uv = new Vector2[colliderMesh.vertices.Length]; //colliderMesh.uv1 = colliderMesh.uv; colliderMesh.uv2 = colliderMesh.uv; colliderMesh.triangles = triangles.ToArray(); colliderMesh.RecalculateNormals(); mc.sharedMesh = colliderMesh; if (physicsMaterial != null) mc.sharedMaterial = physicsMaterial; newSubCollider[i].isStatic = true; } return newSubCollider.ToArray(); }