unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼)


原地址:http://dong2008hong.blog.163.com/blog/static/469688272013111554511948/

在unity里實現,其實很簡單,因為有兩個前提:
1.畫的方框始終是在屏幕空間進行的,而屏幕空間其實就是攝像機的視錐空間的投射了,不需要另外計算視錐。
2.unity攝像機內建的功能,可以方便的把屏幕坐標和世界坐標互換。
 
這樣最簡單的畫框選物體就簡化成了:
1.用GL在屏幕空間動態畫框。
2.把備選對象的transform.position用camera.WorldToScreenPoiont變換成屏幕坐標。
3.判斷這些position的點是否落在畫的方框之內,如果是就把對象切換到畫邊框的層,呈現選中效果。
 
一.屏幕動態畫框
這個過程相對簡單 就是在鼠標按下的時候記下鼠標位置,然后在鼠標移動時在當前鼠標位置和按下的位置之間畫一個方形就行了。
using UnityEngine;
using System.Collections;
 
public class DrawRectangle : MonoBehaviour {
    
public Color rectColor = Color.green;

private Vector3 start = Vector3.zero;//記下鼠標按下位置
    
private Material rectMat = null;//畫線的材質 不設定系統會用當前材質畫線 結果不可控
    
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;    
    
}
 
 
void Update () {
        
if(Input.GetMouseButtonDown(0)){
            
drawRectangle = true;//如果鼠標左鍵按下 設置開始畫線標志
            
start = Input.mousePosition;//記錄按下位置
        
}else if (Input.GetMouseButtonUp(0)){
            
drawRectangle = false;//如果鼠標左鍵放開 結束畫線
        

}
    
 
    
void OnPostRender() {//畫線這種操作推薦在OnPostRender()里進行 而不是直接放在Update,所以需要標志來開啟
        
if(drawRectangle){
            
Vector3 end = Input.mousePosition;//鼠標當前位置
            
GL.PushMatrix();//保存攝像機變換矩陣
       
if (! rectMat)
                
return;
          
rectMat.SetPass( 0 );
            
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();//恢復攝像機投影矩陣
        
}
    
}
}
效果如圖:
unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼) - dong2008hong - 踏浪的編程小屋
注意GL繪圖都是每幀進行的,所以不需要清除,直接不繪制方框就消失了。 
 
二.判斷物體是否選中
有了方框,要判斷物體是否在方框內,按照經典的數學算法可以根據直線方程和點的坐標計算判斷點在線的左邊 右邊 還是線上
 
空間平面方程可表示為:
Ax+By+Cz=0
 
對於點(x1, y1, z1),有
若 Ax1+By1+Cz1 = 0,則點在平面上;
若 Ax1+By1+Cz1 < 0,則點在平面的一側;
若 Ax1+By1+Cz1 > 0,則點在平面的另一側;
 
但是在這里,因為都是水平、垂直的線,我們並不需要考慮畫斜線、曲線框選物體........,所以只需要簡單的比大小就行了,土了那么一點但其實效率更高,畢竟作游戲不是算法比賽,怎么簡單高效怎么用。
如果:物體的屏幕position.x >方框左下角.x ;; 物體的屏幕position.y >方框左下角.y ;; 物體的屏幕position.x <方框右上角.x ;; 物體的屏幕position.y <方框右上角.y, 那么這個物體就是在框選范圍里了。至於z的判定,直接選擇攝像機的near和far距離就行了,小於near大於far都無視。因為屏幕空間是左下角為0,0,判斷是否在方框內要注意這個前提。
有Unity內置的世界坐標->屏幕坐標轉換,其實根本不需要什么復雜的裁剪算法,也不需要搞一大堆參考物體,往外發射一堆射線什么的,一次坐標轉換加6個判斷條件的一條if語句,就完成全部選擇邏輯了,簡單高效。
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.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.nearClipPlane || location.z > camera.farClipPlane)//z方向就用攝像機的設定值,看不見的也不需要選擇了
            
{
disselecting(obj);//上面的條件是篩選 不在選擇范圍內的對象,然后進行取消選擇操作,比如把物體放到default層,就不顯示輪廓線了
            
}
            
else
            
{
selecting(obj);//否則就進行選中操作,比如把物體放到畫輪廓線的層去
            
}
        
}
    
}
效果如圖
unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼) - dong2008hong - 踏浪的編程小屋
 
注意這種判定是以物體的position點為基准,如果物體中心不在幾何中心可能會有奇怪的效果,但對作游戲來說模型中心在幾何中心是基本要求,所以應該不是問題。當然也可以按照部分頂點相交來選中,但是那樣程序就會復雜很多,要先解構物體的mesh頂點,然后把頂點變換到屏幕坐標,然后判定是否在方框范圍內。實際上對大多數游戲來說划過中心點算作選中條件完全可以滿足,足夠有說服力了。
 
附上一個自己作的小Demo,綜合了之前的顯示邊緣光、輪廓效果,按鈕遮罩效果,加上框選物體的效果。 
鼠標左鍵按下畫框 右鍵雙擊地面移動選中的群體 按住滾輪移動鼠標拖動地圖,按住右鍵旋轉鼠標轉動視角,滾動滾輪縮放視角。
語音借用的是那啥3的人類語音,你懂的。
 
選擇:
unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼) - dong2008hong - 踏浪的編程小屋
 
雙擊右鍵移動目標,目標用一組粒子顯示,到達目標或者目標改變時自動消失
unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼) - dong2008hong - 踏浪的編程小屋
 
可以同時移動幾組,有點像1代的C;C......
unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼) - dong2008hong - 踏浪的編程小屋
 
點按鈕生成新的人物,選中按delete刪除人物
unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼) - dong2008hong - 踏浪的編程小屋
 
鼠標划過顯示邊緣光,單擊目標或者畫框選中目標顯示輪廓線
unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼) - dong2008hong - 踏浪的編程小屋
 
選中的目標會始終對着鏡頭,未選中的目標會隨機行動
unity3d結合輪廓顯示,實現完整的框選目標(附Demo代碼) - dong2008hong - 踏浪的編程小屋
 
 
Unity的便利性顯現無疑,基本無需復雜的幾何、線代知識,也沒有繁瑣的外圍代碼,用幾句判斷語句搞定框選。加上所見即所得的集成開發環境,寫到這樣程度的Demo實際大概也就花了幾個小時的人時,真正框選部分不超過1小時,有點像游戲開發的卡拉OK,優勢確實很明顯。

simpleRTS.unitypackage 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM