using System; using System.Collections; using System.Collections.Generic; using AX.MessageSystem; using UnityEngine; using AX.InputSystem; public enum SpreadWay { None, Road,//路径蔓延 Are,//区域蔓延 Around,//四周蔓延 Aoto//自动蔓延 } struct Line { public Vector2 Start, End; } public class CloneFireSpreadLine : CloneBase { public float Height = 0; private SpreadWay spreadWay = SpreadWay.None; public List pathPoints = new List(); public int currentPathPointsCount = 0;//打开火属性设置窗口时当前火已设置的路径点数 public long spreadedFireGameObjID; public Vector3 startPoint; private Vector3 endPoint; private Line lastLine = new Line();//区域或四周蔓延范围多边形最后一条边 private Line lastButOneLine = new Line();//区域或四周蔓延范围多边形倒数第二条边 private Line otherLine = new Line();//区域或四周蔓延范围多边形中除了最后一条边和倒数第二条边的其它边 public static CloneFireSpreadLine instance; private void Awake() { instance = this; } private void Start() { MessageDispatcher.AddListener("ROAD_SPREAD_SELECTED", RoadSpreadSelected); MessageDispatcher.AddListener("ARE_SPREAD_SELECTED", AreSpreadSelected); MessageDispatcher.AddListener("AROUND_SPREAD_SELECTED", AroundSpreadSelected); MessageDispatcher.AddListener("AOTO_SPREAD_SELECTED", AotoSpreadSelected); MessageDispatcher.AddListener("HIDE_ALL_SPREADPATH", HideAllSpreadLine); MessageDispatcher.AddListener("SHOW_SELECTEDFIRE_SPREADPATH", ShowSelectedFireSpreadLine); } public override void OnDestroy() { MessageDispatcher.RemoveListener("ROAD_SPREAD_SELECTED", RoadSpreadSelected); MessageDispatcher.RemoveListener("ARE_SPREAD_SELECTED", AreSpreadSelected); MessageDispatcher.RemoveListener("AROUND_SPREAD_SELECTED", AroundSpreadSelected); MessageDispatcher.RemoveListener("AOTO_SPREAD_SELECTED", AotoSpreadSelected); MessageDispatcher.RemoveListener("HIDE_ALL_SPREADPATH", HideAllSpreadLine); MessageDispatcher.RemoveListener("SHOW_SELECTEDFIRE_SPREADPATH", ShowSelectedFireSpreadLine); } private void RoadSpreadSelected(IMessage obj) { var data = (bool)obj.Data; if (data) { spreadWay = SpreadWay.Road; InputManager.cloneObjType = CloneObjType.FireSpreadLine; InputManager.skill = true; } else { spreadWay = SpreadWay.None; InputManager.cloneObjType = CloneObjType.None; InputManager.skill = false; } } private void AreSpreadSelected(IMessage obj) { var data = (bool)obj.Data; if (data) { spreadWay = SpreadWay.Are; InputManager.cloneObjType = CloneObjType.FireSpreadLine; InputManager.skill = true; } else { spreadWay = SpreadWay.None; InputManager.cloneObjType = CloneObjType.None; InputManager.skill = false; } } private void AroundSpreadSelected(IMessage obj) { var data = (bool)obj.Data; if (data) { spreadWay = SpreadWay.Around; InputManager.cloneObjType = CloneObjType.FireSpreadLine; InputManager.skill = true; } else { spreadWay = SpreadWay.None; InputManager.cloneObjType = CloneObjType.None; InputManager.skill = false; } } private void AotoSpreadSelected(IMessage obj) { var data = (bool)obj.Data; if (data) { spreadWay = SpreadWay.Aoto; InputManager.cloneObjType = CloneObjType.FireSpreadLine; InputManager.skill = true; } else { spreadWay = SpreadWay.None; InputManager.cloneObjType = CloneObjType.None; InputManager.skill = false; } } public void Cancel() { if (spreadWay == SpreadWay.Road) { if (currentPathPointsCount == 0) {//第一次设置时点击取消 ResetSpreadPath(spreadedFireGameObjID); } else {//已经设置并保存过一次或多次 if (pathPoints.Count > currentPathPointsCount) {//再次打开属性窗口,并且设置过一条或多条线段 var count = pathPoints.Count - currentPathPointsCount; for (int i = 1; i <= count; i++) { EntitiesManager.Instance.DeleteObj(transform.GetChild(transform.childCount - i).gameObject); } } } } else if (spreadWay == SpreadWay.Are || spreadWay == SpreadWay.Around) { if (currentPathPointsCount == 0) {//第一次设置时点击取消 ResetSpreadPath(spreadedFireGameObjID); } else {//已经设置并保存过一次或多次 if (pathPoints.Count > currentPathPointsCount) {//再次打开属性窗口,并且设置过一条或多条线段 var count = pathPoints.Count - currentPathPointsCount + 1; for (int i = 1; i <= count; i++) { EntitiesManager.Instance.DeleteObj(transform.GetChild(transform.childCount - i).gameObject); } var count1 = pathPoints.Count - currentPathPointsCount; for (int j = 1; j <= count1; j++) { pathPoints.RemoveAt(pathPoints.Count - 1); } Vector3 ClonedObjPos = (pathPoints[pathPoints.Count - 1] + pathPoints[0]) / 2; ClonedObjPos = new Vector3(ClonedObjPos.x, ClonedObjPos.y + Height, ClonedObjPos.z); var ClonedObj = EntitiesManager.Instance.CreateObj(clonePrefab, ClonedObjPos, transform, EntitiesManager.Instance.CreateObjID(CurrentUserInfo.mySelf.Id)); ClonedObj.transform.forward = (-(pathPoints[0] - pathPoints[pathPoints.Count - 1])).normalized; float Distance = Vector3.Distance(pathPoints[pathPoints.Count - 1], pathPoints[0]); ClonedObj.transform.localScale = new Vector3(30f, 30f, Distance * 108); ClonedObj.GetComponent().fireGameObjID = spreadedFireGameObjID; ClonedObj.GetComponent().flag = true; } } } } public void SetPathPoints(List firePathPoints) { firePathPoints.Clear(); foreach (Vector3 item in pathPoints) { firePathPoints.Add(item); } } private void HideAllSpreadLine(IMessage obj) { foreach (Transform child in this.transform) { child.gameObject.SetActive(false); } } private void ShowSelectedFireSpreadLine(IMessage obj) { var fireGameObjID = (long)obj.Data; foreach (Transform child in transform) { if (child.GetComponent().fireGameObjID == fireGameObjID) { child.gameObject.SetActive(true); } } } public void ResetSpreadPath(long fireGameObjID) { //删除蔓延路径点 pathPoints.Clear(); //删除蔓延路径模型 foreach (Transform child in transform) { if (child.GetComponent().fireGameObjID == fireGameObjID) { EntitiesManager.Instance.DeleteObj(child.gameObject); } } } public override void Execute(IMessage obj) { var gameObjID = (long)obj.Sender; var data = ((CloneCmdArgs)obj.Data); if (data.cloneObjType == cloneObjType /*&& !FireSpreadPanel.Instance.chooseObj.GetComponent().flag*/) { var hitPoint = data.hitPos; endPoint = hitPoint; //加入克隆火蔓延路径或范围时点击的点 pathPoints.Add(endPoint); float distance = Vector3.Distance(startPoint, endPoint);//计算两点的距离 if (distance < 2) { pathPoints.Remove(endPoint); LoadPromptWin.Instance.LoadTextPromptWindow("两点重合",1f); return; } if (spreadWay == SpreadWay.Road) { Vector3 clonedObjPos = (startPoint + endPoint) / 2; clonedObjPos = new Vector3(clonedObjPos.x, clonedObjPos.y + Height, clonedObjPos.z); var clonedObj = EntitiesManager.Instance.CreateObj(clonePrefab, clonedObjPos, transform, gameObjID); clonedObj.transform.forward = (-(endPoint - startPoint)).normalized;//改变线条的朝向 clonedObj.transform.localScale = new Vector3(30f, 30f, distance * 108);//延长线条,连接两点。 clonedObj.GetComponent().fireGameObjID = spreadedFireGameObjID; startPoint = endPoint; //设置克隆物体所在楼层等基本属性,属性从点击的对象上获取 var hitObj = EntitiesManager.Instance.GetEntityByID(data.gameObjID); CloneGameObjInfo hitObjInfo = hitObj.GetComponent(); CloneGameObjInfo cloneObjInfo = clonedObj.GetComponent(); cloneObjInfo.gameObjType = cloneObjType; cloneObjInfo.UserID = CurrentUserInfo.mySelf.Id; cloneObjInfo.buildNum = hitObjInfo.buildNum; cloneObjInfo.floorNum = hitObjInfo.floorNum; cloneObjInfo.interlayerNum = hitObjInfo.interlayerNum; } if (spreadWay == SpreadWay.Are || spreadWay == SpreadWay.Around) { if (startPoint == Vector3.zero) { startPoint = endPoint; return; } Vector3 clonedObjPos = (startPoint + endPoint) / 2; clonedObjPos = new Vector3(clonedObjPos.x, clonedObjPos.y + Height, clonedObjPos.z); var clonedObj = EntitiesManager.Instance.CreateObj(clonePrefab, clonedObjPos, transform, gameObjID); clonedObj.transform.forward = (-(endPoint - startPoint)).normalized;//改变线条的朝向 clonedObj.transform.localScale = new Vector3(30f, 30f, distance * 108);//延长线条,连接两点。 clonedObj.GetComponent().fireGameObjID = spreadedFireGameObjID; startPoint = endPoint; //设置克隆物体所在楼层等基本属性,属性从点击的对象上获取 var hitObj = EntitiesManager.Instance.GetEntityByID(data.gameObjID); CloneGameObjInfo hitObjInfo = hitObj.GetComponent(); CloneGameObjInfo cloneObjInfo = clonedObj.GetComponent(); cloneObjInfo.gameObjType = cloneObjType; cloneObjInfo.UserID = CurrentUserInfo.mySelf.Id; cloneObjInfo.buildNum = hitObjInfo.buildNum; cloneObjInfo.floorNum = hitObjInfo.floorNum; cloneObjInfo.interlayerNum = hitObjInfo.interlayerNum; if (pathPoints.Count >= 3) {//如果是区域蔓延或者四周蔓延,连接蔓延范围的多边形终点与起始点形成封闭多边形。 //终点:克隆蔓延路径或范围最后点击的点。 起点:火源的位置 bool isintersection = CheckPolygon(); if (!isintersection) { if (spreadWay == SpreadWay.Around) { List PathPointList_xz = new List(); for (int i = 0; i < pathPoints.Count; i++) { PathPointList_xz.Add(new Vector2(pathPoints[i].x, pathPoints[i].z)); } var firePos = EntitiesManager.Instance.GetEntityByID(spreadedFireGameObjID).transform.position; var firePos_xz = new Vector2(firePos.x, firePos.z); int wn = wn_PnPoly(PathPointList_xz, firePos_xz); if (wn == 0) { EntitiesManager.Instance.DeleteObj(transform.GetChild(transform.childCount - 1).gameObject); pathPoints.RemoveAt(pathPoints.Count - 1); startPoint = pathPoints[pathPoints.Count - 1]; LoadPromptWin.Instance.LoadTextPromptWindow("四周蔓延需包围火", 1f); return; } } if (pathPoints.Count > 3)//如果是区域蔓延方式或四周蔓延,要删除前一次终点到起始点的边 { foreach (Transform child in transform) { if (child.GetComponent().fireGameObjID == spreadedFireGameObjID && child.GetComponent().flag) { EntitiesManager.Instance.DeleteObj(child.gameObject); } } } Vector3 tempClonedObjPos = (endPoint + pathPoints[0]) / 2; tempClonedObjPos = new Vector3(tempClonedObjPos.x, tempClonedObjPos.y + Height, tempClonedObjPos.z); var tempClonedObj = EntitiesManager.Instance.CreateObj(clonePrefab, tempClonedObjPos, transform, EntitiesManager.Instance.CreateObjID(CurrentUserInfo.mySelf.Id)); tempClonedObj.transform.forward = (-(pathPoints[0] - endPoint)).normalized; float tempDistance = Vector3.Distance(endPoint, pathPoints[0]); tempClonedObj.transform.localScale = new Vector3(30f, 30f, tempDistance * 108); tempClonedObj.GetComponent().fireGameObjID = spreadedFireGameObjID; tempClonedObj.GetComponent().flag = true; startPoint = endPoint; //设置克隆物体所在楼层等基本属性,属性从点击的对象上获取 var tempHitObj = EntitiesManager.Instance.GetEntityByID(data.gameObjID); CloneGameObjInfo tempHitObjInfo = tempHitObj.GetComponent(); CloneGameObjInfo tempCloneObjInfo = clonedObj.GetComponent(); tempCloneObjInfo.gameObjType = cloneObjType; tempCloneObjInfo.UserID = CurrentUserInfo.mySelf.Id; tempCloneObjInfo.buildNum = tempHitObjInfo.buildNum; tempCloneObjInfo.floorNum = tempHitObjInfo.floorNum; tempCloneObjInfo.interlayerNum = tempHitObjInfo.interlayerNum; } else { EntitiesManager.Instance.DeleteObj(transform.GetChild(transform.childCount - 1).gameObject); pathPoints.RemoveAt(pathPoints.Count - 1); startPoint = pathPoints[pathPoints.Count - 1]; LoadPromptWin.Instance.LoadTextPromptWindow("不能形成多边形", 1f); } } } } } /// /// 判断是否能形成多边形 /// /// 最后一条边与除倒数第二条边外的其他边是否相交 /// 倒数第二条边是否与除自己外的其他边相交 private bool CheckPolygon() { bool isintersection1 = false; bool isintersection2 = false; lastLine.Start = new Vector2(endPoint.x, endPoint.z); lastLine.End = new Vector2(pathPoints[0].x, pathPoints[0].z); lastButOneLine.Start = new Vector2(pathPoints[pathPoints.Count - 2].x, pathPoints[pathPoints.Count - 2].z); lastButOneLine.End = new Vector2(endPoint.x, endPoint.z); for (int i = 0; i < pathPoints.Count; i++) { if ((i + 1) <= (pathPoints.Count - 1)) { otherLine.Start = new Vector2(pathPoints[i].x, pathPoints[i].z); otherLine.End = new Vector2(pathPoints[i + 1].x, pathPoints[i + 1].z); isintersection1 = CheckTwoLineCrose(lastLine, otherLine); if (isintersection1) { return true; } } } for (int i = 0; i < pathPoints.Count; i++) { if ((i + 1) <= (pathPoints.Count - 1)) { otherLine.Start = new Vector2(pathPoints[i].x, pathPoints[i].z); otherLine.End = new Vector2(pathPoints[i + 1].x, pathPoints[i + 1].z); if ((lastButOneLine.Start != otherLine.Start) && lastButOneLine.End != otherLine.End)//在其它边中排除倒数第二条边自己 { isintersection2 = CheckTwoLineCrose(lastButOneLine, otherLine); } if (isintersection2) { return true; } } } if (!isintersection1 && !isintersection2) { return false; } return default(bool); } /// /// 判断两条线段是否相交,若相交且交点不是端点。 /// /// 线段1 /// 线段2 /// 相交返回真,否则返回假。 private bool CheckTwoLineCrose(Line line1, Line line2) { bool isintersection = CheckCrose(line1, line2) && CheckCrose(line2, line1); Vector2 intersection = GetIntersection(line1.Start, line1.End, line2.Start, line2.End); bool dengYuDuanDian = true; if (intersection != line1.Start && intersection != line1.End && intersection != line2.Start && intersection != line2.End) { dengYuDuanDian = false; } if (isintersection && !dengYuDuanDian) { return true; } else { return false; } } /// /// 判断直线2的两点是否在直线1的两边。 /// /// 直线1 /// 直线2 /// private bool CheckCrose(Line line1, Line line2) { Vector2 v1 = new Vector2(); Vector2 v2 = new Vector2(); Vector2 v3 = new Vector2(); v1.x = line2.Start.x - line1.End.x; v1.y = line2.Start.y - line1.End.y; v2.x = line2.End.x - line1.End.x; v2.y = line2.End.y - line1.End.y; v3.x = line1.Start.x - line1.End.x; v3.y = line1.Start.y - line1.End.y; return (CrossMul(v1, v3) * CrossMul(v2, v3) <= 0); } /// /// 计算两个向量的叉乘。 /// /// /// /// private float CrossMul(Vector2 pt1, Vector2 pt2) { return pt1.x * pt2.y - pt1.y * pt2.x; } /// /// 求两条线段的交点 /// /// 线段1起点坐标 /// 线段1终点坐标 /// 线段2起点坐标 /// 线段2终点坐标 /// 相交点坐标 private Vector2 GetIntersection(Vector2 a, Vector2 b, Vector2 c, Vector2 d) { Vector2 intersection = new Vector2(); intersection.x = ((b.x - a.x) * (c.x - d.x) * (c.y - a.y) - c.x * (b.x - a.x) * (c.y - d.y) + a.x * (b.y - a.y) * (c.x - d.x)) / ((b.y - a.y) * (c.x - d.x) - (b.x - a.x) * (c.y - d.y)); intersection.y = ((b.y - a.y) * (c.y - d.y) * (c.x - a.x) - c.y * (b.y - a.y) * (c.x - d.x) + a.y * (b.x - a.x) * (c.y - d.y)) / ((b.x - a.x) * (c.y - d.y) - (b.y - a.y) * (c.x - d.x)); if ((intersection.x - a.x) * (intersection.x - b.x) <= 0 && (intersection.x - c.x) * (intersection.x - d.x) <= 0 && (intersection.y - a.y) * (intersection.y - b.y) <= 0 && (intersection.y - c.y) * (intersection.y - d.y) <= 0) { return intersection; //相交 } else { return Vector2.zero; } } // wn_PnPoly(): winding number test for a point in a polygon // Input: point = a point, // polygon = anticlockwise vertex points of a polygon polygon[n] // Return: wn = the winding number (=0 only when P is outside) // See:"http://geomalgorithms.com/a03-_inclusion.html" public int wn_PnPoly(List polygon, Vector2 point) { //polygon顶点的X,Y数组 float[] polyline = new float[2 * polygon.Count]; for (int i = 0; i < polygon.Count; i++) { polyline[i + i] = polygon[i].x; polyline[i + i + 1] = polygon[i].y; } float maxx = 0, minx = 0, maxy = 0, miny = 0; int pointcount = 0; if (polyline != null) { pointcount = polyline.Length / 2; maxx = minx = polyline[0]; maxy = miny = polyline[1]; for (int i = 0; i < pointcount; i++) { if (maxx < polyline[i + i]) maxx = polyline[i + i]; if (minx > polyline[i + i]) minx = polyline[i + i]; if (maxy < polyline[i + i + 1]) maxy = polyline[i + i + 1]; if (miny > polyline[i + i + 1]) miny = polyline[i + i + 1]; } } int n = polygon.Count; int wn = 0; // the winding number counter //首先判断是否在多边形的外框范围内 if (point.x < minx || point.x > maxx || point.y < miny || point.y > maxy) { return 0; } else { // loop through all edges of the polygon for (int i = 0; i < n; i++) { // edge from polygon[i] to polygon[i+1] if (polygon[i].y <= point.y) { // start y <= P.y if (polygon[(i + 1) % n].y > point.y) // an upward crossing if (isLeft(polygon[i], polygon[(i + 1) % n], point) > 0) // P left of edge ++wn; // have a valid up intersect } else { // start y > P.y (no test needed) if (polygon[(i + 1) % n].y <= point.y) // a downward crossing if (isLeft(polygon[i], polygon[(i + 1) % n], point) < 0) // P right of edge --wn; // have a valid down intersect } } return wn; } } //isLeft(): tests if a point is Left|On|Right of an infinite line. //Input: three points P0, P1, and P2 //Return: >0 for P2 left of the line through P0 and P1 // =0 for P2 on the line // <0 for P2 right of the line // See:"http://geomalgorithms.com/a03-_inclusion.html":Algorithm 1 "Area of Triangles and Polygons" // Or "http://geomalgorithms.com/a01-_area.html" float isLeft(Vector2 P0, Vector2 P1, Vector2 P2) { return ((P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y)); } }