/// <summary> /// Adds a Box Collider 2D or 3D to an existing GameObject using one MapObject as properties source /// </summary> /// <param name="gameObject">GameObject to add a Box Collider</param> /// <param name="obj">MapObject which properties will be used to generate this collider.</param> /// <param name="isTrigger">True for Trigger Collider, false otherwise</param> /// <param name="zDepth">Z Depth of the collider.</param> /// <param name="colliderWidth">Width of the collider, in Units</param> /// <param name="used2DColider">True to generate a 2D collider, false to generate a 3D collider.</param> /// <param name="createRigidbody">True to attach a Rigidbody to the created collider</param> /// <param name="rigidbodyIsKinematic">Sets if the attached rigidbody is kinematic or not</param> public static void AddBoxCollider(Map map, GameObject gameObject, MapObject obj, bool used2DColider = true, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { if (used2DColider) AddBoxCollider2D(map, gameObject, obj, isTrigger, null, zDepth, createRigidbody, rigidbodyIsKinematic); else AddBoxCollider3D(map, gameObject, obj, isTrigger, null, zDepth, colliderWidth, createRigidbody, rigidbodyIsKinematic); }
/// <summary> /// Creates a Map Object Layer from node /// </summary> /// <param name="node">XML node to parse</param> /// <param name="tiledMap">MapObjectLayer parent Map</param> /// <param name="layerDepth">This Layer's zDepth</param> /// <param name="materials">List of Materials containing the TileSet textures</param> public MapObjectLayer(NanoXMLNode node, Map tiledMap, int layerDepth) : base(node, tiledMap) { if (node.GetAttribute("color") != null) { // get the color string, removing the leading # string color = node.GetAttribute("color").Value.Substring(1); // get the RGB individually string r = color.Substring(0, 2); string g = color.Substring(2, 2); string b = color.Substring(4, 2); // convert to the color Color = new Color( (byte)int.Parse(r, NumberStyles.AllowHexSpecifier), (byte)int.Parse(g, NumberStyles.AllowHexSpecifier), (byte)int.Parse(b, NumberStyles.AllowHexSpecifier)); } Objects = new List<MapObject>(); foreach (NanoXMLNode objectNode in node.SubNodes) { if (!objectNode.Name.Equals("object")) continue; MapObject mapObjectContent = new MapObject(objectNode, this); mapObjectContent = mapObjectContent.ScaleObject(tiledMap.MapRenderParameter) as MapObject; // Object names need to be unique for our lookup system, but Tiled // doesn't require unique names. string objectName = mapObjectContent.Name; int duplicateCount = 2; // if a object already has the same name... if (Objects.Find(o => o.Name.Equals(objectName)) != null) { // figure out a object name that does work do { objectName = string.Format("{0}{1}", mapObjectContent.Name, duplicateCount); duplicateCount++; } while (Objects.Find(o => o.Name.Equals(objectName)) != null); // log a warning for the user to see //Debug.LogWarning("Renaming object \"" + mapObjectContent.Name + "\" to \"" + objectName + "\" in layer \"" + Name + "\" to make a unique name."); // save that name mapObjectContent.Name = objectName; } //mapObjectContent.CreateTileObject(tiledMap, Name, layerDepth, materials); AddObject(mapObjectContent); } }
/// <summary> /// Creates a map object layer from .tmx /// </summary> /// <param name="node"></param> public MapObjectLayer(XmlNode node, int TileWidth, int TileHeight) : base(node) { if (node.Attributes["color"] != null) { // get the color string, removing the leading # string color = node.Attributes["color"].Value.Substring(1); // get the RGB individually string r = color.Substring(0, 2); string g = color.Substring(2, 2); string b = color.Substring(4, 2); // convert to the color Color = new Color( (byte)int.Parse(r, NumberStyles.AllowHexSpecifier), (byte)int.Parse(g, NumberStyles.AllowHexSpecifier), (byte)int.Parse(b, NumberStyles.AllowHexSpecifier)); } Objects = new List<MapObject>(); foreach (XmlNode objectNode in node.SelectNodes("object")) { MapObject mapObjectContent = new MapObject(objectNode); mapObjectContent.ScaleObject(TileWidth, TileHeight); mapObjectContent.Name = this.Name + "_" + mapObjectContent.Name; // Object names need to be unique for our lookup system, but Tiled // doesn't require unique names. string objectName = mapObjectContent.Name; int duplicateCount = 2; // if a object already has the same name... if (Objects.Find(o => o.Name.Equals(objectName)) != null) { // figure out a object name that does work do { objectName = string.Format("{0}{1}", mapObjectContent.Name, duplicateCount); duplicateCount++; } while (Objects.Find(o => o.Name.Equals(objectName)) != null); // log a warning for the user to see Debug.Log("Renaming object \"" + mapObjectContent.Name + "\" to \"" + objectName + "\" in layer \"" + Name + "\" to make a unique name."); // save that name mapObjectContent.Name = objectName; } //Objects.Add(mapObjectContent); AddObject(mapObjectContent); } }
/// <summary> /// Adds a BoxCollider2D to a GameObject /// </summary> /// <param name="gameObject">GameObject to add the collider</param> /// <param name="obj">MapObject which properties will be used to generate this collider</param> /// <param name="isTrigger">True for Trigger Collider, false otherwise</param> /// <param name="physicsMaterial">PhysicsMaterial2D to be set to the collider</param> /// <param name="zDepth">Z Depth of the collider</param> /// <param name="createRigidbody">True to attach a Rigidbody to the created collider</param> /// <param name="rigidbodyIsKinematic">Sets if the attached rigidbody is kinematic or not</param> public static void AddBoxCollider2D(Map map, GameObject gameObject, MapObject obj, bool isTrigger = false, PhysicsMaterial2D physicsMaterial = null, float zDepth = 0, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { // Orthogonal and Staggered maps can use BoxCollider, Isometric maps must use polygon collider if (map.MapRenderParameter.Orientation != X_UniTMX.Orientation.Isometric) { BoxCollider2D bx = gameObject.AddComponent<BoxCollider2D>(); bx.isTrigger = isTrigger || obj.Type.Equals(Map.Object_Type_Trigger); #if UNITY_5 bx.offset = new Vector2(obj.Bounds.width / 2.0f, -obj.Bounds.height / 2.0f); #else bx.center = new Vector2(obj.Bounds.width / 2.0f, -obj.Bounds.height / 2.0f); #endif bx.size = new Vector2(obj.Bounds.width, obj.Bounds.height); if (physicsMaterial != null) bx.sharedMaterial = physicsMaterial; } else if (map.MapRenderParameter.Orientation == X_UniTMX.Orientation.Isometric) { PolygonCollider2D pc = gameObject.AddComponent<PolygonCollider2D>(); pc.isTrigger = isTrigger || obj.Type.Equals(Map.Object_Type_Trigger); Vector2[] points = new Vector2[4]; points[0] = map.TiledPositionToWorldPoint(obj.Bounds.xMin - obj.Bounds.x, obj.Bounds.yMax - obj.Bounds.y); points[1] = map.TiledPositionToWorldPoint(obj.Bounds.xMin - obj.Bounds.x, obj.Bounds.yMin - obj.Bounds.y); points[2] = map.TiledPositionToWorldPoint(obj.Bounds.xMax - obj.Bounds.x, obj.Bounds.yMin - obj.Bounds.y); points[3] = map.TiledPositionToWorldPoint(obj.Bounds.xMax - obj.Bounds.x, obj.Bounds.yMax - obj.Bounds.y); points[0].x -= map.MapRenderParameter.Width / 2.0f; points[1].x -= map.MapRenderParameter.Width / 2.0f; points[2].x -= map.MapRenderParameter.Width / 2.0f; points[3].x -= map.MapRenderParameter.Width / 2.0f; pc.SetPath(0, points); if (physicsMaterial != null) pc.sharedMaterial = physicsMaterial; } if (createRigidbody) { Rigidbody2D r = gameObject.AddComponent<Rigidbody2D>(); r.isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) gameObject.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); ApplyCustomProperties(gameObject, obj); // Link this collider to the MapObject obj.LinkedGameObject = gameObject; }
/// <summary> /// Generate a collider based on object type /// </summary> /// <param name="obj">Object which properties will be used to generate this collider.</param> /// <param name="used2DColider">True to generate 2D colliders, otherwise 3D colliders will be generated.</param> /// <param name="zDepth">Z Depth of the 3D collider.</param> /// <param name="colliderWidth">>Width of the 3D collider.</param> /// <param name="innerCollision">If true, calculate normals facing the anchor of the collider (inside collisions), else, outside collisions.</param> /// <returns>Generated Game Object containing the Collider.</returns> public GameObject GenerateCollider(MapObject obj, bool isTrigger = false, bool used2DColider = false, float zDepth = 0, float colliderWidth = 1, bool innerCollision = false, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject col = null; switch (obj.MapObjectType) { case MapObjectType.Box: col = GenerateBoxCollider(obj, isTrigger, zDepth, colliderWidth, used2DColider, createRigidbody, rigidbodyIsKinematic); break; case MapObjectType.Ellipse: col = GenerateEllipseCollider(obj, isTrigger, zDepth, colliderWidth, used2DColider, createRigidbody, rigidbodyIsKinematic); break; case MapObjectType.Polygon: col = GeneratePolygonCollider(obj, isTrigger, zDepth, colliderWidth, innerCollision, used2DColider, createRigidbody, rigidbodyIsKinematic); break; case MapObjectType.Polyline: col = GeneratePolylineCollider(obj, isTrigger, zDepth, colliderWidth, innerCollision, used2DColider, createRigidbody, rigidbodyIsKinematic); break; } return col; }
private GameObject GeneratePolylineCollider3D(MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool innerCollision = false, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject newCollider = new GameObject(obj.Name); //newCollider.transform.position = MapObject.transform.position; newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); newCollider.transform.parent = MapObject.transform; Mesh colliderMesh = new Mesh(); colliderMesh.name = "Collider_" + obj.Name; MeshCollider mc = newCollider.AddComponent<MeshCollider>(); mc.isTrigger = isTrigger || obj.Type.Equals("Trigger"); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); GenerateVerticesAndTris(obj.Points, vertices, triangles, zDepth, colliderWidth, innerCollision); colliderMesh.vertices = vertices.ToArray(); colliderMesh.triangles = triangles.ToArray(); colliderMesh.RecalculateNormals(); mc.sharedMesh = colliderMesh; newCollider.isStatic = true; newCollider.SetActive(obj.Visible); if (createRigidbody) { newCollider.AddComponent<Rigidbody>(); newCollider.rigidbody.isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) newCollider.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); return newCollider; }
private GameObject GeneratePolygonCollider3D(MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool innerCollision = false, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject newCollider = new GameObject(obj.Name); newCollider.transform.parent = MapObject.transform; newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); Mesh colliderMesh = new Mesh(); colliderMesh.name = "Collider_" + obj.Name; MeshCollider mc = newCollider.AddComponent<MeshCollider>(); mc.isTrigger = isTrigger || obj.Type.Equals("Trigger"); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); GenerateVerticesAndTris(obj.Points, vertices, triangles, zDepth, colliderWidth, innerCollision); // 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); //// Fill Faces //// Unfortunately I could'n come up with a good solution for both concave and convex polygons :/ //// This code works for convex polygons, so, in case you need it, just uncomment it (and the other comments flagged as Fill Faces) //// Find leftmost vertex //List<int> sweepVerticesFront = new List<int>(); //for (int x = 0; x < vertices.Count; x += 2) //{ // sweepVerticesFront.Add(x); //} //// Sort it by vertex X //SweepSortVerticesList(vertices, sweepVerticesFront); //List<int> L = new List<int>(); //L.Add(sweepVerticesFront[0]); //L.Add(sweepVerticesFront[1]); //int vertex = 0; //int count = 2; //int oppositeVertex1 = 0; //int oppositeVertex2 = 0; //bool b_oppositeVertex1 = false; //bool b_oppositeVertex2 = false; //int indexOppositeVertex1 = 0; //int indexOppositeVertex2 = 0; //int[] cclockwise; //while (sweepVerticesFront.Count > count) //{ // vertex = sweepVerticesFront[count]; // // Is vertex opposite to any other vertex in L? // b_oppositeVertex1 = false; // b_oppositeVertex2 = false; // oppositeVertex1 = vertex - 2; // if (oppositeVertex1 < 0) // oppositeVertex1 = vertices.Count - 2; // oppositeVertex2 = vertex + 2; // if (oppositeVertex2 > vertices.Count - 2) // oppositeVertex2 = 0; // indexOppositeVertex1 = 0; // indexOppositeVertex2 = 0; // for (int x = 0; x < L.Count; x++) // { // if (L[x] == oppositeVertex1) // { // b_oppositeVertex1 = true; // indexOppositeVertex1 = x; // } // if (L[x] == oppositeVertex2) // { // b_oppositeVertex2 = true; // indexOppositeVertex2 = x; // } // } // if (b_oppositeVertex1 || b_oppositeVertex2) // { // while (L.Count > 1) // { // cclockwise = GetCounterClockwiseOrder(vertices, vertex, L[1], L[0]); // triangles.Add(cclockwise[0]); // triangles.Add(cclockwise[1]); // triangles.Add(cclockwise[2]); // cclockwise = GetCounterClockwiseOrder(vertices, vertex + 1, L[1] + 1, L[0] + 1); // triangles.Add(cclockwise[2]); // triangles.Add(cclockwise[1]); // triangles.Add(cclockwise[0]); // if(b_oppositeVertex1) // L.RemoveAt(indexOppositeVertex1 > L.Count - 1 ? L.Count - 1 : indexOppositeVertex1); // else // L.RemoveAt(indexOppositeVertex2 > L.Count - 1 ? L.Count - 1 : indexOppositeVertex2); // } // } // else // { // while (L.Count > 1 && (Vector3.Angle(vertices[L[L.Count - 1]], vertices[L[L.Count - 2]]) + Vector3.Angle(vertices[L[L.Count - 2]], vertices[vertex]) <= 180)) // { // cclockwise = GetCounterClockwiseOrder(vertices, vertex, L[L.Count - 2], L[L.Count - 1]); // triangles.Add(cclockwise[0]); // triangles.Add(cclockwise[1]); // triangles.Add(cclockwise[2]); // cclockwise = GetCounterClockwiseOrder(vertices, vertex + 1, L[L.Count - 2] + 1, L[L.Count - 1] + 1); // triangles.Add(cclockwise[2]); // triangles.Add(cclockwise[1]); // triangles.Add(cclockwise[0]); // L.RemoveAt(L.Count - 1); // } // } // L.Add(vertex); // count++; //} colliderMesh.vertices = vertices.ToArray(); colliderMesh.triangles = triangles.ToArray(); colliderMesh.RecalculateNormals(); mc.sharedMesh = colliderMesh; newCollider.isStatic = true; newCollider.SetActive(obj.Visible); if (createRigidbody) { newCollider.AddComponent<Rigidbody>(); newCollider.rigidbody.isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) { newCollider.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); Debug.Log("rotated!"); } return newCollider; }
private GameObject GenerateBoxCollider3D(MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject newCollider; // Orthogonal and Staggered maps can use BoxCollider, Isometric maps must use polygon collider if (Orientation != X_UniTMX.Orientation.Isometric) { newCollider = new GameObject(obj.Name); newCollider.transform.parent = MapObject.transform; BoxCollider bx = newCollider.AddComponent<BoxCollider>(); bx.isTrigger = isTrigger || obj.Type.Equals("Trigger"); newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); bx.center = new Vector3(obj.Bounds.width / 2.0f, -obj.Bounds.height / 2.0f); bx.size = new Vector3(obj.Bounds.width, obj.Bounds.height, colliderWidth); newCollider.isStatic = true; newCollider.SetActive(obj.Visible); } else { List<Vector2> points = new List<Vector2>(); points.Add(new Vector2(obj.Bounds.xMin - obj.Bounds.x, obj.Bounds.yMax - obj.Bounds.y)); points.Add(new Vector2(obj.Bounds.xMin - obj.Bounds.x, obj.Bounds.yMin - obj.Bounds.y)); points.Add(new Vector2(obj.Bounds.xMax - obj.Bounds.x, obj.Bounds.yMin - obj.Bounds.y)); points.Add(new Vector2(obj.Bounds.xMax - obj.Bounds.x, obj.Bounds.yMax - obj.Bounds.y)); X_UniTMX.MapObject isoBox = new MapObject(obj.Name, obj.Type, obj.Bounds, obj.Properties, obj.GID, points, obj.Rotation); newCollider = GeneratePolygonCollider3D(isoBox, isTrigger, zDepth, colliderWidth); } if (createRigidbody) { newCollider.AddComponent<Rigidbody>(); newCollider.rigidbody.isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) newCollider.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); return newCollider; }
private void CopyCollider(MapObject obj, ref GameObject origin, ref GameObject destination, bool is2DColliders) { switch (obj.MapObjectType) { case MapObjectType.Box: if (is2DColliders) { BoxCollider2D c = destination.AddComponent<BoxCollider2D>(); c.isTrigger = ((BoxCollider2D)origin.collider2D).isTrigger; c.size = ((BoxCollider2D)origin.collider2D).size; c.center = ((BoxCollider2D)origin.collider2D).center; } else { BoxCollider box = destination.AddComponent<BoxCollider>(); box.size = ((BoxCollider)origin.collider).size; box.center = ((BoxCollider)origin.collider).center; } break; case MapObjectType.Ellipse: if (is2DColliders) { CircleCollider2D c = destination.AddComponent<CircleCollider2D>(); c.isTrigger = ((CircleCollider2D)origin.collider2D).isTrigger; c.center = ((CircleCollider2D)origin.collider2D).center; c.radius = ((CircleCollider2D)origin.collider2D).radius; } else { CapsuleCollider capsule = destination.AddComponent<CapsuleCollider>(); capsule.isTrigger = ((CapsuleCollider)origin.collider).isTrigger; capsule.height = ((CapsuleCollider)origin.collider).height; capsule.radius = ((CapsuleCollider)origin.collider).radius; capsule.center = ((CapsuleCollider)origin.collider).center; capsule.direction = ((CapsuleCollider)origin.collider).direction; } break; case MapObjectType.Polygon: if (is2DColliders) { PolygonCollider2D c = destination.AddComponent<PolygonCollider2D>(); c.isTrigger = ((PolygonCollider2D)origin.collider2D).isTrigger; for (int i = 0; i < ((PolygonCollider2D)origin.collider2D).pathCount; i++) { c.SetPath(i, ((PolygonCollider2D)origin.collider2D).GetPath(i)); } } else { MeshCollider mc = destination.AddComponent<MeshCollider>(); mc.isTrigger = ((MeshCollider)origin.collider).isTrigger; mc.convex = ((MeshCollider)origin.collider).convex; mc.smoothSphereCollisions = ((MeshCollider)origin.collider).smoothSphereCollisions; mc.sharedMesh = ((MeshCollider)origin.collider).sharedMesh; mc.sharedMesh.RecalculateBounds(); mc.sharedMesh.RecalculateNormals(); } break; case MapObjectType.Polyline: if (is2DColliders) { EdgeCollider2D c = destination.AddComponent<EdgeCollider2D>(); c.isTrigger = ((EdgeCollider2D)origin.collider2D).isTrigger; c.points = ((EdgeCollider2D)origin.collider2D).points; } else { MeshCollider mc = destination.AddComponent<MeshCollider>(); mc.isTrigger = ((MeshCollider)origin.collider).isTrigger; mc.convex = ((MeshCollider)origin.collider).convex; mc.smoothSphereCollisions = ((MeshCollider)origin.collider).smoothSphereCollisions; mc.sharedMesh = ((MeshCollider)origin.collider).sharedMesh; mc.sharedMesh.RecalculateBounds(); mc.sharedMesh.RecalculateNormals(); } break; } }
/// <summary> /// Adds a MapObject to the layer. /// </summary> /// <param name="mapObject">The MapObject to add.</param> public void AddObject(MapObject mapObject) { // avoid adding the object to the layer twice if (Objects.Contains(mapObject)) return; namedObjects.Add(mapObject.Name, mapObject); Objects.Add(mapObject); }
/// <summary> /// Generate a prefab based in object colider layer /// </summary> /// <param name="obj">Object which properties will be used to generate a prefab.</param> /// <param name="newColliderObject">if null add relative parent object,.</param> /// <param name="addTileName">true to add Map's name to the prefab name</param> /// <param name="is2DColliders">true to generate 2D colliders</param> /// <returns>Generated Game Object containing the Collider.</returns> public void AddPrefabs(MapObject obj, GameObject newColliderObject = null, bool is2DColliders = false, bool addTileName = true) { int indexPrefab = 0; while (obj.HasProperty(string.Concat(indexPrefab.ToString(), Property_PrefabName))) { string prefabName = obj.GetPropertyAsString(indexPrefab + Property_PrefabName); string baseResourcePath = obj.GetPropertyAsString(indexPrefab + Property_PrefabPath); UnityEngine.Object resourceObject = Resources.Load(baseResourcePath + prefabName); Resources.UnloadUnusedAssets(); if (resourceObject != null) { float zDepth = obj.GetPropertyAsFloat(indexPrefab + Property_PrefabZDepth); GameObject newPrefab = UnityEngine.Object.Instantiate(resourceObject) as GameObject; newPrefab.transform.parent = obj.ParentObjectLayer != null ? obj.ParentObjectLayer.LayerGameObject.transform : MapObject.transform; newPrefab.transform.localPosition = TiledPositionToWorldPoint(new Vector3(obj.Bounds.center.x, obj.Bounds.center.y, zDepth)); // copy coliders from newColliderObject // only copy if type of this object is diferent of "NoCollider" if (obj.Type.Equals(Object_Type_NoCollider) == false) { if (obj.GetPropertyAsBoolean(indexPrefab + Property_PrefabAddCollider)) { //CopyCollider(obj, ref newColliderObject, ref newPrefab, is2DColliders); AddCollider(newPrefab, obj, obj.Type.Equals(Object_Type_Trigger), is2DColliders, zDepth); } else // since custom properties are automatically added when a collider is added but this prefab has no collider, we must enforce them to be parsed ApplyCustomProperties(newPrefab, obj); } else // since custom properties are automatically added when a collider is added but this prefab has no collider, we must enforce them to be parsed ApplyCustomProperties(newPrefab, obj); if (obj.GetPropertyAsBoolean(indexPrefab + Property_PrefabFixColliderPosition)) { // Mario: Fixed wrong position in instantiate prefabs if(newColliderObject != null && newPrefab != null) newPrefab.transform.position = newColliderObject.transform.position; } newPrefab.name = (addTileName ? (_mapName + "_") : "") + obj.Name; int indexMessage = 1; string prefabMensage = obj.GetPropertyAsString(indexPrefab + Property_PrefabSendMessage + indexMessage); while (string.IsNullOrEmpty(prefabMensage) == false) { string[] menssage = prefabMensage.Split(new[] { '|' }, StringSplitOptions.None); if (menssage.Length == 2) { newPrefab.BroadcastMessage(menssage[0], menssage[1]); } if (menssage.Length == 1) { newPrefab.BroadcastMessage(menssage[0]); } indexMessage++; prefabMensage = obj.GetPropertyAsString(indexPrefab + Property_PrefabSendMessage + indexMessage); } } else { Debug.LogError("Prefab doesn't exist at: Resources/" + baseResourcePath + prefabName); } indexPrefab++; } }
private void AddBoxCollider3D(GameObject gameObject, MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject gameObjectMesh = null; // Orthogonal and Staggered maps can use BoxCollider, Isometric maps must use polygon collider if (Orientation != X_UniTMX.Orientation.Isometric) { if (obj.GetPropertyAsBoolean(Property_CreateMesh)) { gameObjectMesh = GameObject.CreatePrimitive(PrimitiveType.Cube); gameObjectMesh.name = obj.Name; gameObjectMesh.transform.parent = gameObject.transform; gameObjectMesh.transform.localPosition = Vector3.zero; gameObjectMesh.GetComponent<Collider>().isTrigger = isTrigger || obj.Type.Equals(Object_Type_Trigger); } else { gameObject.AddComponent<BoxCollider>(); gameObject.GetComponent<Collider>().isTrigger = isTrigger || obj.Type.Equals(Object_Type_Trigger); } gameObject.transform.localScale = new Vector3(obj.Bounds.width, obj.Bounds.height, colliderWidth); } else { List<Vector2> points = new List<Vector2>(); points.Add(new Vector2(obj.Bounds.xMin - obj.Bounds.x, obj.Bounds.yMax - obj.Bounds.y)); points.Add(new Vector2(obj.Bounds.xMin - obj.Bounds.x, obj.Bounds.yMin - obj.Bounds.y)); points.Add(new Vector2(obj.Bounds.xMax - obj.Bounds.x, obj.Bounds.yMin - obj.Bounds.y)); points.Add(new Vector2(obj.Bounds.xMax - obj.Bounds.x, obj.Bounds.yMax - obj.Bounds.y)); X_UniTMX.MapObject isoBox = new MapObject(obj.Name, obj.Type, obj.Bounds, obj.Properties, obj.GID, points, obj.Rotation, obj.ParentObjectLayer); AddPolygonCollider3D(gameObject, isoBox, isTrigger, zDepth, colliderWidth); //gameObject = GeneratePolygonCollider3D(isoBox, isTrigger, zDepth, colliderWidth); } if (createRigidbody) { gameObject.AddComponent<Rigidbody>(); gameObject.GetComponent<Rigidbody>().isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) gameObject.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); if(gameObjectMesh != null) ApplyCustomProperties(gameObjectMesh, obj); else ApplyCustomProperties(gameObject, obj); }
/// <summary> /// Generate a Polygon collider mesh /// </summary> /// <param name="obj">Object which properties will be used to generate this collider.</param> /// <param name="zDepth">Z Depth of the collider.</param> /// <param name="colliderWidth">Width of the collider.</param> /// <param name="innerCollision">If true, calculate normals facing the center of the collider (inside collisions), else, outside collisions.</param> /// <returns>Generated Game Object containing the Collider.</returns> public GameObject GeneratePolygonCollider(MapObject obj, float zDepth = 0, float colliderWidth = 1.0f, bool innerCollision = false) { GameObject polygonCollider = new GameObject(obj.Name); polygonCollider.transform.parent = this.Parent.transform; Mesh colliderMesh = new Mesh(); colliderMesh.name = "Collider_" + obj.Name; MeshCollider mc = polygonCollider.AddComponent <MeshCollider>(); List <Vector3> vertices = new List <Vector3>(); List <int> triangles = new List <int>(); Vector3 firstPoint = (Vector3)obj.Points[0]; Vector3 secondPoint, firstFront, firstBack, secondFront, secondBack; for (int i = 1; i < obj.Points.Count; i++) { secondPoint = (Vector3)obj.Points[i]; firstFront = new Vector3(obj.Bounds.center.x + firstPoint.x, -obj.Bounds.center.y - firstPoint.y, zDepth - colliderWidth); firstBack = new Vector3(obj.Bounds.center.x + firstPoint.x, -obj.Bounds.center.y - firstPoint.y, zDepth + colliderWidth); secondFront = new Vector3(obj.Bounds.center.x + secondPoint.x, -obj.Bounds.center.y - secondPoint.y, zDepth - colliderWidth); secondBack = new Vector3(obj.Bounds.center.x + secondPoint.x, -obj.Bounds.center.y - secondPoint.y, zDepth + colliderWidth); if (innerCollision) { vertices.Add(firstBack); // 3 vertices.Add(firstFront); // 2 vertices.Add(secondBack); // 1 vertices.Add(secondFront); // 0 } else { vertices.Add(firstFront); // 3 vertices.Add(firstBack); // 2 vertices.Add(secondFront); // 1 vertices.Add(secondBack); // 2 } triangles.Add((i - 1) * 4 + 3); triangles.Add((i - 1) * 4 + 2); triangles.Add((i - 1) * 4 + 0); triangles.Add((i - 1) * 4 + 0); triangles.Add((i - 1) * 4 + 1); triangles.Add((i - 1) * 4 + 3); firstPoint = secondPoint; } // Connect last point with first point secondPoint = (Vector3)obj.Points[0]; firstFront = new Vector3(obj.Bounds.center.x + firstPoint.x, -obj.Bounds.center.y - firstPoint.y, zDepth - colliderWidth); firstBack = new Vector3(obj.Bounds.center.x + firstPoint.x, -obj.Bounds.center.y - firstPoint.y, zDepth + colliderWidth); secondFront = new Vector3(obj.Bounds.center.x + secondPoint.x, -obj.Bounds.center.y - secondPoint.y, zDepth - colliderWidth); secondBack = new Vector3(obj.Bounds.center.x + secondPoint.x, -obj.Bounds.center.y - secondPoint.y, zDepth + colliderWidth); if (innerCollision) { vertices.Add(firstBack); // 3 vertices.Add(firstFront); // 2 vertices.Add(secondBack); // 1 vertices.Add(secondFront); // 0 } else { vertices.Add(firstFront); // 3 vertices.Add(firstBack); // 2 vertices.Add(secondFront); // 1 vertices.Add(secondBack); // 2 } triangles.Add((obj.Points.Count - 1) * 4 + 3); triangles.Add((obj.Points.Count - 1) * 4 + 2); triangles.Add((obj.Points.Count - 1) * 4 + 0); triangles.Add((obj.Points.Count - 1) * 4 + 0); triangles.Add((obj.Points.Count - 1) * 4 + 1); triangles.Add((obj.Points.Count - 1) * 4 + 3); colliderMesh.vertices = vertices.ToArray(); colliderMesh.triangles = triangles.ToArray(); colliderMesh.RecalculateNormals(); mc.sharedMesh = colliderMesh; polygonCollider.isStatic = true; return(polygonCollider); }
/// <summary> /// Creates a Map Object Layer from node /// </summary> /// <param name="node">XML node to parse</param> /// <param name="tiledMap">MapObjectLayer parent Map</param> /// <param name="layerDepth">This Layer's zDepth</param> /// <param name="materials">List of Materials containing the TileSet textures</param> public MapObjectLayer(NanoXMLNode node, Map tiledMap, int layerDepth, List <Material> materials) : base(node) { if (node.GetAttribute("color") != null) { // get the color string, removing the leading # string color = node.GetAttribute("color").Value.Substring(1); // get the RGB individually string r = color.Substring(0, 2); string g = color.Substring(2, 2); string b = color.Substring(4, 2); // convert to the color Color = new Color( (byte)int.Parse(r, NumberStyles.AllowHexSpecifier), (byte)int.Parse(g, NumberStyles.AllowHexSpecifier), (byte)int.Parse(b, NumberStyles.AllowHexSpecifier)); } LayerGameObject.transform.parent = tiledMap.MapObject.transform; LayerGameObject.transform.localPosition = new Vector3(0, 0, this.LayerDepth); LayerGameObject.isStatic = true; LayerGameObject.SetActive(Visible); Objects = new List <MapObject>(); foreach (NanoXMLNode objectNode in node.SubNodes) { if (!objectNode.Name.Equals("object")) { continue; } MapObject mapObjectContent = new MapObject(objectNode, this); mapObjectContent.ScaleObject(tiledMap.TileWidth, tiledMap.TileHeight, tiledMap.Orientation); mapObjectContent.Name = this.Name + "_" + mapObjectContent.Name; // Object names need to be unique for our lookup system, but Tiled // doesn't require unique names. string objectName = mapObjectContent.Name; int duplicateCount = 2; // if a object already has the same name... if (Objects.Find(o => o.Name.Equals(objectName)) != null) { // figure out a object name that does work do { objectName = string.Format("{0}{1}", mapObjectContent.Name, duplicateCount); duplicateCount++; } while (Objects.Find(o => o.Name.Equals(objectName)) != null); // log a warning for the user to see //Debug.LogWarning("Renaming object \"" + mapObjectContent.Name + "\" to \"" + objectName + "\" in layer \"" + Name + "\" to make a unique name."); // save that name mapObjectContent.Name = objectName; } mapObjectContent.CreateTileObject(tiledMap, Name, layerDepth, materials); AddObject(mapObjectContent); } }
private void ApproximateEllipse2D(GameObject newCollider, MapObject obj, bool isTrigger = false, float zDepth = 0, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { // since there's no "EllipseCollider2D", we must create one by approximating a polygon collider newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); PolygonCollider2D polygonCollider = newCollider.AddComponent<PolygonCollider2D>(); polygonCollider.isTrigger = isTrigger || obj.Type.Equals("Trigger"); int segments = 16; // Increase this for higher-quality ellipsoides. //float increment = 2 * Mathf.PI / segments; // Segments per quadrant int incFactor = Mathf.FloorToInt(segments / 4.0f); float minIncrement = 2 * Mathf.PI / (incFactor * segments / 2.0f); int currentInc = 0; bool grow = true; Vector2[] points = new Vector2[segments]; Vector2 center = new Vector2(obj.Bounds.width / 2.0f, obj.Bounds.height / 2.0f); float r = 0; float angle = 0; for (int i = 0; i < segments; i++) { // Calculate radius at each point angle += currentInc * minIncrement; r = obj.Bounds.width * obj.Bounds.height / Mathf.Sqrt(Mathf.Pow(obj.Bounds.height * Mathf.Cos(angle), 2) + Mathf.Pow(obj.Bounds.width * Mathf.Sin(angle), 2)) / 2.0f; points[i] = r * new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) + center; points[i] = TiledPositionToWorldPoint(points[i].x, points[i].y); // Offset points where needed if (Orientation == X_UniTMX.Orientation.Isometric) points[i].x -= Width / 2.0f; if (Orientation == X_UniTMX.Orientation.Staggered) points[i].y *= TileWidth / (float)TileHeight * 2.0f; if (grow) currentInc++; else currentInc--; if (currentInc > incFactor - 1 || currentInc < 1) grow = !grow; // POG :P if (Orientation != X_UniTMX.Orientation.Isometric) { if (i < 1 || i == segments / 2 - 1) points[i].y -= obj.Bounds.height / 20.0f; if (i >= segments - 1 || i == segments / 2) points[i].y += obj.Bounds.height / 20.0f; } } polygonCollider.SetPath(0, points); }
private void AddEllipseCollider2D(GameObject gameObject, MapObject obj, bool isTrigger = false, float zDepth = 0, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { if (Orientation != X_UniTMX.Orientation.Isometric && obj.Bounds.width == obj.Bounds.height) { CircleCollider2D cc = gameObject.AddComponent<CircleCollider2D>(); cc.isTrigger = isTrigger || obj.Type.Equals(Object_Type_Trigger); gameObject.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); cc.offset = new Vector2(obj.Bounds.width / 2.0f, -obj.Bounds.height / 2.0f); cc.radius = obj.Bounds.width / 2.0f; } else { ApproximateEllipse2D(gameObject, obj, isTrigger, zDepth, createRigidbody, rigidbodyIsKinematic); } if (createRigidbody) { gameObject.AddComponent<Rigidbody2D>(); gameObject.GetComponent<Rigidbody2D>().isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) gameObject.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); ApplyCustomProperties(gameObject, obj); }
/// <summary> /// Removes an object from the layer. /// </summary> /// <param name="mapObject">The object to remove.</param> /// <returns>True if the object was found and removed, false otherwise.</returns> public bool RemoveObject(MapObject mapObject) { return(RemoveObject(mapObject.Name)); }
private void AddEllipseCollider3D(GameObject gameObject, MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject gameObjectMesh = null; if (Orientation != X_UniTMX.Orientation.Isometric && obj.Bounds.width == obj.Bounds.height) { CapsuleCollider cc = null; if (obj.GetPropertyAsBoolean(Property_CreateMesh)) { gameObjectMesh = GameObject.CreatePrimitive(PrimitiveType.Capsule); gameObjectMesh.name = obj.Name; gameObjectMesh.transform.parent = gameObject.transform; gameObjectMesh.transform.localPosition = new Vector3(obj.Bounds.height / 2.0f, -obj.Bounds.width / 2.0f); cc = gameObjectMesh.GetComponent<Collider>() as CapsuleCollider; gameObjectMesh.GetComponent<Collider>().isTrigger = isTrigger || obj.Type.Equals(Object_Type_Trigger); gameObjectMesh.transform.localScale = new Vector3(obj.Bounds.width, colliderWidth, obj.Bounds.height); gameObjectMesh.transform.localRotation = Quaternion.AngleAxis(90, Vector3.right); } else { cc = gameObject.AddComponent<CapsuleCollider>(); cc.isTrigger = isTrigger || obj.Type.Equals(Object_Type_Trigger); cc.center = new Vector3(obj.Bounds.height / 2.0f, -obj.Bounds.width / 2.0f); cc.direction = 0; cc.radius = obj.Bounds.height / 2.0f; cc.height = obj.Bounds.width; } } else { ApproximateEllipse3D(gameObject, obj, isTrigger, zDepth, colliderWidth, createRigidbody, rigidbodyIsKinematic); } if (createRigidbody) { gameObject.AddComponent<Rigidbody>(); gameObject.GetComponent<Rigidbody>().isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) gameObject.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); if(gameObjectMesh) ApplyCustomProperties(gameObjectMesh, obj); else ApplyCustomProperties(gameObject, obj); }
/// <summary> /// Applies to gameObject any custom X-UniTMX properties present on obj /// </summary> /// <param name="gameObject">GameObject to apply custom properties to</param> /// <param name="obj">MapObject to read custom properties from</param> public void ApplyCustomProperties(GameObject gameObject, MapObject obj) { // nothing to do here... if (gameObject == null || obj == null) return; // Set a layer number for gameObject if (obj.HasProperty(Property_Layer)) gameObject.layer = obj.GetPropertyAsInt(Property_Layer); if (obj.HasProperty(Property_LayerName)) gameObject.layer = LayerMask.NameToLayer(obj.GetPropertyAsString(Property_LayerName)); // Add a tag for gameObject if (obj.HasProperty(Property_Tag)) gameObject.tag = obj.GetPropertyAsString(Property_Tag); // Add Components for this gameObject int c = 1; while (obj.HasProperty(Property_AddComponent + c)) { UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(gameObject, "Assets/X-UniTMX/Code/Map.cs (2684,5)", obj.GetPropertyAsString(Property_AddComponent + c)); c++; } c = 1; while (obj.HasProperty(Property_SendMessage + c)) { string messageToSend = obj.GetPropertyAsString(Property_SendMessage + c); string[] menssage = messageToSend.Split('|'); if (menssage.Length == 2) { gameObject.BroadcastMessage(menssage[0], menssage[1]); } if (menssage.Length == 1) { gameObject.BroadcastMessage(menssage[0]); } c++; } if (gameObject.GetComponent<Renderer>() != null) { if (obj.HasProperty(Property_SortingLayerName)) gameObject.GetComponent<Renderer>().sortingLayerName = obj.GetPropertyAsString(Property_SortingLayerName); if (obj.HasProperty(Property_SortingOrder)) gameObject.GetComponent<Renderer>().sortingOrder = obj.GetPropertyAsInt(Property_SortingOrder); if (obj.HasProperty(Property_SetMaterialColor)) { string[] splitColor = obj.GetPropertyAsString(Property_SetMaterialColor).Split(','); if (splitColor.Length >= 1) { gameObject.GetComponent<Renderer>().material = new Material(BaseTileMaterial); gameObject.GetComponent<Renderer>().material.color = new Color32( ((byte)(int.Parse(string.IsNullOrEmpty(splitColor[0]) ? "255" : splitColor[0]))), splitColor.Length >= 2 ? ((byte)(int.Parse(splitColor[1]))) : (byte)255, splitColor.Length >= 3 ? ((byte)(int.Parse(splitColor[2]))) : (byte)255, splitColor.Length >= 4 ? ((byte)(int.Parse(splitColor[3]))) : (byte)255); } } } }
private void AddPolylineCollider2D(GameObject gameObject, MapObject obj, bool isTrigger = false, float zDepth = 0, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { EdgeCollider2D edgeCollider = gameObject.AddComponent<EdgeCollider2D>(); edgeCollider.isTrigger = isTrigger || obj.Type.Equals(Object_Type_Trigger); Vector2[] points = obj.Points.ToArray(); for (int i = 0; i < points.Length; i++) { points[i] = TiledPositionToWorldPoint(points[i].x, points[i].y); if (Orientation == X_UniTMX.Orientation.Isometric) points[i].x -= Width / 2.0f; } edgeCollider.points = points; if (createRigidbody) { gameObject.AddComponent<Rigidbody2D>(); gameObject.GetComponent<Rigidbody2D>().isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) gameObject.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); ApplyCustomProperties(gameObject, obj); }
/// <summary> /// Removes an object from the layer. /// </summary> /// <param name="mapObject">The object to remove.</param> /// <returns>True if the object was found and removed, false otherwise.</returns> public bool RemoveObject(MapObject mapObject) { return RemoveObject(mapObject.Name); }
private void AddPolylineCollider3D(GameObject gameObject, MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool innerCollision = false, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { Mesh colliderMesh = new Mesh(); colliderMesh.name = "Collider_" + obj.Name; MeshCollider mc = gameObject.AddComponent<MeshCollider>(); mc.isTrigger = isTrigger || obj.Type.Equals(Object_Type_Trigger); List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); GenerateVerticesAndTris(obj.Points, vertices, triangles, zDepth, colliderWidth, innerCollision); colliderMesh.vertices = vertices.ToArray(); colliderMesh.uv = new Vector2[colliderMesh.vertices.Length]; colliderMesh.uv2 = colliderMesh.uv; colliderMesh.uv2 = colliderMesh.uv; colliderMesh.triangles = triangles.ToArray(); colliderMesh.RecalculateNormals(); mc.sharedMesh = colliderMesh; if (createRigidbody) { gameObject.AddComponent<Rigidbody>(); gameObject.GetComponent<Rigidbody>().isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) gameObject.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); if (obj.GetPropertyAsBoolean(Property_CreateMesh)) { if (gameObject.GetComponent<MeshFilter>() == null) gameObject.AddComponent<MeshFilter>(); if (gameObject.GetComponent<MeshRenderer>() == null) gameObject.AddComponent<MeshRenderer>(); MeshFilter _meshFilter = gameObject.GetComponent<MeshFilter>(); if (mc != null) { mc.sharedMesh.RecalculateBounds(); mc.sharedMesh.RecalculateNormals(); MathfExtensions.CalculateMeshTangents(mc.sharedMesh); _meshFilter.sharedMesh = mc.sharedMesh; } } ApplyCustomProperties(gameObject, obj); }
private GameObject GenerateBoxCollider2D(MapObject obj, bool isTrigger = false, float zDepth = 0, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject newCollider = new GameObject(obj.Name); newCollider.transform.parent = MapObject.transform; newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); // Orthogonal and Staggered maps can use BoxCollider, Isometric maps must use polygon collider if (Orientation != X_UniTMX.Orientation.Isometric) { BoxCollider2D bx = newCollider.AddComponent<BoxCollider2D>(); bx.isTrigger = isTrigger || obj.Type.Equals("Trigger"); bx.center = new Vector2(obj.Bounds.width / 2.0f, -obj.Bounds.height / 2.0f); bx.size = new Vector2(obj.Bounds.width, obj.Bounds.height); } else if(Orientation == X_UniTMX.Orientation.Isometric) { PolygonCollider2D pc = newCollider.AddComponent<PolygonCollider2D>(); pc.isTrigger = isTrigger || obj.Type.Equals("Trigger"); Vector2[] points = new Vector2[4]; points[0] = TiledPositionToWorldPoint(0, 0); points[1] = TiledPositionToWorldPoint(0, obj.Bounds.height); points[2] = TiledPositionToWorldPoint(obj.Bounds.width, obj.Bounds.height); points[3] = TiledPositionToWorldPoint(obj.Bounds.width, 0); points[0].x -= Width / 2.0f; points[1].x -= Width / 2.0f; points[2].x -= Width / 2.0f; points[3].x -= Width / 2.0f; pc.SetPath(0, points); } newCollider.isStatic = true; newCollider.SetActive(obj.Visible); if (createRigidbody) { newCollider.AddComponent<Rigidbody2D>(); newCollider.rigidbody2D.isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) newCollider.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); return newCollider; }
private void ApproximateEllipse2D(GameObject newCollider, MapObject obj, bool isTrigger = false, float zDepth = 0, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { // since there's no "EllipseCollider2D", we must create one by approximating a polygon collider newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); PolygonCollider2D polygonCollider = newCollider.AddComponent<PolygonCollider2D>(); polygonCollider.isTrigger = isTrigger || obj.Type.Equals(Object_Type_Trigger); int segments = EllipsoideColliderApproximationFactor; // Segments per quadrant int incFactor = Mathf.FloorToInt(segments / 4.0f); float minIncrement = 2 * Mathf.PI / (incFactor * segments / 2.0f); int currentInc = 0; // grow represents if we are going right on x-axis (true) or left (false) bool grow = true; Vector2[] points = new Vector2[segments]; // Ellipsoide center Vector2 center = new Vector2(obj.Bounds.width / 2.0f, obj.Bounds.height / 2.0f); float r = 0; float angle = 0; for (int i = 0; i < segments; i++) { // Calculate radius at each point angle += currentInc * minIncrement; r = obj.Bounds.width * obj.Bounds.height / Mathf.Sqrt(Mathf.Pow(obj.Bounds.height * Mathf.Cos(angle), 2) + Mathf.Pow(obj.Bounds.width * Mathf.Sin(angle), 2)) / 2.0f; // Define the point localization using the calculated radius, angle and center points[i] = r * new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) + center; points[i] = TiledPositionToWorldPoint(points[i].x, points[i].y); // Offset points where needed if (Orientation == X_UniTMX.Orientation.Isometric) points[i].x -= Width / 2.0f; if (Orientation == X_UniTMX.Orientation.Staggered) points[i].y *= TileWidth / (float)TileHeight * 2.0f; // if we are "growing", increment the angle, else, start decrementing it to close the polygon if (grow) currentInc++; else currentInc--; if (currentInc > incFactor - 1 || currentInc < 1) grow = !grow; // POG :P -> Orthogonal and Staggered Isometric generated points are slightly offset on Y if (Orientation != X_UniTMX.Orientation.Isometric) { if (i < 1 || i == segments / 2 - 1) points[i].y -= obj.Bounds.height / 20.0f; if (i >= segments - 1 || i == segments / 2) points[i].y += obj.Bounds.height / 20.0f; } } polygonCollider.SetPath(0, points); }
private GameObject GenerateEllipseCollider3D(MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject newCollider = new GameObject(obj.Name); newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); newCollider.transform.parent = MapObject.transform; if (Orientation != X_UniTMX.Orientation.Isometric && obj.Bounds.width == obj.Bounds.height) { CapsuleCollider cc = newCollider.AddComponent<CapsuleCollider>(); cc.isTrigger = isTrigger || obj.Type.Equals("Trigger"); cc.center = new Vector3(obj.Bounds.height / 2.0f, -obj.Bounds.width / 2.0f); cc.direction = 0; cc.radius = obj.Bounds.height / 2.0f; cc.height = obj.Bounds.width; } else { ApproximateEllipse3D(newCollider, obj, isTrigger, zDepth, colliderWidth, createRigidbody, rigidbodyIsKinematic); } newCollider.isStatic = true; newCollider.SetActive(obj.Visible); if (createRigidbody) { newCollider.AddComponent<Rigidbody>(); newCollider.rigidbody.isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) newCollider.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); return newCollider; }
private GameObject GenerateEllipseCollider2D(MapObject obj, bool isTrigger = false, float zDepth = 0, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject newCollider = new GameObject(obj.Name); newCollider.transform.parent = obj.ParentObjectLayer != null ? obj.ParentObjectLayer.LayerGameObject.transform : MapObject.transform; AddEllipseCollider2D(newCollider, obj, isTrigger, zDepth, createRigidbody, rigidbodyIsKinematic); newCollider.isStatic = true; newCollider.SetActive(obj.Visible); return newCollider; }
private GameObject GeneratePolylineCollider2D(MapObject obj, bool isTrigger = false, float zDepth = 0, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject newCollider = new GameObject(obj.Name); newCollider.transform.parent = MapObject.transform; newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); //newCollider.transform.localPosition = TiledPositionToWorldPoint(0, 0, zDepth); EdgeCollider2D edgeCollider = newCollider.AddComponent<EdgeCollider2D>(); edgeCollider.isTrigger = isTrigger || obj.Type.Equals("Trigger"); Vector2[] points = obj.Points.ToArray(); for (int i = 0; i < points.Length; i++) { points[i] = TiledPositionToWorldPoint(points[i].x, points[i].y); if (Orientation == X_UniTMX.Orientation.Isometric) points[i].x -= Width / 2.0f; //points[x].y -= obj.Bounds.y; } edgeCollider.points = points; newCollider.isStatic = true; newCollider.SetActive(obj.Visible); if (createRigidbody) { newCollider.AddComponent<Rigidbody2D>(); newCollider.rigidbody2D.isKinematic = rigidbodyIsKinematic; } if (obj.Rotation != 0) newCollider.transform.localRotation = Quaternion.AngleAxis(obj.Rotation, Vector3.forward); return newCollider; }
private GameObject GeneratePolylineCollider3D(MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool innerCollision = false, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { GameObject newCollider = new GameObject(obj.Name); newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); newCollider.transform.parent = obj.ParentObjectLayer != null ? obj.ParentObjectLayer.LayerGameObject.transform : MapObject.transform; AddPolylineCollider3D(newCollider, obj, isTrigger, zDepth, colliderWidth, innerCollision, createRigidbody, rigidbodyIsKinematic); newCollider.isStatic = true; newCollider.SetActive(obj.Visible); return newCollider; }
/// <summary> /// Generate a prefab based in object colider layer /// </summary> /// <param name="obj">Object which properties will be used to generate a prefab.</param> /// <param name="newColliderObject">if null add relative parent object,.</param> /// <returns>Generated Game Object containing the Collider.</returns> public void AddPrefabs(MapObject obj, GameObject newColliderObject = null, bool is2DColliders = false, bool addTileName = true) { int indexPrefab = 0; while ("".Equals(obj.GetPropertyAsString(indexPrefab + "-prefab")) == false) { string prefabName = obj.GetPropertyAsString(indexPrefab + "-prefab"); string baseResourcePath = obj.GetPropertyAsString(indexPrefab + "-prefab path"); UnityEngine.Object resourceObject = Resources.Load(baseResourcePath + prefabName); Resources.UnloadUnusedAssets(); if (resourceObject != null) { float zDepth = obj.GetPropertyAsFloat(indexPrefab + "-prefab z depth"); GameObject newPrefab = UnityEngine.Object.Instantiate(resourceObject) as GameObject; //if (newColliderObject == null) //{ // newPrefab.transform.parent = MapObject.transform; // newPrefab.transform.localPosition = new Vector3(obj.Bounds.center.x, -obj.Bounds.center.y, zDepth); //} //else //{ // newPrefab.transform.parent = newColliderObject.transform; // newPrefab.transform.localPosition = new Vector3(0, 0, zDepth); //} newPrefab.transform.parent = MapObject.transform; newPrefab.transform.localPosition = new Vector3(obj.Bounds.center.x, -obj.Bounds.center.y, zDepth); // copy coliders from newColliderObject // only copy if type of this object is diferent of "NoCollider" if ("NoCollider".Equals(obj.Type) == false) { if (obj.GetPropertyAsBoolean(indexPrefab + "-prefab add collider")) { CopyCollider(obj, ref newColliderObject, ref newPrefab, is2DColliders); } } newPrefab.name = (addTileName ? (_mapName + "_") : "") + obj.Name; int indexMessage = 0; string prefabMensage = obj.GetPropertyAsString(indexPrefab + "-prefab sendmessage " + indexMessage); while ("".Equals(prefabMensage) == false) { string[] menssage = prefabMensage.Split(new[] { '|' }, StringSplitOptions.None); if (menssage.Length == 2) { newPrefab.BroadcastMessage(menssage[0], menssage[1]); } indexMessage++; prefabMensage = obj.GetPropertyAsString(indexPrefab + "-prefab sendmessage " + indexMessage); } } else { Debug.LogError("Prefab doesn't exist at: Resources/" + baseResourcePath + prefabName); } indexPrefab++; } }
/// <summary> /// Adds a collider to an existing GameObject based on obj type. /// </summary> /// <param name="gameObject">GameObject to add a collider</param> /// <param name="obj">MapObject which properties will be used to generate this collider.</param> /// <param name="isTrigger">true to generate Trigger collider</param> /// <param name="used2DColider">True to generate 2D colliders, otherwise 3D colliders will be generated.</param> /// <param name="zDepth">Z Depth of the 3D collider.</param> /// <param name="colliderWidth">>Width of the 3D collider.</param> /// <param name="innerCollision">If true, calculate normals facing the anchor of the collider (inside collisions), else, outside collisions.</param> /// <param name="createRigidbody">True to attach a Rigidbody to the created collider</param> /// <param name="rigidbodyIsKinematic">Sets if the attached rigidbody is kinematic or not</param> public void AddCollider(GameObject gameObject, MapObject obj, bool used2DColider = true, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1, bool innerCollision = false, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { switch (obj.ObjectType) { case ObjectType.Box: AddBoxCollider(gameObject, obj, used2DColider, isTrigger, zDepth, colliderWidth, createRigidbody, rigidbodyIsKinematic); break; case ObjectType.Ellipse: AddEllipseCollider(gameObject, obj, used2DColider, isTrigger, zDepth, colliderWidth, createRigidbody, rigidbodyIsKinematic); break; case ObjectType.Polygon: AddPolygonCollider(gameObject, obj, used2DColider, isTrigger, zDepth, colliderWidth, innerCollision, createRigidbody, rigidbodyIsKinematic); break; case ObjectType.Polyline: AddPolylineCollider(gameObject, obj, used2DColider, isTrigger, zDepth, colliderWidth, innerCollision, createRigidbody, rigidbodyIsKinematic); break; } }
/// <summary> /// Generate a Polyline collider mesh, or a sequence of EdgeCollider2D for 2D collisions. /// </summary> /// <param name="obj">Object which properties will be used to generate this collider.</param> /// <param name="isTrigger">True for Trigger Collider, false otherwise</param> /// <param name="zDepth">Z Depth of the collider.</param> /// <param name="colliderWidth">Width of the collider, in Units</param> /// <param name="used2DColider">True to generate a 2D collider, false to generate a 3D collider.</param> /// <param name="innerCollision">If true, calculate normals facing the anchor of the collider (inside collisions), else, outside collisions.</param> /// <returns>Generated Game Object containing the Collider.</returns> public GameObject GeneratePolylineCollider(MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool innerCollision = false, bool used2DColider = false, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { return used2DColider ? GeneratePolylineCollider2D(obj, isTrigger, zDepth, createRigidbody, rigidbodyIsKinematic) : GeneratePolylineCollider3D(obj, isTrigger, zDepth, colliderWidth, innerCollision, createRigidbody, rigidbodyIsKinematic); }
/// <summary> /// Adds a Polyline collider mesh, or a sequence of EdgeCollider2D for 2D collisions, to an existing GameObject using one MapObject as properties source /// </summary> /// <param name="gameObject">GameObject to add a Polyline Collider</param> /// <param name="obj">MapObject which properties will be used to generate this collider.</param> /// <param name="isTrigger">True for Trigger Collider, false otherwise</param> /// <param name="zDepth">Z Depth of the collider.</param> /// <param name="colliderWidth">Width of the collider, in Units</param> /// <param name="used2DColider">True to generate a 2D collider, false to generate a 3D collider.</param> /// <param name="innerCollision">If true, calculate normals facing the anchor of the collider (inside collisions), else, outside collisions.</param> /// <param name="createRigidbody">True to attach a Rigidbody to the created collider</param> /// <param name="rigidbodyIsKinematic">Sets if the attached rigidbody is kinematic or not</param> public void AddPolylineCollider(GameObject gameObject, MapObject obj, bool used2DColider = true, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool innerCollision = false, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { if (used2DColider) AddPolylineCollider2D(gameObject, obj, isTrigger, zDepth, createRigidbody, rigidbodyIsKinematic); else AddPolylineCollider3D(gameObject, obj, isTrigger, zDepth, colliderWidth, innerCollision, createRigidbody, rigidbodyIsKinematic); }
private void ApproximateEllipse3D(GameObject newCollider, MapObject obj, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1.0f, bool createRigidbody = false, bool rigidbodyIsKinematic = true) { // since there's no "EllipseCollider", we must create one by approximating a polygon collider newCollider.transform.localPosition = TiledPositionToWorldPoint(obj.Bounds.x, obj.Bounds.y, zDepth); Mesh colliderMesh = new Mesh(); colliderMesh.name = "Collider_" + obj.Name; MeshCollider mc = newCollider.AddComponent<MeshCollider>(); mc.isTrigger = isTrigger || obj.Type.Equals("Trigger"); int segments = 16; // Increase this for higher-quality ellipsoides. Please note that 3D colliders doubles this value, as they needs front and back vertices. //float increment = 2 * Mathf.PI / segments; // Segments per quadrant int incFactor = Mathf.FloorToInt(segments / 4.0f); float minIncrement = 2 * Mathf.PI / (incFactor * segments / 2.0f); int currentInc = 0; bool grow = true; Vector2[] points = new Vector2[segments]; float width = obj.Bounds.width; float height = obj.Bounds.height; Vector2 center = new Vector2(width / 2.0f, height / 2.0f); float r = 0; float angle = 0; for (int i = 0; i < segments; i++) { // Calculate radius at each point //angle = i * increment; angle += currentInc * minIncrement; r = width * height / Mathf.Sqrt(Mathf.Pow(height * Mathf.Cos(angle), 2) + Mathf.Pow(width * Mathf.Sin(angle), 2)) / 2.0f; points[i] = r * new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) + center; if (Orientation == X_UniTMX.Orientation.Staggered) points[i].y *= -1; if(grow) currentInc++; else currentInc--; if (currentInc > incFactor - 1 || currentInc < 1) grow = !grow; // POG :P if (Orientation != X_UniTMX.Orientation.Isometric) { if (i < 1 || i == segments / 2 - 1) points[i].y -= obj.Bounds.height / 20.0f; if (i >= segments - 1 || i == segments / 2) points[i].y += obj.Bounds.height / 20.0f; } } List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); GenerateVerticesAndTris(new List<Vector2>(points), vertices, triangles, zDepth, colliderWidth, false, !(Orientation == X_UniTMX.Orientation.Staggered)); // 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); colliderMesh.vertices = vertices.ToArray(); colliderMesh.triangles = triangles.ToArray(); colliderMesh.RecalculateNormals(); mc.sharedMesh = colliderMesh; }
/// <summary> /// Creates a map object layer from .tmx /// </summary> /// <param name="node"></param> public MapObjectLayer(XElement node, Map tiledMap, int layerDepth, List <Material> materials) : base(node) { if (node.Attribute("color") != null) { // get the color string, removing the leading # string color = node.Attribute("color").Value.Substring(1); // get the RGB individually string r = color.Substring(0, 2); string g = color.Substring(2, 2); string b = color.Substring(4, 2); // convert to the color Color = new Color( (byte)int.Parse(r, NumberStyles.AllowHexSpecifier), (byte)int.Parse(g, NumberStyles.AllowHexSpecifier), (byte)int.Parse(b, NumberStyles.AllowHexSpecifier)); } Objects = new List <MapObject>(); foreach (XElement objectNode in node.Descendants("object")) { MapObject mapObjectContent = new MapObject(objectNode); //if (tiledMap.Orientation == Orientation.Orthogonal) //{ // mapObjectContent.ScaleObject(tiledMap.TileWidth, tiledMap.TileHeight); //} //// In Isometric maps, we must consider tile width == height for objects so their size can be correctly calculated //else if (tiledMap.Orientation == Orientation.Isometric) //{ // mapObjectContent.ScaleObject(tiledMap.TileHeight, tiledMap.TileHeight); //} //// In Staggered maps, we must pre-alter object position, as it comes mixed between staggered and orthogonal properties //else if (tiledMap.Orientation == Orientation.Staggered) //{ // float x = mapObjectContent.Bounds.x / (float)tiledMap.TileWidth; // float y = mapObjectContent.Bounds.y / (float)tiledMap.TileHeight * 2.0f; // float width = mapObjectContent.Bounds.width / (float)tiledMap.TileWidth; // float height = mapObjectContent.Bounds.height / (float)tiledMap.TileWidth; // if (Mathf.FloorToInt(Mathf.Abs(y)) % 2 > 0) // x -= 0.5f; // mapObjectContent.Bounds = new Rect(x, y, width, height); // if (mapObjectContent.Points != null) // { // for (int i = 0; i < mapObjectContent.Points.Count; i++) // { // mapObjectContent.Points[i] = new Vector2(mapObjectContent.Points[i].x / (float)tiledMap.TileWidth, mapObjectContent.Points[i].y / (float)tiledMap.TileHeight * 2.0f); // } // } //} mapObjectContent.ScaleObject(tiledMap.TileWidth, tiledMap.TileHeight, tiledMap.Orientation); mapObjectContent.Name = this.Name + "_" + mapObjectContent.Name; // Object names need to be unique for our lookup system, but Tiled // doesn't require unique names. string objectName = mapObjectContent.Name; int duplicateCount = 2; // if a object already has the same name... if (Objects.Find(o => o.Name.Equals(objectName)) != null) { // figure out a object name that does work do { objectName = string.Format("{0}{1}", mapObjectContent.Name, duplicateCount); duplicateCount++; } while (Objects.Find(o => o.Name.Equals(objectName)) != null); // log a warning for the user to see Debug.Log("Renaming object \"" + mapObjectContent.Name + "\" to \"" + objectName + "\" in layer \"" + Name + "\" to make a unique name."); // save that name mapObjectContent.Name = objectName; } mapObjectContent.CreateTileObject(tiledMap, Name, layerDepth, materials); AddObject(mapObjectContent); } }