Unity ECS實(shí)現(xiàn)RTS游戲中的游戲單位框選、集結(jié)和移動(dòng)控制

今天想給大家分享的主題是如何實(shí)現(xiàn)RTS類型游戲中的游戲單位角色控制
本文中會(huì)介紹如何運(yùn)用最新的ECS架構(gòu)來(lái)實(shí)現(xiàn)游戲單位控制
版權(quán)聲明
本文為“優(yōu)夢(mèng)創(chuàng)客”原創(chuàng)文章,您可以自由轉(zhuǎn)載,但必須加入完整的版權(quán)聲明
更多學(xué)習(xí)資源請(qǐng)加QQ:1517069595或WX:alice17173獲取(企業(yè)級(jí)性能優(yōu)化/熱更新/Shader特效/服務(wù)器/商業(yè)項(xiàng)目實(shí)戰(zhàn)/每周直播/一對(duì)一指導(dǎo))
點(diǎn)贊、關(guān)注、分享可免費(fèi)獲得配套學(xué)習(xí)資源
詳細(xì)內(nèi)容可參考文末完整視頻
效果演示

效果實(shí)現(xiàn)
選中多個(gè)游戲單位
上方代碼實(shí)現(xiàn)的功能是獲取被鼠標(biāo)框選的游戲單位,如果需要源代碼可以在文末添加愛(ài)麗絲老師的QQ或者微信號(hào)領(lǐng)取
代碼講解
獲取鼠標(biāo)框選方框的左下角和右上角
鼠標(biāo)在按下和彈起的過(guò)程中畫出的方框一般存在兩種情況
鼠標(biāo)的起始位置對(duì)應(yīng)左下角,終止位置對(duì)應(yīng)右上角
鼠標(biāo)按下時(shí)的起始位置是右上角,終止位置則是左下角
在計(jì)算方框的起始位置為右上角,終止位置為左下角時(shí)不能直接用起始位置當(dāng)方框的左下角,要把終止位置當(dāng)做方框左下角的位置
想要統(tǒng)一的獲得左下角和右上角的位置需要寫一些算法,如上方代碼所示,這個(gè)算法很簡(jiǎn)單,就是比較起始位置和終止位置,取較小值作為左下角點(diǎn),然后用兩者的較大值作為右上角點(diǎn)
查找被選中的游戲?qū)嶓w
ForEach方法的作用是遍歷每一個(gè)游戲單位,后面的Lambda表達(dá)式的功能是判斷游戲單位的位置坐標(biāo)是否在鼠標(biāo)框選范圍內(nèi),并打印鼠標(biāo)范圍框選范圍內(nèi)的游戲單位
繪制選區(qū)

在場(chǎng)景中創(chuàng)建一個(gè)空節(jié)點(diǎn),起名為SelectionArea(選擇區(qū)域),再創(chuàng)建一個(gè)空子節(jié)點(diǎn),取名為Sprite(精靈節(jié)點(diǎn))

為精靈節(jié)點(diǎn)添加精靈渲染器,并選擇一張綠色的圖片(把白色圖片設(shè)置成綠色也可以)
這里需要注意一下這張圖片的大小

我們?yōu)檫@張圖片設(shè)置了0.5的偏移值,這是什么意思呢?

也就是說(shuō)SelectionArea(選擇區(qū)域)節(jié)點(diǎn)和Sprite(精靈)節(jié)點(diǎn)的位置關(guān)系變成了上圖的樣子,上圖中紅色坐標(biāo)軸的原點(diǎn)就是SelectionArea(選擇區(qū)域)節(jié)點(diǎn)的位置,藍(lán)色坐標(biāo)軸的原點(diǎn)則代表Sprite(精靈)節(jié)點(diǎn)的位置,這樣偏移以后,將來(lái)拖拽、縮放選框時(shí)就會(huì)以紅顏色的中心點(diǎn)為起點(diǎn),會(huì)比較方便
在代碼中實(shí)現(xiàn)動(dòng)態(tài)繪制選框
注意
SelectionArea節(jié)點(diǎn)被添加到總控腳本ECS_RTSControls里了,所以在上方代碼中是通過(guò)訪問(wèn)總控腳本的單例來(lái)獲取SelectionArea節(jié)點(diǎn)
此腳本是在之前的UnitControlSystem腳本上增加了一個(gè)類和一些代碼
這些代碼會(huì)在鼠標(biāo)按下時(shí)激活SelectionArea節(jié)點(diǎn),并將SelectionArea節(jié)點(diǎn)的位置設(shè)置為鼠標(biāo)按下的位置
在鼠標(biāo)拖拽過(guò)程中會(huì)不斷獲取鼠標(biāo)當(dāng)前位置,并用鼠標(biāo)當(dāng)前為減去鼠標(biāo)初始位置,以得到當(dāng)前鼠標(biāo)框選的選區(qū)大小,然后賦值給selectionArea的localScale,這樣就實(shí)現(xiàn)了selectionArea選區(qū)隨著鼠標(biāo)拖拽自動(dòng)改變大小的效果
最后在鼠標(biāo)抬起時(shí)會(huì)將selectionArea的SetActive設(shè)置為false,隱藏鼠標(biāo)選區(qū),并為被選中的游戲?qū)ο筇砑覷nitSelected結(jié)構(gòu)體
效果演示

