在沙盒游戲里,能自由建造是很重要的特點,比如說風靡全球的《我的世界》,用一個個方塊就能搭建出規模宏大的世界。甚至有偏激的人說,沒有自由建造,就不是一個真正的沙盒游戲。的確,沙盒游戲的魅力有很大一部分是能自由構建一個游戲世界。看着自己一磚一瓦搭建起一個城堡世界會很有成就感的。
現如今的手游,大多數就是一個爭斗和炫耀的世界。不管是傳奇類的狂霸拽酷,還是連連看,消消樂等好友排名,就是消費國人的虛榮心。其實,游戲是第九藝術,要上升到藝術的角度。在游戲里,玩家需要一種情感的宣泄和寄托以及體驗。
說了這么多,還是回到正題。我是如何在自己編寫的獨立游戲《中世紀之路》里實現建築物的擺放呢?
在實現這個功能的時候,我首先考慮的不是代碼。而是考慮其他游戲是如何實現這樣功能的,有那些可以借鑒的地方。我首先想到了《魔獸爭霸》里建築物的擺放,可以自由的拖放。然后想到了《部落沖突》(簡稱COC)里的建築放置方式,在COC里建築是按格子擺放的,在一個平面上。
在我的游戲《中世紀之路》里,我既不想讓玩家一磚一瓦的搭建建築,又想讓玩家一下就把建築整體放置到地上了。我覺得像搭積木那樣,既可以節省玩家時間,又有建造的樂趣。選定了這種方式后,我就要考慮建築模塊的數據儲存方式了:我需要儲存單個模塊的三維空間里的坐標值(x,y,z的值),同時為了讓模型選擇,我還需要儲存模型的旋轉角度數。
然后,我還需要考慮如何用鼠標去執行這個操作。我構想的方案是:點擊背包里的物品后,一個模型就動態產生,然后跟隨鼠標在地面移動。然后按"E"鍵就放置到地面上鼠標所指的位置(E鍵放置,是我學《獸人必須死》得來的)。而組件的旋轉呢?我考慮的是讓鼠標滾輪(也就是中鍵)滾動時,就繞着Y軸旋轉。當然如果更近一步,可以做到繞三個軸都能自由旋轉。具體操作見下圖:
建築擺放動畫
使用鼠標放置篝火
通過上面的操作,我們可以看到代碼的實現效果很好,完美達到了我們的需求。不過想通了上面的操作原理后,還需要我們動腦筋來構思,如何用代碼來實現這些操作功能。這對新手來說可能過難了點,但對於有經驗的開發者就能比較快的找到近似的解決方案,然后加以改進。
我首先想到的是我曾經在手機上做過2D積木的搭建功能。把我們的操作動作拆解開來,無非就是三個步驟:
1.第一次按下手指或者鼠標,找到初始坐標,讓物體動態出現在坐標位置上。
2.然后判斷移動情況,讓物體跟隨鼠標或者手指的移動。
3.最后抬起手指或者鼠標,讓物體固定在最后的坐標位置,把坐標數據寫入到文本或者數據庫里。
想清楚了這三個步驟,我們就心里底了,我們只要實現了這三步操作代碼,基本上物體擺放功能就可以實現了。
在我實現第一個步驟的時候,我就遇到了個問題。我之前在寫搭積木游戲的時候是2D的,坐標很好獲取。但是在《中世紀之路》里,我可是要獲取的是鼠標在地面上的坐標點啊。我開始用的是兩行代碼:
Vector3 mousePosition= Input.mousePosition; //獲取鼠標所在的坐標 Vector3 mouseWorldPosition =Camera.main.ScreenToWorldPoint(mousePosition) ; //把鼠標的坐標變成3D游戲世界里的空間坐標
我以為我的思路是沒問題的,結果運行代碼一看。哈哈,建築物完全不是擺在地面上的啊,是在空中的啊。后來查了相關文檔才知道,這個鼠標位置是鼠標在屏幕這個立體面上的位置(你可以想象屏幕是一個透明的立體牆,這個牆是樹立在地面上的)。再說得專業點,是從主攝像機為起點,鼠標所指為終點的一個射線,與屏幕所在立體面相交的點。對於這個立體面的直觀感覺,大家可以看看在3D空間里2D UI界面所在的那個面。
所以,上面兩行代碼是不能夠解決問題的。不過這么一分析,我就接觸到了射線的概念。我轉念一想,如果我找到這個射線和地面相交的焦點不就行了嘛!我們的思路就變成了以下的偽代碼:
1.創建一個從主攝像機為起點,鼠標所指為終點的一個射線Ray。
2.找到Ray和地面Terrain的交點position。
3.把物體的坐標動態創建於position。
核心代碼如下:
if (Input.GetMouseButton(0)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if(Physics.Raycast(ray, out hit)) { if(hit.transform.name=="Terrain") { position = hit.point;//得到與地面碰撞點的坐標 } } }
解決這個核心難題后,移動的代碼就很好寫了。只要在手指或者鼠標移動的時候,動態更新物體的坐標為新的position就可以了。旋轉物體的代碼也可以寫到一起,動態更新物體的旋轉度就可以了。這段代碼很容易寫了,我就不貼出來了,新手可以鍛煉自我動手能力。老手早就不用看在眼里了。當然需要提醒的是這段代碼是需要在update函數里去運行的。
最后的固定物體坐標的代碼,也就演變成了把最后的position記錄於文本或者寫入數據庫了。這些代碼都不是有多難寫的。
最后回顧下,我們整個解決問題的思路:
構思操作步驟和方式->分解操作步驟->用代碼實現分解后的操作步驟->完善和修正代碼
如果大家問我要整個代碼,坦白的說:我覺得"授人於魚,不如授於漁"。大家能獲取正確解決問題的思路就夠了,然后記住核心代碼就可以了。做程序員到一個“手中無劍,心中有劍”的境界就夠了。當然新手還是多練練劍,比划比划下招式。
PS:游戲DEMO試玩群:198035671 Unity3d技術交流群:308185833 斗魚游戲開發直播地址:www.douyutv.com/unity3d