using AX.MessageSystem; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; [Serializable] public class FireSettingData { public long id; public string name; public string rsw; public string pos; public int type; public int time; public List pathPointList; public string temperature; //火焰温度 public string radiation; //热辐射 } public class FireSettingPanel : ResourceLoadPanel { public InputField RSWInput; public InputField PosInput; public InputField WDInput; public InputField RFSInput; public Toggle StraightSpread; public Toggle AreaSpread; public Toggle AroundSpread; public InputField TimeInput; public Text ErrorText; public Button ResetBtn; public Button SaveBtn; public Button CloseBtn; public List PathPointList = new List(); private RaycastHit hit; private Ray ray; public Vector3 startPosition = Vector3.zero;//定义一个Vector3,用来存储鼠标点击的位置 private Vector3 endPosition = Vector3.zero;//定义一个Vector3,用来存储鼠标点击的位置 public LayerMask layerMask = -1; public Transform lineObjParent; public Transform lineObj; private GameObject lineObjChild; private GameObject linePrefab; private Line line1 = new Line();//多边形最后一条边 private Line line2 = new Line();//多边形除最后一条边的其他各边 private Line line3 = new Line();//多边形倒数第二边 private FireSettingData NowData = new FireSettingData(); private GameObject NowObj; // Use this for initialization void Start() { RSWInput.onValueChanged.AddListener(RSWInput_valueChanged); PosInput.onValueChanged.AddListener(PosInput_valueChanged); TimeInput.onValueChanged.AddListener(TimeInput_valueChanged); WDInput.onValueChanged.AddListener(WDInput_valueChanged); RFSInput.onValueChanged.AddListener(RFSInput_valueChanged); StraightSpread.onValueChanged.AddListener(StraightSpread_valueChanged); AreaSpread.onValueChanged.AddListener(AreaSpread_valueChanged); AroundSpread.onValueChanged.AddListener(AroundSpread_valueChanged); ResetBtn.onClick.AddListener(ResetBtn_Click); SaveBtn.onClick.AddListener(SaveBtn_Click); CloseBtn.onClick.AddListener(CloseBtn_Click); MessageDispatcher.AddListener("SelectChange", selectchange); } private void OnDestroy() { MessageDispatcher.RemoveListener("SelectChange", selectchange); } private void selectchange(IMessage obj) { gameObject.SetActive(false); } public void AddRecordEventFire(GameObject obj, FireSettingData data) { if (ReplaySetting.PlayStatus == PlayStatus.isEditor && RecordManager.Instance.recordStatus == RecordStatus.normal) { var eventData = new EventData(); eventData.time = RecordManager.Instance.RecordTimer; eventData.cloneObjType = obj.GetComponent().gameObjType; eventData.eventType = RecordEventType.Fire; eventData.json = JsonUtility.ToJson(data); RecordManager.Instance.jsonData.eventDataList.Add(eventData); } } public void AddRecordEventFireReset(GameObject obj, FireSettingData data) { if (ReplaySetting.PlayStatus == PlayStatus.isEditor && RecordManager.Instance.recordStatus == RecordStatus.normal) { var eventData = new EventData(); eventData.time = RecordManager.Instance.RecordTimer; eventData.cloneObjType = obj.GetComponent().gameObjType; eventData.eventType = RecordEventType.FireReset; eventData.json = JsonUtility.ToJson(data); RecordManager.Instance.jsonData.eventDataList.Add(eventData); } } public void SettingFire(GameObject Fire) { NowObj = Fire; NowData = Fire.GetComponent().MyData; RSWInput.text = NowData.rsw; PosInput.text = NowData.pos; TimeInput.text = NowData.time.ToString(); WDInput.text = NowData.temperature; RFSInput.text = NowData.radiation; StraightSpread.isOn = NowData.type == 1; AreaSpread.isOn = NowData.type == 2; AroundSpread.isOn = NowData.type == 3; PathPointList = NowData.pathPointList; ErrorText.text = ""; if (PathPointList.Count > 0) { GetLineObj(); //Debug.Log("Line"+lineObj.name); startPosition = PathPointList[PathPointList.Count - 1]; } else { startPosition = Fire.transform.position; } } private void CloseBtn_Click() { gameObject.SetActive(false); //隐藏或删除路径 NowData = null; if (lineObj != null) lineObj.gameObject.SetActive(false); lineObj = null; NowObj = null; } public void SaveBtn_Click() { NowObj.GetComponent().SetTemperatureText(); NowObj.GetComponent().SetRadiationText(); if (!StraightSpread.group.AnyTogglesOn()) { ErrorText.text = "请先选择蔓延方式!"; return; } if (string.IsNullOrEmpty(TimeInput.text)) { ErrorText.text = "请输入时间!"; return; } //if (NowData.time == 0) //{ // ErrorText.text = "请输入正整数时间!"; // return; //} if (NowData.pathPointList.Count == 0) { ErrorText.text = "请划定蔓延区域!"; return; } if (NowObj.GetComponent().isSpreading) { ResourceLoadWindow.Instance.LoadTipWindow("当前火焰正在蔓延,是否结束并开始新的蔓延?", () => { AddRecordEventFire(NowObj, NowData); if (ReplaySetting.PlayStatus != PlayStatus.isReplay) NowObj.GetComponent().Spread(NowData); CloseBtn_Click(); }, () => CloseBtn_Click()); return; } AddRecordEventFire(NowObj, NowData); if (ReplaySetting.PlayStatus != PlayStatus.isReplay) NowObj.GetComponent().Spread(NowData); CloseBtn_Click(); } private void ResetBtn_Click() { ResetPath(); } private void PosInput_valueChanged(string value) { NowData.pos = value; } private void RSWInput_valueChanged(string value) { NowData.rsw = value; } private void WDInput_valueChanged(string value) { NowData.temperature = value; } private void RFSInput_valueChanged(string value) { NowData.radiation = value; } private void AroundSpread_valueChanged(bool Ison) { if (Ison) { if (NowData.type != 3) ResetLinePath(); TimeInput.interactable = true; NowData.type = 3; } else { if (!StraightSpread.group.AnyTogglesOn()) { TimeInput.interactable = false; NowData.type = 0; } } } private void AreaSpread_valueChanged(bool Ison) { if (Ison) { if (NowData.type != 2) ResetLinePath(); TimeInput.interactable = true; NowData.type = 2; } else { if (!StraightSpread.group.AnyTogglesOn()) { TimeInput.interactable = false; NowData.type = 0; } } } private void StraightSpread_valueChanged(bool Ison) { if (Ison) { if (NowData.type != 1) ResetLinePath(); TimeInput.interactable = true; NowData.type = 1; } else { if (!StraightSpread.group.AnyTogglesOn()) { TimeInput.interactable = false; NowData.type = 0; } } } private void TimeInput_valueChanged(string value) { if (string.IsNullOrEmpty(value)) return; int time = int.Parse(value); if (time < 0) { ErrorText.text = "请输入正整数时间!"; TimeInput.text = ""; } else { NowData.time = time; ErrorText.text = ""; } } private void ResetPath() { PathPointList.Clear(); ResetLinePath(); //重置路径时,如果是已经蔓延过一次及存在蔓延出来的火,重置删除蔓延路径的同时,删除蔓延出来的火 Transform pfire = GameObject.Find("P_AllParent").transform.Find("P_Disaster/P_SpreadFire"); foreach (Transform child in pfire) { if (child.GetComponent() && child.GetComponent().SourceId == NowData.id) { Destroy(child.gameObject); } } AddRecordEventFireReset(NowObj, NowData); NowObj.GetComponent().Reset(); } private void ResetLinePath() { GetLineObj(); //重置路线 if (lineObj != null) { foreach (Transform child in lineObj) { Destroy(child.gameObject); } PathPointList.Clear(); startPosition = EntitiesManager.Instance.GetEntityByID(NowData.id).transform.position; } } private void GetLineObj() { if (lineObjParent == null) { lineObjParent = GameObject.Find("P_AllParent").transform.Find("P_Disaster/P_FireLine"); } foreach (Transform t in lineObjParent) { if (t.name == NowData.id.ToString()) { lineObj = t; if (!lineObj.gameObject.activeSelf) { lineObj.gameObject.SetActive(true); } } else { if (t.gameObject.activeSelf) { t.gameObject.SetActive(false); } } } } // Update is called once per frame void Update() { if (gameObject.activeSelf) { if ((StraightSpread.isOn || AreaSpread.isOn || AroundSpread.isOn) && InputManager.cloneObjType == CloneObjType.None) { if (Input.GetMouseButtonDown(0) && !EventSystem.current.IsPointerOverGameObject()) { ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out hit, Mathf.Infinity)) { //if (hit.transform.gameObject.layer == LayerMask.NameToLayer("PathFinding") // || hit.transform.gameObject.layer == LayerMask.NameToLayer("shineiFloor")) if (hit.transform.GetComponent() && hit.transform.GetComponent().CloneableTypes.Contains(CloneObjType.FireNormal) ) { if (NowObj.GetComponent().gameObjType == CloneObjType.FireHuge) { if (Mathf.Abs(hit.transform.position.y - NowObj.transform.position.y) > 7) return; } else { if (Mathf.Abs(hit.transform.position.y - NowObj.transform.position.y) > 3) return; } if (PathPointList.Count == 0)//第一次设置或重置后再设置蔓延路径的情况 { if (StraightSpread.isOn || AreaSpread.isOn) { PathPointList.Add(startPosition); } else if (AroundSpread.isOn) { startPosition = hit.point; } CloneFireSpreadPath(); } else { startPosition = PathPointList[PathPointList.Count - 1]; CloneFireSpreadPath(); } } } } } } } private void CloneFireSpreadPath() { bool isintersection1 = false; bool isintersection2 = false; PathPointList.Add(hit.point); endPosition = hit.point; if (startPosition == endPosition) { return; } Vector3 tempPos = (startPosition + endPosition) / 2; if (linePrefab == null) linePrefab = Resources.Load("Prefab/Diaster/FireSpreadLine"); lineObjChild = Instantiate(linePrefab, tempPos, Quaternion.identity) as GameObject; if (lineObjParent == null) lineObjParent = GameObject.Find("P_AllParent").transform.Find("P_Disaster/P_FireLine"); if (lineObj == null) lineObj = new GameObject().transform; lineObj.name = NowData.id.ToString(); lineObj.SetParent(lineObjParent); lineObjChild.transform.parent = lineObj.transform; lineObjChild.transform.up = (-(endPosition - startPosition)).normalized;//改变线条的朝向 float distance = Vector3.Distance(startPosition, endPosition);//计算两点的距离 lineObjChild.transform.localScale = new Vector3(5f, distance * 18, 5f);//延长线条,连接两点。 if (StraightSpread.isOn) { startPosition = endPosition; } //如果是区域蔓延方式或四周蔓延,在顶点数大于2的情况下,画出终点与起始点连接成的边 if (AreaSpread.isOn || AroundSpread.isOn) { if (PathPointList.Count > 2) { line1.Start = new Vector2(endPosition.x, endPosition.z); line1.End = new Vector2(PathPointList[0].x, PathPointList[0].z); line3.Start = new Vector2(PathPointList[PathPointList.Count - 2].x, PathPointList[PathPointList.Count - 2].z); line3.End = new Vector2(endPosition.x, endPosition.z); for (int i = 0; i < PathPointList.Count; i++) { if ((i + 1) <= (PathPointList.Count - 1)) { line2.Start = new Vector2(PathPointList[i].x, PathPointList[i].z); line2.End = new Vector2(PathPointList[i + 1].x, PathPointList[i + 1].z); isintersection1 = CheckTwoLineCrose(line1, line2, PathPointList); if (isintersection1) { break; } } } for (int i = 0; i < PathPointList.Count; i++) { if ((i + 1) <= (PathPointList.Count - 1)) { line2.Start = new Vector2(PathPointList[i].x, PathPointList[i].z); line2.End = new Vector2(PathPointList[i + 1].x, PathPointList[i + 1].z); if ((line3.Start != line2.Start) && line3.End != line2.End)//排除line3和自己比 { isintersection2 = CheckTwoLineCrose(line3, line2, PathPointList); } if (isintersection2) { break; } } } if (!isintersection1 && !isintersection2) { Vector3 tempPos1 = (endPosition + PathPointList[0]) / 2; lineObjChild = Instantiate(linePrefab, tempPos1, Quaternion.identity) as GameObject; lineObjChild.transform.parent = lineObj.transform; lineObjChild.transform.up = (-(PathPointList[0] - endPosition)).normalized; float distance1 = Vector3.Distance(endPosition, PathPointList[0]); lineObjChild.transform.localScale = new Vector3(5f, distance1 * 18, 5f); startPosition = endPosition; if (PathPointList.Count > 3)//如果是区域蔓延方式或四周蔓延,要删除前一次终点到起始点的边 Destroy(lineObj.transform.GetChild(lineObj.transform.childCount - 3).gameObject); } else { Destroy(lineObj.transform.GetChild(lineObj.transform.childCount - 1).gameObject); PathPointList.RemoveAt(PathPointList.Count - 1); //messageBox.GetComponent().showMessage(ShowMessageType.prompt, "提示信息", "不能形成多边形"); ResourceLoadWindow.Instance.LoadTextHintWindow("不能形成多边形", 1f); } } } } struct Line { public Vector2 Start, End; } /// /// 判断直线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); } /// /// 判断两条线段是否相交,若相交且交点不是端点。 /// /// 线段1 /// 线段2 /// 相交返回真,否则返回假。 private bool CheckTwoLineCrose(Line line1, Line line2, List PathPointList) { //return CheckCrose(line1, line2) && CheckCrose(line2, line1); 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; } } /// /// 计算两个向量的叉乘。 /// /// /// /// 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) { Debug.Log("线段相交于点(" + intersection.x + "," + intersection.y + ")!"); 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" 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)); } }