繪制角色腳下的圓圈
注意
用于繪制角色腳下圓圈的材質(zhì)被添加到總控腳本ECS_RTSControls里了,所以在上方代碼中是通過(guò)訪問(wèn)總控腳本的單例來(lái)獲取的圓圈的材質(zhì)的
圓圈的網(wǎng)格模型則是在ECS_RTSControls調(diào)用Unity動(dòng)畫的創(chuàng)建網(wǎng)格方法動(dòng)態(tài)創(chuàng)建的,所以也通過(guò)ECS_RTSControls的單例獲取
unitSelectedCircleMaterial(角色選中材質(zhì))

如果需要項(xiàng)目源碼或資源可以在文末通過(guò)添加愛(ài)麗絲老師的QQ獲取
效果演示

問(wèn)題
問(wèn)題1:無(wú)法取消選中
上面的代碼在選中了左邊的角色后,點(diǎn)擊空處或再次選中其他角色時(shí)并不會(huì)取消之前被選中角色的選中狀態(tài),為了要解決這個(gè)問(wèn)題我們添加了幾行代碼,讓UnitControlSystem腳本在鼠標(biāo)彈起時(shí)遍歷所有游戲?qū)ο?,刪除它們身上的UnitSelected組件
問(wèn)題2:無(wú)法通過(guò)點(diǎn)擊選中游戲?qū)ο?br>
上面代碼實(shí)現(xiàn)的選中效果必須要把游戲?qū)ο笳w框入才能選中,但在RTS游戲里,游戲玩家對(duì)于單個(gè)游戲?qū)ο笫强梢酝ㄟ^(guò)點(diǎn)擊選中的,而且框選也很麻煩,那怎樣才能讓它可以點(diǎn)中選中一個(gè)對(duì)象呢?方法很簡(jiǎn)單
就是擴(kuò)大最小選區(qū),選擇區(qū)域在點(diǎn)擊時(shí)自動(dòng)擴(kuò)大一圈,這樣就能確保點(diǎn)擊選中單個(gè)角色
注意:
這些代碼會(huì)在鼠標(biāo)抬起時(shí)執(zhí)行,它會(huì)檢測(cè)當(dāng)前鼠標(biāo)選區(qū)大小是否小于鼠標(biāo)選區(qū)最小值,如果小于則會(huì)根據(jù)鼠標(biāo)選區(qū)最小值減去當(dāng)前鼠標(biāo)選區(qū)大小的值放大鼠標(biāo)選區(qū)
這樣就保證了最小區(qū)域是足夠大的,可以通過(guò)點(diǎn)擊選中游戲單位
讓游戲?qū)ο笙蛑付ǖ姆较蛞苿?dòng)
注意:
這段代碼會(huì)在鼠標(biāo)右鍵抬起時(shí)執(zhí)行,它會(huì)為所有被選中的游戲?qū)ο笤O(shè)置移動(dòng)目標(biāo)點(diǎn),并使游戲?qū)ο笙蚰繕?biāo)點(diǎn)移動(dòng)
MoveTo是一個(gè)移動(dòng)腳本,MoveTo的position代表游戲角色移動(dòng)的目標(biāo)點(diǎn),move代表是否開(kāi)始移動(dòng),如果需要完整的ECS源碼資源,可以在添加愛(ài)麗絲老師領(lǐng)取
效果演示

實(shí)現(xiàn)游戲單位按陣列移動(dòng)
上面實(shí)現(xiàn)的效果還有一個(gè)問(wèn)題存在

