using AX.MessageSystem; using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [Serializable] public class SpreadEndData//蔓延结束 { public long gameObjectId; } [Serializable] public class FireSpreadData : RecordObjectBase { public string name; public bool straightSpread; public bool areaSpread; public bool aroundSpread; public int startTime;//单位为分钟 public float spreadSpeed = 4.0f;//蔓延速度 public float repeatRate = 4.0f; public List spreadFirePositions; public List spdFirePositions; public List hasSpreadPos; public int index; public bool isSpreading; public string temperature; public string radiation; public string rsw; public string pos; } [Serializable] public class spdData { public List postions; } public class FireSpreadCtrl : MonoBehaviour { public bool straightSpread; public bool areaSpread; public bool aroundSpread; public int startTime;//单位为分钟 public List pathPointList = new List(); public GameObject fire; public float spreadSpeed = 4.0f;//蔓延速度 public float repeatRate = 4.0f; public float distance; public float spreadDis = 5.0f; public int result; public List spreadFirePositions = new List();//直线蔓延火点数组 public int index = 0; public List> spdFirePositions = new List>();//区域或四周蔓延火点数组 public bool flag = false; public bool isSpreading = false;//是否正在蔓延 public List hasSpreadList = new List(); public void Start() { MessageDispatcher.AddListener("ReplayEvent", ReplayEventSpreadFire); } private void ReplayEventSpreadFire(IMessage obj) { var eventData = (EventData)obj.Data; if (eventData.eventType == RecordEventType.FireSpread) { FireSpreadData data = JsonUtility.FromJson(eventData.json); if (data.name == gameObject.name) { straightSpread = data.straightSpread; areaSpread = data.areaSpread; aroundSpread = data.aroundSpread; startTime = data.startTime; spreadSpeed = data.spreadSpeed; repeatRate = data.repeatRate; spreadFirePositions = data.spreadFirePositions; if (data.spdFirePositions.Count > 0) { spdFirePositions.Clear(); foreach (var item in data.spdFirePositions) { spdFirePositions.Add(item.postions); } } isSpreading = data.isSpreading; index = data.index; if (data.hasSpreadPos.Count > 0) { if (fire == null) { fire = Resources.Load("Prefab/Diaster/FireSpread"); } GameObject spreadFire = Instantiate(fire, data.hasSpreadPos[data.hasSpreadPos.Count - 1], Quaternion.identity, GameObject.Find("P_AllParent").transform.Find("P_Disaster/P_SpreadFire")) as GameObject; var spreadedFire = spreadFire.GetComponent(); spreadedFire.SourceId = GetComponent().GameObjID; if (data.straightSpread) spreadFire.name = "straightSpread" + (index - 1); else if (data.areaSpread) spreadFire.name = "areaSpread" + (index - 1); else spreadFire.name = "aroundSpread" + (index - 1); } if (data.straightSpread && data.index == data.spreadFirePositions.Count || (data.areaSpread && data.index == data.spdFirePositions.Count) || (data.aroundSpread && data.index == data.spdFirePositions.Count)) { ResourceLoadWindow.Instance.LoadTextHintWindow("蔓延已经完成!", 0.5f); } } } } public void OnDestroy() { MessageDispatcher.RemoveListener("ReplayEvent", ReplayEventSpreadFire); } public void Reset() { CancelInvoke(); index = 0; straightSpread = false; areaSpread = false; aroundSpread = false; pathPointList.Clear(); spreadFirePositions.Clear(); spdFirePositions.Clear(); isSpreading = false; } public FireSpreadData GetSpreadData() { FireSpreadData data = new FireSpreadData(); data.name = gameObject.name; data.straightSpread = this.straightSpread; data.areaSpread = this.areaSpread; data.aroundSpread = this.aroundSpread; data.startTime = this.startTime;//单位为分钟 data.spreadSpeed = this.spreadSpeed;//蔓延速度 data.repeatRate = this.repeatRate; data.spreadFirePositions = this.spreadFirePositions; data.spdFirePositions = new List(); if (this.spdFirePositions.Count > 0) { foreach (var item in this.spdFirePositions) { spdData sdata = new spdData(); sdata.postions = item; data.spdFirePositions.Add(sdata); } } data.isSpreading = this.isSpreading; data.index = this.index; if (data.hasSpreadPos == null) data.hasSpreadPos = new List(); foreach (Transform item in GameObject.Find("P_AllParent").transform.Find("P_Disaster/P_SpreadFire")) { if (item.GetComponent().SourceId == GetComponent().gameObjID) { data.hasSpreadPos.Add(item.localPosition); } } return data; } public void Spread(FireSettingData data) { CancelInvoke(); index = 0; straightSpread = data.type == 1; areaSpread = data.type == 2; aroundSpread = data.type == 3; pathPointList = data.pathPointList; GetSpreadFirePositions(); StartCoroutine(WaitForStart(data.time)); } public void AddSpreadEndRecordData() { if (ReplaySetting.PlayStatus == PlayStatus.isEditor && RecordManager.Instance.recordStatus == RecordStatus.normal) { SpreadEndData data = new SpreadEndData(); data.gameObjectId = GetComponent().gameObjID; var eventData = new EventData(); eventData.time = RecordManager.Instance.RecordTimer; eventData.cloneObjType = CloneObjType.None; eventData.eventType = RecordEventType.FireSpreadEnd; eventData.json = JsonConvert.SerializeObject(data); RecordManager.Instance.jsonData.eventDataList.Add(eventData); } } public IEnumerator WaitForStart(float time) { yield return new WaitForSeconds(time * 60 / ReplayManager.playSpeed); InvokeRepeating("CtrlFireSpread", spreadSpeed, repeatRate); isSpreading = true; } // Update is called once per frame public void AddRecordEventSpreadFire(GameObject obj, FireSpreadData 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.FireSpread; eventData.json = JsonUtility.ToJson(data); RecordManager.Instance.jsonData.eventDataList.Add(eventData); } } // private void CtrlFireSpread() { if (fire == null) { fire = Resources.Load("Prefab/Diaster/FireSpread"); } if (!(GameSettings.othersSettings.isReplayMode && GameSettings.othersSettings.isReplayPause)) { if (straightSpread) { if (spreadFirePositions.Count == 0) { CancelInvoke("CtrlFireSpread"); isSpreading = false; ResourceLoadWindow.Instance.LoadTextHintWindow("路径过窄请重新规划!", 3f); } if (index < spreadFirePositions.Count) { GameObject spreadFire = Instantiate(fire, spreadFirePositions[index++], Quaternion.identity, GameObject.Find("P_AllParent").transform.Find("P_Disaster/P_SpreadFire")) as GameObject; var spreadedFire = spreadFire.GetComponent(); spreadedFire.SourceId = GetComponent().GameObjID; spreadFire.name = "straightSpread" + (index - 1); spreadedFire.AddRecordDataCreat(gameObject); // AddRecordEventSpreadFire(gameObject, GetSpreadData()); if (index == spreadFirePositions.Count) { CancelInvoke("CtrlFireSpread"); isSpreading = false; AddSpreadEndRecordData(); ResourceLoadWindow.Instance.LoadTextHintWindow("蔓延已经完成!", 0.5f); } } } if (areaSpread || aroundSpread) { if (spdFirePositions.Count == 0) { CancelInvoke("CtrlFireSpread"); isSpreading = false; ResourceLoadWindow.Instance.LoadTextHintWindow("路径过窄请重新规划!", 3f); } if (index < spdFirePositions.Count) { List tempFirePosList = spdFirePositions[index++]; for (int j = 0; j < tempFirePosList.Count; j++) { if (gameObject.GetComponent().gameObjType==CloneObjType.FireHuge) { tempFirePosList[j] = tempFirePosList[j] + Vector3.down * 2.5f; } GameObject spreadFire = Instantiate(fire, tempFirePosList[j], Quaternion.identity, GameObject.Find("P_AllParent").transform.Find("P_Disaster/P_SpreadFire")) as GameObject; //var spreadedFire = spreadFire.AddComponent(); var spreadedFire = spreadFire.GetComponent(); spreadedFire.SourceId = GetComponent().gameObjID; if (areaSpread) spreadFire.name = "areaSpread" + (index - 1); else spreadFire.name = "aroundSpread" + (index - 1); spreadedFire.AddRecordDataCreat(gameObject); } //AddRecordEventSpreadFire(gameObject, GetSpreadData()); if (index == spdFirePositions.Count) { CancelInvoke("CtrlFireSpread"); isSpreading = false; AddSpreadEndRecordData(); ResourceLoadWindow.Instance.LoadTextHintWindow("蔓延已经完成!", 0.5f); } } } } } private void GetSpreadFirePositions() { //获取蔓延路径上需要克隆火的位置 if (straightSpread) { spreadFirePositions.Clear(); for (int i = 0; i < pathPointList.Count; i++) { if ((i + 1) <= (pathPointList.Count - 1)) { distance = Vector3.Distance(pathPointList[i], pathPointList[i + 1]); result = (int)(distance / spreadDis); for (int j = 1; j <= result; j++) { spreadFirePositions.Add(Vector3.MoveTowards(pathPointList[i], pathPointList[i + 1], spreadDis * j)); } spreadFirePositions.Add(pathPointList[i + 1]); } } } if (areaSpread || aroundSpread) { //找出多边形xz平面包围盒 float[] polyline = new float[2 * pathPointList.Count]; for (int i = 0; i < pathPointList.Count; i++) { polyline[i + i] = pathPointList[i].x; polyline[i + i + 1] = pathPointList[i].z; } float maxx = 0, minx = 0, maxz = 0, minz = 0; int pointcount = 0; if (polyline != null) { pointcount = polyline.Length / 2; maxx = minx = polyline[0]; maxz = minz = 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 (maxz < polyline[i + i + 1]) maxz = polyline[i + i + 1]; if (minz > polyline[i + i + 1]) minz = polyline[i + i + 1]; } } int count_z = (int)((maxz - minz) / spreadDis); int count_x = (int)((maxx - minx) / spreadDis); List temp = new List();//包围盒内按spreadDis等距分割的所有点 for (int i = 0; i <= count_x; i++) { for (int j = 0; j <= count_z; j++) { temp.Add(new Vector2(minx + spreadDis * i, minz + spreadDis * j)); } } List pathPointList_xz = new List();//xz平面多边形 for (int i = 0; i < pathPointList.Count; i++) { pathPointList_xz.Add(new Vector2(pathPointList[i].x, pathPointList[i].z)); } List spreadFirePositions_xz = new List();//xz平面多边形内部的点 for (int i = 0; i < temp.Count; i++) { int wn = wn_PnPoly(pathPointList_xz, temp[i]); if (wn != 0) { spreadFirePositions_xz.Add(new Vector3(temp[i].x, temp[i].y)); } } //分批次构建火点数据结构 Vector2 firePos_xz = new Vector2(this.transform.position.x, this.transform.position.z); float maxFanWei = Vector2.Distance(temp[0], temp[temp.Count - 1]); int pici = (int)(maxFanWei / spreadDis);//蔓延批次 pici = pici + 1; spdFirePositions.Clear(); for (int i = 1; i <= pici; i++) { List tempList1 = new List(); List tempList = new List(); for (int j = 0; j < spreadFirePositions_xz.Count; j++) { float dis = Vector2.Distance(firePos_xz, spreadFirePositions_xz[j]); if (dis != 0 && dis <= i * spreadDis) { tempList1.Add(spreadFirePositions_xz[j]); } } for (int n = 0; n < tempList1.Count; n++) { tempList.Add(new Vector3(tempList1[n].x, this.transform.position.y, tempList1[n].y)); } spdFirePositions.Add(tempList); for (int m = 0; m < tempList1.Count; m++) { if (spreadFirePositions_xz.Contains(tempList1[m])) { spreadFirePositions_xz.Remove(tempList1[m]); } } } //因为批次是按包围盒最大点到最小点的距离除以一个批次的蔓延距离,所以可能出现没有点的批次,需要去除 for (int i = spdFirePositions.Count - 1; i >= 0; i--) { if (spdFirePositions[i].Count == 0) { spdFirePositions.RemoveAt(i); } } } } /// /// 判断点是否在多边形内 /// /// 多边形顶点数组 /// 多边形包围盒内按spreadDis等距分割的各点 /// // 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 pathPointList, Vector2 point) { int n = pathPointList.Count; int wn = 0; // the winding number counter // loop through all edges of the polygon for (int i = 0; i < n; i++) { // edge from polygon[i] to polygon[i+1] if (pathPointList[i].y <= point.y) { // start y <= P.y if (pathPointList[(i + 1) % n].y > point.y) // an upward crossing if (isLeft(pathPointList[i], pathPointList[(i + 1) % n], point) > 0) // P left of edge ++wn; // have a valid up intersect } else { // start y > P.y (no test needed) if (pathPointList[(i + 1) % n].y <= point.y) // a downward crossing if (isLeft(pathPointList[i], pathPointList[(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)); } }