作為一個菜雞,這個高中數學題差不多廢了我兩個上午。。。好了,廢話不多說,直接上代碼。。。
using System.Collections.Generic; using UnityEngine; public class DrawAreaLine : MonoBehaviour { public bool IsAreaLineOn = false; private LineRenderer AreaLine; private List<Vector3> PosList = new List<Vector3>(); private RaycastHit hit; private Ray ray; private int num = 0; // Use this for initialization void Start() { AreaLine = transform.Find("Area").GetComponent<LineRenderer>(); AreaLine.material = new Material(Shader.Find("Sprites/Default")); AreaLine.startColor = Color.blue; AreaLine.endColor = Color.blue; AreaLine.startWidth = 0.05f; AreaLine.endWidth = 0.05f; AreaLine.numCornerVertices = 5; AreaLine.numCapVertices = 5; } // Update is called once per frame void Update() { BeginAreaLine(); } void BeginAreaLine() { if (IsAreaLineOn) { if (Input.GetMouseButtonDown(0)) { if (AreaLine.enabled == false) { AreaLine.enabled = true; } ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out hit)) { Vector3 pos = new Vector3(hit.point.x, hit.point.y, hit.point.z) + new Vector3(0, 0.1f, 0); //判斷是否有相同位置的點 for (int i=0; i<PosList.Count;i++) { if (PosList[i]==pos) { return; } } if (AreaLine.positionCount >= 4) { return; } if (AreaLine.positionCount == 3) { //判斷第四個點位置,判斷能否組成四邊形 //判斷順時針還是逆時針 if (IsClockWise() == false)//ABC為逆時針方向 { //逆時針判斷能否畫四邊形 if (IfCanAntiClock(pos) == false) { return; } } if (IsClockWise() == true) //ABC為順時針方向 { //順時針能否畫四邊形 if(IfCanClick(pos)==false) { return; } } } if (hit.collider.name == "PanelCube") { num++; AreaLine.positionCount = num; AreaLine.SetPosition(num - 1, pos); PosList.Add(pos); if (PosList.Count == 3) { //計算三角形面積 // AreaTriabgle(); print(AreaTriabgle()); } if (PosList.Count == 4) { //計算四邊形面積 //AreaQuadrangle(); print(AreaQuadrangle()); } } //print(AreaLine.positionCount); } } else if (Input.GetKeyDown(KeyCode.Delete)) { DelectLine(); } return; } } /// <summary> /// 判斷逆時針能不能畫成四邊形 /// </summary> /// <param name="PosD"></param> /// <returns>cyt</returns> bool IfCanAntiClock(Vector3 PosD) { //直線一般式表達:(y2-y1)x-(x2-x1)y-x1y2+x2y1=0 bool can = false; Vector2 A = new Vector2(PosList[0].x, PosList[0].z); Vector2 B = new Vector2(PosList[1].x, PosList[1].z); Vector2 C = new Vector2(PosList[2].x, PosList[2].z); Vector2 D = new Vector2(PosD.x, PosD.z); //D在直線AB、AC下方且在BC上方 if (((B.y - A.y) * D.x - (B.x - A.x) * D.y - A.x * B.y + B.x * A.y > 0) && (((C.y - A.y) * D.x - (C.x - A.x) * D.y - A.x * C.y + C.x * A.y > 0)) && ((C.y - B.y) * D.x - (C.x - B.x) * D.y - B.x * C.y + C.x * B.y < 0)) { can = false; } //D在AB上方且在BC、AC下方 else if (((B.y - A.y) * D.x - (B.x - A.x) * D.y - A.x * B.y + B.x * A.y < 0) && ((C.y - B.y) * D.x - (C.x - B.x) * D.y - B.x * C.y + C.x * B.y > 0) && (C.y - A.y) * D.x - (C.x - A.x) * D.y - A.x * C.y + C.x * A.y > 0) { can = false; } else { print("keyi"); can = true; } return can; } /// <summary> /// 順時針能否畫成四邊形 /// </summary> /// <param name="posD"></param> /// <returns>cyt</returns> bool IfCanClick(Vector3 PosD) { //直線一般式表達:(y2-y1)x-(x2-x1)y-x1y2+x2y1=0 bool CanClick = false; Vector2 A = new Vector2(PosList[0].x, PosList[0].z); Vector2 B = new Vector2(PosList[1].x, PosList[1].z); Vector2 C = new Vector2(PosList[2].x, PosList[2].z); Vector2 D = new Vector2(PosD.x, PosD.z); //在AB、AC上方、BC下方,以ac為底邊觀察的 if (((B.y - A.y) * D.x - (B.x - A.x) * D.y - A.x * B.y + B.x * A.y < 0) && (((C.y - A.y) * D.x - (C.x - A.x) * D.y - A.x * C.y + C.x * A.y <0)) && ((C.y - B.y) * D.x - (C.x - B.x) * D.y - B.x * C.y + C.x * B.y >0) ) { CanClick = false; } //在AC、BC上方,在AB下方 else if (((B.y - A.y) * D.x - (B.x - A.x) * D.y - A.x * B.y + B.x * A.y > 0) && (((C.y - A.y) * D.x - (C.x - A.x) * D.y - A.x * C.y + C.x * A.y < 0)) && ((C.y - B.y) * D.x - (C.x - B.x) * D.y - B.x * C.y + C.x * B.y < 0) ) { CanClick = false; } else { CanClick = true; } return CanClick; } /// <summary> /// 計算三角形面積 /// </summary> float AreaTriabgle() { //點到直線距離d=Mathf.Abs( (A*x0+B*y0+C)/Mathf.Sqrt(A*A+B*B) ); float area; Vector2 A = new Vector2(PosList[0].x, PosList[0].z); Vector2 B = new Vector2(PosList[1].x, PosList[1].z); Vector2 C = new Vector2(PosList[2].x, PosList[2].z); //AC長度(底邊長) float AC = Vector2.Distance(A, C); //B到AC的距離(AC邊的高) float h = Mathf.Abs(((C.y - A.y) * B.x - (C.x - A.x) * B.y - A.x * C.y + C.x * A.y) / Mathf.Sqrt((C.y - A.y) * (C.y - A.y) + (C.x - A.x) * (C.x - A.x))); area = AC * h / 2; return area; } /// <summary> /// 計算四邊形面積 /// </summary> float AreaQuadrangle() { float area; Vector2 A = new Vector2(PosList[0].x, PosList[0].z); Vector2 B = new Vector2(PosList[1].x, PosList[1].z); Vector2 C = new Vector2(PosList[2].x, PosList[2].z); Vector2 D = new Vector2(PosList[3].x, PosList[3].z); //AC長度(底邊長) float AC = Vector2.Distance(A, C); //B到AC的距離(AC邊的高) float hB = Mathf.Abs(((C.y - A.y) * B.x - (C.x - A.x) * B.y - A.x * C.y + C.x * A.y) / Mathf.Sqrt((C.y - A.y) * (C.y - A.y) + (C.x - A.x) * (C.x - A.x))); float hD = Mathf.Abs(((C.y - A.y) * D.x - (C.x - A.x) * D.y - A.x * C.y + C.x * A.y) / Mathf.Sqrt((C.y - A.y) * (C.y - A.y) + (C.x - A.x) * (C.x - A.x))); //判斷是否是凹四邊形 if (IsConcaveQuadrilateral() == true) { area = AC * Mathf.Abs((hB - hD)) / 2; } else area = AC * (hB + hD) / 2; return area; } /// <summary> /// 刪除線 /// </summary> void DelectLine() { num = 0; //lineRenderer.SetVertexCount(LengthOfLineRenderer); AreaLine.positionCount = num; PosList.Clear(); AreaLine.enabled = false; } /// <summary> /// 判斷是否是凹四邊形 /// </summary> /// <returns>cyt</returns> bool IsConcaveQuadrilateral() { bool Concave = false; Vector2 A = new Vector2(PosList[0].x, PosList[0].z); Vector2 B = new Vector2(PosList[1].x, PosList[1].z); Vector2 C = new Vector2(PosList[2].x, PosList[2].z); Vector2 D = new Vector2(PosList[3].x, PosList[3].z); //逆時針狀況 //兩種情況,一種是D在三角形ABC內部,另一種是D在直線AB跟BC下方(以ab為底邊觀察) if (IsClockWise()==false) { if (((B.y - A.y) * D.x - (B.x - A.x) * D.y - A.x * B.y + B.x * A.y < 0) && ((C.y - B.y) * D.x - (C.x - B.x) * D.y - B.x * C.y + C.x * B.y < 0) && ((C.y - A.y) * D.x - (C.x - A.x) * D.y - A.x * C.y + C.x * A.y > 0)) { Concave = true; } else if (((B.y - A.y) * D.x - (B.x - A.x) * D.y - A.x * B.y + B.x * A.y > 0) && ((C.y - B.y) * D.x - (C.x - B.x) * D.y - B.x * C.y + C.x * B.y > 0)) { Concave = true; } else { Concave = false; } } //順時針 //兩種情況,在三角形abc內部和在AB上、AC下 if (IsClockWise()==true) { if (((B.y - A.y) * D.x - (B.x - A.x) * D.y - A.x * B.y + B.x * A.y > 0) && ((C.y - B.y) * D.x - (C.x - B.x) * D.y - B.x * C.y + C.x * B.y > 0) && ((C.y - A.y) * D.x - (C.x - A.x) * D.y - A.x * C.y + C.x * A.y < 0) ) { Concave = true; } else if (((B.y - A.y) * D.x - (B.x - A.x) * D.y - A.x * B.y + B.x * A.y < 0) && ((C.y - B.y) * D.x - (C.x - B.x) * D.y - B.x * C.y + C.x * B.y < 0 )) { Concave = true; } else { Concave = false; } } return Concave; } /// <summary> /// 判斷是否為順時針,以ac為底邊觀察 /// </summary> /// <param>cyt</param> /// <returns></returns> bool IsClockWise() { bool IsClock = false; Vector2 A = new Vector2(PosList[0].x, PosList[0].z); Vector2 B = new Vector2(PosList[1].x, PosList[1].z); Vector2 C = new Vector2(PosList[2].x, PosList[2].z); //如果B在A的右邊,C在AB上方為逆時針 if (B.x>=A.x) { if ((B.y - A.y) * C.x - (B.x - A.x) * C.y - A.x * B.y + B.x * A.y < 0) { IsClock = false; } else { IsClock = true; } } //如果B在A的左邊,C在AB下方為逆時針 if (B.x<A.x) { if ((B.y - A.y) * C.x - (B.x - A.x) * C.y - A.x * B.y + B.x * A.y < 0) { IsClock = false; } else { IsClock = true; } } return IsClock; } }
注釋還算比較清楚,各種情況都判斷了,也是想過用向量來判斷能否實現,發現不好使....
AreaLine = transform.Find("Area").GetComponent<LineRenderer>();這個跟
if (hit.collider.name == "PanelCube")這兩句需要自己在場景中設置一下,然后運行的時候把腳本的
IsAreaLineOn變量設為true就能畫了...按下delect可以刪除畫的四邊形,然后在點擊就可以繼續畫了。
實現思路的話首先將所有的點加入一個集合中(每次輸入都排除了重復的點),然后根據集合里的的前三個點判斷畫的三角形是順時針畫的三角形還是逆時針畫的三角形,
然后在根據是順時針和逆時針分別判斷第四個點的位置,判斷第四個點在三角形三條邊所在直線的位置判斷能否畫成四邊形。
計算面積的話是先判斷了順時針話還是逆時針畫,然后分別判斷畫的是凸四邊形還是凹四邊形,然后在計算面積。
哎。。。生無可戀。。。。
希望能幫到人,若需轉載請標明出處,謝謝....