可以看到上面的兩個(gè)角色變成一個(gè)了,因?yàn)槿绻麄兊囊苿?dòng)目標(biāo)點(diǎn)是相同的,所以這兩個(gè)角色在移動(dòng)時(shí)就會(huì)重疊起來(lái)
上面代碼中的movePositionList是游戲單位的移動(dòng)位置列表,當(dāng)鼠標(biāo)右鍵按下設(shè)置目標(biāo)點(diǎn)時(shí),這段代碼會(huì)遍歷所有被選中的游戲單位,并使用positionIndex(位置索引)取出移動(dòng)位置列表里計(jì)算好的移動(dòng)位置,讓這些游戲單位的移動(dòng)位置都不一樣
效果演示

可以看到被選中的游戲單位朝著同一個(gè)目標(biāo)點(diǎn)移動(dòng),并且位置都各不相同,這是因?yàn)槟繕?biāo)點(diǎn)在movePositionList經(jīng)過(guò)處理后,產(chǎn)生四個(gè)位置不同的坐標(biāo)點(diǎn),這樣賦值給游戲單位的就是位置不同的坐標(biāo)點(diǎn)了
這段代碼的問(wèn)題也很明顯:當(dāng)玩家選中四個(gè)以上的游戲單位進(jìn)行移動(dòng)時(shí),仍然會(huì)產(chǎn)生重疊現(xiàn)象
這是因?yàn)閙ovePositionList里的元素只有四個(gè),當(dāng)這段代碼遍歷完所有元素時(shí),就會(huì)從movePositionList的起始位置重新遍歷,所以多出來(lái)的游戲單位位置會(huì)與其他游戲單位重疊
解決這個(gè)問(wèn)題最簡(jiǎn)單的方法就是增加movePositionList里的元素個(gè)數(shù),讓元素個(gè)數(shù)始終大于游戲單位個(gè)數(shù),這個(gè)問(wèn)題自然迎刃而解
不過(guò)在一些游戲單位數(shù)量動(dòng)輒就是幾十、幾百上下的RTS游戲中,這種方法就不夠看了,需要用另一種方法
上面的GetPositionListAround會(huì)返回一個(gè)位置列表,位置列表里的所有元素都會(huì)以該方法的position參數(shù)為圓心,distance為半徑呈圓形排列(如下圖),這些元素的數(shù)量就是positionCount

ApplyRotationToVector的作用則是通過(guò)傳入的角度(angle)來(lái)構(gòu)造旋轉(zhuǎn)值,并使用這個(gè)旋轉(zhuǎn)值旋轉(zhuǎn)向量(vec),旋轉(zhuǎn)到了angle所代表的角度,這個(gè)方法背后的數(shù)學(xué)原理,本文就不去細(xì)講了,因?yàn)閮?nèi)容很多,如果想要學(xué)習(xí)這方面的知識(shí),可以在文末添加愛(ài)麗絲老師了解
對(duì)之前的代碼進(jìn)行修改,使用GetPositionListAround生產(chǎn)位置列表
效果演示

由于在上面的代碼中GetPositionListAround只指定了5個(gè)元素?cái)?shù)量,所以重疊的現(xiàn)象依然存在,代碼還需要繼續(xù)改進(jìn)
最上方的GetPositionListAround是對(duì)之前寫的GetPositionListAround的改進(jìn),可以讓小兵的位置分布成幾個(gè)圓環(huán)(如下圖所示)

新的GetPositionListAround方法的第一個(gè)參數(shù)startPosition仍然代表圓環(huán)中心點(diǎn)的位置,第二個(gè)參數(shù)ringDistance是半徑數(shù)組,用于存放所有圓環(huán)的半徑,第三個(gè)參數(shù)ringPositionCount代表每一個(gè)圓環(huán)上的位置點(diǎn)數(shù)量
效果演示

思考練習(xí)
實(shí)現(xiàn)不受位置點(diǎn)數(shù)量限制,為每一個(gè)游戲單位生成不與其他單位重疊的位置點(diǎn)(因?yàn)橹灰”鴶?shù)量超過(guò)上面代碼中設(shè)置的位置點(diǎn)總數(shù),還是會(huì)有游戲單位重疊現(xiàn)象)
兵種遇到障礙能否實(shí)現(xiàn)游戲單位躲避障礙行走到目標(biāo)點(diǎn)(A*,ECS實(shí)現(xiàn))
寫在最后
更多學(xué)習(xí)資源請(qǐng)加QQ:1517069595或WX:alice17173獲取(企業(yè)級(jí)性能優(yōu)化/熱更新/Shader特效/服務(wù)器/商業(yè)項(xiàng)目實(shí)戰(zhàn)/每周直播/一對(duì)一指導(dǎo))
點(diǎn)贊、關(guān)注、分享可免費(fèi)獲得配套學(xué)習(xí)資源
詳細(xì)內(nèi)容可觀看下方完整視頻
