思路:
在uinity中既可以將屏幕坐標轉換為世界坐標,也可以將世界坐標轉換為屏幕坐標。這樣的話我們就可以通過判斷物體在世界坐標轉換為平幕坐標是否在鼠標框選的矩形區域坐標內,來判斷物體是否在框選范圍。
使用到的API:
GL:http://wiki.ceeger.com/script/unityengine/classes/gl/gl,用來實現在鼠標拖動時在屏幕中繪制出矩形區域。
Camera:http://wiki.ceeger.com/script/unityengine/classes/camera/camera.worldtoscreenpoint,將世界坐標轉換為屏幕坐標
將可選取的對象放置在 characters集合中,rectMat的材質為使用sprite下的default類型的shader的材質即可。
using UnityEngine; using System.Collections; using System.Collections.Generic; public class SelectObjects : MonoBehaviour { public List<GameObject> characters; public Color rectColor = Color.green; private Vector3 start = Vector3.zero;//記下鼠標按下位置 // private Material rectMat = null;//畫線的材質 不設定系統會用當前材質畫線 結果不可控 public Material rectMat = null;//這里使用Sprite下的defaultshader的材質即可 private bool drawRectangle = false;//是否開始畫線標志 // Use this for initialization void Start() { // rectMat = new Material("Shader \"Lines/Colored Blended\" {" + // // "SubShader { Pass { " + // // " Blend SrcAlpha OneMinusSrcAlpha " + // // " ZWrite Off Cull Off Fog { Mode Off } " + // // " BindChannels {" + // // " Bind \"vertex\", vertex Bind \"color\", color }" + // // "} } }");//生成畫線的材質 rectMat.hideFlags = HideFlags.HideAndDontSave; rectMat.shader.hideFlags = HideFlags.HideAndDontSave;//不顯示在hierarchy面板中的組合,不保存到場景並且卸載Resources.UnloadUnusedAssets不卸載的對象。 } void Update() { if (Input.GetMouseButtonDown(0)) { drawRectangle = true;//如果鼠標左鍵按下 設置開始畫線標志 start = Input.mousePosition;//記錄按下位置 } else if (Input.GetMouseButtonUp(0)) { drawRectangle = false;//如果鼠標左鍵放開 結束畫線 checkSelection(start,Input.mousePosition); } } void OnPostRender() {//畫線這種操作推薦在OnPostRender()里進行 而不是直接放在Update,所以需要標志來開啟 if (drawRectangle) { Vector3 end = Input.mousePosition;//鼠標當前位置 GL.PushMatrix();//保存攝像機變換矩陣,把投影視圖矩陣和模型視圖矩陣壓入堆棧保存 if (!rectMat) return; rectMat.SetPass(0);//為渲染激活給定的pass。 GL.LoadPixelMatrix();//設置用屏幕坐標繪圖 GL.Begin(GL.QUADS);//開始繪制矩形 GL.Color(new Color(rectColor.r, rectColor.g, rectColor.b, 0.1f));//設置顏色和透明度,方框內部透明 //繪制頂點 GL.Vertex3(start.x, start.y, 0); GL.Vertex3(end.x, start.y, 0); GL.Vertex3(end.x, end.y, 0); GL.Vertex3(start.x, end.y, 0); GL.End(); GL.Begin(GL.LINES);//開始繪制線 GL.Color(rectColor);//設置方框的邊框顏色 邊框不透明 GL.Vertex3(start.x, start.y, 0); GL.Vertex3(end.x, start.y, 0); GL.Vertex3(end.x, start.y, 0); GL.Vertex3(end.x, end.y, 0); GL.Vertex3(end.x, end.y, 0); GL.Vertex3(start.x, end.y, 0); GL.Vertex3(start.x, end.y, 0); GL.Vertex3(start.x, start.y, 0); GL.End(); GL.PopMatrix();//恢復攝像機投影矩陣 } } //檢測被選擇的物體 void checkSelection(Vector3 start, Vector3 end) { Vector3 p1 = Vector3.zero; Vector3 p2 = Vector3.zero; if (start.x > end.x) {//這些判斷是用來確保p1的xy坐標小於p2的xy坐標,因為畫的框不見得就是左下到右上這個方向的 p1.x = end.x; p2.x = start.x; } else { p1.x = start.x; p2.x = end.x; } if (start.y > end.y) { p1.y = end.y; p2.y = start.y; } else { p1.y = start.y; p2.y = end.y; } foreach (GameObject obj in characters) {//把可選擇的對象保存在characters數組里 Vector3 location = Camera.main.WorldToScreenPoint(obj.transform.position);//把對象的position轉換成屏幕坐標 if (location.x < p1.x || location.x > p2.x || location.y < p1.y || location.y > p2.y || location.z < Camera.main.nearClipPlane || location.z > Camera.main.farClipPlane)//z方向就用攝像機的設定值,看不見的也不需要選擇了 { //disselecting(obj);//上面的條件是篩選 不在選擇范圍內的對象,然后進行取消選擇操作,比如把物體放到default層,就不顯示輪廓線了 print("---"+obj.name); } else { //selecting(obj);//否則就進行選中操作,比如把物體放到畫輪廓線的層去 print("+++" + obj.name); } } } }
效果: