對於PC游戲,在鼠標大行其道的今天,如何由鼠標的位置判定其下的對象是什麼,是幾乎所有游戲都必須面對的問題。
以下提供幾種方法,僅供參考。
1,包圍框法。一般的,對游戲中的每個對象創建一個伴隨的包圍框,通過遍歷所有可見對象,判定鼠標坐標點是否落在某個包圍框的內部來獲取其選取的對象。
這種方法的優點是簡單,算法容易理解,當使用矩形包圍框,而對象數量又比較有限的時候,效率也是很好的。缺點是選取不夠精確,無法對對象的細節做選取。
在2D游戲中,包圍框一般是矩形,或者是若干個矩形的組合,而3D游戲使用包圍盒,或者包圍球或其組合等方式。無論具體方式如何,其算法實質都是一樣的。
2,枚舉法。效率最低的方法之一。
和1,包圍框法類似,它也需要遍歷所有可見對象,但是由於缺少包圍盒機制,只能檢測對象位於鼠標下的那個位置是否有有效象素,或者有效的alpha值,對3D對象而言,就是檢查鼠標點形成的選取射線是否穿越對象的某個面片。
這種方法可以實現很精確的選取,但是由於效率太低,所以很少直接使用,一般先使用方法1減少遍歷對象的數量之後,再使用這個方法達到精確的選取。
3,反饋法。這是一個很有效,也很快捷的方法,尤其在3D游戲中,有無可比擬的優越性。
反饋法的實現很簡單,首先要維護一個後台緩沖區,當繪制目標對象的時候,同時將對象的可見信息(一般是對象圖片的Alpha值,或者Z值) 寫入後台緩沖,然後檢測鼠標對應的緩沖區的位置的值是否有變化,如果變化了,表明剛才繪制的對象可以被鼠標選中。當緩沖使用了復雜一些的Z運算的時候,我們在繪制完成之後,就可以得到一個鼠標可以選取的對象列表,然後只要簡單的根據一定的原則從這個列表中提取需要的對象就可以了。這個機制在2D下,一般不維護額外的緩沖區而直接使用繪圖緩沖區。3D下,像OpenGL提供了內置的反饋方法,更方便了用戶的使用。實際也可以利用Z buffer,模板緩沖等實現類似的機制。
這種方法可以實現精確到象素級的選取,而幾乎不影響運行效率。缺點是需要對繪制部分的代碼有很高的控制權限。
4,直接映射法。這也是一個高效算法,可以達到O(1)的時間復雜度。常見於2D戰棋類游戲中。
在這類游戲中,場景是用一個二維表存儲的,表的每個項,保存著它上面的對象信息,我們可以通過一個簡單的算法,由當前的鼠標位置得到表的索引,然後直接讀取索引對應的項就完成了選取。
在固定視角的3D游戲甚至非固定視角的3D游戲中,也可以使用這種方法。這種方法的缺點是對象在場景中,只能是按二維表,或者多層二維表排布的。這種方法對內存空間的需求也比較大。棋牌類游戲比較適合使用這種方法。
由於每種方法都有其固有的優缺點,而對游戲而言,場景又千變萬化,復雜紛繁。為了能適應實際的需求,上面的方法可以組合使用,從而揚長避短,更好的達成需求。
其他一些復雜的選取,比如范圍選取(框選)等,也可以由以上幾種基本的方法演化而來。
以下是對前面反饋法的補充-反饋法的2D基本實現:
這裡直接使用後台繪制緩沖區作為選取緩沖區。
首先規定一種透明色,這種顏色不允許出現在對象和背景的任何非透明部分,例如規定
trans = RGB(0,0,0);
為透明色。
假設繪圖緩沖區為一個二維數組:
color buf[h][w];
當前鼠標對應的緩沖區坐標為
(x,y)
需要繪制的對象列表為一維數組
object array[count];
最終獲取選中的對象的棧
objstack;
偽代碼如下:color old = buf[y][x]; buf[y][x] = trans; for( int i = 0; i < count; i++ ) { draw(array[i]); if( buf[y][x] != trans ) { old = buf[y][x]; buf[y][x] = trans; objstack.push(array[i]); } } buf[y][x] = old;
完成這個步驟之後,objstack中保存的就是按照繪制順序排序的,鼠標下面所有對象的集合。你只要從中提取需要的對象就可以了。